Skip to content

Commit c631c3c

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

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+5259
-4503
lines changed

lib/loader/tests/build/default.wasm

454 Bytes
Binary file not shown.

lib/loader/tests/build/legacy.wasm

454 Bytes
Binary file not shown.

lib/loader/tests/index.js

+1-1
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

+189-107
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)