diff --git a/.gitignore b/.gitignore index 79f88211..7e041025 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ docs/ node_modules/ out/ raw/ +.history diff --git a/src/builtins.ts b/src/builtins.ts index 3b4ecf25..b193ab16 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -40,7 +40,8 @@ import { getExpressionType, getConstValueI64High, getConstValueI64Low, - getConstValueI32 + getConstValueI32, + AtomicRMWOp } from "./module"; import { @@ -51,7 +52,8 @@ import { OperatorKind, FlowFlags, Global, - DecoratorFlags + DecoratorFlags, + Element } from "./program"; import { @@ -1676,6 +1678,416 @@ export function compileCall( compiler.currentType = Type.void; return module.createStore(typeArguments[0].byteSize, arg0, arg1, type.toNativeType(), offset); } + case "Atomic.load": { // Atomic.load(offset: usize, constantOffset?: usize) -> * + if (operands.length < 1 || operands.length > 2) { + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + } + if (operands.length < 1) { + compiler.error( + DiagnosticCode.Expected_at_least_0_arguments_but_got_1, + reportNode.range, "1", operands.length.toString(10) + ); + } else { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "2", operands.length.toString(10) + ); + } + return module.createUnreachable(); + } + if (!(typeArguments && typeArguments.length == 1)) { + if (typeArguments && typeArguments.length) compiler.currentType = typeArguments[0]; + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + let offset = operands.length == 2 ? evaluateConstantOffset(compiler, operands[1]) : 0; // reports + if (offset < 0) { // reported in evaluateConstantOffset + return module.createUnreachable(); + } + compiler.currentType = typeArguments[0]; + return module.createAtomicLoad( + typeArguments[0].byteSize, + arg0, + typeArguments[0].is(TypeFlags.INTEGER) && + contextualType.is(TypeFlags.INTEGER) && + contextualType.size > typeArguments[0].size + ? (compiler.currentType = contextualType).toNativeType() + : (compiler.currentType = typeArguments[0]).toNativeType(), + offset + ); + } + case "Atomic.store": { // Atomic.store(offset: usize, value: *, constantOffset?: usize) -> void + compiler.currentType = Type.void; + if (operands.length < 2 || operands.length > 3) { + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + } + if (operands.length < 2) { + compiler.error( + DiagnosticCode.Expected_at_least_0_arguments_but_got_1, + reportNode.range, "2", operands.length.toString(10) + ); + } else { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "3", operands.length.toString(10) + ); + } + return module.createUnreachable(); + } + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg1 = compiler.compileExpression( + operands[1], + typeArguments[0], + typeArguments[0].is(TypeFlags.INTEGER) + ? ConversionKind.NONE // no need to convert to small int (but now might result in a float) + : ConversionKind.IMPLICIT, + WrapMode.NONE + ); + let type: Type; + if ( + typeArguments[0].is(TypeFlags.INTEGER) && + ( + !compiler.currentType.is(TypeFlags.INTEGER) || // float to int + compiler.currentType.size < typeArguments[0].size // int to larger int (clear garbage bits) + ) + ) { + arg1 = compiler.convertExpression( + arg1, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[1] + ); + type = typeArguments[0]; + } else { + type = compiler.currentType; + } + let offset = operands.length == 3 ? evaluateConstantOffset(compiler, operands[2]) : 0; // reports + if (offset < 0) { // reported in evaluateConstantOffset + return module.createUnreachable(); + } + compiler.currentType = Type.void; + return module.createAtomicStore(typeArguments[0].byteSize, arg0, arg1, type.toNativeType(), offset); + } + case "Atomic.add": // add(ptr: usize, value: T, constantOffset?: usize): T; + case "Atomic.sub": // sub(ptr: usize, value: T, constantOffset?: usize): T; + case "Atomic.and": // and(ptr: usize, value: T, constantOffset?: usize): T; + case "Atomic.or": // or(ptr: usize, value: T, constantOffset?: usize): T; + case "Atomic.xor": // xor(ptr: usize, value: T, constantOffset?: usize): T; + case "Atomic.xchg": // xchg(ptr: usize, value: T, constantOffset?: usize): T; + { + if (operands.length < 2 || operands.length > 3) { + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + } + if (operands.length < 2) { + compiler.error( + DiagnosticCode.Expected_at_least_0_arguments_but_got_1, + reportNode.range, "2", operands.length.toString(10) + ); + } else { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "3", operands.length.toString(10) + ); + } + return module.createUnreachable(); + } + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg1 = compiler.compileExpression( + operands[1], + typeArguments[0], + typeArguments[0].is(TypeFlags.INTEGER) + ? ConversionKind.NONE // no need to convert to small int (but now might result in a float) + : ConversionKind.IMPLICIT, + WrapMode.NONE + ); + + let type: Type; + if ( + typeArguments[0].is(TypeFlags.INTEGER) && + ( + !compiler.currentType.is(TypeFlags.INTEGER) || // float to int + compiler.currentType.size < typeArguments[0].size // int to larger int (clear garbage bits) + ) + ) { + arg1 = compiler.convertExpression( + arg1, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[1] + ); + type = typeArguments[0]; + } else { + type = compiler.currentType; + } + + let offset = operands.length == 3 ? evaluateConstantOffset(compiler, operands[2]) : 0; // reports + if (offset < 0) { // reported in evaluateConstantOffset + return module.createUnreachable(); + } + let RMWOp: AtomicRMWOp | null = null; + switch (prototype.internalName) { + case "Atomic.add": { RMWOp = AtomicRMWOp.Add; break; } + case "Atomic.sub": { RMWOp = AtomicRMWOp.Sub; break; } + case "Atomic.and": { RMWOp = AtomicRMWOp.And; break; } + case "Atomic.or": { RMWOp = AtomicRMWOp.Or; break; } + case "Atomic.xor": { RMWOp = AtomicRMWOp.Xor; break; } + case "Atomic.xchg": { RMWOp = AtomicRMWOp.Xchg; break; } + } + compiler.currentType = typeArguments[0]; + if (RMWOp !== null) { + return module.createAtomicRMW( + RMWOp, typeArguments[0].byteSize, offset, arg0, arg1, type.toNativeType() + ); + } else { + compiler.error( + DiagnosticCode.Operation_not_supported, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + return module.createUnreachable(); + } + } + case "Atomic.cmpxchg": { // cmpxchg(ptr: usize, expected:T, replacement: T, constantOffset?: usize): T; + if (operands.length < 3 || operands.length > 4) { + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + } + if (operands.length < 3) { + compiler.error( + DiagnosticCode.Expected_at_least_0_arguments_but_got_1, + reportNode.range, "2", operands.length.toString(10) + ); + } else { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "3", operands.length.toString(10) + ); + } + return module.createUnreachable(); + } + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg1 = compiler.compileExpression( + operands[1], + typeArguments[0], + typeArguments[0].is(TypeFlags.INTEGER) + ? ConversionKind.NONE // no need to convert to small int (but now might result in a float) + : ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg2 = compiler.compileExpression( + operands[2], + typeArguments[0], + typeArguments[0].is(TypeFlags.INTEGER) + ? ConversionKind.NONE // no need to convert to small int (but now might result in a float) + : ConversionKind.IMPLICIT, + WrapMode.NONE + ); + + let type: Type; + if ( + typeArguments[0].is(TypeFlags.INTEGER) && + ( + !compiler.currentType.is(TypeFlags.INTEGER) || // float to int + compiler.currentType.size < typeArguments[0].size // int to larger int (clear garbage bits) + ) + ) { + arg1 = compiler.convertExpression( + arg1, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[1] + ); + arg2 = compiler.convertExpression( + arg2, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[2] + ); + type = typeArguments[0]; + } else { + type = compiler.currentType; + } + + let offset = operands.length == 4 ? evaluateConstantOffset(compiler, operands[3]) : 0; // reports + if (offset < 0) { // reported in evaluateConstantOffset + return module.createUnreachable(); + } + compiler.currentType = typeArguments[0]; + return module.createAtomicCmpxchg( + typeArguments[0].byteSize, offset, arg0, arg1, arg2, type.toNativeType() + ); + } + case "Atomic.wait": { // wait(ptr: usize, expected:T, timeout: i64): i32; + let hasError = typeArguments == null; + if (operands.length != 3) { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "3", operands.length.toString(10) + ); + hasError = true; + } + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + hasError = true; + } + + if (!typeArguments || hasError) { + return module.createUnreachable(); + } + + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg1 = compiler.compileExpression( + operands[1], + typeArguments[0], + typeArguments[0].is(TypeFlags.INTEGER) + ? ConversionKind.NONE // no need to convert to small int (but now might result in a float) + : ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg2 = compiler.compileExpression( + operands[2], + Type.i64, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + + let type: Type = typeArguments[0]; + if ( + typeArguments[0].is(TypeFlags.INTEGER) && + ( + !compiler.currentType.is(TypeFlags.INTEGER) || // float to int + compiler.currentType.size < typeArguments[0].size // int to larger int (clear garbage bits) + ) + ) { + arg1 = compiler.convertExpression( + arg1, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[1] + ); + arg2 = compiler.convertExpression( + arg2, + compiler.currentType, typeArguments[0], + ConversionKind.IMPLICIT, + WrapMode.NONE, // still clears garbage bits + operands[2] + ); + } + + return module.createAtomicWait( + arg0, arg1, arg2, type.toNativeType() + ); + } + case "Atomic.notify": { // notify(ptr: usize, count: u32): u32; + let hasError = typeArguments == null; + if (operands.length != 2) { + compiler.error( + DiagnosticCode.Expected_0_arguments_but_got_1, + reportNode.range, "2", operands.length.toString(10) + ); + hasError = true; + } + if (!(typeArguments && typeArguments.length == 1)) { + compiler.error( + DiagnosticCode.Expected_0_type_arguments_but_got_1, + reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0" + ); + hasError = true; + } + + if (!typeArguments || hasError) { + return module.createUnreachable(); + } + + arg0 = compiler.compileExpression( + operands[0], + compiler.options.usizeType, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + arg1 = compiler.compileExpression( + operands[1], + Type.i32, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + + return module.createAtomicWake( + arg0, arg1 + ); + } case "sizeof": { // sizeof() -> usize compiler.currentType = compiler.options.usizeType; if (operands.length != 0) { @@ -2851,6 +3263,83 @@ function deferASMCall( case "i64.store": return deferASM("store", compiler, Type.i64, operands, Type.i64, reportNode); case "f32.store": return deferASM("store", compiler, Type.f32, operands, Type.f32, reportNode); case "f64.store": return deferASM("store", compiler, Type.f64, operands, Type.f64, reportNode); + + case "i32.atomic.load8_u": return deferASM("Atomic.load", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.load16_u": return deferASM("Atomic.load", compiler, Type.u16, operands, Type.u32, reportNode); + case "i32.atomic.load": return deferASM("Atomic.load", compiler, Type.i32, operands, Type.i32, reportNode); + case "i64.atomic.load8_u": return deferASM("Atomic.load", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.load16_u": return deferASM("Atomic.load", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.load32_u": return deferASM("Atomic.load", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.load": return deferASM("Atomic.load", compiler, Type.i64, operands, Type.i64, reportNode); + + case "i32.atomic.store8": return deferASM("Atomic.store", compiler, Type.i8, operands, Type.i32, reportNode); + case "i32.atomic.store16": return deferASM("Atomic.store", compiler, Type.i16, operands, Type.i32, reportNode); + case "i32.atomic.store": return deferASM("Atomic.store", compiler, Type.i32, operands, Type.i32, reportNode); + case "i64.atomic.store8": return deferASM("Atomic.store", compiler, Type.i8, operands, Type.i64, reportNode); + case "i64.atomic.store16": return deferASM("Atomic.store", compiler, Type.i16, operands, Type.i64, reportNode); + case "i64.atomic.store32": return deferASM("Atomic.store", compiler, Type.i32, operands, Type.i64, reportNode); + case "i64.atomic.store": return deferASM("Atomic.store", compiler, Type.i64, operands, Type.i64, reportNode); + + case "i32.atomic.rmw8_u.add": return deferASM("Atomic.add", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.add": return deferASM("Atomic.add", compiler, Type.u16, operands, Type.u32, reportNode); + case "i32.atomic.rmw.add": return deferASM("Atomic.add", compiler, Type.u32, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.add": return deferASM("Atomic.add", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.add": return deferASM("Atomic.add", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.add": return deferASM("Atomic.add", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.add": return deferASM("Atomic.add", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.sub": return deferASM("Atomic.sub", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.sub": return deferASM("Atomic.sub", compiler, Type.u16, operands, Type.u32, reportNode); + case "i32.atomic.rmw.sub": return deferASM("Atomic.sub", compiler, Type.u32, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.sub": return deferASM("Atomic.sub", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.sub": return deferASM("Atomic.sub", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.sub": return deferASM("Atomic.sub", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.sub": return deferASM("Atomic.sub", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.and": return deferASM("Atomic.and", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.and": return deferASM("Atomic.and", compiler, Type.u16, operands, Type.u32, reportNode); + case "i32.atomic.rmw.and": return deferASM("Atomic.and", compiler, Type.u32, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.and": return deferASM("Atomic.and", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.and": return deferASM("Atomic.and", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.and": return deferASM("Atomic.and", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.and": return deferASM("Atomic.and", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.or": return deferASM("Atomic.or", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.or": return deferASM("Atomic.or", compiler, Type.u16, operands, Type.u32, reportNode); + case "i32.atomic.rmw.or": return deferASM("Atomic.or", compiler, Type.u32, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.or": return deferASM("Atomic.or", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.or": return deferASM("Atomic.or", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.or": return deferASM("Atomic.or", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.or": return deferASM("Atomic.or", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.xor": return deferASM("Atomic.xor", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.xor": return deferASM("Atomic.xor", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw.xor": return deferASM("Atomic.xor", compiler, Type.u8, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.xor": return deferASM("Atomic.xor", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.xor": return deferASM("Atomic.xor", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.xor": return deferASM("Atomic.xor", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.xor": return deferASM("Atomic.xor", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.xchg": return deferASM("Atomic.xchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.xchg": return deferASM("Atomic.xchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw.xchg": return deferASM("Atomic.xchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.xchg": return deferASM("Atomic.xchg", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.xchg": return deferASM("Atomic.xchg", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.xchg": return deferASM("Atomic.xchg", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.xchg": return deferASM("Atomic.xchg", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.atomic.rmw8_u.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw16_u.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i32.atomic.rmw.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u8, operands, Type.u32, reportNode); + case "i64.atomic.rmw8_u.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u8, operands, Type.u64, reportNode); + case "i64.atomic.rmw16_u.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u16, operands, Type.u64, reportNode); + case "i64.atomic.rmw32_u.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u32, operands, Type.u64, reportNode); + case "i64.atomic.rmw.cmpxchg": return deferASM("Atomic.cmpxchg", compiler, Type.u64, operands, Type.u64, reportNode); + + case "i32.wait": return deferASM("Atomic.wait", compiler, Type.i32, operands, Type.u32, reportNode); + case "i64.wait": return deferASM("Atomic.wait", compiler, Type.i64, operands, Type.i64, reportNode); + case "i32.notify": return deferASM("Atomic.notify", compiler, Type.i32, operands, Type.u32, reportNode); + case "i64.notify": return deferASM("Atomic.notify", compiler, Type.i64, operands, Type.i64, reportNode); } return 0; } @@ -2864,7 +3353,18 @@ function deferASM( valueType: Type, reportNode: Node ): ExpressionRef { - var prototype = assert(compiler.program.elementsLookup.get(name)); + // Built-in wasm functions can be namespaced like Atomic.{OPERATION} + // Split name by '.' to find member function prototype + var names = name.split("."); + var prototype: Element = assert(compiler.program.elementsLookup.get(names[0])); + if (names.length > 1) { + for (let i = 1; i < names.length; i++) { + const subName = names[i]; + if (prototype && prototype.members) { + prototype = assert(prototype.members.get(subName)); + } + } + } assert(prototype.kind == ElementKind.FUNCTION_PROTOTYPE); return compileCall(compiler, prototype, [ typeArgument ], operands, valueType, reportNode); } diff --git a/std/assembly/builtins.ts b/std/assembly/builtins.ts index ea3fe937..a40b5be9 100644 --- a/std/assembly/builtins.ts +++ b/std/assembly/builtins.ts @@ -43,6 +43,20 @@ @builtin export declare function call_indirect(target: void, ...args: void[]): T; @builtin export declare function instantiate(...args: void[]): T; +export namespace Atomic { + @builtin export declare function load(offset: usize, constantOffset?: usize): T; + @builtin export declare function store(offset: usize, value: void, constantOffset?: usize): void; + @builtin export declare function add(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function sub(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function and(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function or(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function xor(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function xchg(ptr: usize, value: T, constantOffset?: usize): T; + @builtin export declare function cmpxchg(ptr: usize, expected:T, replacement: T, constantOffset?: usize): T; + @builtin export declare function wait(ptr: usize, expected:T, timeout:i64): i32; + @builtin export declare function notify(ptr: usize, count: u32): u32; +} + @builtin export declare function i8(value: void): i8; export namespace i8 { export const MIN_VALUE: i8 = -128; @@ -73,6 +87,49 @@ export namespace i32 { @builtin export declare function store8(offset: usize, value: i32, constantOffset?: usize): void; @builtin export declare function store16(offset: usize, value: i32, constantOffset?: usize): void; @builtin export declare function store(offset: usize, value: i32, constantOffset?: usize): void; + + namespace atomic { + @builtin export declare function load8_s(offset: usize, constantOffset?: usize): i32; + @builtin export declare function load8_u(offset: usize, constantOffset?: usize): i32; + @builtin export declare function load16_s(offset: usize, constantOffset?: usize): i32; + @builtin export declare function load16_u(offset: usize, constantOffset?: usize): i32; + @builtin export declare function load(offset: usize, constantOffset?: usize): i32; + @builtin export declare function store8(offset: usize, value: i32, constantOffset?: usize): void; + @builtin export declare function store16(offset: usize, value: i32, constantOffset?: usize): void; + @builtin export declare function store(offset: usize, value: i32, constantOffset?: usize): void; + @builtin export declare function wait(ptr: usize, expected:i32, timeout:i64): i32; + @builtin export declare function notify(ptr: usize, count:u32): u32; + + namespace rmw8_u { + @builtin export declare function add(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function sub(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function and(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function or(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xor(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xchg(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function cmpxchg(offset: usize, expected:i32, replacement: i32, constantOffset?: usize): i32; + } + + namespace rmw16_u { + @builtin export declare function add(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function sub(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function and(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function or(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xor(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xchg(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function cmpxchg(offset: usize, expected:i32, replacement: i32, constantOffset?: usize): i32; + } + + namespace rmw { + @builtin export declare function add(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function sub(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function and(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function or(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xor(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function xchg(offset: usize, value: i32, constantOffset?: usize): i32 + @builtin export declare function cmpxchg(offset: usize, expected:i32, replacement: i32, constantOffset?: usize): i32; + } + } } @builtin export declare function i64(value: void): i64; @@ -96,6 +153,59 @@ export namespace i64 { @builtin export declare function store16(offset: usize, value: i64, constantOffset?: usize): void; @builtin export declare function store32(offset: usize, value: i64, constantOffset?: usize): void; @builtin export declare function store(offset: usize, value: i64, constantOffset?: usize): void; + + namespace atomic { + @builtin export declare function load8_s(offset: usize, constantOffset?: usize): i64; + @builtin export declare function load8_u(offset: usize, constantOffset?: usize): i64; + @builtin export declare function load16_s(offset: usize, constantOffset?: usize): i64; + @builtin export declare function load16_u(offset: usize, constantOffset?: usize): i64; + @builtin export declare function load(offset: usize, constantOffset?: usize): i64; + @builtin export declare function store8(offset: usize, value: i64, constantOffset?: usize): void; + @builtin export declare function store16(offset: usize, value: i64, constantOffset?: usize): void; + @builtin export declare function store(offset: usize, value: i64, constantOffset?: usize): void; + @builtin export declare function wait(ptr: usize, expected:i64, timeout:i64): i32; + @builtin export declare function notify(ptr: usize, count:u32): u32; + + namespace rmw8_u { + @builtin export declare function add(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function sub(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function and(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function or(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xor(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xchg(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function cmpxchg(offset: usize, expected:i64, replacement: i64, constantOffset?: usize): i64; + } + + namespace rmw16_u { + @builtin export declare function add(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function sub(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function and(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function or(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xor(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xchg(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function cmpxchg(offset: usize, expected:i64, replacement: i64, constantOffset?: usize): i64; + } + + namespace rmw32_u { + @builtin export declare function add(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function sub(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function and(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function or(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xor(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xchg(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function cmpxchg(offset: usize, expected:i64, replacement: i64, constantOffset?: usize): i64; + } + + namespace rmw { + @builtin export declare function add(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function sub(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function and(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function or(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xor(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function xchg(offset: usize, value: i64, constantOffset?: usize): i64 + @builtin export declare function cmpxchg(offset: usize, expected:i64, replacement: i64, constantOffset?: usize): i64; + } + } } @builtin export declare function isize(value: void): isize;