Skip to content

Commit bb4cea3

Browse files
committed
Add a raw static memory mechanism
1 parent 5c7e8d4 commit bb4cea3

File tree

8 files changed

+548
-9
lines changed

8 files changed

+548
-9
lines changed

src/builtins.ts

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -577,10 +577,12 @@ export namespace BuiltinNames {
577577
export const INFO = "~lib/diagnostics/INFO";
578578

579579
// std/memory.ts
580+
export const memory = "~lib/memory/memory";
580581
export const memory_size = "~lib/memory/memory.size";
581582
export const memory_grow = "~lib/memory/memory.grow";
582583
export const memory_copy = "~lib/memory/memory.copy";
583584
export const memory_fill = "~lib/memory/memory.fill";
585+
export const memory_data = "~lib/memory/memory.data";
584586

585587
// std/typedarray.ts
586588
export const Int8Array = "~lib/typedarray/Int8Array";
@@ -1983,10 +1985,10 @@ function builtin_load(ctx: BuiltinContext): ExpressionRef {
19831985
compiler.currentType = outType;
19841986
return module.unreachable();
19851987
}
1986-
if (immAlign > naturalAlign) {
1988+
if (immAlign < 1 || immAlign > naturalAlign) {
19871989
compiler.error(
19881990
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
1989-
operands[2].range, "Alignment", "0", naturalAlign.toString()
1991+
operands[2].range, "Alignment", "1", naturalAlign.toString()
19901992
);
19911993
compiler.currentType = outType;
19921994
return module.unreachable();
@@ -2068,10 +2070,10 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
20682070
compiler.currentType = Type.void;
20692071
return module.unreachable();
20702072
}
2071-
if (immAlign > naturalAlign) {
2073+
if (immAlign < 1 || immAlign > naturalAlign) {
20722074
compiler.error(
20732075
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
2074-
operands[3].range, "Alignment", "0", naturalAlign.toString()
2076+
operands[3].range, "Alignment", "1", naturalAlign.toString()
20752077
);
20762078
compiler.currentType = Type.void;
20772079
return module.unreachable();
@@ -2474,6 +2476,67 @@ builtins.set(BuiltinNames.unreachable, builtin_unreachable);
24742476

24752477
// === Memory =================================================================================
24762478

2479+
// memory(size[, align]) -> usize
2480+
function builtin_memory(ctx: BuiltinContext): ExpressionRef {
2481+
var compiler = ctx.compiler;
2482+
var module = compiler.module;
2483+
compiler.currentType = Type.i32;
2484+
if (
2485+
checkTypeAbsent(ctx) |
2486+
checkArgsOptional(ctx, 1, 2)
2487+
) return module.unreachable();
2488+
var operands = ctx.operands;
2489+
var numOperands = operands.length;
2490+
var usizeType = compiler.options.usizeType;
2491+
var arg0 = compiler.precomputeExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT);
2492+
compiler.currentType = usizeType;
2493+
if (getExpressionId(arg0) != ExpressionId.Const) {
2494+
compiler.error(
2495+
DiagnosticCode.Expression_must_be_a_compile_time_constant,
2496+
operands[0].range
2497+
);
2498+
return module.unreachable();
2499+
}
2500+
var size = getConstValueI32(arg0);
2501+
if (size < 1) {
2502+
compiler.error(
2503+
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
2504+
operands[0].range, "1", i32.MAX_VALUE.toString()
2505+
);
2506+
return module.unreachable();
2507+
}
2508+
var align = 16;
2509+
if (numOperands == 2) {
2510+
align = evaluateImmediateOffset(operands[1], compiler);
2511+
compiler.currentType = usizeType;
2512+
if (align < 0) {
2513+
return module.unreachable();
2514+
}
2515+
if (align < 1 || align > 16) {
2516+
compiler.error(
2517+
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
2518+
operands[1].range, "Alignment", "1", "16"
2519+
);
2520+
return module.unreachable();
2521+
}
2522+
if (!isPowerOf2(align)) {
2523+
compiler.error(
2524+
DiagnosticCode._0_must_be_a_power_of_two,
2525+
operands[1].range, "Alignment"
2526+
);
2527+
return module.unreachable();
2528+
}
2529+
}
2530+
// FIXME: what if recompiles happen? recompiles are bad.
2531+
var offset = compiler.addMemorySegment(new Uint8Array(size), align).offset;
2532+
if (usizeType == Type.usize32) {
2533+
assert(!i64_high(offset));
2534+
return module.i32(i64_low(offset));
2535+
}
2536+
return module.i64(i64_low(offset), i64_high(offset));
2537+
}
2538+
builtins.set(BuiltinNames.memory, builtin_memory);
2539+
24772540
// memory.size() -> i32
24782541
function builtin_memory_size(ctx: BuiltinContext): ExpressionRef {
24792542
var compiler = ctx.compiler;
@@ -3514,10 +3577,10 @@ function builtin_v128_load_splat(ctx: BuiltinContext): ExpressionRef {
35143577
}
35153578
compiler.currentType = Type.v128;
35163579
if (!type.is(TypeFlags.REFERENCE)) {
3517-
if (immAlign > naturalAlign) {
3580+
if (immAlign < 1 || immAlign > naturalAlign) {
35183581
compiler.error(
35193582
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
3520-
operands[2].range, "Alignment", "0", naturalAlign.toString()
3583+
operands[2].range, "Alignment", "1", naturalAlign.toString()
35213584
);
35223585
return module.unreachable();
35233586
}
@@ -3596,10 +3659,10 @@ function builtin_v128_load_ext(ctx: BuiltinContext): ExpressionRef {
35963659
}
35973660
compiler.currentType = Type.v128;
35983661
if (!type.is(TypeFlags.REFERENCE)) {
3599-
if (immAlign > naturalAlign) {
3662+
if (immAlign < 1 || immAlign > naturalAlign) {
36003663
compiler.error(
36013664
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
3602-
operands[2].range, "Alignment", "0", naturalAlign.toString()
3665+
operands[2].range, "Alignment", "1", naturalAlign.toString()
36033666
);
36043667
return module.unreachable();
36053668
}

src/compiler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ import {
187187
writeI64,
188188
writeF32,
189189
writeF64,
190-
makeMap
190+
makeMap,
191+
isPowerOf2
191192
} from "./util";
192193

193194
/** Compiler options. */
@@ -1683,6 +1684,7 @@ export class Compiler extends DiagnosticEmitter {
16831684

16841685
/** Adds a static memory segment with the specified data. */
16851686
addMemorySegment(buffer: Uint8Array, alignment: i32 = 16): MemorySegment {
1687+
assert(isPowerOf2(alignment));
16861688
var memoryOffset = i64_align(this.memoryOffset, alignment);
16871689
var segment = MemorySegment.create(buffer, memoryOffset);
16881690
this.memorySegments.push(segment);

std/assembly/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,8 @@ declare function bswap16<T = i8 | u8 | i16 | u16 | i32 | u32>(value: T): T;
11261126

11271127
// Standard library
11281128

1129+
/** Gets a pointer to a static chunk of memory of the given size. */
1130+
declare function memory(size: i32, align?: i32): usize;
11291131
/** Memory operations. */
11301132
declare namespace memory {
11311133
/** Whether the memory managed interface is implemented. */

std/assembly/memory.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { memcmp, memmove, memset } from "./util/memory";
22
import { E_NOTIMPLEMENTED } from "./util/error";
33

4+
/** Gets a pointer to a static chunk of memory of the given size. */
5+
// @ts-ignore: decorator
6+
@builtin
7+
export declare function memory(size: i32, align?: i32): usize;
8+
49
/** Memory manager interface. */
510
export namespace memory {
611

tests/compiler/memory.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"asc_flags": [
3+
"--runtime none"
4+
]
5+
}

tests/compiler/memory.optimized.wat

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
4+
(type $none_=>_i32 (func (result i32)))
5+
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
6+
(memory $0 1)
7+
(data (i32.const 1040) "\12\00\00\00\01\00\00\00\01\00\00\00\12\00\00\00m\00e\00m\00o\00r\00y\00.\00t\00s")
8+
(global $memory/ptr (mut i32) (i32.const 1088))
9+
(export "memory" (memory $0))
10+
(start $~start)
11+
(func $memory/test (result i32)
12+
(local $0 i32)
13+
i32.const 1024
14+
i32.const 1024
15+
i32.load
16+
local.tee $0
17+
i32.const 1
18+
i32.add
19+
i32.store
20+
local.get $0
21+
)
22+
(func $start:memory
23+
(local $0 i32)
24+
call $memory/test
25+
if
26+
i32.const 0
27+
i32.const 1056
28+
i32.const 10
29+
i32.const 1
30+
call $~lib/builtins/abort
31+
unreachable
32+
end
33+
call $memory/test
34+
i32.const 1
35+
i32.ne
36+
if
37+
i32.const 0
38+
i32.const 1056
39+
i32.const 11
40+
i32.const 1
41+
call $~lib/builtins/abort
42+
unreachable
43+
end
44+
call $memory/test
45+
i32.const 2
46+
i32.ne
47+
if
48+
i32.const 0
49+
i32.const 1056
50+
i32.const 12
51+
i32.const 1
52+
call $~lib/builtins/abort
53+
unreachable
54+
end
55+
global.get $memory/ptr
56+
i32.const 16
57+
i32.add
58+
i32.const 1104
59+
global.set $memory/ptr
60+
i32.const 1104
61+
i32.ne
62+
if
63+
i32.const 0
64+
i32.const 1056
65+
i32.const 17
66+
i32.const 1
67+
call $~lib/builtins/abort
68+
unreachable
69+
end
70+
global.get $memory/ptr
71+
i32.const 8
72+
i32.add
73+
i32.const 1112
74+
global.set $memory/ptr
75+
i32.const 1112
76+
i32.ne
77+
if
78+
i32.const 0
79+
i32.const 1056
80+
i32.const 18
81+
i32.const 1
82+
call $~lib/builtins/abort
83+
unreachable
84+
end
85+
global.get $memory/ptr
86+
i32.const 4
87+
i32.add
88+
i32.const 1116
89+
global.set $memory/ptr
90+
i32.const 1116
91+
i32.ne
92+
if
93+
i32.const 0
94+
i32.const 1056
95+
i32.const 19
96+
i32.const 1
97+
call $~lib/builtins/abort
98+
unreachable
99+
end
100+
global.get $memory/ptr
101+
i32.const 2
102+
i32.add
103+
i32.const 1118
104+
global.set $memory/ptr
105+
i32.const 1118
106+
i32.ne
107+
if
108+
i32.const 0
109+
i32.const 1056
110+
i32.const 20
111+
i32.const 1
112+
call $~lib/builtins/abort
113+
unreachable
114+
end
115+
global.get $memory/ptr
116+
i32.const 1
117+
i32.add
118+
i32.const 1119
119+
global.set $memory/ptr
120+
i32.const 1119
121+
i32.ne
122+
if
123+
i32.const 0
124+
i32.const 1056
125+
i32.const 21
126+
i32.const 1
127+
call $~lib/builtins/abort
128+
unreachable
129+
end
130+
i32.const 1120
131+
global.set $memory/ptr
132+
i32.const 1136
133+
global.set $memory/ptr
134+
i32.const 1144
135+
global.set $memory/ptr
136+
i32.const 1148
137+
global.set $memory/ptr
138+
global.get $memory/ptr
139+
i32.const 2
140+
i32.add
141+
i32.const 1150
142+
global.set $memory/ptr
143+
i32.const 1150
144+
i32.ne
145+
if
146+
i32.const 0
147+
i32.const 1056
148+
i32.const 34
149+
i32.const 1
150+
call $~lib/builtins/abort
151+
unreachable
152+
end
153+
global.get $memory/ptr
154+
i32.const 1
155+
i32.add
156+
i32.const 1151
157+
global.set $memory/ptr
158+
i32.const 1151
159+
i32.ne
160+
if
161+
i32.const 0
162+
i32.const 1056
163+
i32.const 35
164+
i32.const 1
165+
call $~lib/builtins/abort
166+
unreachable
167+
end
168+
)
169+
(func $~start
170+
call $start:memory
171+
)
172+
)

tests/compiler/memory.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Should be static
2+
3+
function test(): i32 {
4+
const ptr = memory(4);
5+
var value = load<i32>(ptr);
6+
store<i32>(ptr, value + 1);
7+
return value;
8+
}
9+
10+
assert(test() == 0);
11+
assert(test() == 1);
12+
assert(test() == 2);
13+
14+
// Should be properly aligned
15+
16+
var ptr = memory(16, 16);
17+
assert(ptr + 16 == (ptr = memory(1, 16)));
18+
assert(ptr + 8 == (ptr = memory(1, 8)));
19+
assert(ptr + 4 == (ptr = memory(1, 4)));
20+
assert(ptr + 2 == (ptr = memory(1, 2)));
21+
assert(ptr + 1 == (ptr = memory(1, 1)));
22+
23+
// Should be static and properly aligned per generic instance
24+
25+
function testGeneric<T>(): usize {
26+
const ptr = memory(1, 1 << alignof<T>());
27+
return ptr;
28+
}
29+
30+
ptr = memory(16, 16);
31+
assert(ptr + 16 == (ptr = testGeneric<v128>()));
32+
assert(ptr + 8 == (ptr = testGeneric<i64>()));
33+
assert(ptr + 4 == (ptr = testGeneric<i32>()));
34+
assert(ptr + 2 == (ptr = testGeneric<i16>()));
35+
assert(ptr + 1 == (ptr = testGeneric<i8>()));

0 commit comments

Comments
 (0)