Skip to content

Commit

Permalink
Add a raw static memory mechanism (AssemblyScript#1233)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodeIO committed Apr 22, 2020
1 parent f2a1916 commit c631c3c
Show file tree
Hide file tree
Showing 49 changed files with 5,259 additions and 4,503 deletions.
Binary file modified lib/loader/tests/build/default.wasm
Binary file not shown.
Binary file modified lib/loader/tests/build/legacy.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion lib/loader/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function test(file) {

// should export memory
assert(exports.memory instanceof WebAssembly.Memory);
assert(typeof exports.memory.copy === "function");
assert(typeof exports.memory.compare === "function");

// should be able to get an exported string
assert.strictEqual(exports.__getString(exports.COLOR), "red");
Expand Down
296 changes: 189 additions & 107 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ import {
Expression,
LiteralKind,
StringLiteralExpression,
CallExpression
CallExpression,
NodeKind,
LiteralExpression,
ArrayLiteralExpression
} from "./ast";

import {
Expand Down Expand Up @@ -581,6 +584,7 @@ export namespace BuiltinNames {
export const memory_grow = "~lib/memory/memory.grow";
export const memory_copy = "~lib/memory/memory.copy";
export const memory_fill = "~lib/memory/memory.fill";
export const memory_data = "~lib/memory/memory.data";

// std/typedarray.ts
export const Int8Array = "~lib/typedarray/Int8Array";
Expand Down Expand Up @@ -1970,37 +1974,21 @@ function builtin_load(ctx: BuiltinContext): ExpressionRef {
) ? contextualType : type;
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
var numOperands = operands.length;
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
if (immOffset < 0) {
compiler.currentType = outType;
return module.unreachable();
}
var immAlign: i32;
var naturalAlign = type.byteSize;
if (numOperands == 3) {
immAlign = evaluateImmediateOffset(operands[2], compiler);
if (immAlign < 0) {
var immOffset = 0;
var immAlign = type.byteSize;
if (numOperands >= 2) {
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
if (immOffset < 0) {
compiler.currentType = outType;
return module.unreachable();
}
if (immAlign > naturalAlign) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
operands[2].range, "Alignment", "0", naturalAlign.toString()
);
compiler.currentType = outType;
return module.unreachable();
}
if (!isPowerOf2(immAlign)) {
compiler.error(
DiagnosticCode._0_must_be_a_power_of_two,
operands[2].range, "Alignment"
);
compiler.currentType = outType;
return module.unreachable();
if (numOperands == 3) {
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
if (immAlign < 0) {
compiler.currentType = outType;
return module.unreachable();
}
}
} else {
immAlign = naturalAlign;
}
compiler.currentType = outType;
return module.load(
Expand All @@ -2024,6 +2012,7 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
checkArgsOptional(ctx, 2, 4)
) return module.unreachable();
var operands = ctx.operands;
var numOperands = operands.length;
var typeArguments = ctx.typeArguments;
var contextualType = ctx.contextualType;
var type = typeArguments![0];
Expand Down Expand Up @@ -2055,37 +2044,21 @@ function builtin_store(ctx: BuiltinContext): ExpressionRef {
);
inType = type;
}
var immOffset = operands.length >= 3 ? evaluateImmediateOffset(operands[2], compiler) : 0; // reports
if (immOffset < 0) {
compiler.currentType = Type.void;
return module.unreachable();
}
var immAlign: i32;
var naturalAlign = type.byteSize;
if (operands.length == 4) {
immAlign = evaluateImmediateOffset(operands[3], compiler);
if (immAlign < 0) {
compiler.currentType = Type.void;
return module.unreachable();
}
if (immAlign > naturalAlign) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
operands[3].range, "Alignment", "0", naturalAlign.toString()
);
var immOffset = 0;
var immAlign = type.byteSize;
if (numOperands >= 3) {
immOffset = evaluateImmediateOffset(operands[2], compiler); // reports
if (immOffset < 0) {
compiler.currentType = Type.void;
return module.unreachable();
}
if (!isPowerOf2(immAlign)) {
compiler.error(
DiagnosticCode._0_must_be_a_power_of_two,
operands[3].range, "Alignment"
);
compiler.currentType = Type.void;
return module.unreachable();
if (numOperands == 4) {
immAlign = evaluateImmediateAlign(operands[3], immAlign, compiler); // reports
if (immAlign < 0) {
compiler.currentType = Type.void;
return module.unreachable();
}
}
} else {
immAlign = naturalAlign;
}
compiler.currentType = Type.void;
return module.store(type.byteSize, arg0, arg1, inType.toNativeType(), immOffset, immAlign);
Expand Down Expand Up @@ -2555,6 +2528,122 @@ function builtin_memory_fill(ctx: BuiltinContext): ExpressionRef {
}
builtins.set(BuiltinNames.memory_fill, builtin_memory_fill);

// memory.data(size[, align]) -> usize
// memory.data<T>(values[, align]) -> usize
function builtin_memory_data(ctx: BuiltinContext): ExpressionRef {
var compiler = ctx.compiler;
var module = compiler.module;
compiler.currentType = Type.i32;
if (
checkTypeOptional(ctx) |
checkArgsOptional(ctx, 1, 2)
) return module.unreachable();
var typeArguments = ctx.typeArguments;
var operands = ctx.operands;
var numOperands = operands.length;
var usizeType = compiler.options.usizeType;
var offset: i64;
if (typeArguments !== null && typeArguments.length > 0) { // data<T>(values[, align])
let elementType = typeArguments[0];
if (!elementType.is(TypeFlags.VALUE)) {
compiler.error(
DiagnosticCode.Operation_0_cannot_be_applied_to_type_1,
ctx.reportNode.typeArgumentsRange, "memory.data", elementType.toString()
);
compiler.currentType = usizeType;
return module.unreachable();
}
let nativeElementType = elementType.toNativeType();
let valuesOperand = operands[0];
if (valuesOperand.kind != NodeKind.LITERAL || (<LiteralExpression>valuesOperand).literalKind != LiteralKind.ARRAY) {
compiler.error(
DiagnosticCode.Array_literal_expected,
operands[0].range
);
compiler.currentType = usizeType;
return module.unreachable();
}
let expressions = (<ArrayLiteralExpression>valuesOperand).elementExpressions;
let numElements = expressions.length;
let exprs = new Array<ExpressionRef>(numElements);
let isStatic = true;
for (let i = 0; i < numElements; ++i) {
let expression = expressions[i];
if (expression) {
let expr = module.precomputeExpression(
compiler.compileExpression(<Expression>expression, elementType,
Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN
)
);
if (getExpressionId(expr) == ExpressionId.Const) {
assert(getExpressionType(expr) == nativeElementType);
exprs[i] = expr;
} else {
isStatic = false;
}
} else {
exprs[i] = compiler.makeZero(elementType);
}
}
if (!isStatic) {
compiler.error(
DiagnosticCode.Expression_must_be_a_compile_time_constant,
valuesOperand.range
);
compiler.currentType = usizeType;
return module.unreachable();
}
let align = elementType.byteSize;
if (numOperands == 2) {
align = evaluateImmediateAlign(operands[1], align, compiler); // reports
if (align < 0) {
compiler.currentType = usizeType;
return module.unreachable();
}
}
let buf = new Uint8Array(numElements * elementType.byteSize);
assert(compiler.writeStaticBuffer(buf, 0, elementType, exprs) == buf.byteLength);
offset = compiler.addMemorySegment(buf, align).offset;
} else { // data(size[, align])
let arg0 = compiler.precomputeExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT);
if (getExpressionId(arg0) != ExpressionId.Const) {
compiler.error(
DiagnosticCode.Expression_must_be_a_compile_time_constant,
operands[0].range
);
compiler.currentType = usizeType;
return module.unreachable();
}
let size = getConstValueI32(arg0);
if (size < 1) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
operands[0].range, "1", i32.MAX_VALUE.toString()
);
compiler.currentType = usizeType;
return module.unreachable();
}
let align = 16;
if (numOperands == 2) {
align = evaluateImmediateAlign(operands[1], align, compiler); // reports
if (align < 0) {
compiler.currentType = usizeType;
return module.unreachable();
}
}
offset = compiler.addMemorySegment(new Uint8Array(size), align).offset;
}
// FIXME: what if recompiles happen? recompiles are bad.
compiler.currentType = usizeType;
if (usizeType == Type.usize32) {
assert(!i64_high(offset));
return module.i32(i64_low(offset));
} else {
return module.i64(i64_low(offset), i64_high(offset));
}
}
builtins.set(BuiltinNames.memory_data, builtin_memory_data);

// === Helpers ================================================================================

// changetype<T!>(value: *) -> T
Expand Down Expand Up @@ -3496,38 +3585,24 @@ function builtin_v128_load_splat(ctx: BuiltinContext): ExpressionRef {
var type = typeArguments[0];
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
var numOperands = operands.length;
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
if (immOffset < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
var immAlign: i32;
var naturalAlign = type.byteSize;
if (numOperands == 3) {
immAlign = evaluateImmediateOffset(operands[2], compiler);
if (immAlign < 0) {
var immOffset = 0;
var immAlign = type.byteSize;
if (numOperands >= 2) {
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
if (immOffset < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
} else {
immAlign = naturalAlign;
if (numOperands == 3) {
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
if (immAlign < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
}
}
compiler.currentType = Type.v128;
if (!type.is(TypeFlags.REFERENCE)) {
if (immAlign > naturalAlign) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
operands[2].range, "Alignment", "0", naturalAlign.toString()
);
return module.unreachable();
}
if (!isPowerOf2(immAlign)) {
compiler.error(
DiagnosticCode._0_must_be_a_power_of_two,
operands[2].range, "Alignment"
);
return module.unreachable();
}
switch (type.kind) {
case TypeKind.I8:
case TypeKind.U8: {
Expand Down Expand Up @@ -3578,38 +3653,24 @@ function builtin_v128_load_ext(ctx: BuiltinContext): ExpressionRef {
var type = typeArguments[0];
var arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT);
var numOperands = operands.length;
var immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports
if (immOffset < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
var immAlign: i32;
var naturalAlign = type.byteSize;
if (numOperands == 3) {
immAlign = evaluateImmediateOffset(operands[2], compiler);
if (immAlign < 0) {
var immOffset = 0;
var immAlign = type.byteSize;
if (numOperands >= 2) {
immOffset = evaluateImmediateOffset(operands[1], compiler); // reports
if (immOffset < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
} else {
immAlign = naturalAlign;
if (numOperands == 3) {
immAlign = evaluateImmediateAlign(operands[2], immAlign, compiler); // reports
if (immAlign < 0) {
compiler.currentType = Type.v128;
return module.unreachable();
}
}
}
compiler.currentType = Type.v128;
if (!type.is(TypeFlags.REFERENCE)) {
if (immAlign > naturalAlign) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
operands[2].range, "Alignment", "0", naturalAlign.toString()
);
return module.unreachable();
}
if (!isPowerOf2(immAlign)) {
compiler.error(
DiagnosticCode._0_must_be_a_power_of_two,
operands[2].range, "Alignment"
);
return module.unreachable();
}
switch (type.kind) {
case TypeKind.I8: return module.simd_load(SIMDLoadOp.LoadI8ToI16x8, arg0, immOffset, immAlign);
case TypeKind.U8: return module.simd_load(SIMDLoadOp.LoadU8ToU16x8, arg0, immOffset, immAlign);
Expand Down Expand Up @@ -8196,6 +8257,27 @@ function evaluateImmediateOffset(expression: Expression, compiler: Compiler): i3
return value;
}

/** Evaluates a compile-time constant immediate align argument. */
function evaluateImmediateAlign(expression: Expression, naturalAlign: i32, compiler: Compiler): i32 {
var align = evaluateImmediateOffset(expression, compiler);
if (align < 0) return align;
if (align < 1 || naturalAlign > 16) {
compiler.error(
DiagnosticCode._0_must_be_a_value_between_1_and_2_inclusive,
expression.range, "Alignment", "1", naturalAlign.toString()
);
return -1;
}
if (!isPowerOf2(align)) {
compiler.error(
DiagnosticCode._0_must_be_a_power_of_two,
expression.range, "Alignment"
);
return -1;
}
return align;
}

/** Checks that the specified feature is enabled. */
function checkFeatureEnabled(ctx: BuiltinContext, feature: Feature): i32 {
var compiler = ctx.compiler;
Expand Down
Loading

0 comments on commit c631c3c

Please sign in to comment.