From e5aa6ca2dc9705ba1a3630b72de2a4f1bfe5b97d Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 21 Sep 2023 17:24:18 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1249482 - OdinMonkey: Remove the needsBoundsCheck flag from the frontend. r=luke (5004e664a0) - Bug 1249513 - OdinMonkey: MIPS: Clean up long jump patching in asm.js. r=luke (18c7f026f5) - fix misspatch of 1234985 (0713b005b4) - Bug 1248598 part 1 - Some code changes required for the i64.const instruction. r=luke (7bb3eaa57f) - Bug 1247855: Resolve named functions and locals (r=luke) (e06d418fd1) - Bug 1249524 - Baldr: add the other (param) declaration form to the text format (r=mbx) (9492f57377) - Bug 1249523 - Baldr: add return (r=mbx) (36a4eca6a3) - Bug 1249316 - Baldr: move testing functions back into TestingFunctions.cpp (r=jandem) (7daf99a017) - Bug 1249531 - Baldr: add text format and encoding support for control operators (r=mbx) (6dee433349) - Bug 1248598 part 2 - Reject wasm i64 imports/exports as JS doesn't have an int64 type. r=luke (72603cdb3d) - Bug 1246658 part 4 - Replace MConstant's js::Value with a custom union. r=luke (1c78b526d2) - Bug 1246658 part 5 - Support int64 constants, add MIRType_Int64. r=luk (db94c230c6) - Bug 1246658 part 1 followup - Fix a bug introduced by the refactoring. (ec3d444596) - Bug 1248859 - OdinMonkey: MIPS: Fix replace retargetWithOffset. r=arai (dd117fcbf5) - Bug 1249525 - Baldr: accept - and $ in text names (r=sunfish) (64e4e1ddf8) - Bug 1249525 - Baldr: accept integer cases in float constants (r=sunfish) (90a8fbb5d5) - Bug 1248859 - OdinMonkey: MIPS: Implement thunkWithPatch and re/patchThunk. r=luke (722240c9b7) - Bug 1248859 - OdinMonkey: MIPS: Refactor callWithPatch via reative branch. r=luke (0684904686) - Bug 1248503 - Improve log output for MSimdBox and MSimdUnbox. r=nbp (6b65608504) - Bug 1248503 - Fix initial heap assertion. r=nbp (bcf704df34) - Bug 1249525 - Baldr: change order of load/store immediates/subexpressions (r=sunfish) (c2ec5329e2) - Bug 1240055: IonMonkey: When spewing info about range analysis, also spew truncation info, r=nbp (cf477cffce) - fix misspatch in symbol visibility (039e111b31) --- js/src/asmjs/AsmJS.cpp | 116 +- js/src/asmjs/Wasm.cpp | 158 +-- js/src/asmjs/Wasm.h | 4 - js/src/asmjs/WasmBinary.h | 28 +- js/src/asmjs/WasmFrameIterator.cpp | 57 +- js/src/asmjs/WasmGenerator.cpp | 26 +- js/src/asmjs/WasmGenerator.h | 7 +- js/src/asmjs/WasmIonCompile.cpp | 106 +- js/src/asmjs/WasmText.cpp | 1236 ++++++++++++++--- js/src/builtin/SIMD.cpp | 12 + js/src/builtin/SIMD.h | 2 + js/src/builtin/TestingFunctions.cpp | 98 +- js/src/jit-test/tests/SIMD/bug1248503.js | 13 + .../jit-test/tests/wasm/basic-control-flow.js | 38 + js/src/jit-test/tests/wasm/basic-float.js | 13 + js/src/jit-test/tests/wasm/basic-integer.js | 4 + js/src/jit-test/tests/wasm/basic-memory.js | 9 +- js/src/jit-test/tests/wasm/basic.js | 46 + js/src/jit-test/tests/wasm/text.js | 13 + js/src/jit/EffectiveAddressAnalysis.cpp | 8 + js/src/jit/IonAnalysis.cpp | 9 +- js/src/jit/IonTypes.h | 16 +- js/src/jit/MCallOptimize.cpp | 12 +- js/src/jit/MIR.cpp | 297 +++- js/src/jit/MIR.h | 148 +- js/src/jit/MIRGenerator.h | 16 +- js/src/jit/MIRGraph.cpp | 6 +- js/src/jit/RangeAnalysis.cpp | 38 +- .../mips-shared/Architecture-mips-shared.h | 3 + .../jit/mips-shared/Assembler-mips-shared.cpp | 22 +- .../jit/mips-shared/Assembler-mips-shared.h | 2 +- .../mips-shared/CodeGenerator-mips-shared.cpp | 12 +- .../MacroAssembler-mips-shared.cpp | 63 +- .../mips-shared/MacroAssembler-mips-shared.h | 4 + js/src/jit/mips32/MacroAssembler-mips32.cpp | 8 + js/src/jit/mips32/MacroAssembler-mips32.h | 5 + js/src/jit/mips64/MacroAssembler-mips64.cpp | 9 + js/src/jit/mips64/MacroAssembler-mips64.h | 5 + js/src/jit/shared/CodeGenerator-shared-inl.h | 2 +- js/src/jsapi-tests/testJitDCEinGVN.cpp | 2 +- js/src/jsapi-tests/testJitFoldsTo.cpp | 6 +- 41 files changed, 1954 insertions(+), 725 deletions(-) create mode 100644 js/src/jit-test/tests/SIMD/bug1248503.js diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 24295a8d04..2b8d5becdd 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -1794,7 +1794,12 @@ class MOZ_STACK_CLASS ModuleValidator return false; } - return mg_.init(Move(genData), Move(filename)); + if (!mg_.init(Move(genData), Move(filename))) + return false; + + mg_.bumpMinHeapLength(module_->minHeapLength); + + return true; } ExclusiveContext* cx() const { return cx_; } @@ -2090,8 +2095,10 @@ class MOZ_STACK_CLASS ModuleValidator if (len > uint64_t(INT32_MAX) + 1) return false; len = RoundUpToNextValidAsmJSHeapLength(len); - if (len > module_->minHeapLength) + if (len > module_->minHeapLength) { module_->minHeapLength = len; + mg_.bumpMinHeapLength(len); + } return true; } @@ -3505,40 +3512,12 @@ IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn, uint32_t* u32) return IsLiteralInt(lit, u32); } -static bool -FoldMaskedArrayIndex(FunctionValidator& f, ParseNode** indexExpr, int32_t* mask, - NeedsBoundsCheck* needsBoundsCheck) -{ - MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND)); - - ParseNode* indexNode = BitwiseLeft(*indexExpr); - ParseNode* maskNode = BitwiseRight(*indexExpr); - - uint32_t mask2; - if (IsLiteralOrConstInt(f, maskNode, &mask2)) { - // Flag the access to skip the bounds check if the mask ensures that an - // 'out of bounds' access can not occur based on the current heap length - // constraint. The unsigned maximum of a masked index is the mask - // itself, so check that the mask is not negative and compare the mask - // to the known minimum heap length. - if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength()) - *needsBoundsCheck = NO_BOUNDS_CHECK; - *mask &= mask2; - *indexExpr = indexNode; - return true; - } - - return false; -} - static const int32_t NoMask = -1; static bool CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr, - Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask) + Scalar::Type* viewType, int32_t* mask) { - *needsBoundsCheck = NEEDS_BOUNDS_CHECK; - if (!viewName->isKind(PNK_NAME)) return f.fail(viewName, "base of array access must be a typed array view name"); @@ -3555,7 +3534,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr return f.fail(indexExpr, "constant index out of range"); *mask = NoMask; - *needsBoundsCheck = NO_BOUNDS_CHECK; return f.writeInt32Lit(byteOffset); } @@ -3577,9 +3555,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr ParseNode* pointerNode = BitwiseLeft(indexExpr); - if (pointerNode->isKind(PNK_BITAND)) - FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck); - Type pointerType; if (!CheckExpr(f, pointerNode, &pointerType)) return false; @@ -3596,9 +3571,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr ParseNode* pointerNode = indexExpr; - if (pointerNode->isKind(PNK_BITAND)) - folded = FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck); - Type pointerType; if (!CheckExpr(f, pointerNode, &pointerType)) return false; @@ -3617,13 +3589,13 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr static bool CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr, - Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask) + Scalar::Type* viewType, int32_t* mask) { size_t prepareAt; if (!f.encoder().writePatchableExpr(&prepareAt)) return false; - if (!CheckArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask)) + if (!CheckArrayAccess(f, viewName, indexExpr, viewType, mask)) return false; // Don't generate the mask op if there is no need for it which could happen for @@ -3641,17 +3613,13 @@ static bool CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type) { Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; size_t opcodeAt; - size_t needsBoundsCheckAt; if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; - if (!f.encoder().writePatchableU8(&needsBoundsCheckAt)) - return false; - if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &needsBoundsCheck, &mask)) + if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &mask)) return false; switch (viewType) { @@ -3666,8 +3634,6 @@ CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type) default: MOZ_CRASH("unexpected scalar type"); } - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - switch (viewType) { case Scalar::Int8: case Scalar::Int16: @@ -3693,16 +3659,12 @@ static bool CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type) { size_t opcodeAt; - size_t needsBoundsCheckAt; if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; - if (!f.encoder().writePatchableU8(&needsBoundsCheckAt)) - return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &needsBoundsCheck, &mask)) + if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &mask)) return false; Type rhsType; @@ -3759,8 +3721,6 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type default: MOZ_CRASH("unexpected scalar type"); } - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - *type = rhsType; return true; } @@ -3999,10 +3959,9 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ static bool CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr, - Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, - int32_t* mask) + Scalar::Type* viewType, int32_t* mask) { - if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask)) + if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, mask)) return false; // The global will be sane, CheckArrayAccess checks it. @@ -4038,10 +3997,9 @@ CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type) } static bool -WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* needsBoundsCheckAt, size_t* viewTypeAt) +WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt) { return f.encoder().writeExpr(opcode) && - f.encoder().writePatchableU8(needsBoundsCheckAt) && f.encoder().writePatchableU8(viewTypeAt); } @@ -4054,18 +4012,15 @@ CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arrayArg = CallArgList(call); ParseNode* indexArg = NextNode(arrayArg); - size_t needsBoundsCheckAt; size_t viewTypeAt; - if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, &needsBoundsCheckAt, &viewTypeAt)) + if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, &viewTypeAt)) return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) + if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask)) return false; - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; @@ -4082,15 +4037,13 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - size_t needsBoundsCheckAt; size_t viewTypeAt; - if (!WriteAtomicOperator(f, Expr::I32AtomicsStore, &needsBoundsCheckAt, &viewTypeAt)) + if (!WriteAtomicOperator(f, Expr::I32AtomicsStore, &viewTypeAt)) return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) + if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask)) return false; Type rhsType; @@ -4100,7 +4053,6 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type) if (!rhsType.isIntish()) return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars()); - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = rhsType; @@ -4117,17 +4069,15 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - size_t needsBoundsCheckAt; size_t viewTypeAt; - if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &needsBoundsCheckAt, &viewTypeAt)) + if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &viewTypeAt)) return false; if (!f.encoder().writeU8(uint8_t(op))) return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) + if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask)) return false; Type valueArgType; @@ -4137,7 +4087,6 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op if (!valueArgType.isIntish()) return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars()); - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; @@ -4171,15 +4120,13 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* oldValueArg = NextNode(indexArg); ParseNode* newValueArg = NextNode(oldValueArg); - size_t needsBoundsCheckAt; size_t viewTypeAt; - if (!WriteAtomicOperator(f, Expr::I32AtomicsCompareExchange, &needsBoundsCheckAt, &viewTypeAt)) + if (!WriteAtomicOperator(f, Expr::I32AtomicsCompareExchange, &viewTypeAt)) return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) + if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask)) return false; Type oldValueArgType; @@ -4196,7 +4143,6 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type) if (!newValueArgType.isIntish()) return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars()); - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; @@ -4213,15 +4159,13 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* indexArg = NextNode(arrayArg); ParseNode* valueArg = NextNode(indexArg); - size_t needsBoundsCheckAt; size_t viewTypeAt; - if (!WriteAtomicOperator(f, Expr::I32AtomicsExchange, &needsBoundsCheckAt, &viewTypeAt)) + if (!WriteAtomicOperator(f, Expr::I32AtomicsExchange, &viewTypeAt)) return false; Scalar::Type viewType; - NeedsBoundsCheck needsBoundsCheck; int32_t mask; - if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) + if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask)) return false; Type valueArgType; @@ -4231,7 +4175,6 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type) if (!valueArgType.isIntish()) return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars()); - f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; @@ -5078,12 +5021,9 @@ CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call) if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) { if (!f.m().tryConstantAccess(indexLit, Simd128DataSize)) return f.fail(indexExpr, "constant index out of range"); - return f.encoder().writeU8(NO_BOUNDS_CHECK) && f.writeInt32Lit(indexLit); + return f.writeInt32Lit(indexLit); } - if (!f.encoder().writeU8(NEEDS_BOUNDS_CHECK)) - return false; - Type indexType; if (!CheckExpr(f, indexExpr, &indexType)) return false; diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 01de9c7302..3cd7357b2c 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -362,13 +362,24 @@ DecodeIfElse(FunctionDecoder& f, bool hasElse, ExprType expected) static bool DecodeLoadStoreAddress(FunctionDecoder &f) { - uint32_t offset, align; - return DecodeExpr(f, ExprType::I32) && - f.d().readVarU32(&offset) && - f.d().readVarU32(&align) && - mozilla::IsPowerOfTwo(align) && - (offset == 0 || f.fail("NYI: address offsets")) && - f.fail("NYI: wasm loads and stores"); + uint32_t offset; + if (!f.d().readVarU32(&offset)) + return f.fail("expected memory access offset"); + + if (offset != 0) + return f.fail("NYI: address offsets"); + + uint32_t align; + if (!f.d().readVarU32(&align)) + return f.fail("expected memory access alignment"); + + if (!mozilla::IsPowerOfTwo(align)) + return f.fail("memory access alignment must be a power of two"); + + if (!DecodeExpr(f, ExprType::I32)) + return false; + + return f.fail("NYI: wasm loads and stores"); } static bool @@ -386,6 +397,13 @@ DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type) CheckType(f, type, expected); } +static bool +DecodeReturn(FunctionDecoder& f) +{ + return f.ret() == ExprType::Void || + DecodeExpr(f, f.ret()); +} + static bool DecodeExpr(FunctionDecoder& f, ExprType expected) { @@ -613,6 +631,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected) return DecodeStore(f, expected, ExprType::F32); case Expr::F64StoreMem: return DecodeStore(f, expected, ExprType::F64); + case Expr::Return: + return DecodeReturn(f); default: break; } @@ -846,6 +866,20 @@ DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) return true; } +static bool +CheckTypeForJS(JSContext* cx, Decoder& d, const Sig& sig) +{ + for (ValType argType : sig.args()) { + if (argType == ValType::I64) + return Fail(cx, d, "cannot import/export i64 argument"); + } + + if (sig.ret() == ExprType::I64) + return Fail(cx, d, "cannot import/export i64 return type"); + + return true; +} + static bool DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames) { @@ -856,6 +890,9 @@ DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVec if (!init->imports.emplaceBack(sig)) return false; + if (!CheckTypeForJS(cx, d, *sig)) + return false; + UniqueChars moduleName = d.readCString(); if (!moduleName) return Fail(cx, d, "expected import module name"); @@ -969,6 +1006,9 @@ DecodeFunctionExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, CStringSet* if (funcIndex >= mg.numFuncSigs()) return Fail(cx, d, "export function index out of range"); + if (!CheckTypeForJS(cx, d, mg.funcSig(funcIndex))) + return false; + UniqueChars fieldName = DecodeFieldName(cx, d, dupSet); if (!fieldName) return false; @@ -1254,14 +1294,6 @@ wasm::HasCompilerSupport(ExclusiveContext* cx) #endif } -static bool -WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setBoolean(HasCompilerSupport(cx)); - return true; -} - static bool CheckCompilerSupport(JSContext* cx) { @@ -1357,99 +1389,3 @@ wasm::Eval(JSContext* cx, Handle code, return true; } - -static bool -WasmEval(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedObject callee(cx, &args.callee()); - - if (args.length() < 1 || args.length() > 2) { - ReportUsageError(cx, callee, "Wrong number of arguments"); - return false; - } - - if (!args[0].isObject() || !args[0].toObject().is()) { - ReportUsageError(cx, callee, "First argument must be an ArrayBuffer"); - return false; - } - - RootedObject importObj(cx); - if (!args.get(1).isUndefined()) { - if (!args.get(1).isObject()) { - ReportUsageError(cx, callee, "Second argument, if present, must be an Object"); - return false; - } - importObj = &args[1].toObject(); - } - - Rooted code(cx, &args[0].toObject().as()); - - RootedObject exportObj(cx); - if (!Eval(cx, code, importObj, &exportObj)) - return false; - - args.rval().setObject(*exportObj); - return true; -} - -static bool -WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedObject callee(cx, &args.callee()); - - if (args.length() != 1) { - ReportUsageError(cx, callee, "Wrong number of arguments"); - return false; - } - - if (!args[0].isString()) { - ReportUsageError(cx, callee, "First argument must be a String"); - return false; - } - - AutoStableStringChars twoByteChars(cx); - if (!twoByteChars.initTwoByte(cx, args[0].toString())) - return false; - - UniqueChars error; - UniqueBytecode bytes = TextToBinary(twoByteChars.twoByteChars(), &error); - if (!bytes) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, - error.get() ? error.get() : "out of memory"); - return false; - } - - Rooted buffer(cx, ArrayBufferObject::create(cx, bytes->length())); - if (!buffer) - return false; - - memcpy(buffer->dataPointer(), bytes->begin(), bytes->length()); - - args.rval().setObject(*buffer); - return true; -} - -static const JSFunctionSpecWithHelp WasmTestingFunctions[] = { - JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0, -"wasmIsSupported()", -" Returns a boolean indicating whether WebAssembly is supported on the current device."), - - JS_FN_HELP("wasmEval", WasmEval, 2, 0, -"wasmEval(buffer, imports)", -" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n" -" and links the module's imports with the given 'imports' object."), - - JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0, -"wasmTextToBinary(str)", -" Translates the given text wasm module into its binary encoding."), - - JS_FS_HELP_END -}; - -bool -wasm::DefineTestingFunctions(JSContext* cx, HandleObject globalObj) -{ - return JS_DefineFunctionsWithHelp(cx, globalObj, WasmTestingFunctions); -} diff --git a/js/src/asmjs/Wasm.h b/js/src/asmjs/Wasm.h index c85f082abd..6c131575ce 100644 --- a/js/src/asmjs/Wasm.h +++ b/js/src/asmjs/Wasm.h @@ -27,10 +27,6 @@ class ArrayBufferObject; namespace wasm { -// Add wasm testing JS functions to the given JS global object. -bool -DefineTestingFunctions(JSContext* cx, JS::HandleObject globalObj); - // Return whether WebAssembly can be compiled on this platform. bool HasCompilerSupport(ExclusiveContext* cx); diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 515d6478c4..bf52fe0eec 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -322,12 +322,6 @@ enum class Expr : uint16_t Limit }; -enum NeedsBoundsCheck : uint8_t -{ - NO_BOUNDS_CHECK, - NEEDS_BOUNDS_CHECK -}; - typedef Vector Bytecode; typedef UniquePtr UniqueBytecode; @@ -713,19 +707,29 @@ class Decoder double uncheckedReadFixedF64() { return uncheckedRead(); } - uint32_t uncheckedReadVarU32() { - uint32_t decoded = 0; + template + UInt uncheckedReadVarU() { + static const unsigned numBits = sizeof(UInt) * CHAR_BIT; + static const unsigned remainderBits = numBits % 7; + static const unsigned numBitsInSevens = numBits - remainderBits; + UInt decoded = 0; uint32_t shift = 0; do { uint8_t byte = *cur_++; if (!(byte & 0x80)) - return decoded | (uint32_t(byte) << shift); - decoded |= uint32_t(byte & 0x7f) << shift; + return decoded | (UInt(byte) << shift); + decoded |= UInt(byte & 0x7f) << shift; shift += 7; - } while (shift != 28); + } while (shift != numBitsInSevens); uint8_t byte = *cur_++; MOZ_ASSERT(!(byte & 0xf0)); - return decoded | (uint32_t(byte) << 28); + return decoded | (UInt(byte) << numBitsInSevens); + } + uint32_t uncheckedReadVarU32() { + return uncheckedReadVarU(); + } + uint64_t uncheckedReadVarU64() { + return uncheckedReadVarU(); } Expr uncheckedReadExpr() { return uncheckedReadEnum(); diff --git a/js/src/asmjs/WasmFrameIterator.cpp b/js/src/asmjs/WasmFrameIterator.cpp index ca4a3d0d46..5636469740 100644 --- a/js/src/asmjs/WasmFrameIterator.cpp +++ b/js/src/asmjs/WasmFrameIterator.cpp @@ -357,16 +357,7 @@ wasm::GenerateFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, FuncO masm.twoByteNop(); #elif defined(JS_CODEGEN_ARM) masm.nop(); -#elif defined(JS_CODEGEN_MIPS32) - masm.nop(); - masm.nop(); - masm.nop(); - masm.nop(); -#elif defined(JS_CODEGEN_MIPS64) - masm.nop(); - masm.nop(); - masm.nop(); - masm.nop(); +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) masm.nop(); masm.nop(); #endif @@ -740,12 +731,12 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo MOZ_CRASH(); void* callee = nullptr; (void)callerRetAddr; -#elif defined(JS_CODEGEN_MIPS32) - Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t)); - void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next()); -#elif defined(JS_CODEGEN_MIPS64) - Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t)); - void* callee = (void*)Assembler::ExtractLoad64Value(instr); +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + uint8_t* caller = callerRetAddr - 2 * sizeof(uint32_t); + InstImm* callerInsn = reinterpret_cast(caller); + BOffImm16 calleeOffset; + callerInsn->extractImm16(&calleeOffset); + void* callee = calleeOffset.getDest(reinterpret_cast(caller)); #elif defined(JS_CODEGEN_NONE) MOZ_CRASH(); void* callee = nullptr; @@ -771,13 +762,8 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo #elif defined(JS_CODEGEN_ARM64) (void)to; MOZ_CRASH(); -#elif defined(JS_CODEGEN_MIPS32) - Assembler::WriteLuiOriInstructions(instr, instr->next(), - ScratchRegister, (uint32_t)to); - instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); -#elif defined(JS_CODEGEN_MIPS64) - Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)to); - instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + callerInsn->setBOffImm16(BOffImm16(to - caller)); #elif defined(JS_CODEGEN_NONE) MOZ_CRASH(); #else @@ -833,25 +819,12 @@ wasm::EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, (void)jump; (void)profilingEpilogue; MOZ_CRASH(); -#elif defined(JS_CODEGEN_MIPS32) - Instruction* instr = (Instruction*)jump; - if (enabled) { - Assembler::WriteLuiOriInstructions(instr, instr->next(), - ScratchRegister, (uint32_t)profilingEpilogue); - instr[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr); - } else { - for (unsigned i = 0; i < 3; i++) - instr[i].makeNop(); - } -#elif defined(JS_CODEGEN_MIPS64) - Instruction* instr = (Instruction*)jump; - if (enabled) { - Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)profilingEpilogue); - instr[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr); - } else { - for (unsigned i = 0; i < 5; i++) - instr[i].makeNop(); - } +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + InstImm* instr = (InstImm*)jump; + if (enabled) + instr->setBOffImm16(BOffImm16(profilingEpilogue - jump)); + else + instr->makeNop(); #elif defined(JS_CODEGEN_NONE) MOZ_CRASH(); #else diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index f0adadb567..34b9602749 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -522,20 +522,6 @@ ModuleGenerator::finishStaticLinkData(uint8_t* code, uint32_t codeBytes, StaticL } #endif -#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - // On MIPS we need to update all the long jumps because they contain an - // absolute adress. The values are correctly patched for the current address - // space, but not after serialization or profiling-mode toggling. - for (size_t i = 0; i < masm_.numLongJumps(); i++) { - size_t off = masm_.longJump(i); - StaticLinkData::InternalLink inLink(StaticLinkData::InternalLink::InstructionImmediate); - inLink.patchAtOffset = off; - inLink.targetOffset = Assembler::ExtractInstructionImmediate(code + off) - uintptr_t(code); - if (!link->internalLinks.append(inLink)) - return false; - } -#endif - #if defined(JS_CODEGEN_X64) // Global data accesses on x64 use rip-relative addressing and thus do // not need patching after deserialization. @@ -651,6 +637,15 @@ ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex) return true; } +void +ModuleGenerator::bumpMinHeapLength(uint32_t newMinHeapLength) +{ + MOZ_ASSERT(isAsmJS()); + MOZ_ASSERT(newMinHeapLength >= shared_->minHeapLength); + + shared_->minHeapLength = newMinHeapLength; +} + const DeclaredSig& ModuleGenerator::funcSig(uint32_t funcIndex) const { @@ -741,9 +736,6 @@ ModuleGenerator::startFuncDefs() if (!threadView_) return false; - if (!funcIndexToExport_.init()) - return false; - uint32_t numTasks; if (ParallelCompilationEnabled(cx_) && HelperThreadState().wasmCompilationInProgress.compareExchange(false, true)) diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index fcbfca316b..f8fc967fa1 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -99,6 +99,7 @@ struct ModuleGeneratorData CompileArgs args; ModuleKind kind; uint32_t numTableElems; + mozilla::Atomic minHeapLength; DeclaredSigVector sigs; TableModuleGeneratorDataVector sigToTable; @@ -111,7 +112,7 @@ struct ModuleGeneratorData } explicit ModuleGeneratorData(ExclusiveContext* cx, ModuleKind kind = ModuleKind::Wasm) - : args(cx), kind(kind), numTableElems(0) + : args(cx), kind(kind), numTableElems(0), minHeapLength(0) {} }; @@ -140,6 +141,9 @@ class ModuleGeneratorThreadView MOZ_ASSERT(!isAsmJS()); return shared_.numTableElems; } + uint32_t minHeapLength() const { + return shared_.minHeapLength; + } const DeclaredSig& sig(uint32_t sigIndex) const { return shared_.sigs[sigIndex]; } @@ -258,6 +262,7 @@ class MOZ_STACK_CLASS ModuleGenerator bool initImport(uint32_t importIndex, uint32_t sigIndex); bool initSigTableLength(uint32_t sigIndex, uint32_t numElems); void initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices); + void bumpMinHeapLength(uint32_t newMinHeapLength); // asm.js global variables: bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index); diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index ead29d0169..816c491e50 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -198,6 +198,15 @@ class FunctionCompiler return constant; } + MDefinition* constant(int64_t i) + { + if (inDeadCode()) + return nullptr; + MConstant* constant = MConstant::NewInt64(alloc(), i); + curBlock_->add(constant); + return constant; + } + template MDefinition* unary(MDefinition* op) { @@ -470,53 +479,46 @@ class FunctionCompiler curBlock_->setSlot(info().localSlot(slot), def); } - MDefinition* loadHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk) + MDefinition* loadHeap(Scalar::Type accessType, MDefinition* ptr) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD loads should use loadSimdHeap"); - MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck); + MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr); curBlock_->add(load); return load; } - MDefinition* loadSimdHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk, - unsigned numElems) + MDefinition* loadSimdHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numElems) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MOZ_ASSERT(Scalar::isSimdType(accessType), "loadSimdHeap can only load from a SIMD view"); - MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, - numElems); + MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, numElems); curBlock_->add(load); return load; } - void storeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, NeedsBoundsCheck chk) + void storeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v) { if (inDeadCode()) return; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD stores should use storeSimdHeap"); - MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck); + MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v); curBlock_->add(store); } void storeSimdHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, - NeedsBoundsCheck chk, unsigned numElems) + unsigned numElems) { if (inDeadCode()) return; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MOZ_ASSERT(Scalar::isSimdType(accessType), "storeSimdHeap can only load from a SIMD view"); - MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, - numElems); + MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, numElems); curBlock_->add(store); } @@ -528,66 +530,59 @@ class FunctionCompiler curBlock_->add(ins); } - MDefinition* atomicLoadHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk) + MDefinition* atomicLoadHeap(Scalar::Type accessType, MDefinition* ptr) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; - MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, - /* numElems */ 0, + MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, /* numElems */ 0, MembarBeforeLoad, MembarAfterLoad); curBlock_->add(load); return load; } - void atomicStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, NeedsBoundsCheck chk) + void atomicStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v) { if (inDeadCode()) return; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; - MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, + MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, /* numElems = */ 0, MembarBeforeStore, MembarAfterStore); curBlock_->add(store); } MDefinition* atomicCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv, - MDefinition* newv, NeedsBoundsCheck chk) + MDefinition* newv) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MAsmJSCompareExchangeHeap* cas = - MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv, needsBoundsCheck); + MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv); curBlock_->add(cas); return cas; } - MDefinition* atomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value, - NeedsBoundsCheck chk) + MDefinition* atomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MAsmJSAtomicExchangeHeap* cas = - MAsmJSAtomicExchangeHeap::New(alloc(), accessType, ptr, value, needsBoundsCheck); + MAsmJSAtomicExchangeHeap::New(alloc(), accessType, ptr, value); curBlock_->add(cas); return cas; } MDefinition* atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type accessType, MDefinition* ptr, - MDefinition* v, NeedsBoundsCheck chk) + MDefinition* v) { if (inDeadCode()) return nullptr; - bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MAsmJSAtomicBinopHeap* binop = - MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v, needsBoundsCheck); + MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v); curBlock_->add(binop); return binop; } @@ -1234,6 +1229,7 @@ class FunctionCompiler uint8_t readU8() { return decoder_.uncheckedReadFixedU8(); } uint32_t readVarU32() { return decoder_.uncheckedReadVarU32(); } + uint64_t readVarU64() { return decoder_.uncheckedReadVarU64(); } float readF32() { return decoder_.uncheckedReadFixedF32(); } double readF64() { return decoder_.uncheckedReadFixedF64(); } Expr readExpr() { return decoder_.uncheckedReadExpr(); } @@ -1364,7 +1360,9 @@ EmitLiteral(FunctionCompiler& f, ExprType type, MDefinition** def) return true; } case ExprType::I64: { - MOZ_CRASH("int64"); + int64_t val = f.readVarU64(); + *def = f.constant(val); + return true; } case ExprType::F32: { float val = f.readF32(); @@ -1425,19 +1423,16 @@ static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullpt static bool EmitLoadArray(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); MDefinition* ptr; if (!EmitExpr(f, ExprType::I32, &ptr)) return false; - *def = f.loadHeap(scalarType, ptr, needsBoundsCheck); + *def = f.loadHeap(scalarType, ptr); return true; } static bool EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); - MDefinition* ptr; if (!EmitExpr(f, ExprType::I32, &ptr)) return false; @@ -1461,7 +1456,7 @@ EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def) default: MOZ_CRASH("unexpected scalar type"); } - f.storeHeap(viewType, ptr, rhs, needsBoundsCheck); + f.storeHeap(viewType, ptr, rhs); *def = rhs; return true; } @@ -1470,7 +1465,6 @@ static bool EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type viewType, MDefinition **def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); MDefinition* ptr; if (!EmitExpr(f, ExprType::I32, &ptr)) return false; @@ -1489,7 +1483,7 @@ EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type vi MOZ_CRASH("unexpected coerced store"); } - f.storeHeap(viewType, ptr, coerced, needsBoundsCheck); + f.storeHeap(viewType, ptr, coerced); *def = rhs; return true; } @@ -1541,19 +1535,17 @@ EmitMathMinMax(FunctionCompiler& f, ExprType type, bool isMax, MDefinition** def static bool EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); Scalar::Type viewType = Scalar::Type(f.readU8()); MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) return false; - *def = f.atomicLoadHeap(viewType, index, needsBoundsCheck); + *def = f.atomicLoadHeap(viewType, index); return true; } static bool EmitAtomicsStore(FunctionCompiler& f, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); Scalar::Type viewType = Scalar::Type(f.readU8()); MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) @@ -1561,7 +1553,7 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def) MDefinition* value; if (!EmitExpr(f, ExprType::I32, &value)) return false; - f.atomicStoreHeap(viewType, index, value, needsBoundsCheck); + f.atomicStoreHeap(viewType, index, value); *def = value; return true; } @@ -1569,7 +1561,6 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def) static bool EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); Scalar::Type viewType = Scalar::Type(f.readU8()); js::jit::AtomicOp op = js::jit::AtomicOp(f.readU8()); MDefinition* index; @@ -1578,14 +1569,13 @@ EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def) MDefinition* value; if (!EmitExpr(f, ExprType::I32, &value)) return false; - *def = f.atomicBinopHeap(op, viewType, index, value, needsBoundsCheck); + *def = f.atomicBinopHeap(op, viewType, index, value); return true; } static bool EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); Scalar::Type viewType = Scalar::Type(f.readU8()); MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) @@ -1596,14 +1586,13 @@ EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def) MDefinition* newValue; if (!EmitExpr(f, ExprType::I32, &newValue)) return false; - *def = f.atomicCompareExchangeHeap(viewType, index, oldValue, newValue, needsBoundsCheck); + *def = f.atomicCompareExchangeHeap(viewType, index, oldValue, newValue); return true; } static bool EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def) { - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); Scalar::Type viewType = Scalar::Type(f.readU8()); MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) @@ -1611,7 +1600,7 @@ EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def) MDefinition* value; if (!EmitExpr(f, ExprType::I32, &value)) return false; - *def = f.atomicExchangeHeap(viewType, index, value, needsBoundsCheck); + *def = f.atomicExchangeHeap(viewType, index, value); return true; } @@ -1980,13 +1969,11 @@ EmitSimdLoad(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition* if (!numElems) numElems = defaultNumElems; - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); - MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) return false; - *def = f.loadSimdHeap(viewType, index, needsBoundsCheck, numElems); + *def = f.loadSimdHeap(viewType, index, numElems); return true; } @@ -1999,8 +1986,6 @@ EmitSimdStore(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition if (!numElems) numElems = defaultNumElems; - NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8()); - MDefinition* index; if (!EmitExpr(f, ExprType::I32, &index)) return false; @@ -2009,7 +1994,7 @@ EmitSimdStore(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition if (!EmitExpr(f, type, &vec)) return false; - f.storeSimdHeap(viewType, index, vec, needsBoundsCheck, numElems); + f.storeSimdHeap(viewType, index, vec, numElems); *def = vec; return true; } @@ -2837,6 +2822,9 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may return EmitAtomicsStore(f, def); case Expr::I32AtomicsBinOp: return EmitAtomicsBinOp(f, def); + // I64 + case Expr::I64Const: + return EmitLiteral(f, ExprType::I64, def); // F32 case Expr::F32Const: return EmitLiteral(f, ExprType::F32, def); @@ -2978,7 +2966,6 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may case Expr::F64Nearest: case Expr::F64Trunc: case Expr::I32WrapI64: - case Expr::I64Const: case Expr::I64ExtendSI32: case Expr::I64ExtendUI32: case Expr::I64TruncSF32: @@ -3060,8 +3047,9 @@ wasm::IonCompileFunction(IonCompileTask* task) MIRGraph graph(&results.alloc()); CompileInfo compileInfo(func.numLocals()); MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo, - IonOptimizations.get(OptimizationLevel::AsmJS), - task->mg().args().useSignalHandlersForOOB); + IonOptimizations.get(OptimizationLevel::AsmJS)); + mir.initUsesSignalHandlersForAsmJSOOB(task->mg().args().useSignalHandlersForOOB); + mir.initMinAsmJSHeapLength(task->mg().minHeapLength()); // Build MIR graph { diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 685b96604a..ef1ad08533 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -44,6 +44,7 @@ using mozilla::PositiveInfinity; using mozilla::SpecificNaN; static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096; +static const uint32_t WasmNoIndex = UINT32_MAX; /*****************************************************************************/ // wasm AST @@ -58,8 +59,77 @@ using WasmAstVector = mozilla::Vector>; template using WasmAstHashMap = HashMap>; +class WasmName +{ + const char16_t* begin_; + const char16_t* end_; + public: + WasmName(const char16_t* begin, size_t length) : begin_(begin), end_(begin + length) {} + WasmName() : begin_(nullptr), end_(nullptr) {} + const char16_t* begin() const { return begin_; } + const char16_t* end() const { return end_; } + size_t length() const { return end_ - begin_; } + bool empty() const { return begin_ == nullptr; } + + bool operator==(WasmName rhs) const { + if (length() != rhs.length()) + return false; + if (begin() == rhs.begin()) + return true; + return EqualChars(begin(), rhs.begin(), length()); + } +}; + +class WasmRef +{ + WasmName name_; + uint32_t index_; + + public: + WasmRef() + : index_(WasmNoIndex) + { + MOZ_ASSERT(isInvalid()); + } + WasmRef(WasmName name, uint32_t index) + : name_(name), index_(index) + { + MOZ_ASSERT(name.empty() ^ (index == WasmNoIndex)); + MOZ_ASSERT(!isInvalid()); + } + bool isInvalid() const { + return name_.empty() && index_ == WasmNoIndex; + } + WasmName name() const { + return name_; + } + size_t index() const { + MOZ_ASSERT(index_ != WasmNoIndex); + return index_; + } + void setIndex(uint32_t index) { + MOZ_ASSERT(index_ == WasmNoIndex); + index_ = index; + } +}; + +struct WasmNameHasher +{ + typedef const WasmName Lookup; + static js::HashNumber hash(Lookup l) { + return mozilla::HashString(l.begin(), l.length()); + } + static bool match(const WasmName key, Lookup lookup) { + return key == lookup; + } +}; + +using WasmNameMap = WasmAstHashMap; + typedef WasmAstVector WasmAstValTypeVector; typedef WasmAstVector WasmAstExprVector; +typedef WasmAstVector WasmNameVector; +typedef WasmAstVector WasmRefVector; struct WasmAstBase { @@ -70,6 +140,7 @@ struct WasmAstBase class WasmAstSig : public WasmAstBase { + WasmName name_; WasmAstValTypeVector args_; ExprType ret_; @@ -82,8 +153,9 @@ class WasmAstSig : public WasmAstBase : args_(Move(args)), ret_(ret) {} - WasmAstSig(WasmAstSig&& rhs) - : args_(Move(rhs.args_)), + WasmAstSig(WasmName name, WasmAstSig&& rhs) + : name_(name), + args_(Move(rhs.args_)), ret_(rhs.ret_) {} void operator=(WasmAstSig&& rhs) { @@ -96,6 +168,9 @@ class WasmAstSig : public WasmAstBase ExprType ret() const { return ret_; } + WasmName name() const { + return name_; + } bool operator==(const WasmAstSig& rhs) const { return ret() == rhs.ret() && EqualContainers(args(), rhs.args()); } @@ -116,6 +191,7 @@ enum class WasmAstExprKind { BinaryOperator, Block, + Branch, Call, CallIndirect, ComparisonOperator, @@ -125,8 +201,10 @@ enum class WasmAstExprKind IfElse, Load, Nop, + Return, SetLocal, Store, + TableSwitch, UnaryOperator, }; @@ -171,33 +249,33 @@ class WasmAstConst : public WasmAstExpr class WasmAstGetLocal : public WasmAstExpr { - uint32_t localIndex_; + WasmRef local_; public: static const WasmAstExprKind Kind = WasmAstExprKind::GetLocal; - explicit WasmAstGetLocal(uint32_t localIndex) + explicit WasmAstGetLocal(WasmRef local) : WasmAstExpr(Kind), - localIndex_(localIndex) + local_(local) {} - uint32_t localIndex() const { - return localIndex_; + WasmRef& local() { + return local_; } }; class WasmAstSetLocal : public WasmAstExpr { - uint32_t localIndex_; + WasmRef local_; WasmAstExpr& value_; public: static const WasmAstExprKind Kind = WasmAstExprKind::SetLocal; - WasmAstSetLocal(uint32_t localIndex, WasmAstExpr& value) + WasmAstSetLocal(WasmRef local, WasmAstExpr& value) : WasmAstExpr(Kind), - localIndex_(localIndex), + local_(local), value_(value) {} - uint32_t localIndex() const { - return localIndex_; + WasmRef& local() { + return local_; } WasmAstExpr& value() const { return value_; @@ -206,52 +284,93 @@ class WasmAstSetLocal : public WasmAstExpr class WasmAstBlock : public WasmAstExpr { + Expr expr_; + WasmName breakName_; + WasmName continueName_; WasmAstExprVector exprs_; public: static const WasmAstExprKind Kind = WasmAstExprKind::Block; - explicit WasmAstBlock(WasmAstExprVector&& exprs) + explicit WasmAstBlock(Expr expr, WasmName breakName, WasmName continueName, + WasmAstExprVector&& exprs) : WasmAstExpr(Kind), + expr_(expr), + breakName_(breakName), + continueName_(continueName), exprs_(Move(exprs)) {} + Expr expr() const { return expr_; } + WasmName breakName() const { return breakName_; } + WasmName continueName() const { return continueName_; } const WasmAstExprVector& exprs() const { return exprs_; } }; +class WasmAstBranch : public WasmAstExpr +{ + Expr expr_; + WasmAstExpr* cond_; + WasmRef target_; + + public: + static const WasmAstExprKind Kind = WasmAstExprKind::Branch; + explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target) + : WasmAstExpr(Kind), + expr_(expr), + cond_(cond), + target_(target) + {} + Expr expr() const { return expr_; } + WasmRef& target() { return target_; } + WasmAstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; } +}; + class WasmAstCall : public WasmAstExpr { Expr expr_; - uint32_t index_; + WasmRef func_; WasmAstExprVector args_; public: static const WasmAstExprKind Kind = WasmAstExprKind::Call; - WasmAstCall(Expr expr, uint32_t index, WasmAstExprVector&& args) - : WasmAstExpr(Kind), expr_(expr), index_(index), args_(Move(args)) + WasmAstCall(Expr expr, WasmRef func, WasmAstExprVector&& args) + : WasmAstExpr(Kind), expr_(expr), func_(func), args_(Move(args)) {} Expr expr() const { return expr_; } - uint32_t index() const { return index_; } + WasmRef& func() { return func_; } const WasmAstExprVector& args() const { return args_; } }; class WasmAstCallIndirect : public WasmAstExpr { - uint32_t sigIndex_; + WasmRef sig_; WasmAstExpr* index_; WasmAstExprVector args_; public: static const WasmAstExprKind Kind = WasmAstExprKind::CallIndirect; - WasmAstCallIndirect(uint32_t sigIndex, WasmAstExpr* index, WasmAstExprVector&& args) - : WasmAstExpr(Kind), sigIndex_(sigIndex), index_(index), args_(Move(args)) + WasmAstCallIndirect(WasmRef sig, WasmAstExpr* index, WasmAstExprVector&& args) + : WasmAstExpr(Kind), sig_(sig), index_(index), args_(Move(args)) {} - - uint32_t sigIndex() const { return sigIndex_; } + WasmRef& sig() { return sig_; } WasmAstExpr* index() const { return index_; } const WasmAstExprVector& args() const { return args_; } }; +class WasmAstReturn : public WasmAstExpr +{ + WasmAstExpr* maybeExpr_; + + public: + static const WasmAstExprKind Kind = WasmAstExprKind::Return; + explicit WasmAstReturn(WasmAstExpr* maybeExpr) + : WasmAstExpr(Kind), + maybeExpr_(maybeExpr) + {} + WasmAstExpr* maybeExpr() const { return maybeExpr_; } +}; + class WasmAstIfElse : public WasmAstExpr { Expr expr_; @@ -261,8 +380,8 @@ class WasmAstIfElse : public WasmAstExpr public: static const WasmAstExprKind Kind = WasmAstExprKind::IfElse; - explicit WasmAstIfElse(Expr expr, WasmAstExpr* cond, WasmAstExpr* ifBody, - WasmAstExpr* elseBody = nullptr) + WasmAstIfElse(Expr expr, WasmAstExpr* cond, WasmAstExpr* ifBody, + WasmAstExpr* elseBody = nullptr) : WasmAstExpr(Kind), expr_(expr), cond_(cond), @@ -334,35 +453,63 @@ class WasmAstStore : public WasmAstExpr WasmAstExpr& value() const { return *value_; } }; +class WasmAstTableSwitch : public WasmAstExpr +{ + WasmAstExpr& index_; + WasmRef default_; + WasmRefVector table_; + + public: + static const WasmAstExprKind Kind = WasmAstExprKind::TableSwitch; + explicit WasmAstTableSwitch(WasmAstExpr& index, WasmRef def, WasmRefVector&& table) + : WasmAstExpr(Kind), + index_(index), + default_(def), + table_(Move(table)) + {} + WasmAstExpr& index() const { return index_; } + WasmRef& def() { return default_; } + WasmRefVector& table() { return table_; } +}; + class WasmAstFunc : public WasmAstNode { - const uint32_t sigIndex_; - WasmAstValTypeVector varTypes_; + WasmName name_; + WasmRef sig_; + WasmAstValTypeVector vars_; + WasmNameVector localNames_; WasmAstExprVector body_; public: - WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExprVector&& body) - : sigIndex_(sigIndex), - varTypes_(Move(varTypes)), + WasmAstFunc(WasmName name, WasmRef sig, WasmAstValTypeVector&& vars, + WasmNameVector&& locals, WasmAstExprVector&& body) + : name_(name), + sig_(sig), + vars_(Move(vars)), + localNames_(Move(locals)), body_(Move(body)) {} - uint32_t sigIndex() const { return sigIndex_; } - const WasmAstValTypeVector& varTypes() const { return varTypes_; } + WasmRef& sig() { return sig_; } + const WasmAstValTypeVector& vars() const { return vars_; } + const WasmNameVector& locals() const { return localNames_; } const WasmAstExprVector& body() const { return body_; } + WasmName name() const { return name_; } }; class WasmAstImport : public WasmAstNode { - TwoByteChars module_; - TwoByteChars func_; + WasmName name_; + WasmName module_; + WasmName func_; uint32_t sigIndex_; public: - WasmAstImport(TwoByteChars module, TwoByteChars func, uint32_t sigIndex) - : module_(module), func_(func), sigIndex_(sigIndex) + WasmAstImport(WasmName name, WasmName module, WasmName func, uint32_t sigIndex) + : name_(name), module_(module), func_(func), sigIndex_(sigIndex) {} - TwoByteChars module() const { return module_; } - TwoByteChars func() const { return func_; } + WasmName name() const { return name_; } + WasmName module() const { return module_; } + WasmName func() const { return func_; } uint32_t sigIndex() const { return sigIndex_; } }; @@ -370,27 +517,23 @@ enum class WasmAstExportKind { Func, Memory }; class WasmAstExport : public WasmAstNode { - TwoByteChars name_; + WasmName name_; WasmAstExportKind kind_; - union { - uint32_t funcIndex_; - } u; + WasmRef func_; public: - WasmAstExport(TwoByteChars name, uint32_t funcIndex) - : name_(name), kind_(WasmAstExportKind::Func) - { - u.funcIndex_ = funcIndex; - } - explicit WasmAstExport(TwoByteChars name) + WasmAstExport(WasmName name, WasmRef func) + : name_(name), kind_(WasmAstExportKind::Func), func_(func) + {} + explicit WasmAstExport(WasmName name) : name_(name), kind_(WasmAstExportKind::Memory) {} - TwoByteChars name() const { return name_; } + WasmName name() const { return name_; } WasmAstExportKind kind() const { return kind_; } - size_t funcIndex() const { MOZ_ASSERT(kind_ == WasmAstExportKind::Func); return u.funcIndex_; } + WasmRef& func() { return func_; } }; -typedef WasmAstVector WasmAstTableElemVector; +typedef WasmAstVector WasmAstTableElemVector; class WasmAstTable : public WasmAstNode { @@ -398,20 +541,20 @@ class WasmAstTable : public WasmAstNode public: explicit WasmAstTable(WasmAstTableElemVector&& elems) : elems_(Move(elems)) {} - const WasmAstTableElemVector& elems() const { return elems_; } + WasmAstTableElemVector& elems() { return elems_; } }; class WasmAstSegment : public WasmAstNode { uint32_t offset_; - TwoByteChars text_; + WasmName text_; public: - WasmAstSegment(uint32_t offset, TwoByteChars text) + WasmAstSegment(uint32_t offset, WasmName text) : offset_(offset), text_(text) {} uint32_t offset() const { return offset_; } - TwoByteChars text() const { return text_; } + WasmName text() const { return text_; } }; typedef WasmAstVector WasmAstSegmentVector; @@ -477,7 +620,7 @@ class WasmAstModule : public WasmAstNode return true; } *sigIndex = sigs_.length(); - return sigs_.append(new (lifo_) WasmAstSig(Move(sig))) && + return sigs_.append(new (lifo_) WasmAstSig(WasmName(), Move(sig))) && sigMap_.add(p, sigs_.back(), *sigIndex); } bool append(WasmAstSig* sig) { @@ -514,7 +657,7 @@ class WasmAstModule : public WasmAstNode table_ = table; return true; } - const WasmAstTable* maybeTable() const { + WasmAstTable* maybeTable() const { return table_; } }; @@ -610,6 +753,8 @@ class WasmToken Align, BinaryOpcode, Block, + Br, + BrIf, Call, CallImport, CallIndirect, @@ -633,6 +778,7 @@ class WasmToken Memory, Load, Local, + Loop, Module, Name, Nop, @@ -640,10 +786,12 @@ class WasmToken OpenParen, Param, Result, + Return, Segment, SetLocal, Store, Table, + TableSwitch, Text, Type, UnaryOpcode, @@ -737,12 +885,15 @@ class WasmToken const char16_t* end() const { return end_; } - TwoByteChars text() const { + WasmName text() const { MOZ_ASSERT(kind_ == Text); MOZ_ASSERT(begin_[0] == '"'); MOZ_ASSERT(end_[-1] == '"'); MOZ_ASSERT(end_ - begin_ >= 2); - return TwoByteChars(begin_ + 1, end_ - begin_ - 2); + return WasmName(begin_ + 1, end_ - begin_ - 2); + } + WasmName name() const { + return WasmName(begin_, end_ - begin_); } uint32_t index() const { MOZ_ASSERT(kind_ == Index); @@ -771,6 +922,8 @@ class WasmToken } }; +} // end anonymous namespace + static bool IsWasmNewLine(char16_t c) { @@ -808,7 +961,7 @@ IsWasmLetter(char16_t c) static bool IsNameAfterDollar(char16_t c) { - return c == '_' || IsWasmDigit(c) || IsWasmLetter(c); + return IsWasmLetter(c) || IsWasmDigit(c) || c == '_' || c == '$' || c == '-'; } static bool @@ -946,6 +1099,8 @@ ConsumeTextByte(const char16_t** curp, const char16_t* end, uint8_t *byte = null return true; } +namespace { + class WasmTokenStream { static const uint32_t LookaheadSize = 2; @@ -970,6 +1125,9 @@ class WasmTokenStream WasmToken fail(const char16_t* begin) const { return WasmToken(begin); } + + WasmToken nan(const char16_t* begin); + WasmToken literal(const char16_t* begin); WasmToken next(); public: @@ -977,7 +1135,7 @@ class WasmTokenStream : cur_(text), end_(text + js_strlen(text)), lineStart_(text), - line_(0), + line_(1), lookaheadIndex_(0), lookaheadDepth_(0) {} @@ -985,7 +1143,6 @@ class WasmTokenStream unsigned column = token.begin() - lineStart_ + 1; error->reset(JS_smprintf("parsing wasm text at %u:%u", line_, column)); } - WasmToken peek() { if (!lookaheadDepth_) { lookahead_[lookaheadIndex_] = next(); @@ -1020,7 +1177,21 @@ class WasmTokenStream } bool getIf(WasmToken::Kind kind) { WasmToken token; - return getIf(kind, &token); + if (getIf(kind, &token)) + return true; + return false; + } + WasmName getIfName() { + WasmToken token; + if (getIf(WasmToken::Name, &token)) + return token.name(); + return WasmName(); + } + bool getIfRef(WasmRef* ref) { + WasmToken token = peek(); + if (token.kind() == WasmToken::Name || token.kind() == WasmToken::Index) + return matchRef(ref, nullptr); + return false; } bool match(WasmToken::Kind expect, WasmToken* token, UniqueChars* error) { *token = get(); @@ -1033,9 +1204,107 @@ class WasmTokenStream WasmToken token; return match(expect, &token, error); } + bool matchRef(WasmRef* ref, UniqueChars* error) { + WasmToken token = get(); + switch (token.kind()) { + case WasmToken::Name: + *ref = WasmRef(token.name(), WasmNoIndex); + break; + case WasmToken::Index: + *ref = WasmRef(WasmName(), token.index()); + break; + default: + generateError(token, error); + return false; + } + return true; + } }; -WasmToken WasmTokenStream::next() +} // end anonymous namespace + +WasmToken +WasmTokenStream::nan(const char16_t* begin) +{ + if (consume(MOZ_UTF16(":"))) { + if (!consume(MOZ_UTF16("0x"))) + return fail(begin); + + uint8_t digit; + while (cur_ != end_ && IsHexDigit(*cur_, &digit)) + cur_++; + } + + return WasmToken(WasmToken::NaN, begin, cur_); +} + +WasmToken +WasmTokenStream::literal(const char16_t* begin) +{ + CheckedInt u = 0; + if (consume(MOZ_UTF16("0x"))) { + if (cur_ == end_) + return fail(begin); + + do { + if (*cur_ == '.' || *cur_ == 'p') + return LexHexFloatLiteral(begin, end_, &cur_); + + uint8_t digit; + if (!IsHexDigit(*cur_, &digit)) + break; + + u *= 16; + u += digit; + if (!u.isValid()) + return LexHexFloatLiteral(begin, end_, &cur_); + + cur_++; + } while (cur_ != end_); + + if (*begin == '-') { + uint64_t value = u.value(); + if (value > uint64_t(INT64_MIN)) + return LexHexFloatLiteral(begin, end_, &cur_); + + value = -value; + return WasmToken(int64_t(value), begin, cur_); + } + } else { + while (cur_ != end_) { + if (*cur_ == '.' || *cur_ == 'e') + return LexDecFloatLiteral(begin, end_, &cur_); + + if (!IsWasmDigit(*cur_)) + break; + + u *= 10; + u += *cur_ - '0'; + if (!u.isValid()) + return LexDecFloatLiteral(begin, end_, &cur_); + + cur_++; + } + + if (*begin == '-') { + uint64_t value = u.value(); + if (value > uint64_t(INT64_MIN)) + return LexDecFloatLiteral(begin, end_, &cur_); + + value = -value; + return WasmToken(int64_t(value), begin, cur_); + } + } + + CheckedInt index = u.value(); + if (index.isValid()) + return WasmToken(index.value(), begin, cur_); + + return WasmToken(u.value(), begin, cur_); +} + +WasmToken +WasmTokenStream::next() { while (cur_ != end_ && IsWasmSpace(*cur_)) { if (IsWasmNewLine(*cur_++)) { @@ -1083,58 +1352,15 @@ WasmToken WasmTokenStream::next() case '+': case '-': cur_++; if (consume(MOZ_UTF16("infinity"))) - goto infinity; + return WasmToken(WasmToken::Infinity, begin, cur_); if (consume(MOZ_UTF16("nan"))) - goto nan; + return nan(begin); if (!IsWasmDigit(*cur_)) break; MOZ_FALLTHROUGH; case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - CheckedInt u = 0; - if (consume(MOZ_UTF16("0x"))) { - if (cur_ == end_) - return fail(begin); - do { - if (*cur_ == '.' || *cur_ == 'p') - return LexHexFloatLiteral(begin, end_, &cur_); - uint8_t digit; - if (!IsHexDigit(*cur_, &digit)) - break; - u *= 16; - u += digit; - if (!u.isValid()) - return fail(begin); - cur_++; - } while (cur_ != end_); - } else { - while (cur_ != end_) { - if (*cur_ == '.' || *cur_ == 'e') - return LexDecFloatLiteral(begin, end_, &cur_); - if (!IsWasmDigit(*cur_)) - break; - u *= 10; - u += *cur_ - '0'; - if (!u.isValid()) - return fail(begin); - cur_++; - } - } - - uint64_t value = u.value(); - if (*begin == '-') { - if (value > uint64_t(INT64_MIN)) - return fail(begin); - value = -value; - return WasmToken(int64_t(value), begin, cur_); - } - - CheckedInt index = u.value(); - if (index.isValid()) - return WasmToken(index.value(), begin, cur_); - - return WasmToken(value, begin, cur_); - } + case '5': case '6': case '7': case '8': case '9': + return literal(begin); case 'a': if (consume(MOZ_UTF16("align"))) @@ -1144,6 +1370,11 @@ WasmToken WasmTokenStream::next() case 'b': if (consume(MOZ_UTF16("block"))) return WasmToken(WasmToken::Block, begin, cur_); + if (consume(MOZ_UTF16("br"))) { + if (consume(MOZ_UTF16("_if"))) + return WasmToken(WasmToken::BrIf, begin, cur_); + return WasmToken(WasmToken::Br, begin, cur_); + } break; case 'c': @@ -1623,10 +1854,8 @@ WasmToken WasmTokenStream::next() } if (consume(MOZ_UTF16("import"))) return WasmToken(WasmToken::Import, begin, cur_); - if (consume(MOZ_UTF16("infinity"))) { - infinity: + if (consume(MOZ_UTF16("infinity"))) return WasmToken(WasmToken::Infinity, begin, cur_); - } if (consume(MOZ_UTF16("if"))) { if (consume(MOZ_UTF16("_else"))) return WasmToken(WasmToken::IfElse, begin, cur_); @@ -1637,6 +1866,8 @@ WasmToken WasmTokenStream::next() case 'l': if (consume(MOZ_UTF16("local"))) return WasmToken(WasmToken::Local, begin, cur_); + if (consume(MOZ_UTF16("loop"))) + return WasmToken(WasmToken::Loop, begin, cur_); break; case 'm': @@ -1647,17 +1878,8 @@ WasmToken WasmTokenStream::next() break; case 'n': - if (consume(MOZ_UTF16("nan"))) { - nan: - if (consume(MOZ_UTF16(":"))) { - if (!consume(MOZ_UTF16("0x"))) - break; - uint8_t digit; - while (cur_ != end_ && IsHexDigit(*cur_, &digit)) - cur_++; - } - return WasmToken(WasmToken::NaN, begin, cur_); - } + if (consume(MOZ_UTF16("nan"))) + return nan(begin); if (consume(MOZ_UTF16("nop"))) return WasmToken(WasmToken::Nop, begin, cur_); break; @@ -1675,6 +1897,8 @@ WasmToken WasmTokenStream::next() case 'r': if (consume(MOZ_UTF16("result"))) return WasmToken(WasmToken::Result, begin, cur_); + if (consume(MOZ_UTF16("return"))) + return WasmToken(WasmToken::Return, begin, cur_); break; case 's': @@ -1685,8 +1909,11 @@ WasmToken WasmTokenStream::next() break; case 't': - if (consume(MOZ_UTF16("table"))) + if (consume(MOZ_UTF16("table"))) { + if (consume(MOZ_UTF16("switch"))) + return WasmToken(WasmToken::TableSwitch, begin, cur_); return WasmToken(WasmToken::Table, begin, cur_); + } if (consume(MOZ_UTF16("type"))) return WasmToken(WasmToken::Type, begin, cur_); break; @@ -1698,8 +1925,6 @@ WasmToken WasmTokenStream::next() return fail(begin); } -} // end anonymous namespace - /*****************************************************************************/ // wasm text format parser @@ -1719,11 +1944,17 @@ struct WasmParseContext dtoaState(NewDtoaState()) {} + bool fail(const char* message) { + error->reset(JS_smprintf(message)); + return false; + } ~WasmParseContext() { DestroyDtoaState(dtoaState); } }; +} // end anonymous namespace + static WasmAstExpr* ParseExprInsideParens(WasmParseContext& c); @@ -1744,10 +1975,16 @@ ParseExpr(WasmParseContext& c) } static WasmAstBlock* -ParseBlock(WasmParseContext& c) +ParseBlock(WasmParseContext& c, Expr expr) { WasmAstExprVector exprs(c.lifo); + WasmName breakName = c.ts.getIfName(); + + WasmName continueName; + if (expr == Expr::Loop) + continueName = c.ts.getIfName(); + while (c.ts.getIf(WasmToken::OpenParen)) { WasmAstExpr* expr = ParseExprInsideParens(c); if (!expr || !exprs.append(expr)) @@ -1756,7 +1993,26 @@ ParseBlock(WasmParseContext& c) return nullptr; } - return new(c.lifo) WasmAstBlock(Move(exprs)); + return new(c.lifo) WasmAstBlock(expr, breakName, continueName, Move(exprs)); +} + +static WasmAstBranch* +ParseBranch(WasmParseContext& c, Expr expr) +{ + MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf); + + WasmRef target; + if (!c.ts.matchRef(&target, c.error)) + return nullptr; + + WasmAstExpr* cond = nullptr; + if (expr == Expr::BrIf) { + cond = ParseExpr(c); + if (!cond) + return nullptr; + } + + return new(c.lifo) WasmAstBranch(expr, cond, target); } static bool @@ -1778,22 +2034,22 @@ ParseCall(WasmParseContext& c, Expr expr) { MOZ_ASSERT(expr == Expr::Call || expr == Expr::CallImport); - WasmToken index; - if (!c.ts.match(WasmToken::Index, &index, c.error)) + WasmRef func; + if (!c.ts.matchRef(&func, c.error)) return nullptr; WasmAstExprVector args(c.lifo); if (!ParseArgs(c, &args)) return nullptr; - return new(c.lifo) WasmAstCall(expr, index.index(), Move(args)); + return new(c.lifo) WasmAstCall(expr, func, Move(args)); } static WasmAstCallIndirect* ParseCallIndirect(WasmParseContext& c) { - WasmToken sigIndex; - if (!c.ts.match(WasmToken::Index, &sigIndex, c.error)) + WasmRef sig; + if (!c.ts.matchRef(&sig, c.error)) return nullptr; WasmAstExpr* index = ParseExpr(c); @@ -1804,7 +2060,7 @@ ParseCallIndirect(WasmParseContext& c) if (!ParseArgs(c, &args)) return nullptr; - return new(c.lifo) WasmAstCallIndirect(sigIndex.index(), index, Move(args)); + return new(c.lifo) WasmAstCallIndirect(sig, index, Move(args)); } static uint_fast8_t @@ -1998,6 +2254,23 @@ template static bool ParseFloatLiteral(WasmParseContext& c, WasmToken token, Float* result) { + switch (token.kind()) { + case WasmToken::Index: + *result = token.index(); + return true; + case WasmToken::UnsignedInteger: + *result = token.uint(); + return true; + case WasmToken::SignedInteger: + *result = token.sint(); + return true; + case WasmToken::Float: + break; + default: + c.ts.generateError(token, c.error); + return false; + } + const char16_t* begin = token.begin(); const char16_t* end = token.end(); const char16_t* cur = begin; @@ -2074,7 +2347,7 @@ ParseConst(WasmParseContext& c, WasmToken constToken) case ValType::I64: { switch (val.kind()) { case WasmToken::Index: - return new(c.lifo) WasmAstConst(Val(val.index())); + return new(c.lifo) WasmAstConst(Val(uint64_t(val.index()))); case WasmToken::UnsignedInteger: return new(c.lifo) WasmAstConst(Val(val.uint())); case WasmToken::SignedInteger: @@ -2085,16 +2358,12 @@ ParseConst(WasmParseContext& c, WasmToken constToken) break; } case ValType::F32: { - if (val.kind() != WasmToken::Float) - break; float result; if (!ParseFloatLiteral(c, val, &result)) break; return new(c.lifo) WasmAstConst(Val(result)); } case ValType::F64: { - if (val.kind() != WasmToken::Float) - break; double result; if (!ParseFloatLiteral(c, val, &result)) break; @@ -2110,25 +2379,39 @@ ParseConst(WasmParseContext& c, WasmToken constToken) static WasmAstGetLocal* ParseGetLocal(WasmParseContext& c) { - WasmToken localIndex; - if (!c.ts.match(WasmToken::Index, &localIndex, c.error)) + WasmRef local; + if (!c.ts.matchRef(&local, c.error)) return nullptr; - return new(c.lifo) WasmAstGetLocal(localIndex.index()); + return new(c.lifo) WasmAstGetLocal(local); } static WasmAstSetLocal* ParseSetLocal(WasmParseContext& c) { - WasmToken localIndex; - if (!c.ts.match(WasmToken::Index, &localIndex, c.error)) + WasmRef local; + if (!c.ts.matchRef(&local, c.error)) return nullptr; WasmAstExpr* value = ParseExpr(c); if (!value) return nullptr; - return new(c.lifo) WasmAstSetLocal(localIndex.index(), *value); + return new(c.lifo) WasmAstSetLocal(local, *value); +} + +static WasmAstReturn* +ParseReturn(WasmParseContext& c) +{ + WasmAstExpr* maybeExpr = nullptr; + + if (c.ts.peek().kind() != WasmToken::CloseParen) { + maybeExpr = ParseExpr(c); + if (!maybeExpr) + return nullptr; + } + + return new(c.lifo) WasmAstReturn(maybeExpr); } static WasmAstUnaryOperator* @@ -2201,17 +2484,10 @@ ParseIfElse(WasmParseContext& c, Expr expr) } static bool -ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base, - int32_t* offset, int32_t* align) +ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, int32_t* align, WasmAstExpr** base) { - *base = ParseExpr(c); - if (!*base) - return false; - - WasmToken token = c.ts.get(); - *offset = 0; - if (token.kind() == WasmToken::Offset) { + if (c.ts.getIf(WasmToken::Offset)) { if (!c.ts.match(WasmToken::Equal, c.error)) return false; WasmToken val = c.ts.get(); @@ -2223,12 +2499,10 @@ ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base, c.ts.generateError(val, c.error); return false; } - - token = c.ts.get(); } *align = 0; - if (token.kind() == WasmToken::Align) { + if (c.ts.getIf(WasmToken::Align)) { if (!c.ts.match(WasmToken::Equal, c.error)) return false; WasmToken val = c.ts.get(); @@ -2240,21 +2514,22 @@ ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base, c.ts.generateError(val, c.error); return false; } - - token = c.ts.get(); } - c.ts.unget(token); + *base = ParseExpr(c); + if (!*base) + return false; + return true; } static WasmAstLoad* ParseLoad(WasmParseContext& c, Expr expr) { - WasmAstExpr* base; int32_t offset; int32_t align; - if (!ParseLoadStoreAddress(c, &base, &offset, &align)) + WasmAstExpr* base; + if (!ParseLoadStoreAddress(c, &offset, &align, &base)) return nullptr; if (align == 0) { @@ -2292,10 +2567,10 @@ ParseLoad(WasmParseContext& c, Expr expr) static WasmAstStore* ParseStore(WasmParseContext& c, Expr expr) { - WasmAstExpr* base; int32_t offset; int32_t align; - if (!ParseLoadStoreAddress(c, &base, &offset, &align)) + WasmAstExpr* base; + if (!ParseLoadStoreAddress(c, &offset, &align, &base)) return nullptr; if (align == 0) { @@ -2329,6 +2604,53 @@ ParseStore(WasmParseContext& c, Expr expr) return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value); } +static WasmAstTableSwitch* +ParseTableSwitch(WasmParseContext& c) +{ + WasmAstExpr* index = ParseExpr(c); + if (!index) + return nullptr; + + if (!c.ts.match(WasmToken::OpenParen, c.error)) + return nullptr; + if (!c.ts.match(WasmToken::Table, c.error)) + return nullptr; + + WasmRefVector table(c.lifo); + + while (c.ts.getIf(WasmToken::OpenParen)) { + if (!c.ts.match(WasmToken::Br, c.error)) + return nullptr; + + WasmRef target; + if (!c.ts.matchRef(&target, c.error)) + return nullptr; + + if (!table.append(target)) + return nullptr; + + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return nullptr; + } + + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return nullptr; + + if (!c.ts.match(WasmToken::OpenParen, c.error)) + return nullptr; + if (!c.ts.match(WasmToken::Br, c.error)) + return nullptr; + + WasmRef def; + if (!c.ts.matchRef(&def, c.error)) + return nullptr; + + if (!c.ts.match(WasmToken::CloseParen, c.error)) + return nullptr; + + return new(c.lifo) WasmAstTableSwitch(*index, def, Move(table)); +} + static WasmAstExpr* ParseExprInsideParens(WasmParseContext& c) { @@ -2340,7 +2662,11 @@ ParseExprInsideParens(WasmParseContext& c) case WasmToken::BinaryOpcode: return ParseBinaryOperator(c, token.expr()); case WasmToken::Block: - return ParseBlock(c); + return ParseBlock(c, Expr::Block); + case WasmToken::Br: + return ParseBranch(c, Expr::Br); + case WasmToken::BrIf: + return ParseBranch(c, Expr::BrIf); case WasmToken::Call: return ParseCall(c, Expr::Call); case WasmToken::CallImport: @@ -2361,10 +2687,16 @@ ParseExprInsideParens(WasmParseContext& c) return ParseGetLocal(c); case WasmToken::Load: return ParseLoad(c, token.expr()); + case WasmToken::Loop: + return ParseBlock(c, Expr::Loop); + case WasmToken::Return: + return ParseReturn(c); case WasmToken::SetLocal: return ParseSetLocal(c); case WasmToken::Store: return ParseStore(c, token.expr()); + case WasmToken::TableSwitch: + return ParseTableSwitch(c); case WasmToken::UnaryOpcode: return ParseUnaryOperator(c, token.expr()); default: @@ -2376,9 +2708,21 @@ ParseExprInsideParens(WasmParseContext& c) static bool ParseValueType(WasmParseContext& c, WasmAstValTypeVector* vec) { - WasmToken valueType; - return c.ts.match(WasmToken::ValueType, &valueType, c.error) && - vec->append(valueType.valueType()); + WasmToken token; + return c.ts.match(WasmToken::ValueType, &token, c.error) && + vec->append(token.valueType()); +} + +static bool +ParseValueTypeList(WasmParseContext& c, WasmAstValTypeVector* vec) +{ + WasmToken token; + while (c.ts.getIf(WasmToken::ValueType, &token)) { + if (!vec->append(token.valueType())) + return false; + } + + return true; } static bool @@ -2389,32 +2733,47 @@ ParseResult(WasmParseContext& c, ExprType* result) return false; } - WasmToken valueType; - if (!c.ts.match(WasmToken::ValueType, &valueType, c.error)) + WasmToken token; + if (!c.ts.match(WasmToken::ValueType, &token, c.error)) return false; - *result = ToExprType(valueType.valueType()); + *result = ToExprType(token.valueType()); return true; } -static const uint32_t BadSigIndex = UINT32_MAX; +static bool +ParseLocal(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* localTypes) +{ + return locals->append(c.ts.getIfName()) && + ParseValueType(c, localTypes); +} + +static bool +ParseParam(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* args) +{ + if (c.ts.peek().kind() == WasmToken::Name) + return ParseLocal(c, locals, args); + + return locals->append(WasmName()) && + ParseValueTypeList(c, args); +} static WasmAstFunc* ParseFunc(WasmParseContext& c, WasmAstModule* module) { WasmAstValTypeVector vars(c.lifo); WasmAstValTypeVector args(c.lifo); - ExprType result = ExprType::Void; + WasmNameVector locals(c.lifo); - uint32_t sigIndex = BadSigIndex; + WasmName funcName = c.ts.getIfName(); + + WasmRef sig; WasmToken openParen; if (c.ts.getIf(WasmToken::OpenParen, &openParen)) { if (c.ts.getIf(WasmToken::Type)) { - WasmToken sigToken; - if (!c.ts.match(WasmToken::Index, &sigToken, c.error)) + if (!c.ts.matchRef(&sig, c.error)) return nullptr; - sigIndex = sigToken.index(); if (!c.ts.match(WasmToken::CloseParen, c.error)) return nullptr; } else { @@ -2423,16 +2782,21 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module) } WasmAstExprVector body(c.lifo); + ExprType result = ExprType::Void; while (c.ts.getIf(WasmToken::OpenParen)) { WasmToken token = c.ts.get(); switch (token.kind()) { case WasmToken::Local: - if (!ParseValueType(c, &vars)) + if (!ParseLocal(c, &locals, &vars)) return nullptr; break; case WasmToken::Param: - if (!ParseValueType(c, &args)) + if (!vars.empty()) { + c.ts.generateError(token, c.error); + return nullptr; + } + if (!ParseParam(c, &locals, &args)) return nullptr; break; case WasmToken::Result: @@ -2450,12 +2814,14 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module) return nullptr; } - if (sigIndex == BadSigIndex) { + if (sig.isInvalid()) { + uint32_t sigIndex; if (!module->declare(WasmAstSig(Move(args), result), &sigIndex)) return nullptr; + sig.setIndex(sigIndex); } - return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), Move(body)); + return new(c.lifo) WasmAstFunc(funcName, sig, Move(vars), Move(locals), Move(body)); } static bool @@ -2468,7 +2834,7 @@ ParseFuncType(WasmParseContext& c, WasmAstSig* sig) WasmToken token = c.ts.get(); switch (token.kind()) { case WasmToken::Param: - if (!ParseValueType(c, &args)) + if (!ParseValueTypeList(c, &args)) return false; break; case WasmToken::Result: @@ -2490,6 +2856,8 @@ ParseFuncType(WasmParseContext& c, WasmAstSig* sig) static WasmAstSig* ParseTypeDef(WasmParseContext& c) { + WasmName name = c.ts.getIfName(); + if (!c.ts.match(WasmToken::OpenParen, c.error)) return nullptr; if (!c.ts.match(WasmToken::Func, c.error)) @@ -2502,7 +2870,7 @@ ParseTypeDef(WasmParseContext& c) if (!c.ts.match(WasmToken::CloseParen, c.error)) return nullptr; - return new(c.lifo) WasmAstSig(Move(sig)); + return new(c.lifo) WasmAstSig(name, Move(sig)); } static WasmAstSegment* @@ -2544,6 +2912,8 @@ ParseMemory(WasmParseContext& c) static WasmAstImport* ParseImport(WasmParseContext& c, WasmAstModule* module) { + WasmName name = c.ts.getIfName(); + WasmToken moduleName; if (!c.ts.match(WasmToken::Text, &moduleName, c.error)) return nullptr; @@ -2560,7 +2930,7 @@ ParseImport(WasmParseContext& c, WasmAstModule* module) if (!module->declare(Move(sig), &sigIndex)) return nullptr; - return new(c.lifo) WasmAstImport(moduleName.text(), funcName.text(), sigIndex); + return new(c.lifo) WasmAstImport(name, moduleName.text(), funcName.text(), sigIndex); } static WasmAstExport* @@ -2573,7 +2943,9 @@ ParseExport(WasmParseContext& c) WasmToken exportee = c.ts.get(); switch (exportee.kind()) { case WasmToken::Index: - return new(c.lifo) WasmAstExport(name.text(), exportee.index()); + return new(c.lifo) WasmAstExport(name.text(), WasmRef(WasmName(), exportee.index())); + case WasmToken::Name: + return new(c.lifo) WasmAstExport(name.text(), WasmRef(exportee.name(), WasmNoIndex)); case WasmToken::Memory: return new(c.lifo) WasmAstExport(name.text()); default: @@ -2590,9 +2962,9 @@ ParseTable(WasmParseContext& c) { WasmAstTableElemVector elems(c.lifo); - WasmToken token; - while (c.ts.getIf(WasmToken::Index, &token)) { - if (!elems.append(token.index())) + WasmRef elem; + while (c.ts.getIfRef(&elem)) { + if (!elems.append(elem)) return nullptr; } @@ -2678,8 +3050,403 @@ ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error) return module; } +/*****************************************************************************/ +// wasm name resolution + +namespace { + +class Resolver +{ + UniqueChars* error_; + WasmNameMap varMap_; + WasmNameMap sigMap_; + WasmNameMap funcMap_; + WasmNameMap importMap_; + WasmNameVector targetStack_; + + bool registerName(WasmNameMap& map, WasmName name, size_t index) { + WasmNameMap::AddPtr p = map.lookupForAdd(name); + if (!p) { + if (!map.add(p, name, index)) + return false; + } else { + return false; + } + return true; + } + bool resolveName(WasmNameMap& map, WasmName name, size_t* index) { + WasmNameMap::Ptr p = map.lookup(name); + if (p) { + *index = p->value(); + return true; + } + return false; + } + bool resolveRef(WasmNameMap& map, WasmRef& ref) { + WasmNameMap::Ptr p = map.lookup(ref.name()); + if (p) { + ref.setIndex(p->value()); + return true; + } + return false; + } + + public: + explicit Resolver(LifoAlloc& lifo, UniqueChars* error) + : error_(error), + varMap_(lifo), + sigMap_(lifo), + funcMap_(lifo), + importMap_(lifo), + targetStack_(lifo) + {} + bool init() { + return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init(); + } + void beginFunc() { + varMap_.clear(); + MOZ_ASSERT(targetStack_.empty()); + } + bool registerSigName(WasmName name, size_t index) { + return name.empty() || registerName(sigMap_, name, index); + } + bool registerFuncName(WasmName name, size_t index) { + return name.empty() || registerName(funcMap_, name, index); + } + bool registerImportName(WasmName name, size_t index) { + return name.empty() || registerName(importMap_, name, index); + } + bool registerVarName(WasmName name, size_t index) { + return name.empty() || registerName(varMap_, name, index); + } + bool pushTarget(WasmName name) { + return targetStack_.append(name); + } + void popTarget(WasmName name) { + MOZ_ASSERT(targetStack_.back() == name); + targetStack_.popBack(); + } + bool resolveSigRef(WasmRef& ref) { + return ref.name().empty() || resolveRef(sigMap_, ref); + } + bool resolveFuncRef(WasmRef& ref) { + return ref.name().empty() || resolveRef(funcMap_, ref); + } + bool resolveImportRef(WasmRef& ref) { + return ref.name().empty() || resolveRef(importMap_, ref); + } + bool resolveVarRef(WasmRef& ref) { + return ref.name().empty() || resolveRef(varMap_, ref); + } + bool resolveTarget(WasmRef& ref) { + for (size_t i = targetStack_.length(); i > 0; i--) { + uint32_t targetIndex = i - 1; + if (targetStack_[targetIndex] == ref.name()) { + ref.setIndex(targetIndex); + return true; + } + } + return false; + } + bool fail(const char*message) { + error_->reset(JS_smprintf("%s", message)); + return false; + } +}; + } // end anonymous namespace +static bool +ResolveExpr(Resolver& r, WasmAstExpr& expr); + +static bool +ResolveBlock(Resolver& r, WasmAstBlock& b) +{ + if (!r.pushTarget(b.breakName())) + return false; + + if (b.expr() == Expr::Loop) { + if (!r.pushTarget(b.continueName())) + return false; + } + + size_t numExprs = b.exprs().length(); + for (size_t i = 0; i < numExprs; i++) { + if (!ResolveExpr(r, *b.exprs()[i])) + return false; + } + + if (b.expr() == Expr::Loop) + r.popTarget(b.continueName()); + r.popTarget(b.breakName()); + return true; +} + +static bool +ResolveBranch(Resolver& r, WasmAstBranch& br) +{ + if (!br.target().name().empty() && !r.resolveTarget(br.target())) + return r.fail("label not found"); + + if (br.expr() == Expr::BrIf) { + if (!ResolveExpr(r, br.cond())) + return false; + } + + return true; +} + +static bool +ResolveArgs(Resolver& r, const WasmAstExprVector& args) +{ + for (WasmAstExpr* arg : args) { + if (!ResolveExpr(r, *arg)) + return false; + } + + return true; +} + +static bool +ResolveCall(Resolver& r, WasmAstCall& c) +{ + if (!ResolveArgs(r, c.args())) + return false; + + if (c.expr() == Expr::Call) { + if (!r.resolveFuncRef(c.func())) + return r.fail("function not found"); + } else { + MOZ_ASSERT(c.expr() == Expr::CallImport); + if (!r.resolveImportRef(c.func())) + return r.fail("import not found"); + } + + return true; +} + +static bool +ResolveCallIndirect(Resolver& r, WasmAstCallIndirect& c) +{ + if (!ResolveExpr(r, *c.index())) + return false; + + if (!ResolveArgs(r, c.args())) + return false; + + if (!r.resolveSigRef(c.sig())) + return r.fail("signature not found"); + + return true; +} + +static bool +ResolveGetLocal(Resolver& r, WasmAstGetLocal& gl) +{ + if (!r.resolveVarRef(gl.local())) + return r.fail("local not found"); + + return true; +} + +static bool +ResolveSetLocal(Resolver& r, WasmAstSetLocal& sl) +{ + if (!ResolveExpr(r, sl.value())) + return false; + + if (!r.resolveVarRef(sl.local())) + return r.fail("local not found"); + + return true; +} + +static bool +ResolveUnaryOperator(Resolver& r, WasmAstUnaryOperator& b) +{ + return ResolveExpr(r, *b.op()); +} + +static bool +ResolveBinaryOperator(Resolver& r, WasmAstBinaryOperator& b) +{ + return ResolveExpr(r, *b.lhs()) && + ResolveExpr(r, *b.rhs()); +} + +static bool +ResolveComparisonOperator(Resolver& r, WasmAstComparisonOperator& b) +{ + return ResolveExpr(r, *b.lhs()) && + ResolveExpr(r, *b.rhs()); +} + +static bool +ResolveConversionOperator(Resolver& r, WasmAstConversionOperator& b) +{ + return ResolveExpr(r, *b.op()); +} + +static bool +ResolveIfElse(Resolver& r, WasmAstIfElse& ie) +{ + return ResolveExpr(r, ie.cond()) && + ResolveExpr(r, ie.ifBody()) && + (!ie.hasElse() || ResolveExpr(r, ie.elseBody())); +} + +static bool +ResolveLoadStoreAddress(Resolver& r, const WasmAstLoadStoreAddress &address) +{ + return ResolveExpr(r, address.base()); +} + +static bool +ResolveLoad(Resolver& r, WasmAstLoad& l) +{ + return ResolveLoadStoreAddress(r, l.address()); +} + +static bool +ResolveStore(Resolver& r, WasmAstStore& s) +{ + return ResolveLoadStoreAddress(r, s.address()) && + ResolveExpr(r, s.value()); +} + +static bool +ResolveReturn(Resolver& r, WasmAstReturn& ret) +{ + return !ret.maybeExpr() || ResolveExpr(r, *ret.maybeExpr()); +} + +static bool +ResolveTableSwitch(Resolver& r, WasmAstTableSwitch& ts) +{ + if (!ts.def().name().empty() && !r.resolveTarget(ts.def())) + return r.fail("switch default not found"); + + for (WasmRef& elem : ts.table()) { + if (!elem.name().empty() && !r.resolveTarget(elem)) + return r.fail("switch element not found"); + } + + return ResolveExpr(r, ts.index()); +} + +static bool +ResolveExpr(Resolver& r, WasmAstExpr& expr) +{ + switch (expr.kind()) { + case WasmAstExprKind::Nop: + return true; + case WasmAstExprKind::BinaryOperator: + return ResolveBinaryOperator(r, expr.as()); + case WasmAstExprKind::Block: + return ResolveBlock(r, expr.as()); + case WasmAstExprKind::Branch: + return ResolveBranch(r, expr.as()); + case WasmAstExprKind::Call: + return ResolveCall(r, expr.as()); + case WasmAstExprKind::CallIndirect: + return ResolveCallIndirect(r, expr.as()); + case WasmAstExprKind::ComparisonOperator: + return ResolveComparisonOperator(r, expr.as()); + case WasmAstExprKind::Const: + return true; + case WasmAstExprKind::ConversionOperator: + return ResolveConversionOperator(r, expr.as()); + case WasmAstExprKind::GetLocal: + return ResolveGetLocal(r, expr.as()); + case WasmAstExprKind::IfElse: + return ResolveIfElse(r, expr.as()); + case WasmAstExprKind::Load: + return ResolveLoad(r, expr.as()); + case WasmAstExprKind::Return: + return ResolveReturn(r, expr.as()); + case WasmAstExprKind::SetLocal: + return ResolveSetLocal(r, expr.as()); + case WasmAstExprKind::Store: + return ResolveStore(r, expr.as()); + case WasmAstExprKind::TableSwitch: + return ResolveTableSwitch(r, expr.as()); + case WasmAstExprKind::UnaryOperator: + return ResolveUnaryOperator(r, expr.as()); + } + MOZ_CRASH("Bad expr kind"); +} + +static bool +ResolveFunc(Resolver& r, WasmAstFunc& func) +{ + r.beginFunc(); + + size_t numVars = func.locals().length(); + for (size_t i = 0; i < numVars; i++) { + if (!r.registerVarName(func.locals()[i], i)) + return r.fail("duplicate var"); + } + + for (WasmAstExpr* expr : func.body()) { + if (!ResolveExpr(r, *expr)) + return false; + } + return true; +} + +static bool +ResolveModule(LifoAlloc& lifo, WasmAstModule* module, UniqueChars* error) +{ + Resolver r(lifo, error); + + if (!r.init()) + return false; + + size_t numSigs = module->sigs().length(); + for (size_t i = 0; i < numSigs; i++) { + WasmAstSig* sig = module->sigs()[i]; + if (!r.registerSigName(sig->name(), i)) + return r.fail("duplicate signature"); + } + + size_t numFuncs = module->funcs().length(); + for (size_t i = 0; i < numFuncs; i++) { + WasmAstFunc* func = module->funcs()[i]; + if (!r.resolveSigRef(func->sig())) + return r.fail("signature not found"); + if (!r.registerFuncName(func->name(), i)) + return r.fail("duplicate function"); + } + + if (module->maybeTable()) { + for (WasmRef& ref : module->maybeTable()->elems()) { + if (!r.resolveFuncRef(ref)) + return r.fail("function not found"); + } + } + + size_t numImports = module->imports().length(); + for (size_t i = 0; i < numImports; i++) { + WasmAstImport* imp = module->imports()[i]; + if (!r.registerImportName(imp->name(), i)) + return r.fail("duplicate import"); + } + + for (WasmAstExport* export_ : module->exports()) { + if (export_->kind() != WasmAstExportKind::Func) + continue; + if (!r.resolveFuncRef(export_->func())) + return r.fail("function not found"); + } + + for (WasmAstFunc* func : module->funcs()) { + if (!ResolveFunc(r, *func)) + return false; + } + + return true; +} + /*****************************************************************************/ // wasm function body serialization @@ -2704,6 +3471,23 @@ EncodeBlock(Encoder& e, WasmAstBlock& b) return true; } +static bool +EncodeBranch(Encoder& e, WasmAstBranch& br) +{ + if (!e.writeExpr(br.expr())) + return false; + + if (!e.writeVarU32(br.target().index())) + return false; + + if (br.expr() == Expr::BrIf) { + if (!EncodeExpr(e, br.cond())) + return false; + } + + return true; +} + static bool EncodeArgs(Encoder& e, const WasmAstExprVector& args) { @@ -2721,7 +3505,7 @@ EncodeCall(Encoder& e, WasmAstCall& c) if (!e.writeExpr(c.expr())) return false; - if (!e.writeVarU32(c.index())) + if (!e.writeVarU32(c.func().index())) return false; if (!EncodeArgs(e, c.args())) @@ -2736,7 +3520,7 @@ EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c) if (!e.writeExpr(Expr::CallIndirect)) return false; - if (!e.writeVarU32(c.sigIndex())) + if (!e.writeVarU32(c.sig().index())) return false; if (!EncodeExpr(e, *c.index())) @@ -2774,14 +3558,14 @@ static bool EncodeGetLocal(Encoder& e, WasmAstGetLocal& gl) { return e.writeExpr(Expr::GetLocal) && - e.writeVarU32(gl.localIndex()); + e.writeVarU32(gl.local().index()); } static bool EncodeSetLocal(Encoder& e, WasmAstSetLocal& sl) { return e.writeExpr(Expr::SetLocal) && - e.writeVarU32(sl.localIndex()) && + e.writeVarU32(sl.local().index()) && EncodeExpr(e, sl.value()); } @@ -2827,9 +3611,9 @@ EncodeIfElse(Encoder& e, WasmAstIfElse& ie) static bool EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address) { - return EncodeExpr(e, address.base()) && - e.writeVarU32(address.offset()) && - e.writeVarU32(address.align()); + return e.writeVarU32(address.offset()) && + e.writeVarU32(address.align()) && + EncodeExpr(e, address.base()); } static bool @@ -2847,6 +3631,33 @@ EncodeStore(Encoder& e, WasmAstStore& s) EncodeExpr(e, s.value()); } +static bool +EncodeReturn(Encoder& e, WasmAstReturn& r) +{ + return e.writeExpr(Expr::Return) && + (!r.maybeExpr() || EncodeExpr(e, *r.maybeExpr())); +} + +static bool +EncodeTableSwitch(Encoder& e, WasmAstTableSwitch& ts) +{ + if (!e.writeExpr(Expr::TableSwitch)) + return false; + + if (!e.writeVarU32(ts.def().index())) + return false; + + if (!e.writeVarU32(ts.table().length())) + return false; + + for (const WasmRef& elem : ts.table()) { + if (!e.writeVarU32(elem.index())) + return false; + } + + return EncodeExpr(e, ts.index()); +} + static bool EncodeExpr(Encoder& e, WasmAstExpr& expr) { @@ -2857,6 +3668,8 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr) return EncodeBinaryOperator(e, expr.as()); case WasmAstExprKind::Block: return EncodeBlock(e, expr.as()); + case WasmAstExprKind::Branch: + return EncodeBranch(e, expr.as()); case WasmAstExprKind::Call: return EncodeCall(e, expr.as()); case WasmAstExprKind::CallIndirect: @@ -2873,13 +3686,16 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr) return EncodeIfElse(e, expr.as()); case WasmAstExprKind::Load: return EncodeLoad(e, expr.as()); + case WasmAstExprKind::Return: + return EncodeReturn(e, expr.as()); case WasmAstExprKind::SetLocal: return EncodeSetLocal(e, expr.as()); case WasmAstExprKind::Store: return EncodeStore(e, expr.as()); + case WasmAstExprKind::TableSwitch: + return EncodeTableSwitch(e, expr.as()); case WasmAstExprKind::UnaryOperator: return EncodeUnaryOperator(e, expr.as()); - default:; } MOZ_CRASH("Bad expr kind"); } @@ -2937,7 +3753,7 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module) return false; for (WasmAstFunc* func : module.funcs()) { - if (!e.writeVarU32(func->sigIndex())) + if (!e.writeVarU32(func->sig().index())) return false; } @@ -2946,9 +3762,10 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module) } static bool -EncodeCString(Encoder& e, TwoByteChars twoByteChars) +EncodeCString(Encoder& e, WasmName wasmName) { - UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, twoByteChars).c_str()); + TwoByteChars range(wasmName.begin(), wasmName.length()); + UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str()); return utf8 && e.writeCString(utf8.get()); } @@ -3025,7 +3842,7 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module) static bool EncodeFunctionExport(Encoder& e, WasmAstExport& exp) { - if (!e.writeVarU32(exp.funcIndex())) + if (!e.writeVarU32(exp.func().index())) return false; if (!EncodeCString(e, exp.name())) @@ -3096,8 +3913,8 @@ EncodeTableSection(Encoder& e, WasmAstModule& module) if (!e.writeVarU32(module.maybeTable()->elems().length())) return false; - for (uint32_t index : module.maybeTable()->elems()) { - if (!e.writeVarU32(index)) + for (WasmRef& ref : module.maybeTable()->elems()) { + if (!e.writeVarU32(ref.index())) return false; } @@ -3115,10 +3932,10 @@ EncodeFunctionSection(Encoder& e, WasmAstFunc& func) if (!e.startSection(&offset)) return false; - if (!e.writeVarU32(func.varTypes().length())) + if (!e.writeVarU32(func.vars().length())) return false; - for (ValType type : func.varTypes()) { + for (ValType type : func.vars()) { if (!e.writeValType(type)) return false; } @@ -3142,14 +3959,14 @@ EncodeDataSegment(Encoder& e, WasmAstSegment& segment) if (!e.writeVarU32(segment.offset())) return false; - TwoByteChars text = segment.text(); + WasmName text = segment.text(); Vector bytes; if (!bytes.reserve(text.length())) return false; - const char16_t* cur = text.start().get(); - const char16_t* end = text.end().get(); + const char16_t* cur = text.begin(); + const char16_t* end = text.end(); while (cur != end) { uint8_t byte; MOZ_ALWAYS_TRUE(ConsumeTextByte(&cur, end, &byte)); @@ -3251,5 +4068,8 @@ wasm::TextToBinary(const char16_t* text, UniqueChars* error) if (!module) return nullptr; + if (!ResolveModule(lifo, module, error)) + return nullptr; + return EncodeModule(*module); } diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 133b4fd87f..fc903303d4 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -95,6 +95,18 @@ js::IsVectorObject(HandleValue v) FOR_EACH_SIMD(InstantiateIsVectorObject_) #undef InstantiateIsVectorObject_ +const char* +js::SimdTypeToString(SimdType type) +{ + switch (type) { +#define RETSTR_(TYPE) case SimdType::TYPE: return #TYPE; + FOR_EACH_SIMD(RETSTR_) +#undef RETSTR_ + case SimdType::Count: break; + } + return ""; +} + static inline bool ErrorBadArgs(JSContext* cx) { diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 72dde4ee30..bdc5dc252b 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -1105,6 +1105,8 @@ struct Bool64x2 { PropertyName* SimdTypeToName(JSContext* cx, SimdType type); +const char* SimdTypeToString(SimdType type); + template JSObject* CreateSimd(JSContext* cx, const typename V::Elem* data); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index b45d4083bb..5436ad1b6a 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -20,6 +20,7 @@ #include "asmjs/AsmJS.h" #include "asmjs/Wasm.h" +#include "asmjs/WasmText.h" #include "jit/InlinableNatives.h" #include "jit/JitFrameIterator.h" #include "js/Debug.h" @@ -487,6 +488,87 @@ IsProxy(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setBoolean(wasm::HasCompilerSupport(cx)); + return true; +} + +static bool +WasmEval(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() < 1 || args.length() > 2) { + ReportUsageError(cx, callee, "Wrong number of arguments"); + return false; + } + + if (!args[0].isObject() || !args[0].toObject().is()) { + ReportUsageError(cx, callee, "First argument must be an ArrayBuffer"); + return false; + } + + RootedObject importObj(cx); + if (!args.get(1).isUndefined()) { + if (!args.get(1).isObject()) { + ReportUsageError(cx, callee, "Second argument, if present, must be an Object"); + return false; + } + importObj = &args[1].toObject(); + } + + Rooted code(cx, &args[0].toObject().as()); + + RootedObject exportObj(cx); + if (!wasm::Eval(cx, code, importObj, &exportObj)) + return false; + + args.rval().setObject(*exportObj); + return true; +} + +static bool +WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (args.length() != 1) { + ReportUsageError(cx, callee, "Wrong number of arguments"); + return false; + } + + if (!args[0].isString()) { + ReportUsageError(cx, callee, "First argument must be a String"); + return false; + } + + AutoStableStringChars twoByteChars(cx); + if (!twoByteChars.initTwoByte(cx, args[0].toString())) + return false; + + UniqueChars error; + wasm::UniqueBytecode bytes = wasm::TextToBinary(twoByteChars.twoByteChars(), &error); + if (!bytes) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, + error.get() ? error.get() : "out of memory"); + return false; + } + + Rooted buffer(cx, ArrayBufferObject::create(cx, bytes->length())); + if (!buffer) + return false; + + memcpy(buffer->dataPointer(), bytes->begin(), bytes->length()); + + args.rval().setObject(*buffer); + return true; +} + static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) { @@ -3575,6 +3657,19 @@ gc::ZealModeHelpText), " Returns whether the given value is a nested function in an asm.js module that has been\n" " both compile- and link-time validated."), + JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0, +"wasmIsSupported()", +" Returns a boolean indicating whether WebAssembly is supported on the current device."), + + JS_FN_HELP("wasmEval", WasmEval, 2, 0, +"wasmEval(buffer, imports)", +" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n" +" and links the module's imports with the given 'imports' object."), + + JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0, +"wasmTextToBinary(str)", +" Translates the given text wasm module into its binary encoding."), + JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0, "isLazyFunction(fun)", " True if fun is a lazy JSFunction."), @@ -3828,9 +3923,6 @@ js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_, disableOOMFunctions = disableOOMFunctions_; - if (!wasm::DefineTestingFunctions(cx, obj)) - return false; - if (!JS_DefineProperties(cx, obj, TestingProperties)) return false; diff --git a/js/src/jit-test/tests/SIMD/bug1248503.js b/js/src/jit-test/tests/SIMD/bug1248503.js new file mode 100644 index 0000000000..36913faba9 --- /dev/null +++ b/js/src/jit-test/tests/SIMD/bug1248503.js @@ -0,0 +1,13 @@ +function assertEqVec(v, w) { + [0].forEach(i => v, w); + function assertEqX4(...opts) {} +} +gczeal(1); +function f() { + SIMD.Float32x4(); + var i1 = SIMD.Int32x4(); + for (j = 0; j < 100000; ++j, eval.eval) + assertEqVec(SIMD.Int32x4.check(i1), i1); +} +f(); + diff --git a/js/src/jit-test/tests/wasm/basic-control-flow.js b/js/src/jit-test/tests/wasm/basic-control-flow.js index 77bcb4c9b2..b684b35d1d 100644 --- a/js/src/jit-test/tests/wasm/basic-control-flow.js +++ b/js/src/jit-test/tests/wasm/basic-control-flow.js @@ -131,3 +131,41 @@ assertEq(counter, 0); assertErrorMessage(() => wasmEvalText('(module (func (result i32) (if (i32.const 42) (i32.const 0))))'), TypeError, mismatchError("void", "i32")); assertErrorMessage(() => wasmEvalText('(module (func (result i32) (if_else (i32.const 1) (i32.const 0) (if (i32.const 1) (i32.const 1)))))'), TypeError, mismatchError("void", "i32")); wasmEvalText('(module (func (if_else (i32.const 1) (i32.const 0) (if (i32.const 1) (i32.const 1)))))'); + +// ---------------------------------------------------------------------------- +// return + +assertEq(wasmEvalText('(module (func (return)) (export "" 0))')(), undefined); +assertEq(wasmEvalText('(module (func (result i32) (return (i32.const 1))) (export "" 0))')(), 1); +assertErrorMessage(() => wasmEvalText('(module (func (result f32) (return (i32.const 1))) (export "" 0))'), TypeError, mismatchError("i32", "f32")); +assertThrowsInstanceOf(() => wasmEvalText('(module (func (result i32) (return)) (export "" 0))'), TypeError); +assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1))) (export "" 0))'), TypeError); + +// TODO: convert these to wasmEval and assert some results once they are implemented + +// ---------------------------------------------------------------------------- +// br / br_if + +wasmTextToBinary('(module (func (block (br 0))))'); +wasmTextToBinary('(module (func (block $l (br $l))))'); +wasmTextToBinary('(module (func (result i32) (block (br_if 0 (i32.const 1)))))'); +wasmTextToBinary('(module (func (result i32) (block $l (br_if $l (i32.const 1)))))'); + +wasmTextToBinary('(module (func (block $l (block $m (br $l)))))'); +wasmTextToBinary('(module (func (block $l (block $m (br $m)))))'); + +// ---------------------------------------------------------------------------- +// loop + +wasmTextToBinary('(module (func (loop (br 0))))'); +wasmTextToBinary('(module (func (loop (br 1))))'); +wasmTextToBinary('(module (func (loop $a (br $a))))'); +wasmTextToBinary('(module (func (loop $a $b (br $a))))'); +wasmTextToBinary('(module (func (loop $a $b (br $b))))'); +wasmTextToBinary('(module (func (loop $a $a (br $a))))'); + +// ---------------------------------------------------------------------------- +// tableswitch + +wasmTextToBinary('(module (func (param i32) (block $b1 (block $b2 (tableswitch (get_local 0) (table (br $b1) (br $b2)) (br $b2))))))'); +wasmTextToBinary('(module (func (param i32) (block $b1 (tableswitch (get_local 0) (table) (br $b1)))))'); diff --git a/js/src/jit-test/tests/wasm/basic-float.js b/js/src/jit-test/tests/wasm/basic-float.js index e5d0f6763e..bdfdba7dae 100644 --- a/js/src/jit-test/tests/wasm/basic-float.js +++ b/js/src/jit-test/tests/wasm/basic-float.js @@ -1,5 +1,18 @@ load(libdir + "wasm.js"); +assertEq(wasmEvalText('(module (func (result f32) (f32.const -1)) (export "" 0))')(), -1); +assertEq(wasmEvalText('(module (func (result f32) (f32.const 1)) (export "" 0))')(), 1); +assertEq(wasmEvalText('(module (func (result f64) (f64.const -2)) (export "" 0))')(), -2); +assertEq(wasmEvalText('(module (func (result f64) (f64.const 2)) (export "" 0))')(), 2); +assertEq(wasmEvalText('(module (func (result f64) (f64.const 4294967296)) (export "" 0))')(), 4294967296); +assertEq(wasmEvalText('(module (func (result f32) (f32.const 1.5)) (export "" 0))')(), 1.5); +assertEq(wasmEvalText('(module (func (result f64) (f64.const 2.5)) (export "" 0))')(), 2.5); +assertEq(wasmEvalText('(module (func (result f64) (f64.const 10e2)) (export "" 0))')(), 10e2); +assertEq(wasmEvalText('(module (func (result f32) (f32.const 10e2)) (export "" 0))')(), 10e2); +assertEq(wasmEvalText('(module (func (result f64) (f64.const -0x8000000000000000)) (export "" 0))')(), -0x8000000000000000); +assertEq(wasmEvalText('(module (func (result f64) (f64.const -9223372036854775808)) (export "" 0))')(), -9223372036854775808); +assertEq(wasmEvalText('(module (func (result f64) (f64.const 1797693134862315708145274e284)) (export "" 0))')(), 1797693134862315708145274e284); + function testUnary(type, opcode, op, expect) { assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect); } diff --git a/js/src/jit-test/tests/wasm/basic-integer.js b/js/src/jit-test/tests/wasm/basic-integer.js index d5a6dd3134..757840cc2c 100644 --- a/js/src/jit-test/tests/wasm/basic-integer.js +++ b/js/src/jit-test/tests/wasm/basic-integer.js @@ -1,5 +1,9 @@ load(libdir + "wasm.js"); +assertEq(wasmEvalText('(module (func (result i32) (i32.const -1)) (export "" 0))')(), -1); +assertEq(wasmEvalText('(module (func (result i32) (i32.const -2147483648)) (export "" 0))')(), -2147483648); +assertEq(wasmEvalText('(module (func (result i32) (i32.const 4294967295)) (export "" 0))')(), -1); + function testUnary(type, opcode, op, expect) { assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect); } diff --git a/js/src/jit-test/tests/wasm/basic-memory.js b/js/src/jit-test/tests/wasm/basic-memory.js index 7316826ef5..b81ed766b5 100644 --- a/js/src/jit-test/tests/wasm/basic-memory.js +++ b/js/src/jit-test/tests/wasm/basic-memory.js @@ -17,9 +17,10 @@ function testLoad(type, ext, base, offset, align, expect) { ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + ' (func (param i32) (result ' + type + ')' + - ' (' + type + '.load' + ext + ' (get_local 0)' + + ' (' + type + '.load' + ext + ' offset=' + offset + ' ' + (align != 0 ? 'align=' + align : '') + + ' (get_local 0)' + ' )' + ' ) (export "" 0))'); assertEq(wasmEvalText( @@ -29,9 +30,10 @@ function testLoad(type, ext, base, offset, align, expect) { ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + ' (func (param i32) (result ' + type + ')' + - ' (' + type + '.load' + ext + ' (get_local 0)' + + ' (' + type + '.load' + ext + ' offset=' + offset + ' ' + (align != 0 ? 'align=' + align : '') + + ' (get_local 0)' + ' )' + ' ) (export "" 0))' )(base), expect); @@ -45,9 +47,10 @@ function testStore(type, ext, base, offset, align, value) { ' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' + ' )' + ' (func (param i32) (param ' + type + ') (result ' + type + ')' + - ' (' + type + '.store' + ext + ' (get_local 0)' + + ' (' + type + '.store' + ext + ' offset=' + offset + ' ' + (align != 0 ? 'align=' + align : '') + + ' (get_local 0)' + ' (get_local 1)' + ' )' + ' ) (export "" 0))' diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index b1de3f9bb3..82ed6c7156 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -46,6 +46,9 @@ assertEq(desc.value(), undefined); wasmEvalText('(module (func) (func) (export "a" 0))'); wasmEvalText('(module (func) (func) (export "a" 1))'); +wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))'); +wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))'); + assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /export function index out of range/); assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /export function index out of range/); @@ -121,6 +124,7 @@ wasmEvalText(code, {a:()=>{}, b:{c:()=>{}}, c:()=>{}}); wasmEvalText('(module (import "a" "" (result i32)))', {a: ()=> {}}); wasmEvalText('(module (import "a" "" (result f32)))', {a: ()=> {}}); wasmEvalText('(module (import "a" "" (result f64)))', {a: ()=> {}}); +wasmEvalText('(module (import $foo "a" "" (result f64)))', {a: ()=> {}}); // ---------------------------------------------------------------------------- // memory @@ -221,6 +225,11 @@ assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (get_ assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), TypeError, /NYI/); +assertEq(wasmEvalText('(module (func (param $a i32) (result i32) (get_local $a)) (export "" 0))')(), 0); +assertEq(wasmEvalText('(module (func (param $a i32) (local $b i32) (result i32) (block (set_local $b (get_local $a)) (get_local $b))) (export "" 0))')(42), 42); + +wasmEvalText('(module (func (local i32) (local $a f32) (set_local 0 (i32.const 1)) (set_local $a (f32.const nan))))'); + // ---------------------------------------------------------------------------- // blocks @@ -252,6 +261,8 @@ wasmEvalText('(module (func (param i32) (nop)) (func (call 0 (i32.const 0))))'); assertEq(wasmEvalText('(module (func (result i32) (i32.const 42)) (func (result i32) (call 0)) (export "" 1))')(), 42); assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 0)) (export "" 0))')(), InternalError); assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 1)) (func (call 0)) (export "" 0))')(), InternalError); +wasmEvalText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (f32.const nan))))'); +assertErrorMessage(() => wasmEvalText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32")); assertThrowsInstanceOf(() => wasmEvalText('(module (import "a" "") (func (call_import 0 (i32.const 0))))', {a:()=>{}}), TypeError); assertThrowsInstanceOf(() => wasmEvalText('(module (import "a" "" (param i32)) (func (call_import 0)))', {a:()=>{}}), TypeError); @@ -336,3 +347,38 @@ for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math assertThrowsInstanceOf(() => i2i(bad, 0), RangeError); assertThrowsInstanceOf(() => i2v(bad, 0), RangeError); } + +// When the test below starts failing, remove it and uncomment the lines below! +assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/); +/* +assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/); +assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/); +assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/); +assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/); +*/ + + +var {v2i, i2i, i2v} = wasmEvalText(`(module + (type $a (func (result i32))) + (type $b (func (param i32) (result i32))) + (type $c (func (param i32))) + (func $a (type $a) (i32.const 13)) + (func $b (type $a) (i32.const 42)) + (func $c (type $b) (i32.add (get_local 0) (i32.const 1))) + (func $d (type $b) (i32.add (get_local 0) (i32.const 2))) + (func $e (type $b) (i32.add (get_local 0) (i32.const 3))) + (func $f (type $b) (i32.add (get_local 0) (i32.const 4))) + (table $a $b $c $d $e $f) + (func (param i32) (result i32) (call_indirect $a (get_local 0))) + (func (param i32) (param i32) (result i32) (call_indirect $b (get_local 0) (get_local 1))) + (func (param i32) (call_indirect $c (get_local 0) (i32.const 0))) + (export "v2i" 6) + (export "i2i" 7) + (export "i2v" 8) +)`); + +wasmEvalText('(module (func $foo (nop)) (func (call $foo)))'); +wasmEvalText('(module (func (call $foo)) (func $foo (nop)))'); +wasmEvalText('(module (import $bar "a" "") (func (call_import $bar)) (func $foo (nop)))', {a:()=>{}}); +assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call_import $abc)))'), SyntaxError, /import not found/); + diff --git a/js/src/jit-test/tests/wasm/text.js b/js/src/jit-test/tests/wasm/text.js index e593e68ffa..11b48e1f61 100644 --- a/js/src/jit-test/tests/wasm/text.js +++ b/js/src/jit-test/tests/wasm/text.js @@ -8,6 +8,19 @@ assertErrorMessage(() => wasmEvalText('(m'), SyntaxError, parsingError); assertErrorMessage(() => wasmEvalText('(module'), SyntaxError, parsingError); assertErrorMessage(() => wasmEvalText('(moduler'), SyntaxError, parsingError); assertErrorMessage(() => wasmEvalText('(module (func) (export "a'), SyntaxError, parsingError); +assertErrorMessage(() => wasmEvalText('(module (func (local $a i32) (param $b f32)))'), SyntaxError, parsingError); + +assertErrorMessage(() => wasmEvalText('(module (func $a) (func) (export "a" $a) (export "b" $b))'), SyntaxError, /function not found/); +assertErrorMessage(() => wasmEvalText('(module (import $foo "a" "b") (import $foo "a" "b"))'), SyntaxError, /duplicate import/); +assertErrorMessage(() => wasmEvalText('(module (func $foo) (func $foo))'), SyntaxError, /duplicate function/); +assertErrorMessage(() => wasmEvalText('(module (func (param $a i32) (local $a i32)))'), SyntaxError, /duplicate var/); +assertErrorMessage(() => wasmEvalText('(module (func (get_local $a)))'), SyntaxError, /local not found/); +assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (type $a (func (param i32))))'), SyntaxError, /duplicate signature/); +assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call_import $abc)))'), SyntaxError, /import not found/); +assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (type $b) (i32.const 13)))'), SyntaxError, /signature not found/); +assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (call_indirect $c (get_local 0) (i32.const 0))))'), SyntaxError, /signature not found/); +assertErrorMessage(() => wasmEvalText('(module (func (br $a)))'), SyntaxError, /label not found/); +assertErrorMessage(() => wasmEvalText('(module (func (block $a) (br $a)))'), SyntaxError, /label not found/); // Note: the s-expression text format is temporary, this file is mostly just to // hold basic error smoke tests. diff --git a/js/src/jit/EffectiveAddressAnalysis.cpp b/js/src/jit/EffectiveAddressAnalysis.cpp index 790a761839..1c1bdcf91f 100644 --- a/js/src/jit/EffectiveAddressAnalysis.cpp +++ b/js/src/jit/EffectiveAddressAnalysis.cpp @@ -148,6 +148,14 @@ EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins) ins->block()->insertBefore(ins, zero); ins->replacePtr(zero); } + + // If the index is within the minimum heap length, we can optimize + // away the bounds check. + if (imm >= 0) { + int32_t end = (uint32_t)imm + ins->byteSize(); + if (end >= imm && (uint32_t)end <= mir_->minAsmJSHeapLength()) + ins->removeBoundsCheck(); + } } else if (ptr->isAdd()) { // Look for heap[a+i] where i is a constant offset, and fold the offset. // Alignment masks have already been moved out of the way by the diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 82679a84bf..80ed45904a 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -1269,7 +1269,9 @@ GuessPhiType(MPhi* phi, bool* hasInputsWithEmptyTypes) // If we only saw definitions that can be converted into Float32 before and // encounter a Float32 value, promote previous values to Float32 type = MIRType_Float32; - } else if (IsNumberType(type) && IsNumberType(in->type())) { + } else if (IsTypeRepresentableAsDouble(type) && + IsTypeRepresentableAsDouble(in->type())) + { // Specialize phis with int32 and double operands as double. type = MIRType_Double; convertibleToFloat32 &= in->canProduceFloat32(); @@ -1329,7 +1331,9 @@ TypeAnalyzer::propagateSpecialization(MPhi* phi) } // Specialize phis with int32 and double operands as double. - if (IsNumberType(use->type()) && IsNumberType(phi->type())) { + if (IsTypeRepresentableAsDouble(use->type()) && + IsTypeRepresentableAsDouble(phi->type())) + { if (!respecialize(use, MIRType_Double)) return false; continue; @@ -2507,6 +2511,7 @@ IsResumableMIRType(MIRType type) case MIRType_ObjectGroup: case MIRType_Doublex2: // NYI, see also RSimdBox::recover case MIRType_SinCosDouble: + case MIRType_Int64: return false; } MOZ_CRASH("Unknown MIRType."); diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 7ca007dd66..669e44b263 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -399,6 +399,7 @@ enum MIRType MIRType_Null, MIRType_Boolean, MIRType_Int32, + MIRType_Int64, MIRType_Double, MIRType_Float32, MIRType_String, @@ -504,6 +505,8 @@ StringFromMIRType(MIRType type) return "Bool"; case MIRType_Int32: return "Int32"; + case MIRType_Int64: + return "Int64"; case MIRType_Double: return "Double"; case MIRType_Float32: @@ -556,7 +559,18 @@ StringFromMIRType(MIRType type) static inline bool IsNumberType(MIRType type) { - return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32; + return type == MIRType_Int32 || + type == MIRType_Double || + type == MIRType_Float32 || + type == MIRType_Int64; +} + +static inline bool +IsTypeRepresentableAsDouble(MIRType type) +{ + return type == MIRType_Int32 || + type == MIRType_Double || + type == MIRType_Float32; } static inline bool diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 5d6e857e95..938d947324 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -1276,7 +1276,7 @@ IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outp // Optimize some constant powers. if (rhs->isConstant()) { - double pow = rhs->toConstant()->toNumber(); + double pow = rhs->toConstant()->numberToDouble(); // Math.pow(x, 0.5) is a sqrt with edge-case detection. if (pow == 0.5) { @@ -1482,7 +1482,7 @@ IonBuilder::inlineMathMinMax(CallInfo& callInfo, bool max) // Don't force a double MMinMax for arguments that would be a NOP // when doing an integer MMinMax. if (arg->isConstant()) { - double cte = arg->toConstant()->toNumber(); + double cte = arg->toConstant()->numberToDouble(); // min(int32, cte >= INT32_MAX) = int32 if (cte >= INT32_MAX && !max) break; @@ -3296,10 +3296,12 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) defVal = constant(Int32Value(0)); } else if (laneType == MIRType_Boolean) { defVal = constant(BooleanValue(false)); - } else { - MOZ_ASSERT(IsFloatingPointType(laneType)); + } else if (laneType == MIRType_Double) { defVal = constant(DoubleNaNValue()); - defVal->setResultType(laneType); + } else { + MOZ_ASSERT(laneType == MIRType_Float32); + defVal = MConstant::NewFloat32(alloc(), GenericNaN()); + current->add(defVal); } } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 1592851bdd..99c3bcc0d0 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -85,7 +85,8 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt MDefinition* left = ins->getOperand(0); MDefinition* right = ins->getOperand(1); - MOZ_ASSERT(IsNumberType(left->type()) && IsNumberType(right->type())); + MOZ_ASSERT(IsTypeRepresentableAsDouble(left->type())); + MOZ_ASSERT(IsTypeRepresentableAsDouble(right->type())); if (!left->isConstant() || !right->isConstant()) return nullptr; @@ -114,25 +115,25 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt ret.setNumber(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F)); break; case MDefinition::Op_Add: - ret.setNumber(lhs->toNumber() + rhs->toNumber()); + ret.setNumber(lhs->numberToDouble() + rhs->numberToDouble()); break; case MDefinition::Op_Sub: - ret.setNumber(lhs->toNumber() - rhs->toNumber()); + ret.setNumber(lhs->numberToDouble() - rhs->numberToDouble()); break; case MDefinition::Op_Mul: - ret.setNumber(lhs->toNumber() * rhs->toNumber()); + ret.setNumber(lhs->numberToDouble() * rhs->numberToDouble()); break; case MDefinition::Op_Div: if (ins->toDiv()->isUnsigned()) ret.setInt32(rhs->isInt32(0) ? 0 : uint32_t(lhs->toInt32()) / uint32_t(rhs->toInt32())); else - ret.setNumber(NumberDiv(lhs->toNumber(), rhs->toNumber())); + ret.setNumber(NumberDiv(lhs->numberToDouble(), rhs->numberToDouble())); break; case MDefinition::Op_Mod: if (ins->toMod()->isUnsigned()) ret.setInt32(rhs->isInt32(0) ? 0 : uint32_t(lhs->toInt32()) % uint32_t(rhs->toInt32())); else - ret.setNumber(NumberMod(lhs->toNumber(), rhs->toNumber())); + ret.setNumber(NumberMod(lhs->numberToDouble(), rhs->numberToDouble())); break; default: MOZ_CRASH("NYI"); @@ -166,7 +167,7 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) return nullptr; int32_t num; - if (!mozilla::NumberIsInt32(right->toConstant()->toNumber(), &num)) + if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num)) return nullptr; // check if rhs is a power of two @@ -175,8 +176,12 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) Value ret; ret.setDouble(1.0 / (double) num); - MConstant* foldedRhs = MConstant::New(alloc, ret); - foldedRhs->setResultType(ins->type()); + MConstant* foldedRhs; + if (ins->type() == MIRType_Float32) + foldedRhs = MConstant::NewFloat32(alloc, ret.toDouble()); + else + foldedRhs = MConstant::New(alloc, ret); + MOZ_ASSERT(foldedRhs->type() == ins->type()); ins->block()->insertBefore(ins, foldedRhs); MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type()); @@ -660,23 +665,26 @@ MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* con } MConstant* -MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, - CompilerConstraintList* constraints) +MConstant::NewFloat32(TempAllocator& alloc, double d) { - MOZ_ASSERT(!IsSimdType(type)); - MOZ_ASSERT_IF(type == MIRType_Float32, - IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble()))); - MConstant* constant = new(alloc) MConstant(v, constraints); - constant->setResultType(type); - return constant; + MOZ_ASSERT(IsNaN(d) || d == double(float(d))); + return new(alloc) MConstant(float(d)); +} + +MConstant* +MConstant::NewInt64(TempAllocator& alloc, int64_t i) +{ + return new(alloc) MConstant(i); } MConstant* MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type) { if (type == MIRType_Float32) - return NewTypedValue(alloc, Float32Value(v.toNumber()), type); - return NewTypedValue(alloc, v, type); + return NewFloat32(alloc, v.toNumber()); + MConstant* res = New(alloc, v); + MOZ_ASSERT(res->type() == type); + return res; } MConstant* @@ -739,16 +747,44 @@ jit::IonCompilationCanUseNurseryPointers() #endif // DEBUG MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints) - : value_(vp) { setResultType(MIRTypeFromValue(vp)); - if (vp.isObject()) { + + MOZ_ASSERT(payload_.asBits == 0); + + switch (type()) { + case MIRType_Undefined: + case MIRType_Null: + break; + case MIRType_Boolean: + payload_.b = vp.toBoolean(); + break; + case MIRType_Int32: + payload_.i32 = vp.toInt32(); + break; + case MIRType_Double: + payload_.d = vp.toDouble(); + break; + case MIRType_String: + MOZ_ASSERT(vp.toString()->isAtom()); + payload_.str = vp.toString(); + break; + case MIRType_Symbol: + payload_.sym = vp.toSymbol(); + break; + case MIRType_Object: + payload_.obj = &vp.toObject(); // Create a singleton type set for the object. This isn't necessary for // other types as the result type encodes all needed information. MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers()); setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject())); - } - if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) { + break; + case MIRType_MagicOptimizedArguments: + case MIRType_MagicOptimizedOut: + case MIRType_MagicHole: + case MIRType_MagicIsConstructing: + break; + case MIRType_MagicUninitializedLexical: // JS_UNINITIALIZED_LEXICAL does not escape to script and is not // observed in type sets. However, it may flow around freely during // Ion compilation. Give it an unknown typeset to poison any type sets @@ -757,38 +793,92 @@ MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints) // TODO We could track uninitialized lexicals more precisely by tracking // them in type sets. setResultTypeSet(MakeUnknownTypeSet()); + break; + default: + MOZ_CRASH("Unexpected type"); } - MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom()); - setMovable(); } MConstant::MConstant(JSObject* obj) - : value_(ObjectValue(*obj)) { MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers()); setResultType(MIRType_Object); + payload_.obj = obj; setMovable(); } +MConstant::MConstant(float f) +{ + setResultType(MIRType_Float32); + payload_.f = f; + setMovable(); +} + +MConstant::MConstant(int64_t i) +{ + setResultType(MIRType_Int64); + payload_.i64 = i; + setMovable(); +} + +#ifdef DEBUG +void +MConstant::assertInitializedPayload() const +{ + // valueHash() and equals() expect the unused payload bits to be + // initialized to zero. Assert this in debug builds. + + switch (type()) { + case MIRType_Int32: + case MIRType_Float32: + MOZ_ASSERT((payload_.asBits >> 32) == 0); + break; + case MIRType_Boolean: + MOZ_ASSERT((payload_.asBits >> 1) == 0); + break; + case MIRType_Double: + case MIRType_Int64: + break; + case MIRType_String: + case MIRType_Object: + case MIRType_Symbol: + MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0); + break; + default: + MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type())); + MOZ_ASSERT(payload_.asBits == 0); + break; + } +} +#endif + HashNumber MConstant::valueHash() const { + static_assert(sizeof(Payload) == sizeof(uint64_t), + "Code below assumes payload fits in 64 bits"); + + assertInitializedPayload(); + + // Build a 64-bit value holding both the payload and the type. + static const size_t TypeBits = 8; + static const size_t TypeShift = 64 - TypeBits; + MOZ_ASSERT(uintptr_t(type()) <= (1 << TypeBits) - 1); + uint64_t bits = (uint64_t(type()) << TypeShift) ^ payload_.asBits; + // Fold all 64 bits into the 32-bit result. It's tempting to just discard // half of the bits, as this is just a hash, however there are many common // patterns of values where only the low or the high bits vary, so // discarding either side would lead to excessive hash collisions. - uint64_t bits = JSVAL_TO_IMPL(value_).asBits; return (HashNumber)bits ^ (HashNumber)(bits >> 32); } bool MConstant::congruentTo(const MDefinition* ins) const { - if (!ins->isConstant()) - return false; - return ins->toConstant()->value_ == value_; + return ins->isConstant() && equals(ins->toConstant()); } void @@ -866,7 +956,7 @@ MConstant::printOpcode(GenericPrinter& out) const bool MConstant::canProduceFloat32() const { - if (!IsNumberType(type())) + if (!isTypeRepresentableAsDouble()) return false; if (type() == MIRType_Int32) @@ -880,7 +970,42 @@ MConstant::canProduceFloat32() const Value MConstant::toJSValue() const { - return value_; + // Wasm has types like int64 that cannot be stored as js::Value. It also + // doesn't want the NaN canonicalization enforced by js::Value. + MOZ_ASSERT(!IsCompilingAsmJS()); + + switch (type()) { + case MIRType_Undefined: + return UndefinedValue(); + case MIRType_Null: + return NullValue(); + case MIRType_Boolean: + return BooleanValue(toBoolean()); + case MIRType_Int32: + return Int32Value(toInt32()); + case MIRType_Double: + return DoubleValue(toDouble()); + case MIRType_Float32: + return Float32Value(toFloat32()); + case MIRType_String: + return StringValue(toString()); + case MIRType_Symbol: + return SymbolValue(toSymbol()); + case MIRType_Object: + return ObjectValue(toObject()); + case MIRType_MagicOptimizedArguments: + return MagicValue(JS_OPTIMIZED_ARGUMENTS); + case MIRType_MagicOptimizedOut: + return MagicValue(JS_OPTIMIZED_OUT); + case MIRType_MagicHole: + return MagicValue(JS_ELEMENTS_HOLE); + case MIRType_MagicIsConstructing: + return MagicValue(JS_IS_CONSTRUCTING); + case MIRType_MagicUninitializedLexical: + return MagicValue(JS_UNINITIALIZED_LEXICAL); + default: + MOZ_CRASH("Unexpected type"); + } } bool @@ -888,14 +1013,16 @@ MConstant::valueToBoolean(bool* res) const { switch (type()) { case MIRType_Boolean: - *res = value_.toBoolean(); + *res = toBoolean(); return true; case MIRType_Int32: - *res = value_.toInt32() != 0; + *res = toInt32() != 0; return true; case MIRType_Double: + *res = !mozilla::IsNaN(toDouble()) && toDouble() != 0.0; + return true; case MIRType_Float32: - *res = !mozilla::IsNaN(value_.toDouble()) && value_.toDouble() != 0; + *res = !mozilla::IsNaN(toFloat32()) && toFloat32() != 0.0f; return true; case MIRType_Null: case MIRType_Undefined: @@ -905,10 +1032,10 @@ MConstant::valueToBoolean(bool* res) const *res = true; return true; case MIRType_String: - *res = value_.toString()->length() != 0; + *res = toString()->length() != 0; return true; case MIRType_Object: - *res = !EmulatesUndefined(&value_.toObject()); + *res = !EmulatesUndefined(&toObject()); return true; default: MOZ_ASSERT(IsMagicType(type())); @@ -955,7 +1082,7 @@ MSimdValueX4::foldsTo(TempAllocator& alloc) case MIRType_Float32x4: { float a[4]; for (size_t i = 0; i < 4; ++i) - a[i] = getOperand(i)->toConstant()->toNumber(); + a[i] = getOperand(i)->toConstant()->numberToDouble(); cst = SimdConstant::CreateX4(a); break; } @@ -991,7 +1118,7 @@ MSimdSplatX4::foldsTo(TempAllocator& alloc) break; } case MIRType_Float32x4: { - float v = op->toConstant()->toNumber(); + float v = op->toConstant()->numberToDouble(); cst = SimdConstant::SplatX4(v); break; } @@ -1232,6 +1359,21 @@ MSimdInsertElement::printOpcode(GenericPrinter& out) const out.printf(" (%s)", MSimdInsertElement::LaneName(lane())); } +void +MSimdBox::printOpcode(GenericPrinter& out) const +{ + MDefinition::printOpcode(out); + out.printf(" (%s%s)", SimdTypeToString(simdType()), + initialHeap() == gc::TenuredHeap ? ", tenured" : ""); +} + +void +MSimdUnbox::printOpcode(GenericPrinter& out) const +{ + MDefinition::printOpcode(out); + out.printf(" (%s)", SimdTypeToString(simdType())); +} + MCloneLiteral* MCloneLiteral::New(TempAllocator& alloc, MDefinition* obj) { @@ -1319,10 +1461,10 @@ MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) { MDefinition* input = getOperand(0); - if (!input->isConstant() || !input->toConstant()->isNumber()) + if (!input->isConstant() || !input->toConstant()->isTypeRepresentableAsDouble()) return this; - double in = input->toConstant()->toNumber(); + double in = input->toConstant()->numberToDouble(); double out; switch (function_) { case Log: @@ -1402,7 +1544,7 @@ MMathFunction::foldsTo(TempAllocator& alloc) } if (input->type() == MIRType_Float32) - return MConstant::NewTypedValue(alloc, DoubleValue(out), MIRType_Float32); + return MConstant::NewFloat32(alloc, out); return MConstant::New(alloc, DoubleValue(out)); } @@ -1890,7 +2032,7 @@ MPhi::foldsTernary() // If testArg is an int32 type we can: // - fold testArg ? testArg : 0 to testArg // - fold testArg ? 0 : testArg to 0 - if (testArg->type() == MIRType_Int32 && c->toNumber() == 0) { + if (testArg->type() == MIRType_Int32 && c->numberToDouble() == 0) { // When folding to the constant we need to hoist it. if (trueDef == c && !c->block()->dominates(block())) c->block()->moveBefore(pred->lastIns(), c); @@ -2032,7 +2174,7 @@ jit::MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet, if (newTypeSet && newTypeSet->empty()) return true; if (newType != *ptype) { - if (IsNumberType(newType) && IsNumberType(*ptype)) { + if (IsTypeRepresentableAsDouble(newType) && IsTypeRepresentableAsDouble(*ptype)) { *ptype = MIRType_Double; } else if (*ptype != MIRType_Value) { if (!*ptypeSet) { @@ -2266,7 +2408,7 @@ IsConstant(MDefinition* def, double v) if (!def->isConstant()) return false; - return NumbersAreIdentical(def->toConstant()->toNumber(), v); + return NumbersAreIdentical(def->toConstant()->numberToDouble(), v); } MDefinition* @@ -2680,11 +2822,14 @@ MMinMax::foldsTo(TempAllocator& alloc) // Directly apply math utility to compare the rhs() and lhs() when // they are both constants. if (lhs()->isConstant() && rhs()->isConstant()) { - if (!lhs()->toConstant()->isNumber() || !rhs()->toConstant()->isNumber()) + if (!lhs()->toConstant()->isTypeRepresentableAsDouble() || + !rhs()->toConstant()->isTypeRepresentableAsDouble()) + { return this; + } - double lnum = lhs()->toConstant()->toNumber(); - double rnum = rhs()->toConstant()->toNumber(); + double lnum = lhs()->toConstant()->numberToDouble(); + double rnum = rhs()->toConstant()->numberToDouble(); double result; if (isMax()) result = js::math_max_impl(lnum, rnum); @@ -2697,12 +2842,11 @@ MMinMax::foldsTo(TempAllocator& alloc) int32_t cast; if (mozilla::NumberEqualsInt32(result, &cast)) return MConstant::New(alloc, Int32Value(cast)); + } else if (type() == MIRType_Float32) { + return MConstant::NewFloat32(alloc, result); } else { - MOZ_ASSERT(IsFloatingPointType(type())); - MConstant* constant = MConstant::New(alloc, DoubleValue(result)); - if (type() == MIRType_Float32) - constant->setResultType(MIRType_Float32); - return constant; + MOZ_ASSERT(type() == MIRType_Double); + return MConstant::New(alloc, DoubleValue(result)); } } @@ -2711,7 +2855,10 @@ MMinMax::foldsTo(TempAllocator& alloc) if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) { // min(int32, cte >= INT32_MAX) = int32 - if (constant->isNumber() && constant->toNumber() >= INT32_MAX && !isMax()) { + if (!isMax() && + constant->isTypeRepresentableAsDouble() && + constant->numberToDouble() >= INT32_MAX) + { MLimitedTruncate* limit = MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate); block()->insertBefore(this, limit); @@ -2720,7 +2867,10 @@ MMinMax::foldsTo(TempAllocator& alloc) } // max(int32, cte <= INT32_MIN) = int32 - if (constant->isNumber() && constant->toNumber() <= INT32_MIN && isMax()) { + if (isMax() && + constant->isTypeRepresentableAsDouble() && + constant->numberToDouble() <= INT32_MIN) + { MLimitedTruncate* limit = MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate); block()->insertBefore(this, limit); @@ -3039,7 +3189,7 @@ MustBeUInt32(MDefinition* def, MDefinition** pwrapped) if (MConstant* defConst = def->maybeConstantValue()) { *pwrapped = defConst; - return defConst->isInt32(0); + return defConst->type() == MIRType_Int32 && defConst->toInt32() >= 0; } return false; @@ -3112,7 +3262,7 @@ MCompare::determineCompareType(JSOp op, MDefinition* left, MDefinition* right) } // Numeric comparisons against a double coerce to double. - if (IsNumberType(lhs) && IsNumberType(rhs)) + if (IsTypeRepresentableAsDouble(lhs) && IsTypeRepresentableAsDouble(rhs)) return Compare_Double; // Any comparison is allowed except strict eq. @@ -3522,7 +3672,7 @@ MToInt32::foldsTo(TempAllocator& alloc) case MIRType_Double: int32_t ival; // Only the value within the range of Int32 can be substituted as constant. - if (mozilla::NumberEqualsInt32(input->toConstant()->toNumber(), &ival)) + if (mozilla::NumberEqualsInt32(input->toConstant()->numberToDouble(), &ival)) return MConstant::New(alloc, Int32Value(ival)); break; default: @@ -3570,8 +3720,8 @@ MToDouble::foldsTo(TempAllocator& alloc) if (input->type() == MIRType_Double) return input; - if (input->isConstant() && input->toConstant()->isNumber()) { - double out = input->toConstant()->toNumber(); + if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble()) { + double out = input->toConstant()->numberToDouble(); return MConstant::New(alloc, DoubleValue(out)); } @@ -3592,12 +3742,9 @@ MToFloat32::foldsTo(TempAllocator& alloc) if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32) return input->toToDouble()->input(); - if (input->isConstant() && input->toConstant()->isNumber()) { - float out = float(input->toConstant()->toNumber()); - MConstant* c = MConstant::New(alloc, DoubleValue(out)); - c->setResultType(MIRType_Float32); - return c; - } + if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble()) + return MConstant::NewFloat32(alloc, float(input->toConstant()->numberToDouble())); + return this; } @@ -3617,8 +3764,8 @@ MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) { if (MConstant* inputConst = input()->maybeConstantValue()) { - if (inputConst->isNumber()) { - int32_t clamped = ClampDoubleToUint8(inputConst->toNumber()); + if (inputConst->isTypeRepresentableAsDouble()) { + int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble()); return MConstant::New(alloc, Int32Value(clamped)); } } @@ -3960,29 +4107,29 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) return true; } - if (!lhs->isNumber() || !rhs->isNumber()) + if (!lhs->isTypeRepresentableAsDouble() || !rhs->isTypeRepresentableAsDouble()) return false; switch (jsop_) { case JSOP_LT: - *result = (lhs->toNumber() < rhs->toNumber()); + *result = (lhs->numberToDouble() < rhs->numberToDouble()); break; case JSOP_LE: - *result = (lhs->toNumber() <= rhs->toNumber()); + *result = (lhs->numberToDouble() <= rhs->numberToDouble()); break; case JSOP_GT: - *result = (lhs->toNumber() > rhs->toNumber()); + *result = (lhs->numberToDouble() > rhs->numberToDouble()); break; case JSOP_GE: - *result = (lhs->toNumber() >= rhs->toNumber()); + *result = (lhs->numberToDouble() >= rhs->numberToDouble()); break; case JSOP_STRICTEQ: // Fall through. case JSOP_EQ: - *result = (lhs->toNumber() == rhs->toNumber()); + *result = (lhs->numberToDouble() == rhs->numberToDouble()); break; case JSOP_STRICTNE: // Fall through. case JSOP_NE: - *result = (lhs->toNumber() != rhs->toNumber()); + *result = (lhs->numberToDouble() != rhs->numberToDouble()); break; default: return false; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6b499642cc..79674f79b1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -605,6 +605,21 @@ class MDefinition : public MNode Truncate = 3 }; + static const char * TruncateKindString(TruncateKind kind) { + switch(kind) { + case NoTruncate: + return "NoTruncate"; + case TruncateAfterBailouts: + return "TruncateAfterBailouts"; + case IndirectTruncate: + return "IndirectTruncate"; + case Truncate: + return "Truncate"; + default: + MOZ_CRASH("Unknown truncate kind."); + } + } + // |needTruncation| records the truncation kind of the results, such that it // can be used to truncate the operands of this instruction. If // |needTruncation| function returns true, then the |truncate| function is @@ -1352,18 +1367,44 @@ class MLimitedTruncate // A constant js::Value. class MConstant : public MNullaryInstruction { - Value value_; + struct Payload { + union { + bool b; + int32_t i32; + int64_t i64; + float f; + double d; + JSString* str; + JS::Symbol* sym; + JSObject* obj; + uint64_t asBits; + }; + Payload() : asBits(0) {} + }; + + Payload payload_; + + static_assert(sizeof(Payload) == sizeof(uint64_t), + "asBits must be big enough for all payload bits"); + +#ifdef DEBUG + void assertInitializedPayload() const; +#else + void assertInitializedPayload() const {} +#endif protected: MConstant(const Value& v, CompilerConstraintList* constraints); explicit MConstant(JSObject* obj); + explicit MConstant(float f); + explicit MConstant(int64_t i); public: INSTRUCTION_HEADER(Constant) static MConstant* New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints = nullptr); - static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, - CompilerConstraintList* constraints = nullptr); + static MConstant* NewFloat32(TempAllocator& alloc, double d); + static MConstant* NewInt64(TempAllocator& alloc, int64_t i); static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type); static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v); @@ -1407,57 +1448,62 @@ class MConstant : public MNullaryInstruction ALLOW_CLONE(MConstant) - bool equals(const MConstant* other) { - return value_ == other->value_; + bool equals(const MConstant* other) const { + assertInitializedPayload(); + return type() == other->type() && payload_.asBits == other->payload_.asBits; } bool toBoolean() const { MOZ_ASSERT(type() == MIRType_Boolean); - return value_.toBoolean(); + return payload_.b; } int32_t toInt32() const { MOZ_ASSERT(type() == MIRType_Int32); - return value_.toInt32(); + return payload_.i32; + } + int64_t toInt64() const { + MOZ_ASSERT(type() == MIRType_Int64); + return payload_.i64; } bool isInt32(int32_t i) const { - return type() == MIRType_Int32 && value_.toInt32() == i; + return type() == MIRType_Int32 && payload_.i32 == i; } double toDouble() const { MOZ_ASSERT(type() == MIRType_Double); - return value_.toDouble(); + return payload_.d; } float toFloat32() const { MOZ_ASSERT(type() == MIRType_Float32); - return value_.toDouble(); + return payload_.f; } JSString* toString() const { MOZ_ASSERT(type() == MIRType_String); - return value_.toString(); + return payload_.str; } JS::Symbol* toSymbol() const { MOZ_ASSERT(type() == MIRType_Symbol); - return value_.toSymbol(); + return payload_.sym; } JSObject& toObject() const { MOZ_ASSERT(type() == MIRType_Object); - return value_.toObject(); + return *payload_.obj; } JSObject* toObjectOrNull() const { if (type() == MIRType_Object) - return &value_.toObject(); + return payload_.obj; MOZ_ASSERT(type() == MIRType_Null); return nullptr; } - bool isNumber() const { - return IsNumberType(type()); + bool isTypeRepresentableAsDouble() const { + return IsTypeRepresentableAsDouble(type()); } - double toNumber() const { + double numberToDouble() const { + MOZ_ASSERT(isTypeRepresentableAsDouble()); if (type() == MIRType_Int32) return toInt32(); if (type() == MIRType_Double) return toDouble(); - MOZ_ASSERT(type() == MIRType_Float32); return toFloat32(); } @@ -3354,7 +3400,9 @@ class MSimdBox const MSimdBox* box = ins->toSimdBox(); if (box->simdType() != simdType()) return false; - MOZ_ASSERT(box->initialHeap() == initialHeap()); + MOZ_ASSERT(box->templateObject() == templateObject()); + if (box->initialHeap() != initialHeap()) + return false; return true; } @@ -3362,6 +3410,7 @@ class MSimdBox return AliasSet::None(); } + void printOpcode(GenericPrinter& out) const override; bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; @@ -3407,6 +3456,8 @@ class MSimdUnbox AliasSet getAliasSet() const override { return AliasSet::None(); } + + void printOpcode(GenericPrinter& out) const override; }; // Creates a new derived type object. At runtime, this is just a call @@ -13657,12 +13708,12 @@ class MAsmJSHeapAccess MemoryBarrierBits barrierAfter_; public: - MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, unsigned numSimdElems = 0, - MemoryBarrierBits barrierBefore = MembarNobits, - MemoryBarrierBits barrierAfter = MembarNobits) + explicit MAsmJSHeapAccess(Scalar::Type accessType, unsigned numSimdElems = 0, + MemoryBarrierBits barrierBefore = MembarNobits, + MemoryBarrierBits barrierAfter = MembarNobits) : offset_(0), accessType_(accessType), - needsBoundsCheck_(needsBoundsCheck), + needsBoundsCheck_(true), numSimdElems_(numSimdElems), barrierBefore_(barrierBefore), barrierAfter_(barrierAfter) @@ -13695,10 +13746,10 @@ class MAsmJSLoadHeap public MAsmJSHeapAccess, public NoTypePolicy::Data { - MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, bool needsBoundsCheck, - unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after) + MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numSimdElems, + MemoryBarrierBits before, MemoryBarrierBits after) : MUnaryInstruction(ptr), - MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after) + MAsmJSHeapAccess(accessType, numSimdElems, before, after) { if (before|after) setGuard(); // Not removable @@ -13736,13 +13787,12 @@ class MAsmJSLoadHeap INSTRUCTION_HEADER(AsmJSLoadHeap) static MAsmJSLoadHeap* New(TempAllocator& alloc, Scalar::Type accessType, - MDefinition* ptr, bool needsBoundsCheck, - unsigned numSimdElems = 0, + MDefinition* ptr, unsigned numSimdElems = 0, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSLoadHeap(accessType, ptr, needsBoundsCheck, - numSimdElems, barrierBefore, barrierAfter); + return new(alloc) MAsmJSLoadHeap(accessType, ptr, numSimdElems, + barrierBefore, barrierAfter); } MDefinition* ptr() const { return getOperand(0); } @@ -13764,10 +13814,10 @@ class MAsmJSStoreHeap public MAsmJSHeapAccess, public NoTypePolicy::Data { - MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, bool needsBoundsCheck, + MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after) + MAsmJSHeapAccess(accessType, numSimdElems, before, after) { if (before|after) setGuard(); // Not removable @@ -13777,13 +13827,12 @@ class MAsmJSStoreHeap INSTRUCTION_HEADER(AsmJSStoreHeap) static MAsmJSStoreHeap* New(TempAllocator& alloc, Scalar::Type accessType, - MDefinition* ptr, MDefinition* v, bool needsBoundsCheck, - unsigned numSimdElems = 0, + MDefinition* ptr, MDefinition* v, unsigned numSimdElems = 0, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, needsBoundsCheck, - numSimdElems, barrierBefore, barrierAfter); + return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, numSimdElems, + barrierBefore, barrierAfter); } MDefinition* ptr() const { return getOperand(0); } @@ -13801,9 +13850,9 @@ class MAsmJSCompareExchangeHeap public NoTypePolicy::Data { MAsmJSCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv, - MDefinition* newv, bool needsBoundsCheck) + MDefinition* newv) : MTernaryInstruction(ptr, oldv, newv), - MAsmJSHeapAccess(accessType, needsBoundsCheck) + MAsmJSHeapAccess(accessType) { setGuard(); // Not removable setResultType(MIRType_Int32); @@ -13814,9 +13863,9 @@ class MAsmJSCompareExchangeHeap static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv, - MDefinition* newv, bool needsBoundsCheck) + MDefinition* newv) { - return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv, needsBoundsCheck); + return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv); } MDefinition* ptr() const { return getOperand(0); } @@ -13833,10 +13882,9 @@ class MAsmJSAtomicExchangeHeap public MAsmJSHeapAccess, public NoTypePolicy::Data { - MAsmJSAtomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value, - bool needsBoundsCheck) + MAsmJSAtomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value) : MBinaryInstruction(ptr, value), - MAsmJSHeapAccess(accessType, needsBoundsCheck) + MAsmJSHeapAccess(accessType) { setGuard(); // Not removable setResultType(MIRType_Int32); @@ -13846,10 +13894,9 @@ class MAsmJSAtomicExchangeHeap INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap) static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType, - MDefinition* ptr, MDefinition* value, - bool needsBoundsCheck) + MDefinition* ptr, MDefinition* value) { - return new(alloc) MAsmJSAtomicExchangeHeap(accessType, ptr, value, needsBoundsCheck); + return new(alloc) MAsmJSAtomicExchangeHeap(accessType, ptr, value); } MDefinition* ptr() const { return getOperand(0); } @@ -13867,10 +13914,9 @@ class MAsmJSAtomicBinopHeap { AtomicOp op_; - MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition* ptr, MDefinition* v, - bool needsBoundsCheck) + MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition* ptr, MDefinition* v) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(accessType, needsBoundsCheck), + MAsmJSHeapAccess(accessType), op_(op) { setGuard(); // Not removable @@ -13881,9 +13927,9 @@ class MAsmJSAtomicBinopHeap INSTRUCTION_HEADER(AsmJSAtomicBinopHeap) static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc, AtomicOp op, Scalar::Type accessType, - MDefinition* ptr, MDefinition* v, bool needsBoundsCheck) + MDefinition* ptr, MDefinition* v) { - return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v, needsBoundsCheck); + return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v); } AtomicOp operation() const { return op_; } diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index bd4edecb27..8656f25c4f 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -37,8 +37,16 @@ class MIRGenerator public: MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options, TempAllocator* alloc, MIRGraph* graph, - const CompileInfo* info, const OptimizationInfo* optimizationInfo, - bool usesSignalHandlersForAsmJSOOB = false); + const CompileInfo* info, const OptimizationInfo* optimizationInfo); + + void initUsesSignalHandlersForAsmJSOOB(bool init) { +#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) + usesSignalHandlersForAsmJSOOB_ = init; +#endif + } + void initMinAsmJSHeapLength(uint32_t init) { + minAsmJSHeapLength_ = init; + } TempAllocator& alloc() { return *alloc_; @@ -141,6 +149,9 @@ class MIRGenerator MOZ_ASSERT(compilingAsmJS()); maxAsmJSStackArgBytes_ = n; } + uint32_t minAsmJSHeapLength() const { + return minAsmJSHeapLength_; + } void setPerformsCall() { performsCall_ = true; } @@ -199,6 +210,7 @@ class MIRGenerator #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) bool usesSignalHandlersForAsmJSOOB_; #endif + uint32_t minAsmJSHeapLength_; void setForceAbort() { shouldForceAbort_ = true; diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 422854190a..34c39772f8 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -19,8 +19,7 @@ using mozilla::Swap; MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options, TempAllocator* alloc, MIRGraph* graph, const CompileInfo* info, - const OptimizationInfo* optimizationInfo, - bool usesSignalHandlersForAsmJSOOB) + const OptimizationInfo* optimizationInfo) : compartment(compartment), info_(info), optimizationInfo_(optimizationInfo), @@ -41,8 +40,9 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti instrumentedProfilingIsCached_(false), safeForMinorGC_(true), #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) - usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB), + usesSignalHandlersForAsmJSOOB_(false), #endif + minAsmJSHeapLength_(0), options(options), gs_(alloc) { } diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 43461cf4f7..8dd75dae1d 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -124,6 +124,20 @@ SpewRange(MDefinition* def) #endif } +static inline void +SpewTruncate(MDefinition* def, MDefinition::TruncateKind kind, bool shouldClone) +{ +#ifdef JS_JITSPEW + if (JitSpewEnabled(JitSpew_Range)) { + JitSpewHeader(JitSpew_Range); + Fprinter& out = JitSpewPrinter(); + out.printf("truncating "); + def->printName(out); + out.printf(" (kind: %s, clone: %d)\n", MDefinition::TruncateKindString(kind), shouldClone); + } +#endif +} + TempAllocator& RangeAnalysis::alloc() const { @@ -182,12 +196,12 @@ RangeAnalysis::addBetaNodes() MConstant* leftConst = left->maybeConstantValue(); MConstant* rightConst = right->maybeConstantValue(); - if (leftConst && leftConst->isNumber()) { - bound = leftConst->toNumber(); + if (leftConst && leftConst->isTypeRepresentableAsDouble()) { + bound = leftConst->numberToDouble(); val = right; jsop = ReverseCompareOp(jsop); - } else if (rightConst && rightConst->isNumber()) { - bound = rightConst->toNumber(); + } else if (rightConst && rightConst->isTypeRepresentableAsDouble()) { + bound = rightConst->numberToDouble(); val = left; } else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) { MDefinition* smaller = nullptr; @@ -1275,8 +1289,8 @@ MBeta::computeRange(TempAllocator& alloc) void MConstant::computeRange(TempAllocator& alloc) { - if (isNumber()) { - double d = toNumber(); + if (isTypeRepresentableAsDouble()) { + double d = numberToDouble(); setRange(Range::NewDoubleSingletonRange(alloc, d)); } else if (type() == MIRType_Boolean) { bool b = toBoolean(); @@ -2383,7 +2397,7 @@ MDefinition::truncate() bool MConstant::needTruncation(TruncateKind kind) { - return value_.isDouble(); + return IsFloatingPointType(type()); } void @@ -2392,8 +2406,9 @@ MConstant::truncate() MOZ_ASSERT(needTruncation(Truncate)); // Truncate the double to int, since all uses truncates it. - int32_t res = ToInt32(value_.toDouble()); - value_.setInt32(res); + int32_t res = ToInt32(numberToDouble()); + payload_.asBits = 0; + payload_.i32 = res; setResultType(MIRType_Int32); if (range()) range()->setInt32(res, res); @@ -3033,6 +3048,8 @@ RangeAnalysis::truncate() if (!iter->needTruncation(kind)) continue; + SpewTruncate(*iter, kind, shouldClone); + // If needed, clone the current instruction for keeping it for the // bailout path. This give us the ability to truncate instructions // even after the removal of branches. @@ -3056,6 +3073,9 @@ RangeAnalysis::truncate() // Truncate this phi if possible. if (shouldClone || !iter->needTruncation(kind)) continue; + + SpewTruncate(*iter, kind, shouldClone); + iter->truncate(); // Delay updates of inputs/outputs to avoid creating node which diff --git a/js/src/jit/mips-shared/Architecture-mips-shared.h b/js/src/jit/mips-shared/Architecture-mips-shared.h index 304e0c7628..c31f6544f3 100644 --- a/js/src/jit/mips-shared/Architecture-mips-shared.h +++ b/js/src/jit/mips-shared/Architecture-mips-shared.h @@ -31,6 +31,9 @@ namespace js { namespace jit { +// How far forward/back can a jump go? Provide a generous buffer for thunks. +static const uint32_t JumpImmediateRange = 100 * 1024; + class Registers { public: diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.cpp b/js/src/jit/mips-shared/Assembler-mips-shared.cpp index 4720caca20..12460e1962 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp @@ -221,6 +221,12 @@ BOffImm16::BOffImm16(InstImm inst) { } +Instruction* +BOffImm16::getDest(Instruction* src) const +{ + return &src[(((int32_t)data << 16) >> 16) + 1]; +} + bool AssemblerMIPSShared::oom() const { @@ -1329,7 +1335,21 @@ AssemblerMIPSShared::bind(Label* label, BufferOffset boff) void AssemblerMIPSShared::bindLater(Label* label, wasm::JumpTarget target) { - MOZ_CRASH("NYI"); + if (label->used()) { + int32_t next; + + BufferOffset b(label); + do { + Instruction* inst = editSrc(b); + + append(target, b.getOffset()); + next = inst[1].encode(); + inst[1].makeNop(); + + b = BufferOffset(next); + } while (next != LabelBase::INVALID_OFFSET); + } + label->reset(); } void diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h index 1eb997e465..c208475312 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.h +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -486,7 +486,7 @@ class BOffImm16 bool isInvalid() { return data == INVALID; } - Instruction* getDest(Instruction* src); + Instruction* getDest(Instruction* src) const; BOffImm16(InstImm inst); }; diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp index e2806d1381..dbc9840479 100644 --- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -1662,7 +1662,7 @@ CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) ToFloatRegister(out)); } else { if (mir->isAtomicAccess()) - masm.ma_b(masm.asmOnOutOfBoundsLabel()); + masm.ma_b(wasm::JumpTarget::OutOfBounds); else masm.move32(Imm32(0), ToRegister(out)); } @@ -1750,7 +1750,7 @@ CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) masm.bind(&outOfRange); // Offset is out of range. if (mir->isAtomicAccess()) - masm.ma_b(masm.asmOnOutOfBoundsLabel()); + masm.ma_b(wasm::JumpTarget::OutOfBounds); masm.bind(&done); memoryBarrier(mir->barrierAfter()); @@ -1777,7 +1777,7 @@ CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap if (mir->needsBoundsCheck()) { BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); maybeCmpOffset = bo.getOffset(); - masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual); + masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual); } masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt, srcAddr, oldval, newval, InvalidReg, @@ -1805,7 +1805,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* if (mir->needsBoundsCheck()) { BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); maybeCmpOffset = bo.getOffset(); - masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual); + masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual); } masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt, srcAddr, value, InvalidReg, valueTemp, @@ -1836,7 +1836,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) if (mir->needsBoundsCheck()) { BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); maybeCmpOffset = bo.getOffset(); - masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual); + masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual); } if (value->isConstant()) atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt, @@ -1874,7 +1874,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHea if (mir->needsBoundsCheck()) { BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); maybeCmpOffset = bo.getOffset(); - masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual); + masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual); } if (value->isConstant()) diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp index 151b854085..4a7f665939 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp @@ -441,12 +441,37 @@ MacroAssemblerMIPSShared::ma_b(Register lhs, ImmPtr imm, Label* l, Condition c, asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind); } +template +void +MacroAssemblerMIPSShared::ma_b(Register lhs, T rhs, wasm::JumpTarget target, Condition c, + JumpKind jumpKind) +{ + Label label; + ma_b(lhs, rhs, &label, c, jumpKind); + bindLater(&label, target); +} + +template void MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, + wasm::JumpTarget target, Condition c, + JumpKind jumpKind); +template void MacroAssemblerMIPSShared::ma_b(Register lhs, ImmTag rhs, + wasm::JumpTarget target, Condition c, + JumpKind jumpKind); + void MacroAssemblerMIPSShared::ma_b(Label* label, JumpKind jumpKind) { asMasm().branchWithCode(getBranchCode(BranchIsJump), label, jumpKind); } +void +MacroAssemblerMIPSShared::ma_b(wasm::JumpTarget target, JumpKind jumpKind) +{ + Label label; + asMasm().branchWithCode(getBranchCode(BranchIsJump), &label, jumpKind); + bindLater(&label, target); +} + Assembler::Condition MacroAssemblerMIPSShared::ma_cmp(Register scratch, Register lhs, Register rhs, Condition c) { @@ -1143,35 +1168,41 @@ MacroAssembler::call(Label* label) CodeOffset MacroAssembler::callWithPatch() { - addLongJump(nextOffset()); - ma_liPatchable(ScratchRegister, ImmWord(0)); - return call(ScratchRegister); + as_bal(BOffImm16(0)); + as_nop(); + return CodeOffset(currentOffset()); } void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) +{ + BufferOffset call(callerOffset - 2 * sizeof(uint32_t)); + InstImm* bal = (InstImm*)editSrc(call); + bal->setBOffImm16(BufferOffset(calleeOffset).diffB(call)); +} + +CodeOffset +MacroAssembler::thunkWithPatch() +{ + addLongJump(nextOffset()); + ma_liPatchable(ScratchRegister, ImmWord(0)); + as_jr(ScratchRegister); + as_nop(); + return CodeOffset(currentOffset()); +} + +void +MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset) { BufferOffset li(callerOffset - Assembler::PatchWrite_NearCallSize()); Assembler::PatchInstructionImmediate((uint8_t*)editSrc(li), PatchedImmPtr((const void*)calleeOffset)); } -CodeOffset -MacroAssembler::thunkWithPatch() -{ - MOZ_CRASH("NYI"); -} - -void -MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset) -{ - MOZ_CRASH("NYI"); -} - void MacroAssembler::repatchThunk(uint8_t* code, uint32_t callerOffset, uint32_t calleeOffset) { - MOZ_CRASH("NYI"); + Assembler::PatchInstructionImmediate(code, PatchedImmPtr((const void*)calleeOffset)); } void diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h index 8839d7bb05..c763391044 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h @@ -140,8 +140,12 @@ class MacroAssemblerMIPSShared : public Assembler ma_li(ScratchRegister, imm); ma_b(lhs, ScratchRegister, l, c, jumpKind); } + template + void ma_b(Register lhs, T rhs, wasm::JumpTarget target, Condition c, + JumpKind jumpKind = LongJump); void ma_b(Label* l, JumpKind jumpKind = LongJump); + void ma_b(wasm::JumpTarget target, JumpKind jumpKind = LongJump); // fp instructions void ma_lis(FloatRegister dest, float value); diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index 8d6f69928b..177e7efe0f 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -1392,6 +1392,14 @@ MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand& va branchTestMagic(cond, value.typeReg(), label); } +void +MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand& value, + wasm::JumpTarget target) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_b(value.typeReg(), ImmTag(JSVAL_TAG_MAGIC), target, cond); +} + void MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, Register tag, Label* label) { diff --git a/js/src/jit/mips32/MacroAssembler-mips32.h b/js/src/jit/mips32/MacroAssembler-mips32.h index fa61146e41..9116002eba 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.h +++ b/js/src/jit/mips32/MacroAssembler-mips32.h @@ -296,6 +296,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS branch(code); } + void jump(wasm::JumpTarget target) { + ma_b(target); + } + void negl(Register reg) { ma_negu(reg, reg); } @@ -417,6 +421,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS void branchTestNumber(Condition cond, Register tag, Label* label); void branchTestMagic(Condition cond, const ValueOperand& value, Label* label); + void branchTestMagic(Condition cond, const ValueOperand& value, wasm::JumpTarget target); void branchTestMagic(Condition cond, Register tag, Label* label); void branchTestMagic(Condition cond, const Address& address, Label* label); void branchTestMagic(Condition cond, const BaseIndex& src, Label* label); diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp index eac29d430a..bc63dc5791 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -1531,6 +1531,15 @@ MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, const ValueOperand& branchTestMagic(cond, SecondScratchReg, label); } +void +MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, const ValueOperand& value, + wasm::JumpTarget target) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + splitTag(value, SecondScratchReg); + ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), target, cond); +} + void MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, Register tag, Label* label) { diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h index 6789ffede4..a2448565bd 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -319,6 +319,10 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 branch(code); } + void jump(wasm::JumpTarget target) { + ma_b(target); + } + void splitTag(Register src, Register dest) { ma_dsrl(dest, src, Imm32(JSVAL_TAG_SHIFT)); } @@ -458,6 +462,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 void branchTestNumber(Condition cond, Register tag, Label* label); void branchTestMagic(Condition cond, const ValueOperand& value, Label* label); + void branchTestMagic(Condition cond, const ValueOperand& value, wasm::JumpTarget target); void branchTestMagic(Condition cond, Register tag, Label* label); void branchTestMagic(Condition cond, const Address& address, Label* label); void branchTestMagic(Condition cond, const BaseIndex& src, Label* label); diff --git a/js/src/jit/shared/CodeGenerator-shared-inl.h b/js/src/jit/shared/CodeGenerator-shared-inl.h index 170875fbd0..2283653442 100644 --- a/js/src/jit/shared/CodeGenerator-shared-inl.h +++ b/js/src/jit/shared/CodeGenerator-shared-inl.h @@ -28,7 +28,7 @@ ToInt32(const LAllocation* a) static inline double ToDouble(const LAllocation* a) { - return a->toConstant()->toNumber(); + return a->toConstant()->numberToDouble(); } static inline Register diff --git a/js/src/jsapi-tests/testJitDCEinGVN.cpp b/js/src/jsapi-tests/testJitDCEinGVN.cpp index 9d6b13ab44..9d3a949210 100644 --- a/js/src/jsapi-tests/testJitDCEinGVN.cpp +++ b/js/src/jsapi-tests/testJitDCEinGVN.cpp @@ -136,7 +136,7 @@ BEGIN_TEST(testJitDCEinGVN_phi) // c1 should be deleted. for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { - CHECK(!ins->isConstant() || (ins->toConstant()->toNumber() != 1.0)); + CHECK(!ins->isConstant() || (ins->toConstant()->numberToDouble() != 1.0)); } return true; } diff --git a/js/src/jsapi-tests/testJitFoldsTo.cpp b/js/src/jsapi-tests/testJitFoldsTo.cpp index ba5278c793..4b5e2c0f1a 100644 --- a/js/src/jsapi-tests/testJitFoldsTo.cpp +++ b/js/src/jsapi-tests/testJitFoldsTo.cpp @@ -42,7 +42,7 @@ BEGIN_TEST(testJitFoldsTo_DivReciprocal) CHECK(op->isMul()); CHECK(op->getOperand(0) == left); CHECK(op->getOperand(1)->isConstant()); - CHECK(op->getOperand(1)->toConstant()->toNumber() == 0.25); + CHECK(op->getOperand(1)->toConstant()->numberToDouble() == 0.25); return true; } END_TEST(testJitFoldsTo_DivReciprocal) @@ -229,7 +229,7 @@ BEGIN_TEST(testJitFoldsTo_UnsignedDiv) // Test that the div got folded to 0. MConstant* op = ret->getOperand(0)->toConstant(); - CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 0.0)); + CHECK(mozilla::NumbersAreIdentical(op->numberToDouble(), 0.0)); return true; } END_TEST(testJitFoldsTo_UnsignedDiv) @@ -254,7 +254,7 @@ BEGIN_TEST(testJitFoldsTo_UnsignedMod) // Test that the mod got folded to 1. MConstant* op = ret->getOperand(0)->toConstant(); - CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 1.0)); + CHECK(mozilla::NumbersAreIdentical(op->numberToDouble(), 1.0)); return true; } END_TEST(testJitFoldsTo_UnsignedMod)