Skip to content

Commit c631c3c

Browse files
authored
Add a raw static memory mechanism (AssemblyScript#1233)
1 parent f2a1916 commit c631c3c

49 files changed

Lines changed: 5259 additions & 4503 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
454 Bytes
Binary file not shown.

lib/loader/tests/build/legacy.wasm

454 Bytes
Binary file not shown.

lib/loader/tests/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function test(file) {
1919

2020
// should export memory
2121
assert(exports.memory instanceof WebAssembly.Memory);
22-
assert(typeof exports.memory.copy === "function");
22+
assert(typeof exports.memory.compare === "function");
2323

2424
// should be able to get an exported string
2525
assert.strictEqual(exports.__getString(exports.COLOR), "red");

src/builtins.ts

Lines changed: 189 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ import {
3838
Expression,
3939
LiteralKind,
4040
StringLiteralExpression,
41-
CallExpression
41+
CallExpression,
42+
NodeKind,
43+
LiteralExpression,
44+
ArrayLiteralExpression
4245
} from "./ast";
4346

4447
import {
@@ -581,6 +584,7 @@ export namespace BuiltinNames {
581584
export const memory_grow = "~lib/memory/memory.grow";
582585
export const memory_copy = "~lib/memory/memory.copy";
583586
export const memory_fill = "~lib/memory/memory.fill";
587+
export const memory_data = "~lib/memory/memory.data";
584588

585589
// std/typedarray.ts
586590
export const Int8Array = "~lib/typedarray/Int8Array";
@@ -1970,37 +1974,21 @@ function builtin_load(ctx: BuiltinContext): ExpressionRef {
19701974
) ? contextualType : type;
19711975
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
19721976
var numOperands = operands.length;
1973-
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
1974-
if (immOffset < 0) {
1975-
compiler.currentType = outType;
1976-
return module.unreachable();
1977-
}
1978-
var immAlign: i32;
1979-
var naturalAlign = type.byteSize;
1980-
if (numOperands == 3) {
1981-
immAlign = evaluateImmediateOffset(operands[2], compiler);
1982-
if (immAlign < 0) {
1977+
var immOffset = 0;
1978+
var immAlign = type.byteSize;
1979+
if (numOperands >= 2) {
1980+
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
1981+
if (immOffset < 0) {
19831982
compiler.currentType = outType;
19841983
return module.unreachable();
19851984
}
1986-
if (immAlign > naturalAlign) {
1987-
compiler.error(
1988-
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
1989-
operands[2].range, "Alignment", "0", naturalAlign.toString()
1990-
);
1991-
compiler.currentType = outType;
1992-
return module.unreachable();
1993-
}
1994-
if (!isPowerOf2(immAlign)) {
1995-
compiler.error(
1996-
DiagnosticCode._0_must_be_a_power_of_two,
1997-
operands[2].range, "Alignment"
1998-
);
1999-
compiler.currentType = outType;
2000-
return module.unreachable();
1985+
if (numOperands == 3) {
1986+
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
1987+
if (immAlign < 0) {
1988+
compiler.currentType = outType;
1989+
return module.unreachable();
1990+
}
20011991
}
2002-
} else {
2003-
immAlign = naturalAlign;
20041992
}
20051993
compiler.currentType = outType;
20061994
return module.load(
@@ -2024,6 +2012,7 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
20242012
checkArgsOptional(ctx, 2, 4)
20252013
) return module.unreachable();
20262014
var operands = ctx.operands;
2015+
var numOperands = operands.length;
20272016
var typeArguments = ctx.typeArguments;
20282017
var contextualType = ctx.contextualType;
20292018
var type = typeArguments![0];
@@ -2055,37 +2044,21 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
20552044
);
20562045
inType = type;
20572046
}
2058-
var immOffset = operands.length >= 3 ? evaluateImmediateOffset(operands[2], compiler) : 0; // reports
2059-
if (immOffset < 0) {
2060-
compiler.currentType = Type.void;
2061-
return module.unreachable();
2062-
}
2063-
var immAlign: i32;
2064-
var naturalAlign = type.byteSize;
2065-
if (operands.length == 4) {
2066-
immAlign = evaluateImmediateOffset(operands[3], compiler);
2067-
if (immAlign < 0) {
2068-
compiler.currentType = Type.void;
2069-
return module.unreachable();
2070-
}
2071-
if (immAlign > naturalAlign) {
2072-
compiler.error(
2073-
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
2074-
operands[3].range, "Alignment", "0", naturalAlign.toString()
2075-
);
2047+
var immOffset = 0;
2048+
var immAlign = type.byteSize;
2049+
if (numOperands >= 3) {
2050+
immOffset = evaluateImmediateOffset(operands[2], compiler); // reports
2051+
if (immOffset < 0) {
20762052
compiler.currentType = Type.void;
20772053
return module.unreachable();
20782054
}
2079-
if (!isPowerOf2(immAlign)) {
2080-
compiler.error(
2081-
DiagnosticCode._0_must_be_a_power_of_two,
2082-
operands[3].range, "Alignment"
2083-
);
2084-
compiler.currentType = Type.void;
2085-
return module.unreachable();
2055+
if (numOperands == 4) {
2056+
immAlign = evaluateImmediateAlign(operands[3], immAlign, compiler); // reports
2057+
if (immAlign < 0) {
2058+
compiler.currentType = Type.void;
2059+
return module.unreachable();
2060+
}
20862061
}
2087-
} else {
2088-
immAlign = naturalAlign;
20892062
}
20902063
compiler.currentType = Type.void;
20912064
return module.store(type.byteSize, arg0, arg1, inType.toNativeType(), immOffset, immAlign);
@@ -2555,6 +2528,122 @@ function builtin_memory_fill(ctx: BuiltinContext): ExpressionRef {
25552528
}
25562529
builtins.set(BuiltinNames.memory_fill, builtin_memory_fill);
25572530

2531+
// memory.data(size[, align]) -> usize
2532+
// memory.data<T>(values[, align]) -> usize
2533+
function builtin_memory_data(ctx: BuiltinContext): ExpressionRef {
2534+
var compiler = ctx.compiler;
2535+
var module = compiler.module;
2536+
compiler.currentType = Type.i32;
2537+
if (
2538+
checkTypeOptional(ctx) |
2539+
checkArgsOptional(ctx, 1, 2)
2540+
) return module.unreachable();
2541+
var typeArguments = ctx.typeArguments;
2542+
var operands = ctx.operands;
2543+
var numOperands = operands.length;
2544+
var usizeType = compiler.options.usizeType;
2545+
var offset: i64;
2546+
if (typeArguments !== null && typeArguments.length > 0) { // data<T>(values[, align])
2547+
let elementType = typeArguments[0];
2548+
if (!elementType.is(TypeFlags.VALUE)) {
2549+
compiler.error(
2550+
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
2551+
ctx.reportNode.typeArgumentsRange, "memory.data", elementType.toString()
2552+
);
2553+
compiler.currentType = usizeType;
2554+
return module.unreachable();
2555+
}
2556+
let nativeElementType = elementType.toNativeType();
2557+
let valuesOperand = operands[0];
2558+
if (valuesOperand.kind != NodeKind.LITERAL || (<LiteralExpression>valuesOperand).literalKind != LiteralKind.ARRAY) {
2559+
compiler.error(
2560+
DiagnosticCode.Array_literal_expected,
2561+
operands[0].range
2562+
);
2563+
compiler.currentType = usizeType;
2564+
return module.unreachable();
2565+
}
2566+
let expressions = (<ArrayLiteralExpression>valuesOperand).elementExpressions;
2567+
let numElements = expressions.length;
2568+
let exprs = new Array<ExpressionRef>(numElements);
2569+
let isStatic = true;
2570+
for (let i = 0; i < numElements; ++i) {
2571+
let expression = expressions[i];
2572+
if (expression) {
2573+
let expr = module.precomputeExpression(
2574+
compiler.compileExpression(<Expression>expression, elementType,
2575+
Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN
2576+
)
2577+
);
2578+
if (getExpressionId(expr) == ExpressionId.Const) {
2579+
assert(getExpressionType(expr) == nativeElementType);
2580+
exprs[i] = expr;
2581+
} else {
2582+
isStatic = false;
2583+
}
2584+
} else {
2585+
exprs[i] = compiler.makeZero(elementType);
2586+
}
2587+
}
2588+
if (!isStatic) {
2589+
compiler.error(
2590+
DiagnosticCode.Expression_must_be_a_compile_time_constant,
2591+
valuesOperand.range
2592+
);
2593+
compiler.currentType = usizeType;
2594+
return module.unreachable();
2595+
}
2596+
let align = elementType.byteSize;
2597+
if (numOperands == 2) {
2598+
align = evaluateImmediateAlign(operands[1], align, compiler); // reports
2599+
if (align < 0) {
2600+
compiler.currentType = usizeType;
2601+
return module.unreachable();
2602+
}
2603+
}
2604+
let buf = new Uint8Array(numElements * elementType.byteSize);
2605+
assert(compiler.writeStaticBuffer(buf, 0, elementType, exprs) == buf.byteLength);
2606+
offset = compiler.addMemorySegment(buf, align).offset;
2607+
} else { // data(size[, align])
2608+
let arg0 = compiler.precomputeExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT);
2609+
if (getExpressionId(arg0) != ExpressionId.Const) {
2610+
compiler.error(
2611+
DiagnosticCode.Expression_must_be_a_compile_time_constant,
2612+
operands[0].range
2613+
);
2614+
compiler.currentType = usizeType;
2615+
return module.unreachable();
2616+
}
2617+
let size = getConstValueI32(arg0);
2618+
if (size < 1) {
2619+
compiler.error(
2620+
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
2621+
operands[0].range, "1", i32.MAX_VALUE.toString()
2622+
);
2623+
compiler.currentType = usizeType;
2624+
return module.unreachable();
2625+
}
2626+
let align = 16;
2627+
if (numOperands == 2) {
2628+
align = evaluateImmediateAlign(operands[1], align, compiler); // reports
2629+
if (align < 0) {
2630+
compiler.currentType = usizeType;
2631+
return module.unreachable();
2632+
}
2633+
}
2634+
offset = compiler.addMemorySegment(new Uint8Array(size), align).offset;
2635+
}
2636+
// FIXME: what if recompiles happen? recompiles are bad.
2637+
compiler.currentType = usizeType;
2638+
if (usizeType == Type.usize32) {
2639+
assert(!i64_high(offset));
2640+
return module.i32(i64_low(offset));
2641+
} else {
2642+
return module.i64(i64_low(offset), i64_high(offset));
2643+
}
2644+
}
2645+
builtins.set(BuiltinNames.memory_data, builtin_memory_data);
2646+
25582647
// === Helpers ================================================================================
25592648

25602649
// changetype<T!>(value: *) -> T
@@ -3496,38 +3585,24 @@ function builtin_v128_load_splat(ctx: BuiltinContext): ExpressionRef {
34963585
var type = typeArguments[0];
34973586
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
34983587
var numOperands = operands.length;
3499-
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
3500-
if (immOffset < 0) {
3501-
compiler.currentType = Type.v128;
3502-
return module.unreachable();
3503-
}
3504-
var immAlign: i32;
3505-
var naturalAlign = type.byteSize;
3506-
if (numOperands == 3) {
3507-
immAlign = evaluateImmediateOffset(operands[2], compiler);
3508-
if (immAlign < 0) {
3588+
var immOffset = 0;
3589+
var immAlign = type.byteSize;
3590+
if (numOperands >= 2) {
3591+
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
3592+
if (immOffset < 0) {
35093593
compiler.currentType = Type.v128;
35103594
return module.unreachable();
35113595
}
3512-
} else {
3513-
immAlign = naturalAlign;
3596+
if (numOperands == 3) {
3597+
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
3598+
if (immAlign < 0) {
3599+
compiler.currentType = Type.v128;
3600+
return module.unreachable();
3601+
}
3602+
}
35143603
}
35153604
compiler.currentType = Type.v128;
35163605
if (!type.is(TypeFlags.REFERENCE)) {
3517-
if (immAlign > naturalAlign) {
3518-
compiler.error(
3519-
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
3520-
operands[2].range, "Alignment", "0", naturalAlign.toString()
3521-
);
3522-
return module.unreachable();
3523-
}
3524-
if (!isPowerOf2(immAlign)) {
3525-
compiler.error(
3526-
DiagnosticCode._0_must_be_a_power_of_two,
3527-
operands[2].range, "Alignment"
3528-
);
3529-
return module.unreachable();
3530-
}
35313606
switch (type.kind) {
35323607
case TypeKind.I8:
35333608
case TypeKind.U8: {
@@ -3578,38 +3653,24 @@ function builtin_v128_load_ext(ctx: BuiltinContext): ExpressionRef {
35783653
var type = typeArguments[0];
35793654
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
35803655
var numOperands = operands.length;
3581-
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
3582-
if (immOffset < 0) {
3583-
compiler.currentType = Type.v128;
3584-
return module.unreachable();
3585-
}
3586-
var immAlign: i32;
3587-
var naturalAlign = type.byteSize;
3588-
if (numOperands == 3) {
3589-
immAlign = evaluateImmediateOffset(operands[2], compiler);
3590-
if (immAlign < 0) {
3656+
var immOffset = 0;
3657+
var immAlign = type.byteSize;
3658+
if (numOperands >= 2) {
3659+
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
3660+
if (immOffset < 0) {
35913661
compiler.currentType = Type.v128;
35923662
return module.unreachable();
35933663
}
3594-
} else {
3595-
immAlign = naturalAlign;
3664+
if (numOperands == 3) {
3665+
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
3666+
if (immAlign < 0) {
3667+
compiler.currentType = Type.v128;
3668+
return module.unreachable();
3669+
}
3670+
}
35963671
}
35973672
compiler.currentType = Type.v128;
35983673
if (!type.is(TypeFlags.REFERENCE)) {
3599-
if (immAlign > naturalAlign) {
3600-
compiler.error(
3601-
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
3602-
operands[2].range, "Alignment", "0", naturalAlign.toString()
3603-
);
3604-
return module.unreachable();
3605-
}
3606-
if (!isPowerOf2(immAlign)) {
3607-
compiler.error(
3608-
DiagnosticCode._0_must_be_a_power_of_two,
3609-
operands[2].range, "Alignment"
3610-
);
3611-
return module.unreachable();
3612-
}
36133674
switch (type.kind) {
36143675
case TypeKind.I8: return module.simd_load(SIMDLoadOp.LoadI8ToI16x8, arg0, immOffset, immAlign);
36153676
case TypeKind.U8: return module.simd_load(SIMDLoadOp.LoadU8ToU16x8, arg0, immOffset, immAlign);
@@ -8196,6 +8257,27 @@ function evaluateImmediateOffset(expression: Expression, compiler: Compiler): i3
81968257
return value;
81978258
}
81988259

8260+
/** Evaluates a compile-time constant immediate align argument. */
8261+
function evaluateImmediateAlign(expression: Expression, naturalAlign: i32, compiler: Compiler): i32 {
8262+
var align = evaluateImmediateOffset(expression, compiler);
8263+
if (align < 0) return align;
8264+
if (align < 1 || naturalAlign > 16) {
8265+
compiler.error(
8266+
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
8267+
expression.range, "Alignment", "1", naturalAlign.toString()
8268+
);
8269+
return -1;
8270+
}
8271+
if (!isPowerOf2(align)) {
8272+
compiler.error(
8273+
DiagnosticCode._0_must_be_a_power_of_two,
8274+
expression.range, "Alignment"
8275+
);
8276+
return -1;
8277+
}
8278+
return align;
8279+
}
8280+
81998281
/** Checks that the specified feature is enabled. */
82008282
function checkFeatureEnabled(ctx: BuiltinContext, feature: Feature): i32 {
82018283
var compiler = ctx.compiler;

0 commit comments

Comments
 (0)