diff --git a/src/builtins.ts b/src/builtins.ts index 3a24a77c..6851c6d2 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -75,10 +75,6 @@ export function initialize(program: Program): void { // addFunction(program, "move_memory"); // addFunction(program, "set_memory"); - // imported - addFunction(program, "parseInt"); - addFunction(program, "parseFloat"); - // other addFunction(program, "changetype", true); addFunction(program, "assert"); @@ -192,66 +188,100 @@ export function compileGetConstant(compiler: Compiler, global: Global, reportNod } /** Compiles a call to a built-in function. */ -export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[] | null, operands: Expression[], reportNode: Node): ExpressionRef { +export function compileCall(compiler: Compiler, prototype: FunctionPrototype, typeArguments: Type[] | null, operands: Expression[], contextualType: Type, reportNode: Node): ExpressionRef { var module = compiler.module; var usizeType = select(Type.usize64, Type.usize32, compiler.options.target == Target.WASM64); var nativeUsizeType = select(NativeType.I64, NativeType.I32, compiler.options.target == Target.WASM64); var arg0: ExpressionRef, arg1: ExpressionRef, - arg2: ExpressionRef; + arg2: ExpressionRef, + ret: ExpressionRef; var tempLocal0: Local, tempLocal1: Local; - var ftype: FunctionTypeRef; + var type: Type, + ftype: FunctionTypeRef; + + // NOTE that some implementations below make use of the select expression where straight-forward. + // whether worth or not should probably be tested once it's known if/how embedders handle it. + // search: createSelect switch (prototype.internalName) { // math - case "isNaN": // isNaN(value: T) -> bool + case "isNaN": // isNaN(value: T) -> bool compiler.currentType = Type.bool; - // if (operands.length != 1) { - // compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); - // return module.createUnreachable(); - // } - // TODO: infer type argument if omitted - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + if (operands.length != 1) { + if (typeArguments && typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((typeArguments)[0].isAnyInteger) - return module.createI32(0); - if ((typeArguments)[0].isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); // reports - compiler.currentType = Type.bool; - if ((typeArguments)[0] == Type.f32) { + } + if (typeArguments) { + if (typeArguments.length != 1) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.F32: tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); - return module.createBinary(BinaryOp.NeF32, + ret = module.createBinary(BinaryOp.NeF32, module.createTeeLocal(tempLocal0.index, arg0), module.createGetLocal(tempLocal0.index, NativeType.F32) ); - } else { + break; + + case TypeKind.F64: tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f64); - return module.createBinary(BinaryOp.NeF64, + ret = module.createBinary(BinaryOp.NeF64, module.createTeeLocal(tempLocal0.index, arg0), module.createGetLocal(tempLocal0.index, NativeType.F64) ); - } - } - break; + break; - case "isFinite": // isFinite(value: T) -> bool + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + + default: // every other type is never NaN + ret = module.createI32(0); + break; + + } compiler.currentType = Type.bool; - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + return ret; + + case "isFinite": // isFinite(value: T) -> bool + compiler.currentType = Type.bool; + if (operands.length != 1) { + if (typeArguments && typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((typeArguments)[0].isAnyInteger) - return module.createI32(1); - if ((typeArguments)[0].isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); // reports - compiler.currentType = Type.bool; - if ((typeArguments)[0] == Type.f32) { + } + if (typeArguments) { + if (typeArguments.length != 1) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.F32: tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); - return module.createSelect( + ret = module.createSelect( module.createBinary(BinaryOp.NeF32, module.createUnary(UnaryOp.AbsF32, module.createTeeLocal(tempLocal0.index, arg0) @@ -264,9 +294,11 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createGetLocal(tempLocal0.index, NativeType.F32) ) ); - } else { + break; + + case TypeKind.F64: tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f64); - return module.createSelect( + ret = module.createSelect( module.createBinary(BinaryOp.NeF64, module.createUnary(UnaryOp.AbsF64, module.createTeeLocal(tempLocal0.index, arg0) @@ -279,542 +311,1556 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty module.createGetLocal(tempLocal0.index, NativeType.F64) ) ); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + + default: // every other type is always finite + ret = module.createI32(1); + break; + } + compiler.currentType = Type.bool; + return ret; + + case "clz": // clz(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); } - } - break; - - case "clz": // clz(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic - ? module.createUnary(UnaryOp.ClzI64, arg0) - : (typeArguments)[0].isSmallInteger - ? module.createBinary(BinaryOp.AndI32, - module.createUnary(UnaryOp.ClzI32, arg0), - module.createI32((typeArguments)[0].smallIntegerMask) - ) - : module.createUnary(UnaryOp.ClzI32, arg0); } - break; + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); - case "ctz": // ctz(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic - ? module.createUnary(UnaryOp.CtzI64, arg0) - : (typeArguments)[0].isSmallInteger - ? module.createBinary(BinaryOp.AndI32, - module.createUnary(UnaryOp.CtzI32, arg0), - module.createI32((typeArguments)[0].smallIntegerMask) - ) - : module.createUnary(UnaryOp.CtzI32, arg0); + switch (compiler.currentType.kind) { + + default: // any integer up to 32-bits incl. bool + ret = module.createUnary(UnaryOp.ClzI32, arg0); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.ISIZE: + ret = module.createUnary(select(UnaryOp.ClzI64, UnaryOp.ClzI32, compiler.options.target == Target.WASM64), arg0); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createUnary(UnaryOp.ClzI64, arg0); + break; + + case TypeKind.F32: + case TypeKind.F64: + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; } - break; + return ret; - case "popcnt": // popcnt(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + case "ctz": // ctz(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic - ? module.createUnary(UnaryOp.PopcntI64, arg0) - : (typeArguments)[0].isSmallInteger - ? module.createBinary(BinaryOp.AndI32, - module.createUnary(UnaryOp.PopcntI32, arg0), - module.createI32((typeArguments)[0].smallIntegerMask) - ) - : module.createUnary(UnaryOp.PopcntI32, arg0); } - break; + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); - case "rotl": // rotl(value: T, shift: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) + switch (compiler.currentType.kind) { + + default: // any integer up to 32-bits incl. bool + ret = module.createUnary(UnaryOp.CtzI32, arg0); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.ISIZE: + ret = module.createUnary(select(UnaryOp.CtzI64, UnaryOp.CtzI32, compiler.options.target == Target.WASM64), arg0); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createUnary(UnaryOp.CtzI64, arg0); + break; + + case TypeKind.F32: + case TypeKind.F64: + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "popcnt": // popcnt(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic - ? module.createBinary(BinaryOp.RotlI64, arg0, arg1) - : (typeArguments)[0].isSmallInteger - ? module.createBinary(BinaryOp.AndI32, + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + default: // any integer up to 32-bits incl. bool + ret = module.createUnary(UnaryOp.PopcntI32, arg0); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.ISIZE: + ret = module.createUnary(select(UnaryOp.PopcntI64, UnaryOp.PopcntI32, compiler.options.target == Target.WASM64), arg0); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createUnary(UnaryOp.PopcntI64, arg0); + break; + + case TypeKind.F32: + case TypeKind.F64: + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "rotl": // rotl(value: T, shift: T) -> T + if (operands.length != 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], compiler.currentType); + + switch (compiler.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + ret = module.createBinary(BinaryOp.ShrI32, + module.createBinary(BinaryOp.ShlI32, module.createBinary(BinaryOp.RotlI32, arg0, arg1), - module.createI32((typeArguments)[0].smallIntegerMask) - ) - : module.createBinary(BinaryOp.RotlI32, arg0, arg1); - } - break; + module.createI32(compiler.currentType.smallIntegerShift) + ), + module.createI32(compiler.currentType.smallIntegerShift) + ); + break; - case "rotr": // rotr(value: T, shift: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + ret = module.createBinary(BinaryOp.AndI32, + module.createBinary(BinaryOp.RotlI32, arg0, arg1), + module.createI32(compiler.currentType.smallIntegerMask) + ); + break; + + case TypeKind.I32: + case TypeKind.U32: + ret = module.createBinary(BinaryOp.RotlI32, arg0, arg1); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.ISIZE: + ret = module.createBinary(select(BinaryOp.RotlI64, BinaryOp.RotlI32, compiler.options.target == Target.WASM64), arg0, arg1); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createBinary(BinaryOp.RotlI64, arg0, arg1); + break; + + default: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "rotr": // rotr(value: T, shift: T) -> T + if (operands.length != 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyInteger) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]).isLongInteger // sic - ? module.createBinary(BinaryOp.RotrI64, arg0, arg1) - : (typeArguments)[0].isSmallInteger - ? module.createBinary(BinaryOp.AndI32, + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], compiler.currentType); + + switch (compiler.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + ret = module.createBinary(BinaryOp.ShrI32, + module.createBinary(BinaryOp.ShlI32, module.createBinary(BinaryOp.RotrI32, arg0, arg1), - module.createI32((typeArguments)[0].smallIntegerMask) + module.createI32(compiler.currentType.smallIntegerShift) + ), + module.createI32(compiler.currentType.smallIntegerShift) + ); + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + ret = module.createBinary(BinaryOp.AndI32, + module.createBinary(BinaryOp.RotrI32, arg0, arg1), + module.createI32(compiler.currentType.smallIntegerMask) + ); + break; + + case TypeKind.I32: + case TypeKind.U32: + ret = module.createBinary(BinaryOp.RotrI32, arg0, arg1); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.ISIZE: + ret = module.createBinary(select(BinaryOp.RotrI64, BinaryOp.RotrI32, compiler.options.target == Target.WASM64), arg0, arg1); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createBinary(BinaryOp.RotrI64, arg0, arg1); + break; + + default: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "abs": // abs(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + // doesn't need sign-extension here because ifFalse below is either positive + // or MIN_VALUE (-MIN_VALUE == MIN_VALUE) if selected + case TypeKind.I32: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createBinary(BinaryOp.SubI32, // ifFalse + module.createI32(0), + module.createGetLocal(tempLocal0.index, NativeType.I32) + ), + module.createBinary(BinaryOp.GtI32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createI32(0) ) - : module.createBinary(BinaryOp.RotrI32, arg0, arg1); - } - break; + ); + break; - case "abs": // abs(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + case TypeKind.ISIZE: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, compiler.options.target == Target.WASM64), + usizeType.toNativeZero(module), + module.createGetLocal(tempLocal0.index, nativeUsizeType) + ), + module.createBinary(select(BinaryOp.GtI64, BinaryOp.GtI32, compiler.options.target == Target.WASM64), + module.createGetLocal(tempLocal0.index, nativeUsizeType), + usizeType.toNativeZero(module) + ) + ); + break; + + case TypeKind.I64: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createBinary(BinaryOp.SubI64, + module.createI64(0, 0), + module.createGetLocal(tempLocal0.index, NativeType.I64), + ), + module.createBinary(BinaryOp.GtI64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createI64(0, 0) + ) + ); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.U64: + case TypeKind.BOOL: + ret = arg0; + break; + + case TypeKind.F32: + ret = module.createUnary(UnaryOp.AbsF32, arg0); + break; + + case TypeKind.F64: + ret = module.createUnary(UnaryOp.AbsF64, arg0); + break; + + case TypeKind.VOID: + ret = module.createUnreachable(); + break; + + default: // void + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "max": // max(left: T, right: T) -> T + if (operands.length != 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]) != Type.void) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) // sic - return (typeArguments)[0] == Type.f32 - ? module.createUnary(UnaryOp.AbsF32, arg0) - : module.createUnary(UnaryOp.AbsF64, arg0); - if ((typeArguments)[0].isAnyInteger) { - if ((typeArguments)[0].isSignedInteger) { - tempLocal0 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); - if ((typeArguments)[0].isLongInteger) - return module.createSelect( - module.createBinary(BinaryOp.SubI64, - module.createI64(0, 0), - module.createTeeLocal(tempLocal0.index, arg0) - ), - module.createGetLocal(tempLocal0.index, NativeType.I64), - module.createBinary(BinaryOp.LtI64, - module.createGetLocal(tempLocal0.index, NativeType.I64), - module.createI64(0, 0) - ) - ); - else - return module.createSelect( - module.createBinary(BinaryOp.SubI32, - module.createI32(0), - module.createTeeLocal(tempLocal0.index, arg0) - ), - module.createGetLocal(tempLocal0.index, NativeType.I32), - module.createBinary(BinaryOp.LtI32, - module.createGetLocal(tempLocal0.index, NativeType.I32), - module.createI32(0) - ) - ); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], compiler.currentType); + + switch (compiler.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.GtI32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) + ) + ); + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.GtU32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) + ) + ); + break; + + case TypeKind.I64: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.GtI64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) + ) + ); + break; + + case TypeKind.U64: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.GtU64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) + ) + ); + break; + + case TypeKind.ISIZE: + tempLocal0 = compiler.currentFunction.getTempLocal(usizeType); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(select(BinaryOp.GtI64, BinaryOp.GtI32, compiler.options.target == Target.WASM64), + module.createGetLocal(tempLocal0.index, nativeUsizeType), + module.createGetLocal(tempLocal1.index, nativeUsizeType) + ) + ); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + tempLocal0 = compiler.currentFunction.getTempLocal(usizeType); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(select(BinaryOp.GtU64, BinaryOp.GtU32, compiler.options.target == Target.WASM64), + module.createGetLocal(tempLocal0.index, nativeUsizeType), + module.createGetLocal(tempLocal1.index, nativeUsizeType) + ) + ); + break; + + case TypeKind.F32: + ret = module.createBinary(BinaryOp.MaxF32, arg0, arg1); + break; + + case TypeKind.F64: + ret = module.createBinary(BinaryOp.MaxF64, arg0, arg1); + break; + + default: // void + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "min": // min(left: T, right: T) -> T + if (operands.length != 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], compiler.currentType); + + switch (compiler.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.LtI32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) + ) + ); + break; + + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.BOOL: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i32); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.LtU32, + module.createGetLocal(tempLocal0.index, NativeType.I32), + module.createGetLocal(tempLocal1.index, NativeType.I32) + ) + ); + break; + + case TypeKind.I64: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.LtI64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) + ) + ); + break; + + case TypeKind.U64: + tempLocal0 = compiler.currentFunction.getTempLocal(Type.i64); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(BinaryOp.LtU64, + module.createGetLocal(tempLocal0.index, NativeType.I64), + module.createGetLocal(tempLocal1.index, NativeType.I64) + ) + ); + break; + + case TypeKind.ISIZE: + tempLocal0 = compiler.currentFunction.getTempLocal(usizeType); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(select(BinaryOp.LtI64, BinaryOp.LtI32, compiler.options.target == Target.WASM64), + module.createGetLocal(tempLocal0.index, nativeUsizeType), + module.createGetLocal(tempLocal1.index, nativeUsizeType) + ) + ); + break; + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + tempLocal0 = compiler.currentFunction.getTempLocal(usizeType); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + compiler.currentFunction.freeTempLocal(tempLocal0); + ret = module.createSelect( + module.createTeeLocal(tempLocal0.index, arg0), + module.createTeeLocal(tempLocal1.index, arg1), + module.createBinary(select(BinaryOp.LtU64, BinaryOp.LtU32, compiler.options.target == Target.WASM64), + module.createGetLocal(tempLocal0.index, nativeUsizeType), + module.createGetLocal(tempLocal1.index, nativeUsizeType) + ) + ); + break; + + case TypeKind.F32: + ret = module.createBinary(BinaryOp.MinF32, arg0, arg1); + break; + + case TypeKind.F64: + ret = module.createBinary(BinaryOp.MinF64, arg0, arg1); + break; + + default: // void + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "ceil": // ceil(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + default: // any integer + ret = arg0; + break; + + case TypeKind.F32: + ret = module.createUnary(UnaryOp.CeilF32, arg0); + break; + + case TypeKind.F64: + ret = module.createUnary(UnaryOp.CeilF64, arg0); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "floor": // floor(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + default: // any integer + ret = arg0; + break; + + case TypeKind.F32: + ret = module.createUnary(UnaryOp.FloorF32, arg0); + break; + + case TypeKind.F64: + ret = module.createUnary(UnaryOp.FloorF64, arg0); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "copysign": // copysign(left: T, right: T) -> T + if (operands.length != 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], compiler.currentType); + + switch (compiler.currentType.kind) { + + // TODO: does an integer version make sense? + + case TypeKind.F32: + ret = module.createBinary(BinaryOp.CopysignF32, arg0, arg1); + break; + + case TypeKind.F64: + ret = module.createBinary(BinaryOp.CopysignF64, arg0, arg1); + break; + + default: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "nearest": // nearest(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + default: // any integer + ret = arg0; + break; + + case TypeKind.F32: + ret = module.createUnary(UnaryOp.NearestF32, arg0); + break; + + case TypeKind.F64: + ret = module.createUnary(UnaryOp.NearestF64, arg0); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "reinterpret": // reinterpret(value: T1) -> T2 + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length >= 2) + compiler.currentType = typeArguments[1]; + if (typeArguments.length != 2) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "2", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 2) { + if (typeArguments.length >= 2) + compiler.currentType = typeArguments[1]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "2", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); + + switch (compiler.currentType.kind) { + + case TypeKind.I32: + case TypeKind.U32: + if (typeArguments) { + if (typeArguments[1].kind != TypeKind.F32) { + compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); + return module.createUnreachable(); + } + compiler.currentType = typeArguments[1]; } else - return arg0; + compiler.currentType = Type.f32; + ret = module.createUnary(UnaryOp.ReinterpretI32, arg0); + break; + + case TypeKind.I64: + case TypeKind.U64: + if (typeArguments) { + if (typeArguments[1].kind != TypeKind.F64) { + compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); + return module.createUnreachable(); + } + compiler.currentType = typeArguments[1]; + } else + compiler.currentType = Type.f64; + ret = module.createUnary(UnaryOp.ReinterpretI64, arg0); + break; + + case TypeKind.F32: + if (typeArguments) { + if (!(typeArguments[1].isAnyInteger && typeArguments[1].size == 32)) { + compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); + return module.createUnreachable(); + } + compiler.currentType = typeArguments[1]; + } else + compiler.currentType = Type.i32; + ret = module.createUnary(UnaryOp.ReinterpretF32, arg0); + break; + + case TypeKind.F64: + if (typeArguments) { + if (!(typeArguments[1].isLongInteger && !typeArguments[1].isReference)) { + compiler.error(DiagnosticCode.Type_0_cannot_be_reinterpreted_as_type_1, reportNode.range, typeArguments[0].toString(), typeArguments[1].toString()); + return module.createUnreachable(); + } + compiler.currentType = typeArguments[1]; + } else + compiler.currentType = Type.i64; + ret = module.createUnary(UnaryOp.ReinterpretF64, arg0); + break; + + default: // small integers and void + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "sqrt": // sqrt(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); } - } - break; - - case "max": // max(left: T, right: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]) != Type.void) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) // sic - return (typeArguments)[0] == Type.f32 - ? module.createBinary(BinaryOp.MaxF32, arg0, arg1) - : module.createBinary(BinaryOp.MaxF64, arg0, arg1); - if ((typeArguments)[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.getTempLocal((typeArguments)[0]); - tempLocal1 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); - compiler.currentFunction.freeTempLocal(tempLocal0); - if ((typeArguments)[0].isLongInteger) - return module.createSelect( - module.createTeeLocal(tempLocal0.index, arg0), - module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.GtI64 : BinaryOp.GtU64, - module.createGetLocal(tempLocal0.index, NativeType.I64), - module.createGetLocal(tempLocal1.index, NativeType.I64) - ) - ); - else - return module.createSelect( - module.createTeeLocal(tempLocal0.index, arg0), - module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.GtI32 : BinaryOp.GtU32, - module.createGetLocal(tempLocal0.index, NativeType.I32), - module.createGetLocal(tempLocal1.index, NativeType.I32) - ) - ); + } + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); } - } - break; + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); - case "min": // min(left: T, right: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]) != Type.void) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) // sic - return (typeArguments)[0] == Type.f32 - ? module.createBinary(BinaryOp.MinF32, arg0, arg1) - : module.createBinary(BinaryOp.MinF64, arg0, arg1); - if ((typeArguments)[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.getTempLocal((typeArguments)[0]); - tempLocal1 = compiler.currentFunction.getAndFreeTempLocal((typeArguments)[0]); - compiler.currentFunction.freeTempLocal(tempLocal0); - if ((typeArguments)[0].isLongInteger) - return module.createSelect( - module.createTeeLocal(tempLocal0.index, arg0), - module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.LtI64 : BinaryOp.LtU64, - module.createGetLocal(tempLocal0.index, NativeType.I64), - module.createGetLocal(tempLocal1.index, NativeType.I64) - ) - ); - else - return module.createSelect( - module.createTeeLocal(tempLocal0.index, arg0), - module.createTeeLocal(tempLocal1.index, arg1), - module.createBinary((typeArguments)[0].isSignedInteger ? BinaryOp.LtI32 : BinaryOp.LtU32, - module.createGetLocal(tempLocal0.index, NativeType.I32), - module.createGetLocal(tempLocal1.index, NativeType.I32) - ) - ); + switch (compiler.currentType.kind) { + + // TODO: integer versions (that return f64 or convert)? + + case TypeKind.F32: + ret = module.createUnary(UnaryOp.SqrtF32, arg0); + break; + + case TypeKind.F64: + ret = module.createUnary(UnaryOp.SqrtF64, arg0); + break; + + default: + // case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; + + case "trunc": // trunc(value: T) -> T + if (operands.length != 1) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); } - } - break; - - case "ceil": // ceil(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createUnary(UnaryOp.CeilF32, arg0) - : module.createUnary(UnaryOp.CeilF64, arg0); } - break; + if (typeArguments && typeArguments.length) { + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.f64, ConversionKind.NONE); - case "floor": // floor(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createUnary(UnaryOp.FloorF32, arg0) - : module.createUnary(UnaryOp.FloorF64, arg0); - } - break; + switch (compiler.currentType.kind) { - case "copysign": // copysign(left: T, right: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createBinary(BinaryOp.CopysignF32, arg0, arg1) - : module.createBinary(BinaryOp.CopysignF64, arg0, arg1); - } - break; + case TypeKind.USIZE: + if (compiler.currentType.isReference) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + // fall-through + default: // any integer + ret = arg0; + break; - case "nearest": // nearest(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createUnary(UnaryOp.NearestF32, arg0) - : module.createUnary(UnaryOp.NearestF64, arg0); - } - break; + // TODO: truncate to contextual type directly (if not void etc.)? - case "reinterpret": // reinterpret(value: T1) -> T2 - if (!validateCall(compiler, typeArguments, 2, operands, 1, reportNode)) - return module.createUnreachable(); - compiler.currentType = (typeArguments)[1]; - if ((typeArguments)[0].isLongInteger && (typeArguments)[1] == Type.f64) { - arg0 = compiler.compileExpression(operands[0], Type.i64); // reports - compiler.currentType = Type.f64; - return module.createUnary(UnaryOp.ReinterpretI64, arg0); - } - if ((typeArguments)[0].isAnyInteger && (typeArguments)[0].byteSize == 4 && (typeArguments)[1] == Type.f32) { - arg0 = compiler.compileExpression(operands[0], Type.i32); // reports - compiler.currentType = Type.f32; - return module.createUnary(UnaryOp.ReinterpretI32, arg0); - } - if ((typeArguments)[0] == Type.f64 && (typeArguments)[1].isLongInteger) { - arg0 = compiler.compileExpression(operands[0], Type.f64); // reports - compiler.currentType = (typeArguments)[1]; - return module.createUnary(UnaryOp.ReinterpretF64, arg0); - } - if ((typeArguments)[0] == Type.f32 && (typeArguments)[1].isAnyInteger && (typeArguments)[1].byteSize == 4) { - arg0 = compiler.compileExpression(operands[0], Type.f32); // reports - compiler.currentType = (typeArguments)[1]; - return module.createUnary(UnaryOp.ReinterpretF32, arg0); - } - break; + case TypeKind.F32: + ret = module.createUnary(UnaryOp.TruncF32, arg0); + break; - case "sqrt": // sqrt(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createUnary(UnaryOp.SqrtF32, arg0) - : module.createUnary(UnaryOp.SqrtF64, arg0); - } - break; + case TypeKind.F64: + ret = module.createUnary(UnaryOp.TruncF64, arg0); + break; - case "trunc": // trunc(value: T) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) - return module.createUnreachable(); - if ((compiler.currentType = (typeArguments)[0]).isAnyFloat) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); - return (compiler.currentType = (typeArguments)[0]) == Type.f32 // sic - ? module.createUnary(UnaryOp.TruncF32, arg0) - : module.createUnary(UnaryOp.TruncF64, arg0); + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; } - break; + return ret; // memory access - case "load": // load(offset: usize) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + case "load": // load(offset: usize) -> T + if (operands.length != 1) { + if (!(typeArguments && typeArguments.length == 1)) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0"); + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); return module.createUnreachable(); - arg0 = compiler.compileExpression(operands[0], usizeType); // reports - if ((compiler.currentType = (typeArguments)[0]) != Type.void) - return module.createLoad((typeArguments)[0].byteSize, (typeArguments)[0].isSignedInteger, arg0, (typeArguments)[0].toNativeType()); - break; - - case "store": // store(offset: usize, value: T) -> void - compiler.currentType = Type.void; - if (!validateCall(compiler, typeArguments, 1, operands, 2, reportNode)) + } + 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], usizeType); // reports - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); // reports - compiler.currentType = Type.void; - if ((typeArguments)[0] != Type.void) - return module.createStore((typeArguments)[0].byteSize, arg0, arg1, (typeArguments)[0].toNativeType()); - break; + } + arg0 = compiler.compileExpression(operands[0], usizeType); + compiler.currentType = typeArguments[0]; + return module.createLoad(typeArguments[0].size >>> 3, typeArguments[0].isAnySignedInteger, arg0, typeArguments[0].toNativeType()); - case "sizeof": // sizeof() -> usize + case "store": // store(offset: usize, value: T) -> void + compiler.currentType = Type.void; + if (operands.length != 2) { + if (typeArguments && typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length != 1) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], usizeType); + arg1 = compiler.compileExpression(operands[1], typeArguments[0]); + } else { + arg0 = compiler.compileExpression(operands[0], usizeType); + arg1 = compiler.compileExpression(operands[1], Type.i32, ConversionKind.NONE); + } + type = compiler.currentType; + compiler.currentType = Type.void; + return module.createStore(type.size >>> 3, arg0, arg1, type.toNativeType()); + + case "sizeof": // sizeof() -> usize compiler.currentType = usizeType; - if (!validateCall(compiler, typeArguments, 1, operands, 0, reportNode)) + if (operands.length != 0) { + if (!(typeArguments && typeArguments.length == 1)) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0"); + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "0", operands.length.toString(10)); return module.createUnreachable(); - return usizeType.isLongInteger - ? module.createI64((typeArguments)[0].byteSize, 0) - : module.createI32((typeArguments)[0].byteSize); + } + if (typeArguments) { + if (typeArguments.length != 1) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + ret = usizeType.size == 64 ? module.createI64(typeArguments[0].byteSize, 0) : module.createI32(typeArguments[0].byteSize); + } else { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", "0"); + return module.createUnreachable(); + } + return ret; // control flow - case "select": // select(ifTrue: T, ifFalse: T, condition: bool) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 3, reportNode)) + case "select": // select(ifTrue: T, ifFalse: T, condition: bool) -> T + if (operands.length != 3) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "3", operands.length.toString(10)); return module.createUnreachable(); - if ((typeArguments)[0] != Type.void) { - arg0 = compiler.compileExpression(operands[0], (typeArguments)[0]); // reports - arg1 = compiler.compileExpression(operands[1], (typeArguments)[0]); // reports - arg2 = compiler.compileExpression(operands[2], Type.i32); // reports - compiler.currentType = (typeArguments)[0]; - return module.createSelect(arg0, arg1, arg2); } - break; + if (typeArguments) { + if (typeArguments.length != 1) { + if (typeArguments.length) + compiler.currentType = typeArguments[0]; + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); + arg1 = compiler.compileExpression(operands[1], type = compiler.currentType); + arg2 = compiler.compileExpression(operands[2], Type.bool); + compiler.currentType = type; + + switch (compiler.currentType.kind) { + + default: // any value type + ret = module.createSelect(arg0, arg1, arg2); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + return ret; case "unreachable": // unreachable() -> * - // does not modify currentType - validateCall(compiler, typeArguments, 0, operands, 0, reportNode); + if (operands.length != 0) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "0", operands.length.toString(10)); + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); return module.createUnreachable(); // host operations case "current_memory": // current_memory() -> i32 compiler.currentType = Type.i32; - if (!validateCall(compiler, typeArguments, 0, operands, 0, reportNode)) - return module.createUnreachable(); + if (operands.length != 0) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "0", operands.length.toString(10)); + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); return module.createHost(HostOp.CurrentMemory); case "grow_memory": // grow_memory(pages: i32) -> i32 compiler.currentType = Type.i32; - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) - return module.createUnreachable(); - arg0 = compiler.compileExpression(operands[0], Type.i32); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "0", operands.length.toString(10)); + arg0 = module.createUnreachable(); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32); + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); return module.createHost(HostOp.GrowMemory, null, [ arg0 ]); // see: https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md - /* case "move_memory": // move_memory(dest: usize, src: usize: n: usize) -> void - compiler.currentType = Type.void; - if (!validateCall(compiler, typeArguments, 0, operands, 3, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 3) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "3", operands.length.toString(10)); + compiler.currentType = Type.void; return module.createUnreachable(); + } arg0 = compiler.compileExpression(operands[0], usizeType); arg1 = compiler.compileExpression(operands[1], usizeType); arg2 = compiler.compileExpression(operands[2], usizeType); compiler.currentType = Type.void; - return module.createHost(HostOp.MoveMemory, null, [ arg0, arg1, arg2 ]); + throw new Error("not implemented"); + // return module.createHost(HostOp.MoveMemory, null, [ arg0, arg1, arg2 ]); case "set_memory": // set_memory(dest: usize, value: u32, n: usize) -> void - compiler.currentType = Type.void; - if (!validateCall(compiler, typeArguments, 0, operands, 3, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 3) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "3", operands.length.toString(10)); + compiler.currentType = Type.void; return module.createUnreachable(); + } arg0 = compiler.compileExpression(operands[0], usizeType); arg1 = compiler.compileExpression(operands[1], Type.u32); arg2 = compiler.compileExpression(operands[2], usizeType); compiler.currentType = Type.void; - return module.createHost(HostOp.SetMemory, null, [ arg0, arg1, arg2 ]); - */ - - // imported - - case "parseInt": // takes a pointer to the string - compiler.currentType = Type.f64; - if (typeArguments && typeArguments.length) { - compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); - return module.createUnreachable(); - } - if (operands.length < 1) { - compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); - return module.createUnreachable(); - } - if (operands.length > 2) { - compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); - return module.createUnreachable(); - } - if (!prototype.isCompiled) { - if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType, NativeType.I32 ]))) - ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FIi" : "Fii", NativeType.F64, [ nativeUsizeType, NativeType.I32 ]); - module.addFunctionImport("parseInt", "env", "parseInt", ftype); // FIXME: can't call with i64 pointers (WASM64) - prototype.isCompiled = true; - } - arg0 = compiler.compileExpression(operands[0], usizeType); // reports - arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], Type.i32) : module.createI32(-1); // -1 marks omitted - return module.createCallImport("parseInt", [ arg0, arg1 ], NativeType.F64); - - case "parseFloat": // takes a pointer to the string - compiler.currentType = Type.f64; - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) - return module.createUnreachable(); - if (!prototype.isCompiled) { - if (!(ftype = module.getFunctionTypeBySignature(NativeType.F64, [ nativeUsizeType ]))) - ftype = module.addFunctionType(nativeUsizeType == NativeType.I64 ? "FI" : "Fi", NativeType.F64, [ nativeUsizeType ]); - module.addFunctionImport("parseFloat", "env", "parseFloat", ftype); // FIXME: can't call with i64 pointers (WASM64) - prototype.isCompiled = true; - } - arg0 = compiler.compileExpression(operands[0], usizeType); // reports - return module.createCallImport("parseFloat", [ arg0 ], NativeType.F64); + throw new Error("not implemented"); + // return module.createHost(HostOp.SetMemory, null, [ arg0, arg1, arg2 ]); // other - case "changetype": // changetype(value: *) -> T - if (!validateCall(compiler, typeArguments, 1, operands, 1, reportNode)) + case "changetype": // changetype(value: *) -> T + 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], Type.void, ConversionKind.NONE); - if (compiler.currentType != Type.void && compiler.currentType.kind == (typeArguments)[0].kind) { - compiler.currentType = (typeArguments)[0]; + } else if (typeArguments[0].kind != TypeKind.USIZE) { // any usize + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + compiler.currentType = typeArguments[0]; + return module.createUnreachable(); + } + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = typeArguments[0]; + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], usizeType, ConversionKind.NONE); + compiler.currentType = typeArguments[0]; + if (compiler.currentType.kind != TypeKind.USIZE) { + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + return module.createUnreachable(); + } + return arg0; // any usize to any usize + + case "assert": // assert(isTrueish: T, message?: string) -> T with T != null (see also assembly.d.ts) + if (operands.length < 1 || operands.length > 2) { + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0].nonNullableType; + if (typeArguments.length != 1) + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + } + if (operands.length < 1) + compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + else if (operands.length > 2) + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } + if (typeArguments) { + if (typeArguments.length) + compiler.currentType = typeArguments[0].nonNullableType; + if (typeArguments.length != 1) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "1", typeArguments.length.toString(10)); + return module.createUnreachable(); + } + arg0 = compiler.compileExpression(operands[0], typeArguments[0]); + } else + arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE); + + // TODO: report message to embedder, requires strings + type = compiler.currentType; + // arg1 = operands.length == 2 ? compiler.compileExpression(operands[1], Type.string) : usizeType.toNativeZero(module); + compiler.currentType = type.nonNullableType; + + // just return ifTrueish if assertions are disabled, or simplify if dropped anyway + if (compiler.options.noAssert) { + if (contextualType == Type.void) { + compiler.currentType = Type.void; + return module.createNop(); + } return arg0; } - compiler.error(DiagnosticCode.Type_0_cannot_be_changed_to_type_1, reportNode.range, compiler.currentType.toString(), (typeArguments)[0].toString()); - return module.createUnreachable(); - case "assert": // assert(isTrue: bool) -> void - compiler.currentType = Type.void; - if (typeArguments && typeArguments.length) { - compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); - return module.createUnreachable(); + if (contextualType == Type.void) { // simplify if dropped anyway + switch (compiler.currentType.kind) { + + default: // any integer up to 32-bits incl. bool + ret = module.createIf( + module.createUnary(UnaryOp.EqzI32, + arg0 + ), + module.createUnreachable() + ); + break; + + case TypeKind.I64: + case TypeKind.U64: + ret = module.createIf( + module.createUnary(UnaryOp.EqzI64, + arg0 + ), + module.createUnreachable() + ); + break; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + ret = module.createIf( + module.createUnary(select(UnaryOp.EqzI64, UnaryOp.EqzI32, compiler.options.target == Target.WASM64), + arg0 + ), + module.createUnreachable() + ); + break; + + // TODO: also check for NaN in float assertions, as in `Boolean(NaN) -> false`? + + case TypeKind.F32: + ret = module.createIf( + module.createBinary(BinaryOp.EqF32, + arg0, + module.createF32(0) + ), + module.createUnreachable() + ); + break; + + case TypeKind.F64: + ret = module.createIf( + module.createBinary(BinaryOp.EqF64, + arg0, + module.createF64(0) + ), + module.createUnreachable() + ); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } + compiler.currentType = Type.void; + } else { + switch (compiler.currentType.kind) { + + default: // any integer up to 32-bits incl. bool + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.i32); + ret = module.createIf( + module.createUnary(UnaryOp.EqzI32, + module.createTeeLocal(tempLocal0.index, arg0) + ), + module.createUnreachable(), + module.createGetLocal(tempLocal0.index, NativeType.I32) + ); + break; + + case TypeKind.I64: + case TypeKind.U64: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.i64); + ret = module.createIf( + module.createUnary(UnaryOp.EqzI64, + module.createTeeLocal(tempLocal0.index, arg0) + ), + module.createUnreachable(), + module.createGetLocal(tempLocal0.index, NativeType.I64) + ); + break; + + case TypeKind.ISIZE: + case TypeKind.USIZE: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(usizeType); + ret = module.createIf( + module.createUnary(select(UnaryOp.EqzI64, UnaryOp.EqzI32, compiler.options.target == Target.WASM64), + module.createTeeLocal(tempLocal0.index, arg0) + ), + module.createUnreachable(), + module.createGetLocal(tempLocal0.index, nativeUsizeType) + ); + break; + + case TypeKind.F32: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); + ret = module.createIf( + module.createBinary(BinaryOp.EqF32, + module.createTeeLocal(tempLocal0.index, arg0), + module.createF32(0) + ), + module.createUnreachable(), + module.createGetLocal(tempLocal0.index, NativeType.F32) + ); + break; + + case TypeKind.F64: + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f64); + ret = module.createIf( + module.createBinary(BinaryOp.EqF64, + module.createTeeLocal(tempLocal0.index, arg0), + module.createF64(0) + ), + module.createUnreachable(), + module.createGetLocal(tempLocal0.index, NativeType.F64) + ); + break; + + case TypeKind.VOID: + compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); + ret = module.createUnreachable(); + break; + } } - if (operands.length < 1) { - compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); - return module.createUnreachable(); - } - if (operands.length > 2) { - compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); - return module.createUnreachable(); - } - arg0 = compiler.compileExpression(operands[0], Type.i32); // reports - arg1 = operands.length > 1 ? compiler.compileExpression(operands[1], usizeType) : usizeType.toNativeZero(module); // TODO: string type - compiler.currentType = Type.void; - return compiler.options.noAssert - ? module.createNop() - : module.createIf( - module.createUnary(UnaryOp.EqzI32, arg0), - module.createUnreachable() // TODO: report message to embedder - ); + return ret; // conversions case "i8": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.i8; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.i8, ConversionKind.EXPLICIT); case "i16": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.i16; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.i16, ConversionKind.EXPLICIT); case "i32": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.i32; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.i32, ConversionKind.EXPLICIT); case "i64": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.i64; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.i64, ConversionKind.EXPLICIT); case "isize": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = select(Type.isize64, Type.isize32, compiler.options.target == Target.WASM64); return module.createUnreachable(); + } return compiler.compileExpression(operands[0], select(Type.isize64, Type.isize32, compiler.options.target == Target.WASM64), ConversionKind.EXPLICIT); case "u8": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.u8; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.u8, ConversionKind.EXPLICIT); case "u16": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.u16; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.u16, ConversionKind.EXPLICIT); case "u32": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.u32; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.u32, ConversionKind.EXPLICIT); case "u64": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.u64; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.u64, ConversionKind.EXPLICIT); case "usize": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = usizeType; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], usizeType, ConversionKind.EXPLICIT); case "bool": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.bool; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.bool, ConversionKind.EXPLICIT); case "f32": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.f32; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.f32, ConversionKind.EXPLICIT); case "f64": - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments) + compiler.error(DiagnosticCode.Type_0_is_not_generic, reportNode.range, prototype.internalName); + if (operands.length != 1) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + compiler.currentType = Type.f64; return module.createUnreachable(); + } return compiler.compileExpression(operands[0], Type.f64, ConversionKind.EXPLICIT); } compiler.error(DiagnosticCode.Operation_not_supported, reportNode.range); return module.createUnreachable(); } - -/** Pre-validates a call to a built-in function. */ -function validateCall(compiler: Compiler, typeArguments: Type[] | null, expectedTypeArguments: i32, operands: Expression[], expectedOperands: i32, reportNode: Node): bool { - var numTypeArguments = typeArguments && typeArguments.length || 0; - if (numTypeArguments != expectedTypeArguments) { - compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), numTypeArguments.toString(10)); - return false; - } - if (operands.length != expectedOperands) { - compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, expectedOperands.toString(10), operands.length.toString(10)); - return false; - } - return true; -} diff --git a/src/compiler.ts b/src/compiler.ts index b3360ce9..4e18caaa 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -356,8 +356,8 @@ export class Compiler extends DiagnosticEmitter { return false; } global.type = resolvedType; - } else if (declaration.initializer) { - initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable + } else if (declaration.initializer) { // infer type using void/NONE for proper literal inference + initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports if (this.currentType == Type.void) { this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.range, this.currentType.toString(), ""); return false; @@ -1018,9 +1018,9 @@ export class Compiler extends DiagnosticEmitter { if (!type) continue; if (declaration.initializer) - init = this.compileExpression(declaration.initializer, type); // reports and returns unreachable - } else if (declaration.initializer) { // infer type - init = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable + init = this.compileExpression(declaration.initializer, type); // reports + } else if (declaration.initializer) { // infer type using void/NONE for proper literal inference + init = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports if (this.currentType == Type.void) { this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.range, this.currentType.toString(), ""); continue; @@ -1089,7 +1089,7 @@ export class Compiler extends DiagnosticEmitter { // expressions - compileExpression(expression: Expression, contextualType: Type, conversionKind: ConversionKind = ConversionKind.IMPLICIT): ExpressionRef { + compileExpression(expression: Expression, contextualType: Type, conversionKind: ConversionKind = ConversionKind.IMPLICIT, wrapSmallIntegers: bool = true): ExpressionRef { this.currentType = contextualType; var expr: ExpressionRef; @@ -1100,7 +1100,7 @@ export class Compiler extends DiagnosticEmitter { break; case NodeKind.BINARY: - expr = this.compileBinaryExpression(expression, contextualType); + expr = this.compileBinaryExpression(expression, contextualType, wrapSmallIntegers); break; case NodeKind.CALL: @@ -1148,7 +1148,7 @@ export class Compiler extends DiagnosticEmitter { break; case NodeKind.UNARYPREFIX: - expr = this.compileUnaryPrefixExpression(expression, contextualType); + expr = this.compileUnaryPrefixExpression(expression, contextualType, wrapSmallIntegers); break; default: @@ -1181,8 +1181,10 @@ export class Compiler extends DiagnosticEmitter { } convertExpression(expr: ExpressionRef, fromType: Type, toType: Type, conversionKind: ConversionKind, reportNode: Node): ExpressionRef { - if (conversionKind == ConversionKind.NONE) + if (conversionKind == ConversionKind.NONE) { + assert(false); return expr; + } // void to any if (fromType.kind == TypeKind.VOID) { @@ -1222,7 +1224,7 @@ export class Compiler extends DiagnosticEmitter { // f32 to int if (fromType.kind == TypeKind.F32) { - if (toType.isSignedInteger) { + if (toType.isAnySignedInteger) { if (toType.isLongInteger) expr = mod.createUnary(UnaryOp.TruncF32ToI64, expr); else { @@ -1244,7 +1246,7 @@ export class Compiler extends DiagnosticEmitter { // f64 to int } else { - if (toType.isSignedInteger) { + if (toType.isAnySignedInteger) { if (toType.isLongInteger) expr = mod.createUnary(UnaryOp.TruncF64ToI64, expr); else { @@ -1274,14 +1276,14 @@ export class Compiler extends DiagnosticEmitter { if (toType.kind == TypeKind.F32) { if (fromType.isLongInteger) { losesInformation = true; - if (fromType.isSignedInteger) + if (fromType.isAnySignedInteger) expr = mod.createUnary(UnaryOp.ConvertI64ToF32, expr); else expr = mod.createUnary(UnaryOp.ConvertU64ToF32, expr); } else { if (!fromType.isSmallInteger) losesInformation = true; - if (fromType.isSignedInteger) + if (fromType.isAnySignedInteger) expr = mod.createUnary(UnaryOp.ConvertI32ToF32, expr); else expr = mod.createUnary(UnaryOp.ConvertU32ToF32, expr); @@ -1291,12 +1293,12 @@ export class Compiler extends DiagnosticEmitter { } else { if (fromType.isLongInteger) { losesInformation = true; - if (fromType.isSignedInteger) + if (fromType.isAnySignedInteger) expr = mod.createUnary(UnaryOp.ConvertI64ToF64, expr); else expr = mod.createUnary(UnaryOp.ConvertU64ToF64, expr); } else - if (fromType.isSignedInteger) + if (fromType.isAnySignedInteger) expr = mod.createUnary(UnaryOp.ConvertI32ToF64, expr); else expr = mod.createUnary(UnaryOp.ConvertU32ToF64, expr); @@ -1311,7 +1313,7 @@ export class Compiler extends DiagnosticEmitter { losesInformation = true; expr = mod.createUnary(UnaryOp.WrapI64, expr); // discards upper bits if (toType.isSmallInteger) { - if (toType.isSignedInteger) { + if (toType.isAnySignedInteger) { expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift)); } else @@ -1321,7 +1323,7 @@ export class Compiler extends DiagnosticEmitter { // i32 to i64 } else if (toType.isLongInteger) { - if (toType.isSignedInteger) + if (toType.isAnySignedInteger) expr = mod.createUnary(UnaryOp.ExtendI32, expr); else expr = mod.createUnary(UnaryOp.ExtendU32, expr); @@ -1329,7 +1331,7 @@ export class Compiler extends DiagnosticEmitter { // i32 or smaller to even smaller int } else if (toType.isSmallInteger && fromType.size > toType.size) { losesInformation = true; - if (toType.isSignedInteger) { + if (toType.isAnySignedInteger) { expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift)); } else @@ -1350,19 +1352,21 @@ export class Compiler extends DiagnosticEmitter { return this.compileExpression(expression.expression, toType, ConversionKind.EXPLICIT); } - compileBinaryExpression(expression: BinaryExpression, contextualType: Type): ExpressionRef { - var op: BinaryOp; + compileBinaryExpression(expression: BinaryExpression, contextualType: Type, wrapSmallIntegers: bool = true): ExpressionRef { var left: ExpressionRef; var right: ExpressionRef; - var compound: Token = 0; - var condition: ExpressionRef; + var expr: ExpressionRef; + + var compound = false; + var possiblyOverflows = false; + var tempLocal: Local; switch (expression.operator) { case Token.LESSTHAN: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1370,49 +1374,50 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.I8: case TypeKind.I16: case TypeKind.I32: - op = BinaryOp.LtI32; - break; - - case TypeKind.ISIZE: - op = select(BinaryOp.LtI64, BinaryOp.LtI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(BinaryOp.LtI32, left, right); break; case TypeKind.I64: - op = BinaryOp.LtI64; + expr = this.module.createBinary(BinaryOp.LtI64, left, right); + break; + + case TypeKind.ISIZE: + expr = this.module.createBinary(select(BinaryOp.LtI64, BinaryOp.LtI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U8: case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.LtU32; + expr = this.module.createBinary(BinaryOp.LtU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.LtU64, BinaryOp.LtU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.LtU64, BinaryOp.LtU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.LtU64; + expr = this.module.createBinary(BinaryOp.LtU64, left, right); break; case TypeKind.F32: - op = BinaryOp.LtF32; + expr = this.module.createBinary(BinaryOp.LtF32, left, right); break; case TypeKind.F64: - op = BinaryOp.LtF64; + expr = this.module.createBinary(BinaryOp.LtF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; break; case Token.GREATERTHAN: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1420,49 +1425,50 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.I8: case TypeKind.I16: case TypeKind.I32: - op = BinaryOp.GtI32; + expr = this.module.createBinary(BinaryOp.GtI32, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.GtI64, BinaryOp.GtI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.GtI64, BinaryOp.GtI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: - op = BinaryOp.GtI64; + expr = this.module.createBinary(BinaryOp.GtI64, left, right); break; case TypeKind.U8: case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.GtU32; + expr = this.module.createBinary(BinaryOp.GtU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.GtU64, BinaryOp.GtU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.GtU64, BinaryOp.GtU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.GtU64; + expr = this.module.createBinary(BinaryOp.GtU64, left, right); break; case TypeKind.F32: - op = BinaryOp.GtF32; + expr = this.module.createBinary(BinaryOp.GtF32, left, right); break; case TypeKind.F64: - op = BinaryOp.GtF64; + expr = this.module.createBinary(BinaryOp.GtF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; break; case Token.LESSTHAN_EQUALS: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1470,49 +1476,50 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.I8: case TypeKind.I16: case TypeKind.I32: - op = BinaryOp.LeI32; + expr = this.module.createBinary(BinaryOp.LeI32, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.LeI64, BinaryOp.LeI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.LeI64, BinaryOp.LeI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: - op = BinaryOp.LeI64; + expr = this.module.createBinary(BinaryOp.LeI64, left, right); break; case TypeKind.U8: case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.LeU32; + expr = this.module.createBinary(BinaryOp.LeU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.LeU64, BinaryOp.LeU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.LeU64, BinaryOp.LeU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.LeU64; + expr = this.module.createBinary(BinaryOp.LeU64, left, right); break; case TypeKind.F32: - op = BinaryOp.LeF32; + expr = this.module.createBinary(BinaryOp.LeF32, left, right); break; case TypeKind.F64: - op = BinaryOp.LeF64; + expr = this.module.createBinary(BinaryOp.LeF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; break; case Token.GREATERTHAN_EQUALS: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1520,55 +1527,57 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.I8: case TypeKind.I16: case TypeKind.I32: - op = BinaryOp.GeI32; + expr = this.module.createBinary(BinaryOp.GeI32, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.GeI64, BinaryOp.GeI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.GeI64, BinaryOp.GeI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: - op = BinaryOp.GeI64; + expr = this.module.createBinary(BinaryOp.GeI64, left, right); break; case TypeKind.U8: case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.GeU32; + expr = this.module.createBinary(BinaryOp.GeU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.GeU64, BinaryOp.GeU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.GeU64, BinaryOp.GeU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.GeU64; + expr = this.module.createBinary(BinaryOp.GeU64, left, right); break; case TypeKind.F32: - op = BinaryOp.GeF32; + expr = this.module.createBinary(BinaryOp.GeF32, left, right); break; case TypeKind.F64: - op = BinaryOp.GeF64; + expr = this.module.createBinary(BinaryOp.GeF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; break; - case Token.EQUALS_EQUALS: case Token.EQUALS_EQUALS_EQUALS: + // TODO? + case Token.EQUALS_EQUALS: // NOTE that this favors correctness, in terms of emitting a binary expression, over // checking for a possible use of unary EQZ. while the most classic of all optimizations, // that's not what the source told us to do. for reference, `!left` emits unary EQZ. - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1580,37 +1589,39 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.EqI32; + expr = this.module.createBinary(BinaryOp.EqI32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.EqI64, BinaryOp.EqI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.EqI64, BinaryOp.EqI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.EqI64; + expr = this.module.createBinary(BinaryOp.EqI64, left, right); break; case TypeKind.F32: - op = BinaryOp.EqF32; + expr = this.module.createBinary(BinaryOp.EqF32, left, right); break; case TypeKind.F64: - op = BinaryOp.EqF64; + expr = this.module.createBinary(BinaryOp.EqF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; break; - case Token.EXCLAMATION_EQUALS: case Token.EXCLAMATION_EQUALS_EQUALS: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + // TODO? + case Token.EXCLAMATION_EQUALS: + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); switch (this.currentType.kind) { @@ -1622,29 +1633,30 @@ export class Compiler extends DiagnosticEmitter { case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.NeI32; + expr = this.module.createBinary(BinaryOp.NeI32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.NeI64, BinaryOp.NeI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.NeI64, BinaryOp.NeI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.NeI64; + expr = this.module.createBinary(BinaryOp.NeI64, left, right); break; case TypeKind.F32: - op = BinaryOp.NeF32; + expr = this.module.createBinary(BinaryOp.NeF32, left, right); break; case TypeKind.F64: - op = BinaryOp.NeF64; + expr = this.module.createBinary(BinaryOp.NeF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } this.currentType = Type.bool; @@ -1654,40 +1666,41 @@ export class Compiler extends DiagnosticEmitter { return this.compileAssignment(expression.left, expression.right, contextualType); case Token.PLUS_EQUALS: - compound = Token.EQUALS; - case Token.PLUS: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.PLUS: // retains low bits of small integers + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { case TypeKind.I8: case TypeKind.I16: - case TypeKind.I32: case TypeKind.U8: case TypeKind.U16: - case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.AddI32; + possiblyOverflows = true; + case TypeKind.I32: + case TypeKind.U32: + expr = this.module.createBinary(BinaryOp.AddI32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.AddI64, BinaryOp.AddI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.AddI64, BinaryOp.AddI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.AddI64; + expr = this.module.createBinary(BinaryOp.AddI64, left, right); break; case TypeKind.F32: - op = BinaryOp.AddF32; + expr = this.module.createBinary(BinaryOp.AddF32, left, right); break; case TypeKind.F64: - op = BinaryOp.AddF64; + expr = this.module.createBinary(BinaryOp.AddF64, left, right); break; default: @@ -1696,361 +1709,426 @@ export class Compiler extends DiagnosticEmitter { break; case Token.MINUS_EQUALS: - compound = Token.EQUALS; - case Token.MINUS: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.MINUS: // retains low bits of small integers + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { case TypeKind.I8: case TypeKind.I16: - case TypeKind.I32: case TypeKind.U8: case TypeKind.U16: - case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.SubI32; + possiblyOverflows = true; + case TypeKind.I32: + case TypeKind.U32: + expr = this.module.createBinary(BinaryOp.SubI32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.SubI64; + expr = this.module.createBinary(BinaryOp.SubI64, left, right); break; case TypeKind.F32: - op = BinaryOp.SubF32; + expr = this.module.createBinary(BinaryOp.SubF32, left, right); break; case TypeKind.F64: - op = BinaryOp.SubF64; + expr = this.module.createBinary(BinaryOp.SubF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } break; case Token.ASTERISK_EQUALS: - compound = Token.EQUALS; - case Token.ASTERISK: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.ASTERISK: // retains low bits of small integers + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { case TypeKind.I8: case TypeKind.I16: - case TypeKind.I32: case TypeKind.U8: case TypeKind.U16: - case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.MulI32; + possiblyOverflows = true; + // fall-through + case TypeKind.I32: + case TypeKind.U32: + expr = this.module.createBinary(BinaryOp.MulI32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.MulI64, BinaryOp.MulI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.MulI64, BinaryOp.MulI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.MulI64; + expr = this.module.createBinary(BinaryOp.MulI64, left, right); break; case TypeKind.F32: - op = BinaryOp.MulF32; + expr = this.module.createBinary(BinaryOp.MulF32, left, right); break; case TypeKind.F64: - op = BinaryOp.MulF64; + expr = this.module.createBinary(BinaryOp.MulF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } break; case Token.SLASH_EQUALS: - compound = Token.EQUALS; - case Token.SLASH: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.SLASH: // TODO: when can division remain unwrapped? does it overflow? + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); switch (this.currentType.kind) { case TypeKind.I8: case TypeKind.I16: + possiblyOverflows = true; case TypeKind.I32: - op = BinaryOp.DivI32; + expr = this.module.createBinary(BinaryOp.DivI32, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.DivI64, BinaryOp.DivI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.DivI64, BinaryOp.DivI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: - op = BinaryOp.DivI64; + expr = this.module.createBinary(BinaryOp.DivI64, left, right); break; case TypeKind.U8: case TypeKind.U16: - case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.DivU32; + possiblyOverflows = true; + case TypeKind.U32: + expr = this.module.createBinary(BinaryOp.DivU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.DivU64, BinaryOp.DivU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.DivU64, BinaryOp.DivU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.DivU64; + expr = this.module.createBinary(BinaryOp.DivU64, left, right); break; case TypeKind.F32: - op = BinaryOp.DivF32; + expr = this.module.createBinary(BinaryOp.DivF32, left, right); break; case TypeKind.F64: - op = BinaryOp.DivF64; + expr = this.module.createBinary(BinaryOp.DivF64, left, right); break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } break; case Token.PERCENT_EQUALS: - compound = Token.EQUALS; - case Token.PERCENT: - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.PERCENT: // TODO: when can remainder remain unwrapped? may it overflow? + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); switch (this.currentType.kind) { case TypeKind.I8: case TypeKind.I16: case TypeKind.I32: - op = BinaryOp.RemI32; + expr = this.module.createBinary(BinaryOp.RemI32, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.RemI64, BinaryOp.RemI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.RemI64, BinaryOp.RemI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.I64: - op = BinaryOp.RemI64; + expr = this.module.createBinary(BinaryOp.RemI64, left, right); break; case TypeKind.U8: case TypeKind.U16: case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.RemU32; + expr = this.module.createBinary(BinaryOp.RemU32, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.RemU64, BinaryOp.RemU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.RemU64, BinaryOp.RemU32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U64: - op = BinaryOp.RemU64; + expr = this.module.createBinary(BinaryOp.RemU64, left, right); break; case TypeKind.F32: case TypeKind.F64: // TODO: internal fmod, possibly simply imported from JS this.error(DiagnosticCode.Operation_not_supported, expression.range); - return this.module.createUnreachable(); + expr = this.module.createUnreachable(); + break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("concrete type expected"); } break; case Token.LESSTHAN_LESSTHAN_EQUALS: - compound = Token.EQUALS; - case Token.LESSTHAN_LESSTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.LESSTHAN_LESSTHAN: // retains low bits of small integers + left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; default: - op = BinaryOp.ShlI32; + expr = this.module.createBinary(BinaryOp.ShlI32, left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.ShlI64; + expr = this.module.createBinary(BinaryOp.ShlI64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.ShlI64, BinaryOp.ShlI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.ShlI64, BinaryOp.ShlI32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; case Token.GREATERTHAN_GREATERTHAN_EQUALS: - compound = Token.EQUALS; - case Token.GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.GREATERTHAN_GREATERTHAN: // must wrap small integers + left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); switch (this.currentType.kind) { default: - op = BinaryOp.ShrI32; + // assumes signed shr on signed small integers does not overflow + expr = this.module.createBinary(BinaryOp.ShrI32, left, right); break; case TypeKind.I64: - op = BinaryOp.ShrI64; + expr = this.module.createBinary(BinaryOp.ShrI64, left, right); break; case TypeKind.ISIZE: - op = select(BinaryOp.ShrI64, BinaryOp.ShrI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.ShrI64, BinaryOp.ShrI32, this.options.target == Target.WASM64), left, right); break; case TypeKind.U8: case TypeKind.U16: - case TypeKind.U32: case TypeKind.BOOL: - op = BinaryOp.ShrU32; + // assumes unsigned shr on unsigned small integers does not overflow + case TypeKind.U32: + expr = this.module.createBinary(BinaryOp.ShrU32, left, right); break; case TypeKind.U64: - op = BinaryOp.ShrU64; + expr = this.module.createBinary(BinaryOp.ShrU64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload - op = select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: - compound = Token.EQUALS; - case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: // modifies low bits of small integers if unsigned + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT); switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + possiblyOverflows = true; + // fall-through default: - op = BinaryOp.ShrU32; + // assumes that unsigned shr on unsigned small integers does not overflow + expr = this.module.createBinary(BinaryOp.ShrU32, left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.ShrU64; + expr = this.module.createBinary(BinaryOp.ShrU64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.ShrU64, BinaryOp.ShrU32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; case Token.AMPERSAND_EQUALS: - compound = Token.EQUALS; - case Token.AMPERSAND: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.AMPERSAND: // retains low bits of small integers + left = this.compileExpression(expression.left, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // if left or right already did default: - op = BinaryOp.AndI32; + expr = this.module.createBinary(BinaryOp.AndI32, left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.AndI64; + expr = this.module.createBinary(BinaryOp.AndI64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.AndI64, BinaryOp.AndI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.AndI64, BinaryOp.AndI32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; case Token.BAR_EQUALS: - compound = Token.EQUALS; - case Token.BAR: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.BAR: // retains low bits of small integers + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // if left or right already did default: - op = BinaryOp.OrI32; + expr = this.module.createBinary(BinaryOp.OrI32, left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.OrI64; + expr = this.module.createBinary(BinaryOp.OrI64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.OrI64, BinaryOp.OrI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.OrI64, BinaryOp.OrI32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; case Token.CARET_EQUALS: - compound = Token.EQUALS; - case Token.CARET: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); - right = this.compileExpression(expression.right, this.currentType); + compound = true; + case Token.CARET: // retains low bits of small integers + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + right = this.compileExpression(expression.right, this.currentType, ConversionKind.IMPLICIT, false); switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // if left or right already did default: - op = BinaryOp.XorI32; + expr = this.module.createBinary(BinaryOp.XorI32, left, right); break; case TypeKind.I64: case TypeKind.U64: - op = BinaryOp.XorI64; + expr = this.module.createBinary(BinaryOp.XorI64, left, right); break; case TypeKind.USIZE: // TODO: check operator overload case TypeKind.ISIZE: - op = select(BinaryOp.XorI64, BinaryOp.XorI32, this.options.target == Target.WASM64); + expr = this.module.createBinary(select(BinaryOp.XorI64, BinaryOp.XorI32, this.options.target == Target.WASM64), left, right); break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); } break; // logical (no overloading) case Token.AMPERSAND_AMPERSAND: // left && right - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); // simplify if left is free of side effects while tolerating one level of nesting, e.g., i32.load(i32.const) - if (condition = this.module.cloneExpression(left, true, 1)) - return this.module.createIf( + if (condition = this.module.cloneExpression(left, true, 1)) { + expr = this.module.createIf( this.currentType.isLongInteger ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) : this.currentType == Type.f64 @@ -2061,11 +2139,13 @@ export class Compiler extends DiagnosticEmitter { right, left ); + break; + } // otherwise use a temporary local for the intermediate value tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType); condition = this.module.createTeeLocal(tempLocal.index, left); - return this.module.createIf( + expr = this.module.createIf( this.currentType.isLongInteger ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) : this.currentType == Type.f64 @@ -2076,14 +2156,15 @@ export class Compiler extends DiagnosticEmitter { right, this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()) ); + break; case Token.BAR_BAR: // left || right - left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); + left = this.compileExpression(expression.left, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); // simplify if left is free of side effects while tolerating one level of nesting - if (condition = this.module.cloneExpression(left, true, 1)) - return this.module.createIf( + if (condition = this.module.cloneExpression(left, true, 1)) { + expr = this.module.createIf( this.currentType.isLongInteger ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) : this.currentType == Type.f64 @@ -2094,11 +2175,13 @@ export class Compiler extends DiagnosticEmitter { left, right ); + break; + } // otherwise use a temporary local for the intermediate value tempLocal = this.currentFunction.getAndFreeTempLocal(this.currentType); condition = this.module.createTeeLocal(tempLocal.index, left); - return this.module.createIf( + expr = this.module.createIf( this.currentType.isLongInteger ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) : this.currentType == Type.f64 @@ -2109,16 +2192,19 @@ export class Compiler extends DiagnosticEmitter { this.module.createGetLocal(tempLocal.index, this.currentType.toNativeType()), right ); + break; default: this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("not implemented"); } - if (compound) { - right = this.module.createBinary(op, left, right); - return this.compileAssignmentWithValue(expression.left, right, contextualType != Type.void); + if (possiblyOverflows && wrapSmallIntegers) { + assert(this.currentType.isSmallInteger); + expr = wrapSmallInteger(expr, this.currentType, this.module); } - return this.module.createBinary(op, left, right); + return compound + ? this.compileAssignmentWithValue(expression.left, expr, contextualType != Type.void) + : expr; } compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef { @@ -2161,7 +2247,7 @@ export class Compiler extends DiagnosticEmitter { // now compile the value and do the assignment this.currentType = elementType; - return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, elementType, ConversionKind.IMPLICIT), contextualType != Type.void); + return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, elementType), contextualType != Type.void); } compileAssignmentWithValue(expression: Expression, valueWithCorrectType: ExpressionRef, tee: bool = false): ExpressionRef { @@ -2292,8 +2378,7 @@ export class Compiler extends DiagnosticEmitter { functionInstance = functionPrototype.instances.get(""); if (!functionInstance) { - this.currentType = contextualType; - var expr = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, expression); + var expr = compileBuiltinCall(this, functionPrototype, resolvedTypeArguments, expression.arguments, contextualType, expression); if (!expr) { this.error(DiagnosticCode.Operation_not_supported, expression.range); return this.module.createUnreachable(); @@ -2502,8 +2587,12 @@ export class Compiler extends DiagnosticEmitter { return this.module.createF32(intValue.toF64()); if (contextualType.isLongInteger) return this.module.createI64(intValue.lo, intValue.hi); + if (!intValue.fitsInI32) { + this.currentType = select(Type.i64, Type.u64, contextualType.isAnySignedInteger); + return this.module.createI64(intValue.lo, intValue.hi); + } if (contextualType.isSmallInteger) { - var smallIntValue: i32 = contextualType.isSignedInteger + var smallIntValue: i32 = contextualType.isAnySignedInteger ? intValue.lo << contextualType.smallIntegerShift >> contextualType.smallIntegerShift : intValue.lo & contextualType.smallIntegerMask; return this.module.createI32(smallIntValue); @@ -2512,7 +2601,7 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.i64; return this.module.createI64(intValue.lo, intValue.hi); } - this.currentType = contextualType.isSignedInteger ? Type.i32 : Type.u32; + this.currentType = contextualType.isAnySignedInteger ? Type.i32 : Type.u32; return this.module.createI32(intValue.toI32()); } @@ -2566,7 +2655,7 @@ export class Compiler extends DiagnosticEmitter { assert((element).memoryOffset >= 0); targetExpr = this.compileExpression(resolved.targetExpression, select(Type.usize64, Type.usize32, this.options.target == Target.WASM64)); this.currentType = (element).type; - return this.module.createLoad((element).type.byteSize, (element).type.isSignedInteger, + return this.module.createLoad((element).type.byteSize, (element).type.isAnySignedInteger, targetExpr, (element).type.toNativeType(), (element).memoryOffset @@ -2601,226 +2690,358 @@ export class Compiler extends DiagnosticEmitter { var operator = expression.operator; // make a getter for the expression (also obtains the type) - var getValue = this.compileExpression(expression.operand, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); - - // use a temp local for the intermediate value - var tempLocal = this.currentFunction.getTempLocal(this.currentType); - assert(tempLocal.type != null); + var getValue = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); var op: BinaryOp; var nativeType: NativeType; var nativeOne: ExpressionRef; + var possiblyOverflows = false; - switch ((tempLocal.type).kind) { + switch (expression.operator) { + + case Token.PLUS_PLUS: + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; + default: + op = BinaryOp.AddI32; + nativeType = NativeType.I32; + nativeOne = this.module.createI32(1); + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.AddI64, BinaryOp.AddI32, this.options.target == Target.WASM64); + nativeType = select(NativeType.I64, NativeType.I32, this.options.target == Target.WASM64); + nativeOne = this.currentType.toNativeOne(this.module); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.AddI64; + nativeType = NativeType.I64; + nativeOne = this.module.createI64(1, 0); + break; + + case TypeKind.F32: + op = BinaryOp.AddF32; + nativeType = NativeType.F32; + nativeOne = this.module.createF32(1); + break; + + case TypeKind.F64: + op = BinaryOp.AddF64; + nativeType = NativeType.F64; + nativeOne = this.module.createF64(1); + break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); + } + break; + + case Token.MINUS_MINUS: + switch (this.currentType.kind) { + + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; + default: + op = BinaryOp.SubI32; + nativeType = NativeType.I32; + nativeOne = this.module.createI32(1); + break; + + case TypeKind.USIZE: + // TODO: check operator overload + case TypeKind.ISIZE: + op = select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64); + nativeType = select(NativeType.I64, NativeType.I32, this.options.target == Target.WASM64); + nativeOne = this.currentType.toNativeOne(this.module); + break; + + case TypeKind.I64: + case TypeKind.U64: + op = BinaryOp.SubI64; + nativeType = NativeType.I64; + nativeOne = this.module.createI64(1, 0); + break; + + case TypeKind.F32: + op = BinaryOp.SubF32; + nativeType = NativeType.F32; + nativeOne = this.module.createF32(1); + break; + + case TypeKind.F64: + op = BinaryOp.SubF64; + nativeType = NativeType.F64; + nativeOne = this.module.createF64(1); + break; + + case TypeKind.VOID: + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("concrete type expected"); + } + break; default: - op = select(BinaryOp.AddI32, BinaryOp.SubI32, operator == Token.PLUS_PLUS); - nativeType = NativeType.I32; - nativeOne = this.module.createI32(1); - break; - - case TypeKind.USIZE: - // TODO: check operator overload - case TypeKind.ISIZE: - if (this.options.target != Target.WASM64) { - op = select(BinaryOp.AddI32, BinaryOp.SubI32, operator == Token.PLUS_PLUS); - nativeType = NativeType.I32; - nativeOne = this.module.createI32(1); - break; - } - // fall-through - - case TypeKind.I64: - case TypeKind.U64: - op = select(BinaryOp.AddI64, BinaryOp.SubI64, operator == Token.PLUS_PLUS); - nativeType = NativeType.I64; - nativeOne = this.module.createI64(1, 0); - break; - - case TypeKind.F32: - op = select(BinaryOp.AddF32, BinaryOp.SubF32, operator == Token.PLUS_PLUS); - nativeType = NativeType.F32; - nativeOne = this.module.createF32(1); - break; - - case TypeKind.F64: - op = select(BinaryOp.AddF64, BinaryOp.SubF64, operator == Token.PLUS_PLUS); - nativeType = NativeType.F64; - nativeOne = this.module.createF64(1); - break; + this.error(DiagnosticCode.Operation_not_supported, expression.range); + throw new Error("unary postfix operator expected"); } - // make a setter that sets the new value (temp value +/- 1) - // note that this is not inlined below because it modifies currentType - var setValue = this.compileAssignmentWithValue(expression.operand, - this.module.createBinary(op, - this.module.createGetLocal(tempLocal.index, nativeType), - nativeOne - ), false - ); + // simplify if dropped anyway + if (contextualType == Type.void) + return this.compileAssignmentWithValue(expression.operand, + this.module.createBinary(op, + getValue, + nativeOne + ), false + ); - // NOTE: can't preemptively tee_local the return value on the stack because binaryen expects - // this to be well-formed. becomes a tee_local when optimizing, though. - this.currentType = tempLocal.type; + // use a temp local for the intermediate value and prepare a setter + var tempLocal = this.currentFunction.getTempLocal(this.currentType); + var setValue = this.module.createBinary(op, + this.module.createGetLocal(tempLocal.index, nativeType), + nativeOne + ); + if (possiblyOverflows) { + assert(this.currentType.isSmallInteger); + setValue = wrapSmallInteger(setValue, this.currentType, this.module); + } + setValue = this.compileAssignmentWithValue(expression.operand, setValue, false); // sets currentType = void + this.currentType = tempLocal.type; this.currentFunction.freeTempLocal(tempLocal); return this.module.createBlock(null, [ - this.module.createSetLocal(tempLocal.index, getValue), // +++ this.module.createTeeLocal(tempLocal.index, getValue), + this.module.createSetLocal(tempLocal.index, getValue), setValue, - this.module.createGetLocal(tempLocal.index, nativeType) // --- + this.module.createGetLocal(tempLocal.index, nativeType) ], nativeType); } - compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): ExpressionRef { - var operandExpression = expression.operand; - - var operand: ExpressionRef; - var op: UnaryOp; + compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type, wrapSmallIntegers: bool = true): ExpressionRef { + var possiblyOverflows = false; + var compound = false; + var expr: ExpressionRef; switch (expression.operator) { case Token.PLUS: - return this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + if (this.currentType.isReference) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + } + expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + possiblyOverflows = this.currentType.isSmallInteger; // if operand already did + break; case Token.MINUS: - operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + switch (this.currentType.kind) { - default: - return this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand); - - case TypeKind.ISIZE: - case TypeKind.USIZE: - if (this.options.target != Target.WASM64) - return this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand); + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // or if operand already did // fall-through + default: + expr = this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), expr); + break; + + case TypeKind.USIZE: + if (this.currentType.isReference) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + } + // fall-through + case TypeKind.ISIZE: + expr = this.module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64), this.currentType.toNativeZero(this.module), expr); + break; case TypeKind.I64: case TypeKind.U64: - return this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand); + expr = this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), expr); + break; case TypeKind.F32: - op = UnaryOp.NegF32; + expr = this.module.createUnary(UnaryOp.NegF32, expr); break; case TypeKind.F64: - op = UnaryOp.NegF64; + expr = this.module.createUnary(UnaryOp.NegF64, expr); break; } break; case Token.PLUS_PLUS: - operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + compound = true; + expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // or if operand already did default: - operand = this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)); + expr = this.module.createBinary(BinaryOp.AddI32, expr, this.module.createI32(1)); break; - case TypeKind.ISIZE: case TypeKind.USIZE: - if (this.options.target != Target.WASM64) { - operand = this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)); - break; + if (this.currentType.isReference) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); } // fall-through + case TypeKind.ISIZE: + expr = this.module.createBinary(select(BinaryOp.AddI64, BinaryOp.AddI32, this.options.target == Target.WASM64), expr, this.currentType.toNativeOne(this.module)); + break; case TypeKind.I64: case TypeKind.U64: - operand = this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)); + expr = this.module.createBinary(BinaryOp.AddI64, expr, this.module.createI64(1, 0)); break; case TypeKind.F32: - operand = this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)); + expr = this.module.createBinary(BinaryOp.AddF32, expr, this.module.createF32(1)); break; case TypeKind.F64: - operand = this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)); + expr = this.module.createBinary(BinaryOp.AddF64, expr, this.module.createF64(1)); break; } - return this.compileAssignmentWithValue(operandExpression, operand, contextualType != Type.void); + break; case Token.MINUS_MINUS: - operand = this.compileExpression(operandExpression, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + compound = true; + expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE, false); + switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // or if operand already did + // fall-through default: - operand = this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)); + expr = this.module.createBinary(BinaryOp.SubI32, expr, this.module.createI32(1)); break; - case TypeKind.ISIZE: case TypeKind.USIZE: - if (this.options.target != Target.WASM64) { - operand = this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)); - break; + if (this.currentType.isReference) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); } // fall-through + case TypeKind.ISIZE: + expr = this.module.createBinary(select(BinaryOp.SubI64, BinaryOp.SubI32, this.options.target == Target.WASM64), expr, this.currentType.toNativeOne(this.module)); + break; case TypeKind.I64: case TypeKind.U64: - operand = this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)); + expr = this.module.createBinary(BinaryOp.SubI64, expr, this.module.createI64(1, 0)); break; case TypeKind.F32: - operand = this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)); + expr = this.module.createBinary(BinaryOp.SubF32, expr, this.module.createF32(1)); break; case TypeKind.F64: - operand = this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)); + expr = this.module.createBinary(BinaryOp.SubF64, expr, this.module.createF64(1)); break; } - return this.compileAssignmentWithValue(operandExpression, operand, contextualType != Type.void); + break; + + case Token.EXCLAMATION: // must wrap small integers + expr = this.compileExpression(expression.operand, select(Type.i32, contextualType, contextualType == Type.void), ConversionKind.NONE); - case Token.EXCLAMATION: - operand = this.compileExpression(operandExpression, Type.bool, ConversionKind.NONE); switch (this.currentType.kind) { default: - op = UnaryOp.EqzI32; + expr = this.module.createUnary(UnaryOp.EqzI32, expr); break; case TypeKind.ISIZE: case TypeKind.USIZE: - op = select(UnaryOp.EqzI64, UnaryOp.EqzI32, this.options.target == Target.WASM64); + expr = this.module.createUnary(select(UnaryOp.EqzI64, UnaryOp.EqzI32, this.options.target == Target.WASM64), expr); break; case TypeKind.I64: case TypeKind.U64: - op = UnaryOp.EqzI64; + expr = this.module.createUnary(UnaryOp.EqzI64, expr); break; case TypeKind.F32: - this.currentType = Type.bool; - return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0)); + expr = this.module.createBinary(BinaryOp.EqF32, expr, this.module.createF32(0)); + break; case TypeKind.F64: - this.currentType = Type.bool; - return this.module.createBinary(BinaryOp.EqF64, operand, this.module.createF64(0)); + expr = this.module.createBinary(BinaryOp.EqF64, expr, this.module.createF64(0)); + break; } this.currentType = Type.bool; break; - case Token.TILDE: - operand = this.compileExpression(operandExpression, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + case Token.TILDE: // retains low bits of small integers + expr = this.compileExpression(expression.operand, select(Type.i64, select(Type.i32, contextualType, contextualType == Type.void), contextualType.isAnyFloat), select(ConversionKind.NONE, ConversionKind.IMPLICIT, contextualType == Type.void), false); + switch (this.currentType.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + possiblyOverflows = true; // or if operand already did default: - return this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); + expr = this.module.createBinary(BinaryOp.XorI32, expr, this.module.createI32(-1)); + break; - case TypeKind.ISIZE: case TypeKind.USIZE: - if (this.options.target != Target.WASM64) - return this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1)); + if (this.currentType.isReference) { + this.error(DiagnosticCode.Operation_not_supported, expression.range); + return this.module.createUnreachable(); + } // fall-through + case TypeKind.ISIZE: + expr = this.module.createBinary(select(BinaryOp.XorI64, BinaryOp.XorI32, this.options.target == Target.WASM64), expr, this.currentType.toNativeNegOne(this.module)); + break; case TypeKind.I64: case TypeKind.U64: - return this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1)); + expr = this.module.createBinary(BinaryOp.XorI64, expr, this.module.createI64(-1, -1)); + break; } + break; default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("unary operator expected"); } - return this.module.createUnary(op, operand); + if (possiblyOverflows && wrapSmallIntegers) { + assert(this.currentType.isSmallInteger); + expr = wrapSmallInteger(expr, this.currentType, this.module); + } + return compound + ? this.compileAssignmentWithValue(expression.operand, expr, contextualType != Type.void) + : expr; } } @@ -2886,3 +3107,51 @@ function makeInlineConstant(element: VariableLikeElement, module: Module): Expre } throw new Error("concrete type expected"); } + +/** Wraps a 32-bit integer expression so it evaluates to a valid value in the range of the specified small integer type. */ +export function wrapSmallInteger(expr: ExpressionRef, type: Type, module: Module) { + switch (type.kind) { + + case TypeKind.I8: + expr = module.createBinary(BinaryOp.ShrI32, + module.createBinary(BinaryOp.ShlI32, + expr, + module.createI32(24) + ), + module.createI32(24) + ); + break; + + case TypeKind.I16: + expr = module.createBinary(BinaryOp.ShrI32, + module.createBinary(BinaryOp.ShlI32, + expr, + module.createI32(16) + ), + module.createI32(16) + ); + break; + + case TypeKind.U8: + expr = module.createBinary(BinaryOp.AndI32, + expr, + module.createI32(0xff) + ); + break; + + case TypeKind.U16: + expr = module.createBinary(BinaryOp.AndI32, + expr, + module.createI32(0xffff) + ); + break; + + case TypeKind.BOOL: + expr = module.createBinary(BinaryOp.AndI32, + expr, + module.createI32(0x1) + ); + break; + } + return expr; +} diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 8fbbf4eb..cb20ec77 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -11,6 +11,7 @@ export enum DiagnosticCode { Structs_cannot_extend_classes_and_vice_versa = 107, Structs_cannot_implement_interfaces = 108, Invalid_regular_expression_flags = 109, + Type_0_cannot_be_reinterpreted_as_type_1 = 110, Unterminated_string_literal = 1002, Identifier_expected = 1003, _0_expected = 1005, @@ -97,6 +98,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 107: return "Structs cannot extend classes and vice-versa."; case 108: return "Structs cannot implement interfaces."; case 109: return "Invalid regular expression flags."; + case 110: return "Type '{0}' cannot be reinterpreted as type '{1}'."; case 1002: return "Unterminated string literal."; case 1003: return "Identifier expected."; case 1005: return "'{0}' expected."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 192eea18..a91c46ac 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -9,6 +9,7 @@ "Structs cannot extend classes and vice-versa.": 107, "Structs cannot implement interfaces.": 108, "Invalid regular expression flags.": 109, + "Type '{0}' cannot be reinterpreted as type '{1}'.": 110, "Unterminated string literal.": 1002, "Identifier expected.": 1003, diff --git a/src/tokenizer.ts b/src/tokenizer.ts index f36bca78..89a6b61b 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -351,6 +351,8 @@ export class Range { } } +declare function parseFloat(str: string): f64; + export class Tokenizer extends DiagnosticEmitter { source: Source; diff --git a/src/types.ts b/src/types.ts index 93fce621..7e11614a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,8 +58,10 @@ export class Type { functionType: Function | null; /** Whether nullable or not. */ isNullable: bool = false; - /** Respective nullable type, if nullable. */ + /** Respective nullable type, if non-nullable. */ nullableType: Type | null = null; + /** Respective non-nullable type, if nullable. */ + nonNullableType: Type; /** Constructs a new resolved type. */ constructor(kind: TypeKind, size: i32) { @@ -67,6 +69,7 @@ export class Type { this.size = size; this.byteSize = ceil(size / 8); this.classType = null; + this.nonNullableType = this; } /** Sign-extending 32-bit shift, if a small signed integer. */ @@ -75,23 +78,157 @@ export class Type { get smallIntegerMask(): i32 { return -1 >>> (32 - this.size); } /** Tests if this type is of any integer kind. */ - get isAnyInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.BOOL; } - /** Tests if this type is of any small integer kind. */ - get isSmallInteger(): bool { return this.size != 0 && this.size < 32; } - /** Tests if this type is of any long integer kind. */ - get isLongInteger(): bool { return this.size == 64 && this.kind != TypeKind.F64; } + get isAnyInteger(): bool { + switch (this.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.I64: + case TypeKind.ISIZE: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.U64: + case TypeKind.USIZE: + case TypeKind.BOOL: + return true; + default: + return false; + } + } + /** Tests if this type is of any unsigned integer kind. */ - get isUnsignedInteger(): bool { return this.kind >= TypeKind.U8 && this.kind <= TypeKind.BOOL; } + get isAnyUnsignedInteger(): bool { + switch (this.kind) { + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.U64: + case TypeKind.USIZE: + case TypeKind.BOOL: + return true; + default: + return false; + } + } + /** Tests if this type is of any signed integer kind. */ - get isSignedInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.ISIZE; } - /** Tests if this type is of any size kind, i.e., `isize` or `usize`. */ - get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; } + get isAnySignedInteger(): bool { + switch (this.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: + case TypeKind.I64: + case TypeKind.ISIZE: + return true; + default: + return false; + } + } + + /** Tests if this type is of any small integer kind. */ + get isSmallInteger(): bool { + switch (this.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + return true; + default: + return false; + } + } + + /** Tests if this type is of any small signed integer kind. */ + get isSmallSignedInteger(): bool { + switch (this.kind) { + case TypeKind.I8: + case TypeKind.I16: + return true; + default: + return false; + } + } + + /** Tests if this type is of any small unsigned integer kind. */ + get isSmallUnsignedInteger(): bool { + switch (this.kind) { + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.BOOL: + return true; + default: + return false; + } + } + + /** Tests if this type is of any long integer kind. */ + get isLongInteger(): bool { + switch (this.kind) { + case TypeKind.I64: + case TypeKind.U64: + return true; + case TypeKind.ISIZE: + case TypeKind.USIZE: + return this.size == 64; + default: + return false; + } + } + + /** Tests if this type is of any long signed integer kind. */ + get isLongSignedInteger(): bool { + switch (this.kind) { + case TypeKind.I64: + return true; + case TypeKind.ISIZE: + return this.size == 64; + default: + return false; + } + } + + /** Tests if this type is of any long unsigned integer kind. */ + get isLongUnsignedInteger(): bool { + switch (this.kind) { + case TypeKind.U64: + return true; + case TypeKind.USIZE: + return this.size == 64; + default: + return false; + } + } + + /** Tests if this type is of any size kind, that is `isize` or `usize`. */ + get isAnySize(): bool { + switch (this.kind) { + case TypeKind.ISIZE: + case TypeKind.USIZE: + return true; + default: + return false; + } + } + /** Tests if this type is of any float kind, i.e., `f32` or `f64`. */ - get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; } + get isAnyFloat(): bool { + switch (this.kind) { + case TypeKind.F32: + case TypeKind.F64: + return true; + default: + return false; + } + } + /** Tests if this type is a class type. */ get isClass(): bool { return this.classType != null; } /** Tests if this type is a function type. */ get isFunction(): bool { return this.functionType != null; } + /** Tests if this type is a reference type. */ + get isReference(): bool { return this.classType != null || this.functionType != null; } /** Composes a class type from this type and a class. */ asClass(classType: Class): Type { @@ -103,7 +240,7 @@ export class Type { /** Composes a function type from this type and a function. */ asFunction(functionType: Function): Type { - assert(this.kind == TypeKind.USIZE); + assert(this.kind == TypeKind.USIZE && !this.isReference); var ret = new Type(this.kind, this.size); ret.functionType = functionType; return ret; @@ -111,9 +248,12 @@ export class Type { /** Composes the respective nullable type of this type. */ asNullable(): Type | null { - assert(this.kind == TypeKind.USIZE); - if (this.isNullable && !this.nullableType) + assert(this.kind == TypeKind.USIZE && !this.isReference); + if (this.isNullable && !this.nullableType) { (this.nullableType = new Type(this.kind, this.size)).isNullable = true; + this.nullableType.classType = this.classType; + this.nullableType.functionType = this.functionType; + } return this.nullableType; } @@ -226,6 +366,34 @@ export class Type { } } + /** Converts this type to its native `-1` value. */ + toNativeNegOne(module: Module): ExpressionRef { + switch (this.kind) { + + case TypeKind.VOID: + assert(false); + + default: + return module.createI32(-1); + + case TypeKind.ISIZE: + case TypeKind.USIZE: + if (this.size != 64) + return module.createI32(-1); + // fall-through + + case TypeKind.I64: + case TypeKind.U64: + return module.createI64(-1, -1); + + case TypeKind.F32: + return module.createF32(-1); + + case TypeKind.F64: + return module.createF64(-1); + } + } + /** Converts this type to its signature string. */ toSignatureString(): string { switch (this.kind) { diff --git a/std/assembly.d.ts b/std/assembly.d.ts index 85819f6f..d5cb7a2e 100644 --- a/std/assembly.d.ts +++ b/std/assembly.d.ts @@ -173,14 +173,14 @@ declare const Infinity: f32 | f64; declare const HEAP_BASE: usize; /** Determines the byte size of the specified core or class type. Compiles to a constant. */ declare function sizeof(): usize; -/** Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa. */ +/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/ declare function changetype(value: any): T; /** Tests if a 32-bit or 64-bit float is `NaN`. */ declare function isNaN(value: T): bool; /** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */ declare function isFinite(value: T): bool; -/** Traps if the specified value evaluates to `false`. */ -declare function assert(isTrue: bool, message?: string): void; +/** Traps if the specified value is not true-ish, otherwise returns the value. */ +declare function assert(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`? /** Parses an integer string to a 64-bit float. */ declare function parseInt(str: string, radix?: i32): f64; /** Parses a string to a 64-bit float. */ diff --git a/std/assembly/regexp.ts b/std/assembly/regexp.ts new file mode 100644 index 00000000..daf44cf5 --- /dev/null +++ b/std/assembly/regexp.ts @@ -0,0 +1,13 @@ +@global +class RegExp { + + // @binding(CALL_NEW, [ STRING, STRING], OBJECT_HANDLE) + constructor(pattern: string, flags: string = "") { throw new Error("unreachable"); } + + // @binding(CALL_THIS, [ STRING ], PASS_THRU) + test(search: string): bool { throw new Error("unreachable"); } + + // @binding(CALL_THIS, [], STRING) + toString(): string { throw new Error("unreachable"); } + +} diff --git a/std/assembly/string.ts b/std/assembly/string.ts index 38521c43..2ed5cac2 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -196,3 +196,15 @@ function isWhiteSpaceOrLineTerminator(c: u16): bool { return false; } } + +// @global +// @binding(CALL, [ STRING, PASS_THRU ], PASS_THRU) +export function parseInt(str: string, radix: i32 = 10): f64 { + throw new Error("not implemented"); +} + +// @global +// @binding(CALL, [ STRING ], PASS_THRU) +export function parseFloat(str: string): f64 { + throw new Error("not implemented"); +} diff --git a/std/portable.d.ts b/std/portable.d.ts index 77656aaa..e24e56b1 100644 --- a/std/portable.d.ts +++ b/std/portable.d.ts @@ -123,10 +123,10 @@ declare function store(offset: usize, value: T): void; /** Emits an unreachable operation that results in a runtime error when executed. */ declare function unreachable(): any; // sic -/** Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa. */ +/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/ declare function changetype(value: any): T; -/** Traps if the specified value evaluates to `false`. */ -declare function assert(isTrue: bool, message?: string): void; +/** Traps if the specified value is not true-ish, otherwise returns the value. */ +declare function assert(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`? /** Parses an integer string to a 64-bit float. */ declare function parseInt(str: string, radix?: i32): f64; /** Parses a floating point string to a 64-bit float. */ diff --git a/std/portable.js b/std/portable.js index 415db249..cda83fe6 100644 --- a/std/portable.js +++ b/std/portable.js @@ -93,7 +93,7 @@ function AssertionError(message) { AssertionError.prototype = Object.create(Error.prototype); AssertionError.prototype.name = "AssertionError"; -globalScope["assert"] = function assert(isTrue, message) { if (!isTrue) throw new AssertionError(message); }; +globalScope["assert"] = function assert(isTrueish, message) { if (isTrueish) return isTrueish; throw new AssertionError(message); }; globalScope["changetype"] = function changetype(value) { return value; } String["fromCharCodes"] = function fromCharCodes(arr) { return String.fromCharCode.apply(String, arr); } diff --git a/tests/compiler.js b/tests/compiler.js index 9aef86a9..acd7b3f3 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -17,13 +17,17 @@ var filter = process.argv.length > 2 && !isCreate ? "**/" + process.argv[2] + ". var stdFiles = glob.sync("*.ts", { cwd: __dirname + "/../std/assembly" }); +var success = 0; +var failures = 0; + glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { if (filename.charAt(0) == "_") return; - console.log(chalk.default.whiteBright("Testing compiler/" + filename)); + console.log(chalk.whiteBright("Testing compiler/" + filename)); var fixture = path.basename(filename, ".ts"); + var startTime = process.hrtime(); var parser = new Parser(); if (filename.startsWith("std/")) { stdFiles.forEach(file => { @@ -45,18 +49,24 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { parser.parseFile(nextSourceText, nextFile, false); } var program = parser.finish(); + var parseTime = process.hrtime(startTime); + startTime = process.hrtime(); var module = Compiler.compile(program); + var compileTime = process.hrtime(startTime); var actual = module.toText() + "(;\n[program.elements]\n " + elements(program.elements) + "\n[program.exports]\n " + elements(program.exports) + "\n;)\n"; var actualOptimized = null; var actualInlined = null; + console.log("parse incl. I/O: " + ((parseTime[0] * 1e9 + parseTime[1]) / 1e6).toFixed(3) + "ms / compile: " + ((compileTime[0] * 1e9 + compileTime[1]) / 1e6).toFixed(3) + "ms"); + + var failed = false; if (module.validate()) { - console.log(chalk.default.green("validate OK")); + console.log(chalk.green("validate OK")); try { module.interpret(); - console.log(chalk.default.green("interpret OK")); + console.log(chalk.green("interpret OK")); try { var binary = module.toBinary(); var wasmModule = new WebAssembly.Module(binary); @@ -70,22 +80,22 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { externalConst: 2 } }); - console.log(chalk.default.green("instantiate OK")); + console.log(chalk.green("instantiate OK")); } catch (e) { - process.exitCode = 1; - console.log(chalk.default.red("instantiate ERROR: ") + e); + failed = true; + console.log(chalk.red("instantiate ERROR: ") + e); } } catch (e) { - process.exitCode = 1; - console.log(chalk.default.red("interpret ERROR:") + e); + failed = true; + console.log(chalk.red("interpret ERROR:") + e); } module.optimize(); actualOptimized = module.toText(); module.runPasses([ "inlining" ]); actualInlined = module.toText(); } else { - process.exitCode = 1; - console.log(chalk.default.red("validate ERROR")); + failed = true; + console.log(chalk.red("validate ERROR")); } if (isCreate) { @@ -107,25 +117,25 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => { } } } else { + var expected; try { - var expected = fs.readFileSync(__dirname + "/compiler/" + fixture + ".wast", { encoding: "utf8" }); - var diffs = diff(filename + ".wast", expected, actual); - if (diffs !== null) { - process.exitCode = 1; - console.log(diffs); - console.log(chalk.default.red("diff ERROR")); - } else { - console.log(chalk.default.green("diff OK")); - } + expected = fs.readFileSync(__dirname + "/compiler/" + fixture + ".wast", { encoding: "utf8" }); } catch (e) { - process.exitCode = 1; - console.log(e.message); - console.log(chalk.default.red("diff ERROR")); + expected = e.message + "\n"; } + var diffs = diff(filename + ".wast", expected, actual); + if (diffs !== null) { + console.log(diffs); + console.log(chalk.red("diff ERROR")); + failed = true; + } else + console.log(chalk.green("diff OK")); } module.dispose(); console.log(); + if (failed) + ++failures; }); function elements(map) { @@ -135,3 +145,9 @@ function elements(map) { }); return arr.join("\n "); } + +if (failures) { + process.exitCode = 1; + console.log(chalk.red("ERROR: ") + failures + " compiler tests failed"); +} else + console.log(chalk.whiteBright("SUCCESS")); diff --git a/tests/compiler/assert.optimized.wast b/tests/compiler/assert.optimized.wast index bfec40ca..453abe27 100644 --- a/tests/compiler/assert.optimized.wast +++ b/tests/compiler/assert.optimized.wast @@ -4,6 +4,18 @@ (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) - (nop) + (local $0 i32) + (if + (i32.eqz + (if (result i32) + (tee_local $0 + (i32.const 1) + ) + (get_local $0) + (unreachable) + ) + ) + (unreachable) + ) ) ) diff --git a/tests/compiler/assert.ts b/tests/compiler/assert.ts index 5dfd0d5f..97958cca 100644 --- a/tests/compiler/assert.ts +++ b/tests/compiler/assert.ts @@ -1,3 +1,11 @@ assert(true); -assert(1 == 1); +assert(1); +assert(1 > 0); +assert(0.5); assert(0.5 > 0.4); +assert(0x100000000); +assert(0x100000000 > 1); + +// can be used as an expression +if (!assert(true, "must be true")) + unreachable(); diff --git a/tests/compiler/assert.wast b/tests/compiler/assert.wast index 79f48403..87a71261 100644 --- a/tests/compiler/assert.wast +++ b/tests/compiler/assert.wast @@ -5,6 +5,7 @@ (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) + (local $0 i32) (if (i32.eqz (i32.const 1) @@ -13,13 +14,26 @@ ) (if (i32.eqz - (i32.eq - (i32.const 1) + (i32.const 1) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.gt_s (i32.const 1) + (i32.const 0) ) ) (unreachable) ) + (if + (f64.eq + (f64.const 0.5) + (f64.const 0) + ) + (unreachable) + ) (if (i32.eqz (f64.gt @@ -29,6 +43,35 @@ ) (unreachable) ) + (if + (i64.eqz + (i64.const 4294967296) + ) + (unreachable) + ) + (if + (i32.eqz + (i64.gt_s + (i64.const 4294967296) + (i64.const 1) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (if (result i32) + (i32.eqz + (tee_local $0 + (i32.const 1) + ) + ) + (unreachable) + (get_local $0) + ) + ) + (unreachable) + ) ) ) (; @@ -59,8 +102,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast index 0019a23e..7b6c5992 100644 --- a/tests/compiler/binary.wast +++ b/tests/compiler/binary.wast @@ -844,8 +844,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/builtins.optimized.wast b/tests/compiler/builtins.optimized.wast index 62604dd7..47a76c51 100644 --- a/tests/compiler/builtins.optimized.wast +++ b/tests/compiler/builtins.optimized.wast @@ -65,14 +65,14 @@ ) (set_global $builtins/i (select + (tee_local $2 + (i32.const -42) + ) (i32.sub (i32.const 0) - (tee_local $2 - (i32.const -42) - ) + (get_local $2) ) - (get_local $2) - (i32.lt_s + (i32.gt_s (get_local $2) (i32.const 0) ) @@ -144,14 +144,14 @@ ) (set_global $builtins/I (select + (tee_local $4 + (i64.const -42) + ) (i64.sub (i64.const 0) - (tee_local $4 - (i64.const -42) - ) + (get_local $4) ) - (get_local $4) - (i64.lt_s + (i64.gt_s (get_local $4) (i64.const 0) ) diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index c9aac83e..997c3e36 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -77,17 +77,17 @@ ) (drop (select - (i32.sub - (i32.const 0) - (tee_local $0 - (i32.sub - (i32.const 0) - (i32.const 42) - ) + (tee_local $0 + (i32.sub + (i32.const 0) + (i32.const 42) ) ) - (get_local $0) - (i32.lt_s + (i32.sub + (i32.const 0) + (get_local $0) + ) + (i32.gt_s (get_local $0) (i32.const 0) ) @@ -150,17 +150,17 @@ ) (set_global $builtins/i (select - (i32.sub - (i32.const 0) - (tee_local $0 - (i32.sub - (i32.const 0) - (i32.const 42) - ) + (tee_local $0 + (i32.sub + (i32.const 0) + (i32.const 42) ) ) - (get_local $0) - (i32.lt_s + (i32.sub + (i32.const 0) + (get_local $0) + ) + (i32.gt_s (get_local $0) (i32.const 0) ) @@ -250,17 +250,17 @@ ) (drop (select - (i64.sub - (i64.const 0) - (tee_local $2 - (i64.sub - (i64.const 0) - (i64.const 42) - ) + (tee_local $2 + (i64.sub + (i64.const 0) + (i64.const 42) ) ) - (get_local $2) - (i64.lt_s + (i64.sub + (i64.const 0) + (get_local $2) + ) + (i64.gt_s (get_local $2) (i64.const 0) ) @@ -295,17 +295,17 @@ ) (set_global $builtins/I (select - (i64.sub - (i64.const 0) - (tee_local $2 - (i64.sub - (i64.const 0) - (i64.const 42) - ) + (tee_local $2 + (i64.sub + (i64.const 0) + (i64.const 42) ) ) - (get_local $2) - (i64.lt_s + (i64.sub + (i64.const 0) + (get_local $2) + ) + (i64.gt_s (get_local $2) (i64.const 0) ) @@ -1370,8 +1370,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/class-extends.wast b/tests/compiler/class-extends.wast index 4a5d89eb..506934de 100644 --- a/tests/compiler/class-extends.wast +++ b/tests/compiler/class-extends.wast @@ -53,8 +53,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/class.ts b/tests/compiler/class.ts index cfa847a4..ed66240e 100644 --- a/tests/compiler/class.ts +++ b/tests/compiler/class.ts @@ -25,8 +25,8 @@ export function test(animal: Animal): Animal { animal.three; animal.one = 0 + 1; - animal.two = 1 + 1; - animal.three = 1 + 1 + 1; + animal.two = 1 + 1; // checks overflow + animal.three = 1 + 1 + 1; // checks overflow (once) var ptr = changetype(animal); var cls = changetype>(ptr); diff --git a/tests/compiler/class.wast b/tests/compiler/class.wast index c920f1ee..2a4972c0 100644 --- a/tests/compiler/class.wast +++ b/tests/compiler/class.wast @@ -100,19 +100,31 @@ ) (i32.store16 offset=4 (get_local $0) - (i32.add - (i32.const 1) - (i32.const 1) + (i32.shr_s + (i32.shl + (i32.add + (i32.const 1) + (i32.const 1) + ) + (i32.const 16) + ) + (i32.const 16) ) ) (i32.store8 offset=6 (get_local $0) - (i32.add - (i32.add - (i32.const 1) - (i32.const 1) + (i32.shr_s + (i32.shl + (i32.add + (i32.add + (i32.const 1) + (i32.const 1) + ) + (i32.const 1) + ) + (i32.const 24) ) - (i32.const 1) + (i32.const 24) ) ) (block @@ -184,8 +196,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/comma.wast b/tests/compiler/comma.wast index d31e4383..c139be76 100644 --- a/tests/compiler/comma.wast +++ b/tests/compiler/comma.wast @@ -47,18 +47,10 @@ (unreachable) ) (block - (drop - (block (result i32) - (set_local $0 - (get_global $comma/a) - ) - (set_global $comma/a - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $comma/a + (i32.add + (get_global $comma/a) + (i32.const 1) ) ) (set_global $comma/b @@ -93,18 +85,10 @@ ) (set_global $comma/b (block (result i32) - (drop - (block (result i32) - (set_local $0 - (get_global $comma/a) - ) - (set_global $comma/a - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $comma/a + (i32.add + (get_global $comma/a) + (i32.const 1) ) ) (get_global $comma/a) @@ -130,18 +114,10 @@ ) (set_global $comma/a (block (result i32) - (drop - (block (result i32) - (set_local $0 - (get_global $comma/a) - ) - (set_global $comma/a - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $comma/a + (i32.add + (get_global $comma/a) + (i32.const 1) ) ) (block (result i32) @@ -185,32 +161,16 @@ (block (nop) (block - (drop - (block (result i32) - (set_local $0 - (get_global $comma/a) - ) - (set_global $comma/a - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $comma/a + (i32.sub + (get_global $comma/a) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_local $1) - ) - (set_local $1 - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_local $1 + (i32.add + (get_local $1) + (i32.const 1) ) ) ) @@ -269,8 +229,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast index 482ddb6f..67719604 100644 --- a/tests/compiler/declare.wast +++ b/tests/compiler/declare.wast @@ -47,8 +47,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/do.wast b/tests/compiler/do.wast index 96ce67a7..f07eb88a 100644 --- a/tests/compiler/do.wast +++ b/tests/compiler/do.wast @@ -12,32 +12,16 @@ (block $break|0 (loop $continue|0 (block - (drop - (block (result i32) - (set_local $0 - (get_global $do/n) - ) - (set_global $do/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/n + (i32.sub + (get_global $do/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $do/m) - ) - (set_global $do/m - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/m + (i32.add + (get_global $do/m) + (i32.const 1) ) ) ) @@ -107,63 +91,31 @@ (block $break|2 (loop $continue|2 (block - (drop - (block (result i32) - (set_local $0 - (get_global $do/n) - ) - (set_global $do/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/n + (i32.sub + (get_global $do/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $do/m) - ) - (set_global $do/m - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/m + (i32.add + (get_global $do/m) + (i32.const 1) ) ) (block $break|3 (loop $continue|3 (block - (drop - (block (result i32) - (set_local $0 - (get_global $do/n) - ) - (set_global $do/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/n + (i32.sub + (get_global $do/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $do/o) - ) - (set_global $do/o - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $do/o + (i32.add + (get_global $do/o) + (i32.const 1) ) ) ) @@ -253,8 +205,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/empty.wast b/tests/compiler/empty.wast index 91cd4aaf..5f8f2bff 100644 --- a/tests/compiler/empty.wast +++ b/tests/compiler/empty.wast @@ -31,8 +31,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/enum.wast b/tests/compiler/enum.wast index 4162dc41..1d09bd7e 100644 --- a/tests/compiler/enum.wast +++ b/tests/compiler/enum.wast @@ -76,8 +76,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast index 5291ec16..55cc6737 100644 --- a/tests/compiler/export.wast +++ b/tests/compiler/export.wast @@ -64,8 +64,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/fmod.optimized.wast b/tests/compiler/fmod.optimized.wast index 2e10f928..4bd05683 100644 --- a/tests/compiler/fmod.optimized.wast +++ b/tests/compiler/fmod.optimized.wast @@ -174,14 +174,14 @@ ) (i64.shl (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -231,14 +231,14 @@ ) (i64.shl (get_local $5) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $6) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -372,14 +372,14 @@ ) (i64.shr_u (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) diff --git a/tests/compiler/fmod.wast b/tests/compiler/fmod.wast index b1dec1e7..bc99d431 100644 --- a/tests/compiler/fmod.wast +++ b/tests/compiler/fmod.wast @@ -188,14 +188,14 @@ (set_local $2 (i64.shl (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $4) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -265,14 +265,14 @@ (set_local $3 (i64.shl (get_local $3) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $5) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -348,18 +348,10 @@ ) ) ) - (drop - (block (result i32) - (set_local $8 - (get_local $4) - ) - (set_local $4 - (i32.sub - (get_local $8) - (i32.const 1) - ) - ) - (get_local $8) + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) ) ) (br $continue|2) @@ -455,14 +447,14 @@ (set_local $2 (i64.shr_u (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $4) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -1076,8 +1068,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/for.wast b/tests/compiler/for.wast index 53d87193..2cdd3aff 100644 --- a/tests/compiler/for.wast +++ b/tests/compiler/for.wast @@ -175,8 +175,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/function.wast b/tests/compiler/function.wast index 6be7a0b5..6b3bab37 100644 --- a/tests/compiler/function.wast +++ b/tests/compiler/function.wast @@ -193,8 +193,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/game-of-life.optimized.wast b/tests/compiler/game-of-life.optimized.wast index 06c7dfeb..4daed3d6 100644 --- a/tests/compiler/game-of-life.optimized.wast +++ b/tests/compiler/game-of-life.optimized.wast @@ -87,28 +87,39 @@ ) (block (set_local $2 - (i32.add + (i32.and (i32.add (i32.add (i32.add (i32.add (i32.add (i32.add - (i32.load8_u - (i32.add - (i32.mul - (get_local $3) - (get_global $../../examples/game-of-life/assembly/game-of-life/w) - ) - (tee_local $2 - (select - (i32.sub - (get_local $1) - (i32.const 1) - ) - (get_local $6) - (get_local $1) + (i32.add + (i32.load8_u + (i32.add + (i32.mul + (get_local $3) + (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) + (tee_local $2 + (select + (i32.sub + (get_local $1) + (i32.const 1) + ) + (get_local $6) + (get_local $1) + ) + ) + ) + ) + (i32.load8_u + (i32.add + (i32.mul + (get_local $3) + (get_global $../../examples/game-of-life/assembly/game-of-life/w) + ) + (get_local $1) ) ) ) @@ -118,29 +129,29 @@ (get_local $3) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $1) + (tee_local $7 + (select + (i32.const 0) + (i32.add + (get_local $1) + (i32.const 1) + ) + (i32.eq + (get_local $1) + (get_local $6) + ) + ) + ) ) ) ) (i32.load8_u (i32.add (i32.mul - (get_local $3) + (get_local $0) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (tee_local $7 - (select - (i32.const 0) - (i32.add - (get_local $1) - (i32.const 1) - ) - (i32.eq - (get_local $1) - (get_local $6) - ) - ) - ) + (get_local $2) ) ) ) @@ -150,17 +161,17 @@ (get_local $0) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $2) + (get_local $7) ) ) ) (i32.load8_u (i32.add (i32.mul - (get_local $0) + (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $7) + (get_local $2) ) ) ) @@ -170,7 +181,7 @@ (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $2) + (get_local $1) ) ) ) @@ -180,19 +191,11 @@ (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $1) + (get_local $7) ) ) ) - (i32.load8_u - (i32.add - (i32.mul - (get_local $4) - (get_global $../../examples/game-of-life/assembly/game-of-life/w) - ) - (get_local $7) - ) - ) + (i32.const 255) ) ) (if diff --git a/tests/compiler/game-of-life.wast b/tests/compiler/game-of-life.wast index 8df0eac8..0682552d 100644 --- a/tests/compiler/game-of-life.wast +++ b/tests/compiler/game-of-life.wast @@ -133,20 +133,31 @@ ) (block (set_local $8 - (i32.add + (i32.and (i32.add (i32.add (i32.add (i32.add (i32.add (i32.add - (i32.load8_u - (i32.add - (i32.mul - (get_local $3) - (get_global $../../examples/game-of-life/assembly/game-of-life/w) + (i32.add + (i32.load8_u + (i32.add + (i32.mul + (get_local $3) + (get_global $../../examples/game-of-life/assembly/game-of-life/w) + ) + (get_local $6) + ) + ) + (i32.load8_u + (i32.add + (i32.mul + (get_local $3) + (get_global $../../examples/game-of-life/assembly/game-of-life/w) + ) + (get_local $5) ) - (get_local $6) ) ) (i32.load8_u @@ -155,17 +166,17 @@ (get_local $3) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $5) + (get_local $7) ) ) ) (i32.load8_u (i32.add (i32.mul - (get_local $3) + (get_local $2) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $7) + (get_local $6) ) ) ) @@ -175,17 +186,17 @@ (get_local $2) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $6) + (get_local $7) ) ) ) (i32.load8_u (i32.add (i32.mul - (get_local $2) + (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $7) + (get_local $6) ) ) ) @@ -195,7 +206,7 @@ (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $6) + (get_local $5) ) ) ) @@ -205,19 +216,11 @@ (get_local $4) (get_global $../../examples/game-of-life/assembly/game-of-life/w) ) - (get_local $5) + (get_local $7) ) ) ) - (i32.load8_u - (i32.add - (i32.mul - (get_local $4) - (get_global $../../examples/game-of-life/assembly/game-of-life/w) - ) - (get_local $7) - ) - ) + (i32.const 255) ) ) ) @@ -334,8 +337,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/getter-setter.wast b/tests/compiler/getter-setter.wast index bd8500a7..b9839ecc 100644 --- a/tests/compiler/getter-setter.wast +++ b/tests/compiler/getter-setter.wast @@ -83,8 +83,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/i64-polyfill.wast b/tests/compiler/i64-polyfill.wast index f59995d6..e2660946 100644 --- a/tests/compiler/i64-polyfill.wast +++ b/tests/compiler/i64-polyfill.wast @@ -1217,8 +1217,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/if.wast b/tests/compiler/if.wast index 4bc40888..f74f60f4 100644 --- a/tests/compiler/if.wast +++ b/tests/compiler/if.wast @@ -74,8 +74,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index 36941e8a..8a9b4f46 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -72,8 +72,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/infer-type.ts b/tests/compiler/infer-type.ts index 04e7f0cc..8f7b8233 100644 --- a/tests/compiler/infer-type.ts +++ b/tests/compiler/infer-type.ts @@ -1,10 +1,10 @@ -const i = 10; +const i = 10; // infers i32 because i; -const I = 0x100000000; +const I = 0x100000000; // infers i64 because the value doesn't fit into 32 bits I; -const F = 1.5; +const F = 1.5; // infers f64 because of float notation F; function locals(): void { diff --git a/tests/compiler/infer-type.wast b/tests/compiler/infer-type.wast index a137b856..778b5b6e 100644 --- a/tests/compiler/infer-type.wast +++ b/tests/compiler/infer-type.wast @@ -168,8 +168,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/inlining.wast b/tests/compiler/inlining.wast index 7ceddccf..5e80fff6 100644 --- a/tests/compiler/inlining.wast +++ b/tests/compiler/inlining.wast @@ -56,8 +56,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/limits.wast b/tests/compiler/limits.wast index f41e914e..8dba93d9 100644 --- a/tests/compiler/limits.wast +++ b/tests/compiler/limits.wast @@ -135,8 +135,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/literals.wast b/tests/compiler/literals.wast index 91cdc14b..0564025c 100644 --- a/tests/compiler/literals.wast +++ b/tests/compiler/literals.wast @@ -167,8 +167,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/logical.wast b/tests/compiler/logical.wast index 08617a39..f5d9c6f6 100644 --- a/tests/compiler/logical.wast +++ b/tests/compiler/logical.wast @@ -253,8 +253,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/memcpy.wast b/tests/compiler/memcpy.wast index e5faa05e..b7721a09 100644 --- a/tests/compiler/memcpy.wast +++ b/tests/compiler/memcpy.wast @@ -64,18 +64,10 @@ ) ) ) - (drop - (block (result i32) - (set_local $7 - (get_local $2) - ) - (set_local $2 - (i32.sub - (get_local $7) - (i32.const 1) - ) - ) - (get_local $7) + (set_local $2 + (i32.sub + (get_local $2) + (i32.const 1) ) ) ) @@ -2079,8 +2071,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/namespace.wast b/tests/compiler/namespace.wast index d6ed5f5f..f811e290 100644 --- a/tests/compiler/namespace.wast +++ b/tests/compiler/namespace.wast @@ -62,8 +62,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/portable-conversions.wast b/tests/compiler/portable-conversions.wast index 375c464b..2e0cbdb3 100644 --- a/tests/compiler/portable-conversions.wast +++ b/tests/compiler/portable-conversions.wast @@ -357,8 +357,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/recursive.wast b/tests/compiler/recursive.wast index 08dfc6b0..051dc28b 100644 --- a/tests/compiler/recursive.wast +++ b/tests/compiler/recursive.wast @@ -60,8 +60,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast index 132b7600..fecce9bd 100644 --- a/tests/compiler/reexport.wast +++ b/tests/compiler/reexport.wast @@ -77,8 +77,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/retain-i32.optimized.wast b/tests/compiler/retain-i32.optimized.wast new file mode 100644 index 00000000..a21d1ba2 --- /dev/null +++ b/tests/compiler/retain-i32.optimized.wast @@ -0,0 +1,731 @@ +(module + (type $iiv (func (param i32 i32))) + (type $v (func)) + (global $retain-i32/si (mut i32) (i32.const 0)) + (global $retain-i32/ui (mut i32) (i32.const 0)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $retain-i32/test (; 0 ;) (type $iiv) (param $0 i32) (param $1 i32) + (if + (i32.ne + (i32.and + (i32.add + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.add + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.sub + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.sub + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.mul + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.mul + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.and + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.and + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.or + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.or + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.xor + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.xor + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.shl + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.shl + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.add + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.add + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.sub + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.sub + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.mul + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.mul + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.and + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.or + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.or + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.xor + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.xor + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.ne + (i32.and + (i32.shl + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.shl + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + (unreachable) + ) + ) + (func $start (; 1 ;) (type $v) + (local $0 i32) + (call $retain-i32/test + (i32.const 0) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 1) + ) + (call $retain-i32/test + (i32.const -1) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const -1) + ) + (call $retain-i32/test + (i32.const 0) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 1) + ) + (call $retain-i32/test + (i32.const -1) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const -1) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 0) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 1) + ) + (call $retain-i32/test + (i32.const -1) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const -1) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 255) + ) + (set_local $0 + (i32.const -128) + ) + (loop $continue|0 + (if + (i32.le_s + (get_local $0) + (i32.const 255) + ) + (block + (call $retain-i32/test + (i32.const 0) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 1) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -1) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -128) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 127) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 255) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -32768) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 32767) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 65535) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 2147483647) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -2147483648) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -1) + (get_local $0) + ) + (set_local $0 + (i32.add + (get_local $0) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + (set_global $retain-i32/si + (i32.const -1) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -1) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -1) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -1) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -2) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -2) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -128) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -128) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -128) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -128) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -127) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -127) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const -128) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const -128) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const 0) + ) + (if + (get_global $retain-i32/si) + (unreachable) + ) + (set_global $retain-i32/si + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/si) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 255) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 255) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 255) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 255) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 254) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 254) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 1) + ) + (if + (i32.ne + (get_global $retain-i32/ui) + (i32.const 1) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.const 0) + ) + (if + (get_global $retain-i32/ui) + (unreachable) + ) + ) +) diff --git a/tests/compiler/retain-i32.ts b/tests/compiler/retain-i32.ts new file mode 100644 index 00000000..079150e3 --- /dev/null +++ b/tests/compiler/retain-i32.ts @@ -0,0 +1,131 @@ +function test(a: i32, b: i32): void { + + // signed + assert((a + b) == (a + b)); + assert((a - b) == (a - b)); + assert((a * b) == (a * b)); + assert((a & b) == (a & b)); + assert((a | b) == (a | b)); + assert((a ^ b) == (a ^ b)); + assert((a << b) == (a << b)); + + // unsigned + assert((a + b) == (a + b)); + assert((a - b) == (a - b)); + assert((a * b) == (a * b)); + assert((a & b) == (a & b)); + assert((a | b) == (a | b)); + assert((a ^ b) == (a ^ b)); + assert((a << b) == (a << b)); +} + +// signed +test(0, i8.MAX_VALUE); +test(i8.MAX_VALUE, 0); + +test(1, i8.MAX_VALUE); +test(i8.MAX_VALUE, 1); + +test(-1, i8.MAX_VALUE); +test(i8.MAX_VALUE, -1); + +test(0, i8.MIN_VALUE); +test(i8.MIN_VALUE, 0); + +test(1, i8.MIN_VALUE); +test(i8.MIN_VALUE, 1); + +test(-1, i8.MIN_VALUE); +test(i8.MIN_VALUE, -1); + +test(i8.MAX_VALUE, i8.MAX_VALUE); +test(i8.MIN_VALUE, i8.MIN_VALUE); +test(i8.MAX_VALUE, i8.MIN_VALUE); +test(i8.MIN_VALUE, i8.MAX_VALUE); + +// unsigned +test(0, u8.MAX_VALUE); +test(u8.MAX_VALUE, 0); + +test(1, u8.MAX_VALUE); +test(u8.MAX_VALUE, 1); + +test(-1, u8.MAX_VALUE); +test(u8.MAX_VALUE, -1); + +test(u8.MAX_VALUE, u8.MAX_VALUE); + +// various +for (var i: i32 = i8.MIN_VALUE; i <= u8.MAX_VALUE; ++i) { + test(0, i); + test(1, i); + test(-1, i); + test(i8.MIN_VALUE, i); + test(i8.MAX_VALUE, i); + test(u8.MAX_VALUE, i); + test(i16.MIN_VALUE, i); + test(i16.MAX_VALUE, i); + test(u16.MAX_VALUE, i); + test(i32.MAX_VALUE, i); + test(i32.MIN_VALUE, i); + test(u32.MAX_VALUE, i); +} + +// visually +var si: i8; + +si = 127 + 127 + 1; // sign-extends exactly once +assert(si == -1); + +si = 127 - 1 - 127; // sign-extends exactly once +assert(si == -1); + +si = 127 * 2; // sign-extends exactly once +assert(si == -2); + +si = -(-128); // -MIN_VALUE == MIN_VALUE +assert(si == -128); + +si = -128 * -1; // -MIN_VALUE == MIN_VALUE +assert(si == -128); + +si = 127 / -1; +assert(si == -127); + +si = -128 / -1; // -MIN_VALUE == MIN_VALUE +assert(si == -128); + +si = 127 % 2; +assert(si == 1); + +si = 1 % 127; +assert(si == 1); + +si = -128 % 2; +assert(si == 0); + +si = 1 % -128; +assert(si == 1); + +var ui: u8; + +ui = 255 + 255 + 1; +assert(ui == 255); + +ui = 255 - 1 - 255; +assert(ui == 255); + +ui = 255 * 2; +assert(ui == 254); + +ui = 255 * 255; +assert(ui == 1); + +ui = 255 / 255; +assert(ui == 1); + +ui = 255 % 2; +assert(ui == 1); + +ui = 255 % 255; +assert(ui == 0); diff --git a/tests/compiler/retain-i32.wast b/tests/compiler/retain-i32.wast new file mode 100644 index 00000000..3f3b6edf --- /dev/null +++ b/tests/compiler/retain-i32.wast @@ -0,0 +1,1185 @@ +(module + (type $iiv (func (param i32 i32))) + (type $v (func)) + (global $i8.MAX_VALUE i32 (i32.const 127)) + (global $i8.MIN_VALUE i32 (i32.const -128)) + (global $u8.MAX_VALUE i32 (i32.const 255)) + (global $i16.MIN_VALUE i32 (i32.const -32768)) + (global $i16.MAX_VALUE i32 (i32.const 32767)) + (global $u16.MAX_VALUE i32 (i32.const 65535)) + (global $i32.MAX_VALUE i32 (i32.const 2147483647)) + (global $i32.MIN_VALUE i32 (i32.const -2147483648)) + (global $u32.MAX_VALUE i32 (i32.const -1)) + (global $retain-i32/si (mut i32) (i32.const 0)) + (global $retain-i32/ui (mut i32) (i32.const 0)) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "memory" (memory $0)) + (start $start) + (func $retain-i32/test (; 0 ;) (type $iiv) (param $0 i32) (param $1 i32) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.add + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.add + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.sub + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.sub + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.mul + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.mul + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.and + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.and + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.or + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.or + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.xor + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.xor + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.shr_s + (i32.shl + (i32.shl + (get_local $0) + (get_local $1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.shl + (i32.shr_s + (i32.shl + (get_local $0) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (get_local $1) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.add + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.add + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.sub + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.sub + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.mul + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.mul + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.and + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.and + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.or + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.or + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.xor + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.xor + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.eq + (i32.and + (i32.shl + (get_local $0) + (get_local $1) + ) + (i32.const 255) + ) + (i32.and + (i32.shl + (i32.and + (get_local $0) + (i32.const 255) + ) + (i32.and + (get_local $1) + (i32.const 255) + ) + ) + (i32.const 255) + ) + ) + ) + (unreachable) + ) + ) + (func $start (; 1 ;) (type $v) + (local $0 i32) + (call $retain-i32/test + (i32.const 0) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 1) + ) + (call $retain-i32/test + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 127) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (call $retain-i32/test + (i32.const 0) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 1) + ) + (call $retain-i32/test + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const 127) + (i32.const -128) + ) + (call $retain-i32/test + (i32.const -128) + (i32.const 127) + ) + (call $retain-i32/test + (i32.const 0) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 0) + ) + (call $retain-i32/test + (i32.const 1) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 1) + ) + (call $retain-i32/test + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 255) + ) + (call $retain-i32/test + (i32.const 255) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (call $retain-i32/test + (i32.const 255) + (i32.const 255) + ) + (block $break|0 + (block + (set_local $0 + (i32.const -128) + ) + ) + (loop $continue|0 + (if + (i32.le_s + (get_local $0) + (i32.const 255) + ) + (block + (block + (call $retain-i32/test + (i32.const 0) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 1) + (get_local $0) + ) + (call $retain-i32/test + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -128) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 127) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 255) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -32768) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 32767) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 65535) + (get_local $0) + ) + (call $retain-i32/test + (i32.const 2147483647) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -2147483648) + (get_local $0) + ) + (call $retain-i32/test + (i32.const -1) + (get_local $0) + ) + ) + (set_local $0 + (i32.add + (get_local $0) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.add + (i32.add + (i32.const 127) + (i32.const 127) + ) + (i32.const 1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.sub + (i32.sub + (i32.const 127) + (i32.const 1) + ) + (i32.const 127) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.mul + (i32.const 127) + (i32.const 2) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 2) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.mul + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.sub + (i32.const 0) + (i32.const 1) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.div_s + (i32.const 127) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 127) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.shr_s + (i32.shl + (i32.div_s + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const 1) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.rem_s + (i32.const 127) + (i32.const 2) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.rem_s + (i32.const 1) + (i32.const 127) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.rem_s + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + (i32.const 2) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.const 0) + ) + ) + (unreachable) + ) + (set_global $retain-i32/si + (i32.rem_s + (i32.const 1) + (i32.shr_s + (i32.shl + (i32.sub + (i32.const 0) + (i32.const -128) + ) + (i32.const 24) + ) + (i32.const 24) + ) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/si) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.and + (i32.add + (i32.add + (i32.const 255) + (i32.const 255) + ) + (i32.const 1) + ) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 255) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.and + (i32.sub + (i32.sub + (i32.const 255) + (i32.const 1) + ) + (i32.const 255) + ) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 255) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.and + (i32.mul + (i32.const 255) + (i32.const 2) + ) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 254) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.and + (i32.mul + (i32.const 255) + (i32.const 255) + ) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.and + (i32.div_u + (i32.const 255) + (i32.const 255) + ) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.rem_u + (i32.const 255) + (i32.const 2) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 1) + ) + ) + (unreachable) + ) + (set_global $retain-i32/ui + (i32.rem_u + (i32.const 255) + (i32.const 255) + ) + ) + (if + (i32.eqz + (i32.eq + (get_global $retain-i32/ui) + (i32.const 0) + ) + ) + (unreachable) + ) + ) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE + FUNCTION_PROTOTYPE: retain-i32/test + GLOBAL: retain-i32/si + GLOBAL: retain-i32/ui +[program.exports] + +;) diff --git a/tests/compiler/showcase.optimized-inlined.wast b/tests/compiler/showcase.optimized-inlined.wast index 4c56074e..abfbfd38 100644 --- a/tests/compiler/showcase.optimized-inlined.wast +++ b/tests/compiler/showcase.optimized-inlined.wast @@ -1806,14 +1806,14 @@ ) (i64.shl (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -1863,14 +1863,14 @@ ) (i64.shl (get_local $5) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $6) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -2004,14 +2004,14 @@ ) (i64.shr_u (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -3443,14 +3443,14 @@ ) (set_global $builtins/i (select + (tee_local $2 + (i32.const -42) + ) (i32.sub (i32.const 0) - (tee_local $2 - (i32.const -42) - ) + (get_local $2) ) - (get_local $2) - (i32.lt_s + (i32.gt_s (get_local $2) (i32.const 0) ) @@ -3522,14 +3522,14 @@ ) (set_global $builtins/I (select + (tee_local $3 + (i64.const -42) + ) (i64.sub (i64.const 0) - (tee_local $3 - (i64.const -42) - ) + (get_local $3) ) - (get_local $3) - (i64.lt_s + (i64.gt_s (get_local $3) (i64.const 0) ) diff --git a/tests/compiler/showcase.optimized.wast b/tests/compiler/showcase.optimized.wast index d14eddf4..ae421719 100644 --- a/tests/compiler/showcase.optimized.wast +++ b/tests/compiler/showcase.optimized.wast @@ -1827,14 +1827,14 @@ ) (i64.shl (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -1884,14 +1884,14 @@ ) (i64.shl (get_local $5) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $6) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -2025,14 +2025,14 @@ ) (i64.shr_u (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $3) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -3465,14 +3465,14 @@ ) (set_global $builtins/i (select + (tee_local $2 + (i32.const -42) + ) (i32.sub (i32.const 0) - (tee_local $2 - (i32.const -42) - ) + (get_local $2) ) - (get_local $2) - (i32.lt_s + (i32.gt_s (get_local $2) (i32.const 0) ) @@ -3544,14 +3544,14 @@ ) (set_global $builtins/I (select + (tee_local $3 + (i64.const -42) + ) (i64.sub (i64.const 0) - (tee_local $3 - (i64.const -42) - ) + (get_local $3) ) - (get_local $3) - (i64.lt_s + (i64.gt_s (get_local $3) (i64.const 0) ) diff --git a/tests/compiler/showcase.wast b/tests/compiler/showcase.wast index 904d26b3..efba2d99 100644 --- a/tests/compiler/showcase.wast +++ b/tests/compiler/showcase.wast @@ -172,18 +172,10 @@ ) ) ) - (drop - (block (result i32) - (set_local $7 - (get_local $2) - ) - (set_local $2 - (i32.sub - (get_local $7) - (i32.const 1) - ) - ) - (get_local $7) + (set_local $2 + (i32.sub + (get_local $2) + (i32.const 1) ) ) ) @@ -2114,14 +2106,14 @@ (set_local $2 (i64.shl (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $4) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -2191,14 +2183,14 @@ (set_local $3 (i64.shl (get_local $3) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $5) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -2274,18 +2266,10 @@ ) ) ) - (drop - (block (result i32) - (set_local $8 - (get_local $4) - ) - (set_local $4 - (i32.sub - (get_local $8) - (i32.const 1) - ) - ) - (get_local $8) + (set_local $4 + (i32.sub + (get_local $4) + (i32.const 1) ) ) (br $continue|2) @@ -2381,14 +2365,14 @@ (set_local $2 (i64.shr_u (get_local $2) - (i64.add - (i64.sub - (i64.const 0) - (i64.extend_u/i32 + (i64.extend_u/i32 + (i32.add + (i32.sub + (i32.const 0) (get_local $4) ) + (i32.const 1) ) - (i64.const 1) ) ) ) @@ -2943,32 +2927,16 @@ (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $unary/i) - ) - (set_global $unary/i - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $unary/i + (i32.add + (get_global $unary/i) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $unary/i) - ) - (set_global $unary/i - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $unary/i + (i32.sub + (get_global $unary/i) + (i32.const 1) ) ) (set_global $unary/i @@ -3093,32 +3061,16 @@ (i64.const 1) ) ) - (drop - (block (result i64) - (set_local $1 - (get_global $unary/I) - ) - (set_global $unary/I - (i64.add - (get_local $1) - (i64.const 1) - ) - ) - (get_local $1) + (set_global $unary/I + (i64.add + (get_global $unary/I) + (i64.const 1) ) ) - (drop - (block (result i64) - (set_local $1 - (get_global $unary/I) - ) - (set_global $unary/I - (i64.sub - (get_local $1) - (i64.const 1) - ) - ) - (get_local $1) + (set_global $unary/I + (i64.sub + (get_global $unary/I) + (i64.const 1) ) ) (set_global $unary/I @@ -3132,8 +3084,8 @@ ) (set_global $unary/I (i64.extend_s/i32 - (i32.eqz - (i32.const 1) + (i64.eqz + (i64.const 1) ) ) ) @@ -3241,32 +3193,16 @@ (f32.const 1) ) ) - (drop - (block (result f32) - (set_local $2 - (get_global $unary/f) - ) - (set_global $unary/f - (f32.add - (get_local $2) - (f32.const 1) - ) - ) - (get_local $2) + (set_global $unary/f + (f32.add + (get_global $unary/f) + (f32.const 1) ) ) - (drop - (block (result f32) - (set_local $2 - (get_global $unary/f) - ) - (set_global $unary/f - (f32.sub - (get_local $2) - (f32.const 1) - ) - ) - (get_local $2) + (set_global $unary/f + (f32.sub + (get_global $unary/f) + (f32.const 1) ) ) (set_global $unary/f @@ -3373,32 +3309,16 @@ (f64.const 1) ) ) - (drop - (block (result f64) - (set_local $3 - (get_global $unary/F) - ) - (set_global $unary/F - (f64.add - (get_local $3) - (f64.const 1) - ) - ) - (get_local $3) + (set_global $unary/F + (f64.add + (get_global $unary/F) + (f64.const 1) ) ) - (drop - (block (result f64) - (set_local $3 - (get_global $unary/F) - ) - (set_global $unary/F - (f64.sub - (get_local $3) - (f64.const 1) - ) - ) - (get_local $3) + (set_global $unary/F + (f64.sub + (get_global $unary/F) + (f64.const 1) ) ) (set_global $unary/F @@ -4528,17 +4448,17 @@ ) (drop (select - (i32.sub - (i32.const 0) - (tee_local $0 - (i32.sub - (i32.const 0) - (i32.const 42) - ) + (tee_local $0 + (i32.sub + (i32.const 0) + (i32.const 42) ) ) - (get_local $0) - (i32.lt_s + (i32.sub + (i32.const 0) + (get_local $0) + ) + (i32.gt_s (get_local $0) (i32.const 0) ) @@ -4601,17 +4521,17 @@ ) (set_global $builtins/i (select - (i32.sub - (i32.const 0) - (tee_local $0 - (i32.sub - (i32.const 0) - (i32.const 42) - ) + (tee_local $0 + (i32.sub + (i32.const 0) + (i32.const 42) ) ) - (get_local $0) - (i32.lt_s + (i32.sub + (i32.const 0) + (get_local $0) + ) + (i32.gt_s (get_local $0) (i32.const 0) ) @@ -4701,17 +4621,17 @@ ) (drop (select - (i64.sub - (i64.const 0) - (tee_local $1 - (i64.sub - (i64.const 0) - (i64.const 42) - ) + (tee_local $1 + (i64.sub + (i64.const 0) + (i64.const 42) ) ) - (get_local $1) - (i64.lt_s + (i64.sub + (i64.const 0) + (get_local $1) + ) + (i64.gt_s (get_local $1) (i64.const 0) ) @@ -4746,17 +4666,17 @@ ) (set_global $builtins/I (select - (i64.sub - (i64.const 0) - (tee_local $1 - (i64.sub - (i64.const 0) - (i64.const 42) - ) + (tee_local $1 + (i64.sub + (i64.const 0) + (i64.const 42) ) ) - (get_local $1) - (i64.lt_s + (i64.sub + (i64.const 0) + (get_local $1) + ) + (i64.gt_s (get_local $1) (i64.const 0) ) @@ -6319,8 +6239,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/std/array.wast b/tests/compiler/std/array.wast index cf844f2a..9936be1d 100644 --- a/tests/compiler/std/array.wast +++ b/tests/compiler/std/array.wast @@ -31,8 +31,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 @@ -73,12 +71,16 @@ FUNCTION_PROTOTYPE: std:heap/Heap.compare CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: Map + CLASS_PROTOTYPE: std:regexp/RegExp + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: std:string/parseFloat [program.exports] CLASS_PROTOTYPE: std:array/Array CLASS_PROTOTYPE: std:array/CArray @@ -88,4 +90,6 @@ CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: std:set/Set CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: std:string/parseFloat ;) diff --git a/tests/compiler/std/heap.wast b/tests/compiler/std/heap.wast index ebdcaa69..2f5238fd 100644 --- a/tests/compiler/std/heap.wast +++ b/tests/compiler/std/heap.wast @@ -565,18 +565,10 @@ ) ) ) - (drop - (block (result i32) - (set_local $6 - (get_local $2) - ) - (set_local $2 - (i32.sub - (get_local $6) - (i32.const 1) - ) - ) - (get_local $6) + (set_local $2 + (i32.sub + (get_local $2) + (i32.const 1) ) ) ) @@ -2330,7 +2322,6 @@ ) ) (func $std:heap/Heap.compare (; 3 ;) (type $iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) - (local $3 i32) (if (i32.eq (get_local $0) @@ -2357,46 +2348,22 @@ ) (block (block - (drop - (block (result i32) - (set_local $3 - (get_local $2) - ) - (set_local $2 - (i32.sub - (get_local $3) - (i32.const 1) - ) - ) - (get_local $3) + (set_local $2 + (i32.sub + (get_local $2) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $3 - (get_local $0) - ) - (set_local $0 - (i32.add - (get_local $3) - (i32.const 1) - ) - ) - (get_local $3) + (set_local $0 + (i32.add + (get_local $0) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $3 - (get_local $1) - ) - (set_local $1 - (i32.add - (get_local $3) - (i32.const 1) - ) - ) - (get_local $3) + (set_local $1 + (i32.add + (get_local $1) + (i32.const 1) ) ) ) @@ -2580,8 +2547,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 @@ -2622,12 +2587,16 @@ FUNCTION_PROTOTYPE: std:heap/Heap.compare CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: Map + CLASS_PROTOTYPE: std:regexp/RegExp + CLASS_PROTOTYPE: RegExp CLASS_PROTOTYPE: std:set/Set CLASS_PROTOTYPE: Set GLOBAL: std:string/EMPTY CLASS_PROTOTYPE: std:string/String CLASS_PROTOTYPE: String FUNCTION_PROTOTYPE: std:string/isWhiteSpaceOrLineTerminator + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: std:string/parseFloat GLOBAL: std/heap/size GLOBAL: std/heap/ptr1 GLOBAL: std/heap/ptr2 @@ -2641,4 +2610,6 @@ CLASS_PROTOTYPE: std:map/Map CLASS_PROTOTYPE: std:set/Set CLASS_PROTOTYPE: std:string/String + FUNCTION_PROTOTYPE: std:string/parseInt + FUNCTION_PROTOTYPE: std:string/parseFloat ;) diff --git a/tests/compiler/switch.wast b/tests/compiler/switch.wast index 908d4399..96d7b16e 100644 --- a/tests/compiler/switch.wast +++ b/tests/compiler/switch.wast @@ -173,8 +173,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/ternary.wast b/tests/compiler/ternary.wast index bc8cd634..12d35f74 100644 --- a/tests/compiler/ternary.wast +++ b/tests/compiler/ternary.wast @@ -86,8 +86,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/tlsf.wast b/tests/compiler/tlsf.wast index b0fa1119..98118002 100644 --- a/tests/compiler/tlsf.wast +++ b/tests/compiler/tlsf.wast @@ -364,8 +364,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/typealias.wast b/tests/compiler/typealias.wast index bdb4ec3c..36b78187 100644 --- a/tests/compiler/typealias.wast +++ b/tests/compiler/typealias.wast @@ -43,8 +43,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/unary.wast b/tests/compiler/unary.wast index ffad74a2..f45898d4 100644 --- a/tests/compiler/unary.wast +++ b/tests/compiler/unary.wast @@ -79,32 +79,16 @@ (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $unary/i) - ) - (set_global $unary/i - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $unary/i + (i32.add + (get_global $unary/i) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $unary/i) - ) - (set_global $unary/i - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $unary/i + (i32.sub + (get_global $unary/i) + (i32.const 1) ) ) (set_global $unary/i @@ -229,32 +213,16 @@ (i64.const 1) ) ) - (drop - (block (result i64) - (set_local $1 - (get_global $unary/I) - ) - (set_global $unary/I - (i64.add - (get_local $1) - (i64.const 1) - ) - ) - (get_local $1) + (set_global $unary/I + (i64.add + (get_global $unary/I) + (i64.const 1) ) ) - (drop - (block (result i64) - (set_local $1 - (get_global $unary/I) - ) - (set_global $unary/I - (i64.sub - (get_local $1) - (i64.const 1) - ) - ) - (get_local $1) + (set_global $unary/I + (i64.sub + (get_global $unary/I) + (i64.const 1) ) ) (set_global $unary/I @@ -268,8 +236,8 @@ ) (set_global $unary/I (i64.extend_s/i32 - (i32.eqz - (i32.const 1) + (i64.eqz + (i64.const 1) ) ) ) @@ -377,32 +345,16 @@ (f32.const 1) ) ) - (drop - (block (result f32) - (set_local $2 - (get_global $unary/f) - ) - (set_global $unary/f - (f32.add - (get_local $2) - (f32.const 1) - ) - ) - (get_local $2) + (set_global $unary/f + (f32.add + (get_global $unary/f) + (f32.const 1) ) ) - (drop - (block (result f32) - (set_local $2 - (get_global $unary/f) - ) - (set_global $unary/f - (f32.sub - (get_local $2) - (f32.const 1) - ) - ) - (get_local $2) + (set_global $unary/f + (f32.sub + (get_global $unary/f) + (f32.const 1) ) ) (set_global $unary/f @@ -509,32 +461,16 @@ (f64.const 1) ) ) - (drop - (block (result f64) - (set_local $3 - (get_global $unary/F) - ) - (set_global $unary/F - (f64.add - (get_local $3) - (f64.const 1) - ) - ) - (get_local $3) + (set_global $unary/F + (f64.add + (get_global $unary/F) + (f64.const 1) ) ) - (drop - (block (result f64) - (set_local $3 - (get_global $unary/F) - ) - (set_global $unary/F - (f64.sub - (get_local $3) - (f64.const 1) - ) - ) - (get_local $3) + (set_global $unary/F + (f64.sub + (get_global $unary/F) + (f64.const 1) ) ) (set_global $unary/F @@ -649,8 +585,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/compiler/while.wast b/tests/compiler/while.wast index 0134242e..0efdca79 100644 --- a/tests/compiler/while.wast +++ b/tests/compiler/while.wast @@ -15,32 +15,16 @@ (get_global $while/n) (block (block - (drop - (block (result i32) - (set_local $0 - (get_global $while/n) - ) - (set_global $while/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/n + (i32.sub + (get_global $while/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $while/m) - ) - (set_global $while/m - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/m + (i32.add + (get_global $while/m) + (i32.const 1) ) ) ) @@ -79,32 +63,16 @@ (get_global $while/n) (block (block - (drop - (block (result i32) - (set_local $0 - (get_global $while/n) - ) - (set_global $while/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/n + (i32.sub + (get_global $while/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $while/m) - ) - (set_global $while/m - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/m + (i32.add + (get_global $while/m) + (i32.const 1) ) ) (block $break|2 @@ -113,32 +81,16 @@ (get_global $while/n) (block (block - (drop - (block (result i32) - (set_local $0 - (get_global $while/n) - ) - (set_global $while/n - (i32.sub - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/n + (i32.sub + (get_global $while/n) + (i32.const 1) ) ) - (drop - (block (result i32) - (set_local $0 - (get_global $while/o) - ) - (set_global $while/o - (i32.add - (get_local $0) - (i32.const 1) - ) - ) - (get_local $0) + (set_global $while/o + (i32.add + (get_global $while/o) + (i32.const 1) ) ) ) @@ -291,8 +243,6 @@ FUNCTION_PROTOTYPE: unreachable FUNCTION_PROTOTYPE: current_memory FUNCTION_PROTOTYPE: grow_memory - FUNCTION_PROTOTYPE: parseInt - FUNCTION_PROTOTYPE: parseFloat FUNCTION_PROTOTYPE: changetype FUNCTION_PROTOTYPE: assert FUNCTION_PROTOTYPE: i8 diff --git a/tests/parser.js b/tests/parser.js index c11eab4b..8a14482a 100644 --- a/tests/parser.js +++ b/tests/parser.js @@ -10,6 +10,7 @@ var Parser = require("../src/parser").Parser; var isCreate = process.argv[2] === "--create"; var filter = process.argv.length > 2 && !isCreate ? "*" + process.argv[2] + "*.ts" : "**.ts"; +var failures = 0; glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => { if (filename.charAt(0) == "_" || filename.endsWith(".fixture.ts")) @@ -17,6 +18,7 @@ glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => { console.log(chalk.default.whiteBright("Testing parser/" + filename)); + var failed = false; var parser = new Parser(); parser.silentDiagnostics = true; var sourceText = fs.readFileSync(__dirname + "/parser/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, ""); @@ -34,7 +36,7 @@ glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => { var expected = fs.readFileSync(__dirname + "/parser/" + fixture, { encoding: "utf8" }); var diffs = diff("parser/" + fixture, expected, actual); if (diffs !== null) { - process.exitCode = 1; + failed = true; console.log(diffs); console.log(chalk.default.red("diff ERROR")); } else { @@ -43,4 +45,12 @@ glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => { } console.log(); + if (failed) + ++failures; }); + +if (failures) { + process.exitCode = 1; + console.log(chalk.red("ERROR: ") + failures + " parser tests failed"); +} else + console.log(chalk.whiteBright("SUCCESS")); diff --git a/tests/util/diff.js b/tests/util/diff.js index 108055b0..3d0e7b1c 100644 --- a/tests/util/diff.js +++ b/tests/util/diff.js @@ -2,7 +2,7 @@ var JsDiff = require("diff"); var chalk = require("chalk"); module.exports = function diff(filename, expected, actual) { - var diff = JsDiff.structuredPatch(filename, filename, expected, actual, "expected", "actual", { context: 2 }); + var diff = JsDiff.structuredPatch(filename, filename, expected, actual, "expected", "actual", { context: 5 }); if (!diff.hunks.length) return null;