From 2e9d0efbca8f37446a20453f4086f8e8606e71a4 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Mon, 18 Sep 2023 11:20:40 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1238935 - r=jonco (262e919be3) - Bug 1246658 part 3 - Rewrite MConstant's public interface to not expose js::Value directly. r=luke (44f1c7ac03) - Bug 1246331: Make unary decoding expect the real type, not the parent type; r=sunfish (fd159749d1) - Bug 1244571 - BaldrMonkey: Remove obsolete opcodes. r=luke (670ae82404) - Bug 1248488 - Odin: switch more integer immediates to variable-length u32s (r=sunfish) (c7e118cb58) - Bug 1248860 - Odin: remove Expr::InterruptCheck (r=bbouvier) (c9824a364d) - Bug 1248860 - Odin: move CompileArgs to ModuleGeneratorData (r=bbouvier) (5292a52fa9) - Bug 1248860 - Baldr: change function bodies to an expr list from expr (r=bbouvier) (f8fd39d1b0) - Bug 1248625 - Baldr: make string labels more regular (r=sunfish) (d14c7e6640) - Bug 1248625 - Change 'return false' to 'return nullptr' (r=me) (ae1f98f059) - Bug 1244889 - Remove toMIRType() and simdType() methods. r=bbouvier (0c281e9bc8) - Bug 1244889 - Rename Type:isVarType() out of the way. r=luke (b7e0812cab) - Bug 1244889 - Define a canonical subset of Type::Which. r=luke (4cb69fd6ec) - Bug 1244889 - Reduce dependency on wasm::ValType. r=luke (53f41aad5d) - Bug 1244889 - Reduce dependency on wasm::ExprType. r=luke (7353ebd8ed) - Bug 1244889 - Fix CheckCoercionArg and CheckReturnType. r=luke (4824150189) - Bug 1244889 - Remove SimdTypeToLength(). r=bbouvier (a2399e9e72) - Bug 1243647 - Fix -Wimplicit-fallthrough warnings by breaking at unintentional switch fallthroughs in AsmJS.cpp. r=bbouvier (cb851f375d) - Bug 1244889 - Amend wasm opcodes for unsigned I32x4 operations. r=bbouvier (ca05c907c8) - fix NetBSD build (again) (08b0f8f447) - Bug 1239007 - Replace nsIntRect by strongly-typed rects in a couple of nsCocoaUtils functions. r=mstange (501f73b896) - Bug 1239855 - Fix for widget positioning/sizing on hidpi displays under Gtk. r=emk (f37be964c0) - bug 1225970 dispatch an event to release the widget after draw r=roc (61fe380407) - Bug 1232219 (part 2) - Fix -Wunused warnings in widget/gtk/. r=karlt. (3415ffc27a) --- accessible/mac/mozTextAccessible.mm | 8 +- dom/events/moz.build | 4 + js/src/asmjs/AsmJS.cpp | 918 ++++++++---------- js/src/asmjs/Wasm.cpp | 151 ++- js/src/asmjs/WasmBinary.h | 477 +++++---- js/src/asmjs/WasmGenerator.cpp | 4 +- js/src/asmjs/WasmGenerator.h | 8 +- js/src/asmjs/WasmIonCompile.cpp | 255 +++-- js/src/asmjs/WasmIonCompile.h | 13 +- js/src/asmjs/WasmText.cpp | 137 ++- js/src/builtin/SIMD.h | 32 +- js/src/jit-test/tests/wasm/basic-float.js | 1 + js/src/jit-test/tests/wasm/basic.js | 2 + js/src/jit-test/tests/wasm/binary.js | 99 +- js/src/jit/AlignmentMaskAnalysis.cpp | 4 +- js/src/jit/CodeGenerator.cpp | 18 +- js/src/jit/EffectiveAddressAnalysis.cpp | 13 +- js/src/jit/IonAnalysis.cpp | 9 +- js/src/jit/IonBuilder.cpp | 31 +- js/src/jit/LIR.h | 14 +- js/src/jit/LoopUnroller.cpp | 2 +- js/src/jit/Lowering.cpp | 26 +- js/src/jit/MCallOptimize.cpp | 35 +- js/src/jit/MIR.cpp | 262 +++-- js/src/jit/MIR.h | 122 +-- js/src/jit/MIRGraph.cpp | 4 +- js/src/jit/RangeAnalysis.cpp | 26 +- js/src/jit/ScalarReplacement.cpp | 12 +- js/src/jit/arm/Lowering-arm.cpp | 14 +- js/src/jit/shared/CodeGenerator-shared.cpp | 6 +- js/src/jit/shared/Lowering-shared-inl.h | 21 +- js/src/jit/shared/Lowering-shared.cpp | 15 +- js/src/jit/x64/CodeGenerator-x64.cpp | 4 +- js/src/jit/x64/Lowering-x64.cpp | 2 +- js/src/jit/x86-shared/Lowering-x86-shared.cpp | 8 +- js/src/jit/x86/Lowering-x86.cpp | 2 +- js/src/jsapi-tests/testJitDCEinGVN.cpp | 2 +- js/src/jsapi-tests/testJitFoldsTo.cpp | 6 +- widget/cocoa/nsCocoaUtils.h | 12 +- widget/cocoa/nsCocoaUtils.mm | 11 +- widget/cocoa/nsCocoaWindow.mm | 14 +- widget/cocoa/nsScreenCocoa.mm | 4 +- widget/cocoa/nsScreenManagerCocoa.mm | 9 +- widget/gtk/gtk3drawing.c | 22 +- widget/gtk/mozcontainer.c | 5 - widget/gtk/nsWindow.cpp | 38 +- widget/gtk/nsWindow.h | 5 + widget/gtkxtbin/gtk2xtbin.c | 2 - 48 files changed, 1372 insertions(+), 1517 deletions(-) diff --git a/accessible/mac/mozTextAccessible.mm b/accessible/mac/mozTextAccessible.mm index 3f413341fd..0909cd5129 100644 --- a/accessible/mac/mozTextAccessible.mm +++ b/accessible/mac/mozTextAccessible.mm @@ -218,11 +218,13 @@ ToNSString(id aValue) int32_t start = range.location; int32_t end = start + range.length; - nsIntRect bounds; + DesktopIntRect bounds; if (textAcc) { - bounds = textAcc->TextBounds(start, end); + bounds = + DesktopIntRect::FromUnknownRect(textAcc->TextBounds(start, end)); } else if (proxy) { - bounds = proxy->TextBounds(start, end); + bounds = + DesktopIntRect::FromUnknownRect(proxy->TextBounds(start, end)); } return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)]; diff --git a/dom/events/moz.build b/dom/events/moz.build index d7d5decdf7..4803bc8f41 100644 --- a/dom/events/moz.build +++ b/dom/events/moz.build @@ -147,3 +147,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': LOCAL_INCLUDES += [ '/dom/wifi', ] + +if CONFIG['MOZ_X11']: + CFLAGS += CONFIG['XCFLAGS'] + CXXFLAGS += CONFIG['XCFLAGS'] diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index ecc0b84a3c..24295a8d04 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -1017,27 +1017,6 @@ class NumLit return false; } - ValType type() const { - switch (which_) { - case NumLit::Fixnum: - case NumLit::NegativeInt: - case NumLit::BigUnsigned: - return ValType::I32; - case NumLit::Double: - return ValType::F64; - case NumLit::Float: - return ValType::F32; - case NumLit::Int32x4: - return ValType::I32x4; - case NumLit::Float32x4: - return ValType::F32x4; - case NumLit::Bool32x4: - return ValType::B32x4; - case NumLit::OutOfRangeInt:; - } - MOZ_CRASH("bad literal"); - } - Val value() const { switch (which_) { case NumLit::Fixnum: @@ -1060,7 +1039,20 @@ class NumLit } }; -// Respresents the type of a general asm.js expression. +// Represents the type of a general asm.js expression. +// +// A canonical subset of types representing the coercion targets: Int, Float, +// Double, and the SIMD types. This is almost equivalent to wasm::ValType, +// except the integer SIMD types have signed/unsigned variants. +// +// Void is also part of the canonical subset which then maps to wasm::ExprType. +// +// Note that while the canonical subset distinguishes signed and unsigned SIMD +// types, it only uses |Int| to represent signed and unsigned 32-bit integers. +// This is because the scalar coersions x|0 and x>>>0 work with any kind of +// integer input, while the SIMD check functions throw a TypeError if the passed +// type doesn't match. +// class Type { public: @@ -1127,6 +1119,13 @@ class Type MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type"); } + // Map an already canonicalized Type to the return type of a function call. + static Type ret(Type t) { + MOZ_ASSERT(t.isCanonical()); + // The 32-bit external type is Signed, not Int. + return t.isInt() ? Signed: t; + } + static Type lit(const NumLit& lit) { MOZ_ASSERT(lit.valid()); Which which = Type::Which(lit.which()); @@ -1136,6 +1135,42 @@ class Type return t; } + // Map |t| to one of the canonical vartype representations of a + // wasm::ExprType. + static Type canonicalize(Type t) { + switch(t.which()) { + case Fixnum: + case Signed: + case Unsigned: + case Int: + return Int; + + case Float: + return Float; + + case DoubleLit: + case Double: + return Double; + + case Void: + return Void; + + case Int32x4: + case Float32x4: + case Bool32x4: + return t; + + case MaybeDouble: + case MaybeFloat: + case Floatish: + case Intish: + // These types need some kind of coercion, they can't be mapped + // to an ExprType. + break; + } + MOZ_CRASH("Invalid vartype"); + } + Which which() const { return which_; } bool operator==(Type rhs) const { return which_ == rhs.which_; } @@ -1162,20 +1197,6 @@ class Type MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected rhs type"); } - bool operator<=(ValType rhs) const { - switch (rhs) { - case ValType::I32: return isInt(); - case ValType::I64: MOZ_CRASH("no int64 in asm.js"); - case ValType::F32: return isFloat(); - case ValType::F64: return isDouble(); - case ValType::I32x4: return isInt32x4(); - case ValType::F32x4: return isFloat32x4(); - case ValType::B32x4: return isBool32x4(); - case ValType::Limit: break; - } - MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected rhs type"); - } - bool isFixnum() const { return which_ == Fixnum; } @@ -1244,79 +1265,52 @@ class Type return isInt32x4() || isFloat32x4() || isBool32x4(); } - bool isVarType() const { + // Check if this is one of the valid types for a function argument. + bool isArgType() const { return isInt() || isFloat() || isDouble() || isSimd(); } - ValType checkedValueType() const { - MOZ_ASSERT(isVarType()); - if (isInt()) - return ValType::I32; - else if (isFloat()) - return ValType::F32; - else if (isDouble()) - return ValType::F64; - else if (isInt32x4()) - return ValType::I32x4; - else if (isBool32x4()) - return ValType::B32x4; - MOZ_ASSERT(isFloat32x4()); - return ValType::F32x4; + // Check if this is one of the valid types for a function return value. + bool isReturnType() const { + return isSigned() || isFloat() || isDouble() || isSimd() || isVoid(); } - MIRType toMIRType() const { - switch (which_) { - case Double: - case DoubleLit: - case MaybeDouble: - return MIRType_Double; - case Float: - case Floatish: - case MaybeFloat: - return MIRType_Float32; - case Fixnum: + // Check if this is one of the canonical vartype representations of a + // wasm::ExprType. See Type::canonicalize(). + bool isCanonical() const { + switch (which()) { case Int: - case Signed: - case Unsigned: - case Intish: - return MIRType_Int32; - case Int32x4: - return MIRType_Int32x4; - case Float32x4: - return MIRType_Float32x4; - case Bool32x4: - return MIRType_Bool32x4; + case Float: + case Double: case Void: - return MIRType_None; + return true; + default: + return isSimd(); } - MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type"); } - SimdType simdType() const { - MOZ_ASSERT(isSimd()); - switch (which_) { - case Int32x4: - return SimdType::Int32x4; - case Float32x4: - return SimdType::Float32x4; - case Bool32x4: - return SimdType::Bool32x4; - // Scalar types - case Double: - case DoubleLit: - case MaybeDouble: - case Float: - case MaybeFloat: - case Floatish: - case Fixnum: - case Int: - case Signed: - case Unsigned: - case Intish: - case Void: - break; + // Check if this is a canonical representation of a wasm::ValType. + bool isCanonicalValType() const { + return !isVoid() && isCanonical(); + } + + // Convert this canonical type to a wasm::ExprType. + ExprType canonicalToExprType() const { + switch (which()) { + case Int: return ExprType::I32; + case Float: return ExprType::F32; + case Double: return ExprType::F64; + case Void: return ExprType::Void; + case Int32x4: return ExprType::I32x4; + case Float32x4: return ExprType::F32x4; + case Bool32x4: return ExprType::B32x4; + default: MOZ_CRASH("Need canonical type"); } - MOZ_CRASH("not a SIMD Type"); + } + + // Convert this canonical type to a wasm::ValType. + ValType canonicalToValType() const { + return NonVoidToValType(canonicalToExprType()); } const char* toChars() const { @@ -1783,7 +1777,7 @@ class MOZ_STACK_CLASS ModuleValidator } #undef ADDSTDLIBSIMDOPNAME - UniqueModuleGeneratorData genData = MakeUnique(ModuleKind::AsmJS); + UniqueModuleGeneratorData genData = MakeUnique(cx_, ModuleKind::AsmJS); if (!genData || !genData->sigs.resize(MaxSigs) || !genData->funcSigs.resize(MaxFuncs) || @@ -1833,7 +1827,9 @@ class MOZ_STACK_CLASS ModuleValidator } bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) { uint32_t index; - if (!mg_.allocateGlobalVar(lit.type(), isConst, &index)) + Type litType = Type::lit(lit); + Type canonicalType = Type::canonicalize(litType); + if (!mg_.allocateGlobalVar(canonicalType.canonicalToValType(), isConst, &index)) return false; Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable; @@ -1841,7 +1837,7 @@ class MOZ_STACK_CLASS ModuleValidator if (!global) return false; global->u.varOrConst.index_ = index; - global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which(); + global->u.varOrConst.type_ = (isConst ? litType : canonicalType).which(); if (isConst) global->u.varOrConst.literalValue_ = lit; if (!globalMap_.putNew(var, global)) @@ -2285,20 +2281,8 @@ IsCallToGlobal(ModuleValidator& m, ParseNode* pn, const ModuleValidator::Global* return !!*global; } -static ValType -ToValType(SimdType type) -{ - switch (type) { - case SimdType::Int32x4: return ValType::I32x4; - case SimdType::Float32x4: return ValType::F32x4; - case SimdType::Bool32x4: return ValType::B32x4; - default: break; - } - MOZ_CRASH("unexpected SIMD type"); -} - static bool -IsCoercionCall(ModuleValidator& m, ParseNode* pn, ValType* coerceTo, ParseNode** coercedExpr) +IsCoercionCall(ModuleValidator& m, ParseNode* pn, Type* coerceTo, ParseNode** coercedExpr) { const ModuleValidator::Global* global; if (!IsCallToGlobal(m, pn, &global)) @@ -2311,12 +2295,12 @@ IsCoercionCall(ModuleValidator& m, ParseNode* pn, ValType* coerceTo, ParseNode** *coercedExpr = CallArgList(pn); if (global->isMathFunction() && global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) { - *coerceTo = ValType::F32; + *coerceTo = Type::Float; return true; } if (global->isSimdOperation() && global->simdOperation() == SimdOperation::Fn_check) { - *coerceTo = ToValType(global->simdOperationType()); + *coerceTo = global->simdOperationType(); return true; } @@ -2327,29 +2311,15 @@ static bool IsFloatLiteral(ModuleValidator& m, ParseNode* pn) { ParseNode* coercedExpr; - ValType coerceTo; + Type coerceTo; if (!IsCoercionCall(m, pn, &coerceTo, &coercedExpr)) return false; // Don't fold into || to avoid clang/memcheck bug (bug 1077031). - if (coerceTo != ValType::F32) + if (!coerceTo.isFloat()) return false; return IsNumericNonFloatLiteral(coercedExpr); } -static unsigned -SimdTypeToLength(SimdType type) -{ - switch (type) { - case SimdType::Int32x4: - case SimdType::Float32x4: - case SimdType::Bool32x4: - return 4; - default: - break; - } - MOZ_CRASH("unexpected SIMD type"); -} - static bool IsSimdTuple(ModuleValidator& m, ParseNode* pn, SimdType* type) { @@ -2360,7 +2330,7 @@ IsSimdTuple(ModuleValidator& m, ParseNode* pn, SimdType* type) if (!global->isSimdCtor()) return false; - if (CallArgListLength(pn) != SimdTypeToLength(global->simdCtorType())) + if (CallArgListLength(pn) != GetSimdLanes(global->simdCtorType())) return false; *type = global->simdCtorType(); @@ -2384,7 +2354,7 @@ IsSimdLiteral(ModuleValidator& m, ParseNode* pn) return false; ParseNode* arg = CallArgList(pn); - unsigned length = SimdTypeToLength(type); + unsigned length = GetSimdLanes(type); for (unsigned i = 0; i < length; i++) { if (!IsNumericLiteral(m, arg)) return false; @@ -2449,7 +2419,7 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn) ParseNode* arg = CallArgList(pn); switch (type) { case SimdType::Int32x4: { - MOZ_ASSERT(SimdTypeToLength(type) == 4); + MOZ_ASSERT(GetSimdLanes(type) == 4); int32_t val[4]; for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) { uint32_t u32; @@ -2460,7 +2430,7 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn) return NumLit(NumLit::Int32x4, SimdConstant::CreateX4(val)); } case SimdType::Float32x4: { - MOZ_ASSERT(SimdTypeToLength(type) == 4); + MOZ_ASSERT(GetSimdLanes(type) == 4); float val[4]; for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) val[i] = float(ExtractNumericNonFloatValue(arg)); @@ -2468,7 +2438,7 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn) return NumLit(NumLit::Float32x4, SimdConstant::CreateX4(val)); } case SimdType::Bool32x4: { - MOZ_ASSERT(SimdTypeToLength(type) == 4); + MOZ_ASSERT(GetSimdLanes(type) == 4); int32_t val[4]; for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) { uint32_t u32; @@ -2580,14 +2550,32 @@ static inline Expr SimdToExpr(SimdType type, SimdOperation op) { switch (type) { + case SimdType::Uint32x4: + // Handle the special unsigned opcodes, then fall through to Int32x4. + switch(op) { + case SimdOperation::Fn_shiftRightByScalar: return Expr::I32x4shiftRightByScalarU; + case SimdOperation::Fn_lessThan: return Expr::I32x4lessThanU; + case SimdOperation::Fn_lessThanOrEqual: return Expr::I32x4lessThanOrEqualU; + case SimdOperation::Fn_greaterThan: return Expr::I32x4greaterThanU; + case SimdOperation::Fn_greaterThanOrEqual: return Expr::I32x4greaterThanOrEqualU; + case SimdOperation::Fn_fromFloat32x4: return Expr::I32x4fromFloat32x4U; + case SimdOperation::Fn_fromInt32x4Bits: return Expr::Id; + default: break; + } + MOZ_FALLTHROUGH; case SimdType::Int32x4: { + // Bitcasts Uint32x4 <--> Int32x4 become noops. + if (op == SimdOperation::Fn_fromUint32x4Bits) return Expr::Id; ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32CASE) + break; } case SimdType::Float32x4: { ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32CASE) + break; } case SimdType::Bool32x4: { ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32CASE) + break; } default: break; } @@ -2608,9 +2596,11 @@ class MOZ_STACK_CLASS FunctionValidator public: struct Local { - ValType type; + Type type; unsigned slot; - Local(ValType t, unsigned slot) : type(t), slot(slot) {} + Local(Type t, unsigned slot) : type(t), slot(slot) { + MOZ_ASSERT(type.isCanonicalValType()); + } }; private: @@ -2675,12 +2665,12 @@ class MOZ_STACK_CLASS FunctionValidator /***************************************************** Local scope setup */ - bool addLocal(ParseNode* pn, PropertyName* name, ValType type) { + bool addLocal(ParseNode* pn, PropertyName* name, Type type) { LocalMap::AddPtr p = locals_.lookupForAdd(name); if (p) return failName(pn, "duplicate local name '%s' not allowed", name); return locals_.add(p, name, Local(type, locals_.count())) && - fg_.addLocal(type); + fg_.addLocal(type.canonicalToValType()); } /****************************** For consistency of returns in a function */ @@ -2733,106 +2723,51 @@ class MOZ_STACK_CLASS FunctionValidator size_t numLocals() const { return locals_.count(); } - /************************************************* Packing interface */ + /**************************************************** Encoding interface */ Encoder& encoder() { return *encoder_; } - bool writeCall(ParseNode* pn, Expr op) { - return writeOp(op) && - fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin)); + MOZ_WARN_UNUSED_RESULT bool writeInt32Lit(int32_t i32) { + return encoder().writeExpr(Expr::I32Const) && + encoder().writeVarU32(i32); } - bool tempCall(ParseNode* pn, size_t* offset) { - return tempOp(offset) && - fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin)); - } - - MOZ_WARN_UNUSED_RESULT - bool writeOp(Expr op) { - return encoder().writeExpr(op); - } - MOZ_WARN_UNUSED_RESULT - bool writeExprType(ExprType type) { - return encoder().writeExprType(type); - } - MOZ_WARN_UNUSED_RESULT - bool writeSimdOp(SimdType simdType, SimdOperation op) { - return writeOp(SimdToExpr(simdType, op)); - } - - MOZ_WARN_UNUSED_RESULT - bool writeU8(uint8_t u8) { - return encoder().writeU8(u8); - } - MOZ_WARN_UNUSED_RESULT - bool writeVarU32(uint32_t u32) { - return encoder().writeVarU32(u32); - } - MOZ_WARN_UNUSED_RESULT - bool writeU32(uint32_t u32) { - return encoder().writeU32(u32); - } - MOZ_WARN_UNUSED_RESULT - bool writeI32(int32_t i32) { - return encoder().writeI32(i32); - } - MOZ_WARN_UNUSED_RESULT - bool writeInt32Lit(int32_t i32) { - return writeOp(Expr::I32Const) && encoder().writeVarU32(i32); - } - - MOZ_WARN_UNUSED_RESULT - bool writeLit(NumLit lit) { + MOZ_WARN_UNUSED_RESULT bool writeLit(NumLit lit) { switch (lit.which()) { case NumLit::Fixnum: case NumLit::NegativeInt: case NumLit::BigUnsigned: return writeInt32Lit(lit.toInt32()); case NumLit::Float: - return writeOp(Expr::F32Const) && encoder().writeF32(lit.toFloat()); + return encoder().writeExpr(Expr::F32Const) && + encoder().writeFixedF32(lit.toFloat()); case NumLit::Double: - return writeOp(Expr::F64Const) && encoder().writeF64(lit.toDouble()); + return encoder().writeExpr(Expr::F64Const) && + encoder().writeFixedF64(lit.toDouble()); case NumLit::Int32x4: - return writeOp(Expr::I32x4Const) && encoder().writeI32X4(lit.simdValue().asInt32x4()); + return encoder().writeExpr(Expr::I32x4Const) && + encoder().writeFixedI32x4(lit.simdValue().asInt32x4()); case NumLit::Float32x4: - return writeOp(Expr::F32x4Const) && encoder().writeF32X4(lit.simdValue().asFloat32x4()); + return encoder().writeExpr(Expr::F32x4Const) && + encoder().writeFixedF32x4(lit.simdValue().asFloat32x4()); case NumLit::Bool32x4: // Boolean vectors use the Int32x4 memory representation. - return writeOp(Expr::B32x4Const) && encoder().writeI32X4(lit.simdValue().asInt32x4()); + return encoder().writeExpr(Expr::B32x4Const) && + encoder().writeFixedI32x4(lit.simdValue().asInt32x4()); case NumLit::OutOfRangeInt: break; } MOZ_CRASH("unexpected literal type"); } - - void patchOp(size_t pos, Expr stmt) { - encoder().patchExpr(pos, stmt); + MOZ_WARN_UNUSED_RESULT bool writeCall(ParseNode* pn, Expr op) { + return encoder().writeExpr(op) && + fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin)); } - void patchU8(size_t pos, uint8_t u8) { - encoder().patchU8(pos, u8); + MOZ_WARN_UNUSED_RESULT bool patchableCall(ParseNode* pn, size_t* offset) { + return encoder().writePatchableExpr(offset) && + fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin)); } - template - void patch32(size_t pos, T val) { - static_assert(sizeof(T) == sizeof(uint32_t), "patch32 is used for 4-bytes long ops"); - encoder().patch32(pos, val); - } - - MOZ_WARN_UNUSED_RESULT - bool tempU8(size_t* offset) { - return encoder().writeU8(uint8_t(Expr::Unreachable), offset); - } - MOZ_WARN_UNUSED_RESULT - bool tempOp(size_t* offset) { - return encoder().writeExpr(Expr::Unreachable, offset); - } - MOZ_WARN_UNUSED_RESULT - bool temp32(size_t* offset) { - if (!encoder().writeU8(uint8_t(Expr::Unreachable), offset)) - return false; - for (size_t i = 1; i < 4; i++) { - if (!encoder().writeU8(uint8_t(Expr::Unreachable))) - return false; - } - return true; + MOZ_WARN_UNUSED_RESULT bool writeSimdOp(SimdType simdType, SimdOperation op) { + return encoder().writeExpr(SimdToExpr(simdType, op)); } }; @@ -2963,7 +2898,7 @@ CheckGlobalVariableInitConstant(ModuleValidator& m, PropertyName* varName, Parse } static bool -CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, ValType* coerceTo, +CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, Type* coerceTo, ParseNode** coercedExpr = nullptr) { switch (coercionNode->getKind()) { @@ -2972,13 +2907,13 @@ CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, ValType* coerce uint32_t i; if (!IsLiteralInt(m, rhs, &i) || i != 0) return m.fail(rhs, "must use |0 for argument/return coercion"); - *coerceTo = ValType::I32; + *coerceTo = Type::Int; if (coercedExpr) *coercedExpr = BitwiseLeft(coercionNode); return true; } case PNK_POS: { - *coerceTo = ValType::F64; + *coerceTo = Type::Double; if (coercedExpr) *coercedExpr = UnaryKid(coercionNode); return true; @@ -3017,11 +2952,11 @@ static bool CheckGlobalVariableInitImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode, bool isConst) { - ValType coerceTo; + Type coerceTo; ParseNode* coercedExpr; if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr)) return false; - return CheckGlobalVariableImportExpr(m, varName, coerceTo, coercedExpr, isConst); + return CheckGlobalVariableImportExpr(m, varName, coerceTo.canonicalToValType(), coercedExpr, isConst); } static bool @@ -3352,7 +3287,7 @@ ArgFail(FunctionValidator& f, PropertyName* argName, ParseNode* stmt) } static bool -CheckArgumentType(FunctionValidator& f, ParseNode* stmt, PropertyName* name, ValType* type) +CheckArgumentType(FunctionValidator& f, ParseNode* stmt, PropertyName* name, Type* type) { if (!stmt || !IsExpressionStatement(stmt)) return ArgFail(f, name, stmt ? stmt : f.fn()); @@ -3402,11 +3337,11 @@ CheckArguments(FunctionValidator& f, ParseNode** stmtIter, ValTypeVector* argTyp if (!CheckArgument(f.m(), argpn, &name)) return false; - ValType type; + Type type; if (!CheckArgumentType(f, stmt, name, &type)) return false; - if (!argTypes->append(type)) + if (!argTypes->append(type.canonicalToValType())) return false; if (!f.addLocal(argpn, name, type)) @@ -3453,13 +3388,13 @@ CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt) static bool SetLocal(FunctionValidator& f, NumLit lit) { - return f.writeOp(Expr::SetLocal) && - f.writeVarU32(f.numLocals()) && + return f.encoder().writeExpr(Expr::SetLocal) && + f.encoder().writeVarU32(f.numLocals()) && f.writeLit(lit); } static bool -CheckVariable(FunctionValidator& f, ParseNode* var) +CheckVariable(FunctionValidator& f, ParseNode* var, uint32_t* numStmts) { if (!IsDefinition(var)) return f.fail(var, "local variable names must not restate argument names"); @@ -3480,20 +3415,23 @@ CheckVariable(FunctionValidator& f, ParseNode* var) if (!lit.valid()) return f.failName(var, "var '%s' initializer out of range", name); - if (!lit.isZeroBits() && !SetLocal(f, lit)) - return false; + if (!lit.isZeroBits()) { + ++*numStmts; + if (!SetLocal(f, lit)) + return false; + } - return f.addLocal(var, name, lit.type()); + return f.addLocal(var, name, Type::canonicalize(Type::lit(lit))); } static bool -CheckVariables(FunctionValidator& f, ParseNode** stmtIter) +CheckVariables(FunctionValidator& f, ParseNode** stmtIter, uint32_t* numStmts) { ParseNode* stmt = *stmtIter; for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) { for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) { - if (!CheckVariable(f, var)) + if (!CheckVariable(f, var, numStmts)) return false; } } @@ -3521,12 +3459,11 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) PropertyName* name = varRef->name(); if (const FunctionValidator::Local* local = f.lookupLocal(name)) { - if (!f.writeOp(Expr::GetLocal)) + if (!f.encoder().writeExpr(Expr::GetLocal)) return false; - MOZ_ASSERT(local->type != ValType::I64, "no int64 in asm.js"); - if (!f.writeVarU32(local->slot)) + if (!f.encoder().writeVarU32(local->slot)) return false; - *type = Type::var(local->type); + *type = local->type; return true; } @@ -3538,7 +3475,8 @@ CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type) case ModuleValidator::Global::ConstantImport: case ModuleValidator::Global::Variable: { *type = global->varOrConstType(); - return f.writeOp(Expr::LoadGlobal) && f.writeVarU32(global->varOrConstIndex()); + return f.encoder().writeExpr(Expr::LoadGlobal) && + f.encoder().writeVarU32(global->varOrConstIndex()); } case ModuleValidator::Global::Function: case ModuleValidator::Global::FFI: @@ -3682,7 +3620,7 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask) { size_t prepareAt; - if (!f.tempOp(&prepareAt)) + if (!f.encoder().writePatchableExpr(&prepareAt)) return false; if (!CheckArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask)) @@ -3691,11 +3629,11 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* // Don't generate the mask op if there is no need for it which could happen for // a shift of zero or a SIMD access. if (*mask != NoMask) { - f.patchOp(prepareAt, Expr::I32And); + f.encoder().patchExpr(prepareAt, Expr::I32And); return f.writeInt32Lit(*mask); } - f.patchOp(prepareAt, Expr::Id); + f.encoder().patchExpr(prepareAt, Expr::Id); return true; } @@ -3708,25 +3646,27 @@ CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type) size_t opcodeAt; size_t needsBoundsCheckAt; - if (!f.tempOp(&opcodeAt) || !f.tempU8(&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)) return false; switch (viewType) { - case Scalar::Int8: f.patchOp(opcodeAt, Expr::I32LoadMem8S); break; - case Scalar::Uint8: f.patchOp(opcodeAt, Expr::I32LoadMem8U); break; - case Scalar::Int16: f.patchOp(opcodeAt, Expr::I32LoadMem16S); break; - case Scalar::Uint16: f.patchOp(opcodeAt, Expr::I32LoadMem16U); break; + case Scalar::Int8: f.encoder().patchExpr(opcodeAt, Expr::I32LoadMem8S); break; + case Scalar::Uint8: f.encoder().patchExpr(opcodeAt, Expr::I32LoadMem8U); break; + case Scalar::Int16: f.encoder().patchExpr(opcodeAt, Expr::I32LoadMem16S); break; + case Scalar::Uint16: f.encoder().patchExpr(opcodeAt, Expr::I32LoadMem16U); break; case Scalar::Uint32: - case Scalar::Int32: f.patchOp(opcodeAt, Expr::I32LoadMem); break; - case Scalar::Float32: f.patchOp(opcodeAt, Expr::F32LoadMem); break; - case Scalar::Float64: f.patchOp(opcodeAt, Expr::F64LoadMem); break; + case Scalar::Int32: f.encoder().patchExpr(opcodeAt, Expr::I32LoadMem); break; + case Scalar::Float32: f.encoder().patchExpr(opcodeAt, Expr::F32LoadMem); break; + case Scalar::Float64: f.encoder().patchExpr(opcodeAt, Expr::F64LoadMem); break; default: MOZ_CRASH("unexpected scalar type"); } - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); switch (viewType) { case Scalar::Int8: @@ -3754,7 +3694,9 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type { size_t opcodeAt; size_t needsBoundsCheckAt; - if (!f.tempOp(&opcodeAt) || !f.tempU8(&needsBoundsCheckAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) + return false; + if (!f.encoder().writePatchableU8(&needsBoundsCheckAt)) return false; Scalar::Type viewType; @@ -3792,32 +3734,32 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type switch (viewType) { case Scalar::Int8: case Scalar::Uint8: - f.patchOp(opcodeAt, Expr::I32StoreMem8); + f.encoder().patchExpr(opcodeAt, Expr::I32StoreMem8); break; case Scalar::Int16: case Scalar::Uint16: - f.patchOp(opcodeAt, Expr::I32StoreMem16); + f.encoder().patchExpr(opcodeAt, Expr::I32StoreMem16); break; case Scalar::Int32: case Scalar::Uint32: - f.patchOp(opcodeAt, Expr::I32StoreMem); + f.encoder().patchExpr(opcodeAt, Expr::I32StoreMem); break; case Scalar::Float32: if (rhsType.isFloatish()) - f.patchOp(opcodeAt, Expr::F32StoreMem); + f.encoder().patchExpr(opcodeAt, Expr::F32StoreMem); else - f.patchOp(opcodeAt, Expr::F64StoreMemF32); + f.encoder().patchExpr(opcodeAt, Expr::F64StoreMemF32); break; case Scalar::Float64: if (rhsType.isFloatish()) - f.patchOp(opcodeAt, Expr::F32StoreMemF64); + f.encoder().patchExpr(opcodeAt, Expr::F32StoreMemF64); else - f.patchOp(opcodeAt, Expr::F64StoreMem); + f.encoder().patchExpr(opcodeAt, Expr::F64StoreMem); break; default: MOZ_CRASH("unexpected scalar type"); } - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); *type = rhsType; return true; @@ -3829,7 +3771,9 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type RootedPropertyName name(f.cx(), lhs->name()); if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) { - if (!f.writeOp(Expr::SetLocal) || !f.writeVarU32(lhsVar->slot)) + if (!f.encoder().writeExpr(Expr::SetLocal)) + return false; + if (!f.encoder().writeVarU32(lhsVar->slot)) return false; Type rhsType; @@ -3838,9 +3782,8 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type if (!(rhsType <= lhsVar->type)) { return f.failf(lhs, "%s is not a subtype of %s", - rhsType.toChars(), Type::var(lhsVar->type).toChars()); + rhsType.toChars(), lhsVar->type.toChars()); } - MOZ_ASSERT(lhsVar->type != ValType::I64, "no int64 in asm.js"); *type = rhsType; return true; } @@ -3849,7 +3792,9 @@ CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type if (global->which() != ModuleValidator::Global::Variable) return f.failName(lhs, "'%s' is not a mutable variable", name); - if (!f.writeOp(Expr::StoreGlobal) || !f.writeVarU32(global->varOrConstIndex())) + if (!f.encoder().writeExpr(Expr::StoreGlobal)) + return false; + if (!f.encoder().writeVarU32(global->varOrConstIndex())) return false; Type rhsType; @@ -3892,7 +3837,7 @@ CheckMathIMul(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* lhs = CallArgList(call); ParseNode* rhs = NextNode(lhs); - if (!f.writeOp(Expr::I32Mul)) + if (!f.encoder().writeExpr(Expr::I32Mul)) return false; Type lhsType; @@ -3918,7 +3863,7 @@ CheckMathClz32(FunctionValidator& f, ParseNode* call, Type* type) if (CallArgListLength(call) != 1) return f.fail(call, "Math.clz32 must be passed 1 argument"); - if (!f.writeOp(Expr::I32Clz)) + if (!f.encoder().writeExpr(Expr::I32Clz)) return false; ParseNode* arg = CallArgList(call); @@ -3943,7 +3888,7 @@ CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arg = CallArgList(call); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type argType; @@ -3951,19 +3896,19 @@ CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type) return false; if (argType.isSigned()) { - f.patchOp(opcodeAt, Expr::I32Abs); + f.encoder().patchExpr(opcodeAt, Expr::I32Abs); *type = Type::Unsigned; return true; } if (argType.isMaybeDouble()) { - f.patchOp(opcodeAt, Expr::F64Abs); + f.encoder().patchExpr(opcodeAt, Expr::F64Abs); *type = Type::Double; return true; } if (argType.isMaybeFloat()) { - f.patchOp(opcodeAt, Expr::F32Abs); + f.encoder().patchExpr(opcodeAt, Expr::F32Abs); *type = Type::Floatish; return true; } @@ -3980,7 +3925,7 @@ CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type) ParseNode* arg = CallArgList(call); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type argType; @@ -3988,13 +3933,13 @@ CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type) return false; if (argType.isMaybeDouble()) { - f.patchOp(opcodeAt, Expr::F64Sqrt); + f.encoder().patchExpr(opcodeAt, Expr::F64Sqrt); *type = Type::Double; return true; } if (argType.isMaybeFloat()) { - f.patchOp(opcodeAt, Expr::F32Sqrt); + f.encoder().patchExpr(opcodeAt, Expr::F32Sqrt); *type = Type::Floatish; return true; } @@ -4009,7 +3954,7 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ return f.fail(callNode, "Math.min/max must be passed at least 2 arguments"); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; ParseNode* firstArg = CallArgList(callNode); @@ -4034,12 +3979,12 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ return f.failf(firstArg, "%s is not a subtype of double?, float? or signed", firstType.toChars()); } - f.patchOp(opcodeAt, expr); + f.encoder().patchExpr(opcodeAt, expr); unsigned numArgs = CallArgListLength(callNode); ParseNode* nextArg = NextNode(firstArg); for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) { - if (i != numArgs - 1 && !f.writeOp(expr)) + if (i != numArgs - 1 && !f.encoder().writeExpr(expr)) return false; Type nextType; @@ -4089,15 +4034,15 @@ CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type) return f.fail(call, "Atomics.fence must be passed 0 arguments"); *type = Type::Void; - return f.writeOp(Expr::AtomicsFence); + return f.encoder().writeExpr(Expr::AtomicsFence); } static bool WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* needsBoundsCheckAt, size_t* viewTypeAt) { - return f.writeOp(opcode) && - f.tempU8(needsBoundsCheckAt) && - f.tempU8(viewTypeAt); + return f.encoder().writeExpr(opcode) && + f.encoder().writePatchableU8(needsBoundsCheckAt) && + f.encoder().writePatchableU8(viewTypeAt); } static bool @@ -4120,8 +4065,8 @@ CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type) if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask)) return false; - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - f.patchU8(viewTypeAt, uint8_t(viewType)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; return true; @@ -4155,8 +4100,8 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type) if (!rhsType.isIntish()) return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars()); - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - f.patchU8(viewTypeAt, uint8_t(viewType)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = rhsType; return true; @@ -4176,7 +4121,7 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op size_t viewTypeAt; if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &needsBoundsCheckAt, &viewTypeAt)) return false; - if (!f.writeU8(uint8_t(op))) + if (!f.encoder().writeU8(uint8_t(op))) return false; Scalar::Type viewType; @@ -4192,8 +4137,8 @@ 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.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - f.patchU8(viewTypeAt, uint8_t(viewType)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; return true; @@ -4251,8 +4196,8 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type) if (!newValueArgType.isIntish()) return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars()); - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - f.patchU8(viewTypeAt, uint8_t(viewType)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; return true; @@ -4286,8 +4231,8 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type) if (!valueArgType.isIntish()) return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars()); - f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); - f.patchU8(viewTypeAt, uint8_t(viewType)); + f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck)); + f.encoder().patchU8(viewTypeAt, uint8_t(viewType)); *type = Type::Int; return true; @@ -4340,7 +4285,7 @@ CheckCallArgs(FunctionValidator& f, ParseNode* callNode, ValTypeVector* args) if (!checkArg(f, argNode, type)) return false; - if (!args->append(type.checkedValueType())) + if (!args->append(Type::canonicalize(type).canonicalToValType())) return false; } return true; @@ -4389,34 +4334,37 @@ CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Sig&& sig, Property } static bool -CheckIsVarType(FunctionValidator& f, ParseNode* argNode, Type type) +CheckIsArgType(FunctionValidator& f, ParseNode* argNode, Type type) { - if (!type.isVarType()) + if (!type.isArgType()) return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars()); return true; } static bool CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calleeName, - ExprType ret, Type* type) + Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + if (!f.writeCall(callNode, Expr::Call)) return false; // Function's index, to find out the function's entry size_t funcIndexAt; - if (!f.temp32(&funcIndexAt)) + if (!f.encoder().writePatchableVarU32(&funcIndexAt)) return false; ValTypeVector args; - if (!CheckCallArgs(f, callNode, &args)) + if (!CheckCallArgs(f, callNode, &args)) return false; + Sig sig(Move(args), ret.canonicalToExprType()); ModuleValidator::Func* callee; - if (!CheckFunctionSignature(f.m(), callNode, Sig(Move(args), ret), calleeName, &callee)) + if (!CheckFunctionSignature(f.m(), callNode, Move(sig), calleeName, &callee)) return false; - f.patch32(funcIndexAt, callee->index()); + f.encoder().patchVarU32(funcIndexAt, callee->index()); *type = Type::ret(ret); return true; } @@ -4450,8 +4398,10 @@ CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyN } static bool -CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* type) +CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + ParseNode* callee = CallCallee(callNode); ParseNode* tableNode = ElemBase(callee); ParseNode* indexExpr = ElemIndex(callee); @@ -4481,7 +4431,7 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* // Call signature size_t sigIndexAt; - if (!f.temp32(&sigIndexAt)) + if (!f.encoder().writePatchableVarU32(&sigIndexAt)) return false; Type indexType; @@ -4492,16 +4442,16 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars()); ValTypeVector args; - if (!CheckCallArgs(f, callNode, &args)) + if (!CheckCallArgs(f, callNode, &args)) return false; - Sig sig(Move(args), ret); + Sig sig(Move(args), ret.canonicalToExprType()); uint32_t tableIndex; if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &tableIndex)) return false; - f.patch32(sigIndexAt, f.m().funcPtrTable(tableIndex).sigIndex()); + f.encoder().patchVarU32(sigIndexAt, f.m().funcPtrTable(tableIndex).sigIndex()); *type = Type::ret(ret); return true; @@ -4516,14 +4466,15 @@ CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type) } static bool -CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprType ret, - Type* type) +CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + PropertyName* calleeName = CallCallee(callNode)->name(); - if (ret == ExprType::F32) + if (ret.isFloat()) return f.fail(callNode, "FFI calls can't return float"); - if (IsSimdType(ret)) + if (ret.isSimd()) return f.fail(callNode, "FFI calls can't return SIMD values"); // Opcode @@ -4532,18 +4483,19 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprT // Import index size_t importIndexAt; - if (!f.temp32(&importIndexAt)) + if (!f.encoder().writePatchableVarU32(&importIndexAt)) return false; ValTypeVector args; if (!CheckCallArgs(f, callNode, &args)) return false; + Sig sig(Move(args), ret.canonicalToExprType()); uint32_t importIndex; - if (!f.m().declareImport(calleeName, Sig(Move(args), ret), ffiIndex, &importIndex)) + if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &importIndex)) return false; - f.patch32(importIndexAt, importIndex); + f.encoder().patchVarU32(importIndexAt, importIndex); *type = Type::ret(ret); return true; @@ -4554,19 +4506,19 @@ CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType size_t opcodeAt) { if (inputType.isMaybeDouble()) { - f.patchOp(opcodeAt, Expr::F32DemoteF64); + f.encoder().patchExpr(opcodeAt, Expr::F32DemoteF64); return true; } if (inputType.isSigned()) { - f.patchOp(opcodeAt, Expr::F32ConvertSI32); + f.encoder().patchExpr(opcodeAt, Expr::F32ConvertSI32); return true; } if (inputType.isUnsigned()) { - f.patchOp(opcodeAt, Expr::F32ConvertUI32); + f.encoder().patchExpr(opcodeAt, Expr::F32ConvertUI32); return true; } if (inputType.isFloatish()) { - f.patchOp(opcodeAt, Expr::Id); + f.encoder().patchExpr(opcodeAt, Expr::Id); return true; } @@ -4575,44 +4527,36 @@ CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType } static bool -CheckCoercedCall(FunctionValidator& f, ParseNode* call, ExprType ret, Type* type); +CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type); static bool -CheckCoercionArg(FunctionValidator& f, ParseNode* arg, ValType expected, Type* type) +CheckCoercionArg(FunctionValidator& f, ParseNode* arg, Type expected, Type* type) { - ExprType ret = ToExprType(expected); + MOZ_ASSERT(expected.isCanonicalValType()); + if (arg->isKind(PNK_CALL)) - return CheckCoercedCall(f, arg, ret, type); + return CheckCoercedCall(f, arg, expected, type); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type argType; if (!CheckExpr(f, arg, &argType)) return false; - switch (expected) { - case ValType::F32: + if (expected.isFloat()) { if (!CheckFloatCoercionArg(f, arg, argType, opcodeAt)) return false; - break; - case ValType::I64: - MOZ_CRASH("no int64 in asm.js"); - case ValType::I32x4: - case ValType::F32x4: - case ValType::B32x4: + } else if (expected.isSimd()) { if (!(argType <= expected)) return f.fail(arg, "argument to SIMD coercion isn't from the correct SIMD type"); - f.patchOp(opcodeAt, Expr::Id); - break; - case ValType::I32: - case ValType::F64: - case ValType::Limit: + f.encoder().patchExpr(opcodeAt, Expr::Id); + } else { MOZ_CRASH("not call coercions"); } - *type = Type::ret(ret); + *type = Type::ret(expected); return true; } @@ -4624,7 +4568,7 @@ CheckMathFRound(FunctionValidator& f, ParseNode* callNode, Type* type) ParseNode* argNode = CallArgList(callNode); Type argType; - if (!CheckCoercionArg(f, argNode, ValType::F32, &argType)) + if (!CheckCoercionArg(f, argNode, Type::Float, &argType)) return false; MOZ_ASSERT(argType == Type::Float); @@ -4667,7 +4611,7 @@ CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltin return f.failf(callNode, "call passed %u arguments, expected %u", actualArity, arity); size_t opcodeAt; - if (!f.tempCall(callNode, &opcodeAt)) + if (!f.patchableCall(callNode, &opcodeAt)) return false; Type firstType; @@ -4683,9 +4627,9 @@ CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltin return f.fail(callNode, "math builtin cannot be used as float"); if (opIsDouble) - f.patchOp(opcodeAt, f64); + f.encoder().patchExpr(opcodeAt, f64); else - f.patchOp(opcodeAt, f32); + f.encoder().patchExpr(opcodeAt, f32); if (arity == 2) { Type secondType; @@ -4742,7 +4686,7 @@ CheckSimdCallArgsPatchable(FunctionValidator& f, ParseNode* call, unsigned expec MOZ_ASSERT(!!arg); Type argType; size_t patchAt; - if (!f.tempOp(&patchAt)) + if (!f.encoder().writePatchableExpr(&patchAt)) return false; if (!CheckExpr(f, arg, &argType)) return false; @@ -4810,14 +4754,14 @@ class CheckSimdScalarArgs // We emitted a double literal and actually want a float32. MOZ_ASSERT(patchAt != size_t(-1)); - f.patchOp(patchAt, Expr::F32DemoteF64); + f.encoder().patchExpr(patchAt, Expr::F32DemoteF64); return true; } if (patchAt == size_t(-1)) return true; - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); return true; } }; @@ -4867,7 +4811,7 @@ class CheckSimdVectorScalarArgs if (patchAt == size_t(-1)) return true; - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); return true; } @@ -4899,7 +4843,7 @@ class CheckSimdExtractLaneArgs // Second argument is the lane < vector length if (!IsLiteralOrConstInt(f, arg, &laneIndex)) return f.failf(arg, "lane selector should be a constant integer literal"); - if (laneIndex >= SimdTypeToLength(formalSimdType_)) + if (laneIndex >= GetSimdLanes(formalSimdType_)) return f.failf(arg, "lane selector should be in bounds"); return true; } @@ -4924,15 +4868,15 @@ class CheckSimdReplaceLaneArgs return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(), Type(formalSimdType_).toChars()); } - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); return true; case 1: // Second argument is the lane (< vector length). if (!IsLiteralOrConstInt(f, arg, &u32)) return f.failf(arg, "lane selector should be a constant integer literal"); - if (u32 >= SimdTypeToLength(formalSimdType_)) + if (u32 >= GetSimdLanes(formalSimdType_)) return f.failf(arg, "lane selector should be in bounds"); - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); return true; case 2: // Third argument is the scalar @@ -5073,7 +5017,7 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t return false; for (unsigned i = 0; i < 4; i++) { - if (!f.writeU8(uint8_t(lanes[i]))) + if (!f.encoder().writeU8(uint8_t(lanes[i]))) return false; } @@ -5106,7 +5050,7 @@ CheckSimdShuffle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t return false; for (unsigned i = 0; i < 4; i++) { - if (!f.writeU8(uint8_t(lanes[i]))) + if (!f.encoder().writeU8(uint8_t(lanes[i]))) return false; } @@ -5134,10 +5078,10 @@ 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.writeU8(NO_BOUNDS_CHECK) && f.writeInt32Lit(indexLit); + return f.encoder().writeU8(NO_BOUNDS_CHECK) && f.writeInt32Lit(indexLit); } - if (!f.writeU8(NEEDS_BOUNDS_CHECK)) + if (!f.encoder().writeU8(NEEDS_BOUNDS_CHECK)) return false; Type indexType; @@ -5231,7 +5175,7 @@ CheckSimdAnyTrue(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t static bool CheckSimdCheck(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type) { - ValType coerceTo; + Type coerceTo; ParseNode* argNode; if (!IsCoercionCall(f.m(), call, &coerceTo, &argNode)) return f.failf(call, "expected 1 argument in call to check"); @@ -5345,7 +5289,7 @@ CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator:: if (!f.writeSimdOp(simdType, SimdOperation::Constructor)) return false; - unsigned length = SimdTypeToLength(simdType); + unsigned length = GetSimdLanes(simdType); if (!CheckSimdCallArgsPatchable(f, call, length, CheckSimdScalarArgs(simdType))) return false; @@ -5377,55 +5321,44 @@ CheckUncoercedCall(FunctionValidator& f, ParseNode* expr, Type* type) } static bool -CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actual, size_t patchAt, +CoerceResult(FunctionValidator& f, ParseNode* expr, Type expected, Type actual, size_t patchAt, Type* type) { + MOZ_ASSERT(expected.isCanonical()); + // At this point, the bytecode resembles this: // | patchAt | the thing we wanted to coerce | current position |> - switch (expected) { - case ExprType::Void: - f.patchOp(patchAt, Expr::Id); + switch (expected.which()) { + case Type::Void: + f.encoder().patchExpr(patchAt, Expr::Id); break; - case ExprType::I32: + case Type::Int: if (!actual.isIntish()) return f.failf(expr, "%s is not a subtype of intish", actual.toChars()); - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); break; - case ExprType::I64: - MOZ_CRASH("no int64 in asm.js"); - case ExprType::F32: + case Type::Float: if (!CheckFloatCoercionArg(f, expr, actual, patchAt)) return false; break; - case ExprType::F64: + case Type::Double: if (actual.isMaybeDouble()) - f.patchOp(patchAt, Expr::Id); + f.encoder().patchExpr(patchAt, Expr::Id); else if (actual.isMaybeFloat()) - f.patchOp(patchAt, Expr::F64PromoteF32); + f.encoder().patchExpr(patchAt, Expr::F64PromoteF32); else if (actual.isSigned()) - f.patchOp(patchAt, Expr::F64ConvertSI32); + f.encoder().patchExpr(patchAt, Expr::F64ConvertSI32); else if (actual.isUnsigned()) - f.patchOp(patchAt, Expr::F64ConvertUI32); + f.encoder().patchExpr(patchAt, Expr::F64ConvertUI32); else return f.failf(expr, "%s is not a subtype of double?, float?, signed or unsigned", actual.toChars()); break; - case ExprType::I32x4: - if (!actual.isInt32x4()) - return f.failf(expr, "%s is not a subtype of int32x4", actual.toChars()); - f.patchOp(patchAt, Expr::Id); + default: + MOZ_ASSERT(expected.isSimd(), "Incomplete switch"); + if (actual != expected) + return f.failf(expr, "got type %s, expected %s", actual.toChars(), expected.toChars()); + f.encoder().patchExpr(patchAt, Expr::Id); break; - case ExprType::F32x4: - if (!actual.isFloat32x4()) - return f.failf(expr, "%s is not a subtype of float32x4", actual.toChars()); - f.patchOp(patchAt, Expr::Id); - break; - case ExprType::B32x4: - if (!actual.isBool32x4()) - return f.failf(expr, "%s is not a subtype of bool32x4", actual.toChars()); - f.patchOp(patchAt, Expr::Id); - break; - case ExprType::Limit: - MOZ_CRASH("Limit"); } *type = Type::ret(expected); @@ -5434,10 +5367,10 @@ CoerceResult(FunctionValidator& f, ParseNode* expr, ExprType expected, Type actu static bool CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func, - ExprType ret, Type* type) + Type ret, Type* type) { size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type actual; if (!CheckMathBuiltinCall(f, callNode, func, &actual)) @@ -5447,10 +5380,12 @@ CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMath static bool CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global, - ExprType ret, Type* type) + Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type actual; @@ -5469,10 +5404,12 @@ CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidato static bool CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, - AsmJSAtomicsBuiltinFunction func, ExprType ret, Type* type) + AsmJSAtomicsBuiltinFunction func, Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type actual; if (!CheckAtomicsBuiltinCall(f, callNode, func, &actual)) @@ -5481,13 +5418,15 @@ CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, } static bool -CheckCoercedCall(FunctionValidator& f, ParseNode* call, ExprType ret, Type* type) +CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type) { + MOZ_ASSERT(ret.isCanonical()); + JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed()); if (IsNumericLiteral(f.m(), call)) { size_t coerceOp; - if (!f.tempOp(&coerceOp)) + if (!f.encoder().writePatchableExpr(&coerceOp)) return false; NumLit lit = ExtractNumericLiteral(f.m(), call); if (!f.writeLit(lit)) @@ -5538,17 +5477,17 @@ CheckPos(FunctionValidator& f, ParseNode* pos, Type* type) ParseNode* operand = UnaryKid(pos); if (operand->isKind(PNK_CALL)) - return CheckCoercedCall(f, operand, ExprType::F64, type); + return CheckCoercedCall(f, operand, Type::Double, type); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type actual; if (!CheckExpr(f, operand, &actual)) return false; - return CoerceResult(f, operand, ExprType::F64, actual, opcodeAt, type); + return CoerceResult(f, operand, Type::Double, actual, opcodeAt, type); } static bool @@ -5557,7 +5496,7 @@ CheckNot(FunctionValidator& f, ParseNode* expr, Type* type) MOZ_ASSERT(expr->isKind(PNK_NOT)); ParseNode* operand = UnaryKid(expr); - if (!f.writeOp(Expr::I32Not)) + if (!f.encoder().writeExpr(Expr::I32Not)) return false; Type operandType; @@ -5578,7 +5517,7 @@ CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type) ParseNode* operand = UnaryKid(expr); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type operandType; @@ -5586,19 +5525,19 @@ CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type) return false; if (operandType.isInt()) { - f.patchOp(opcodeAt, Expr::I32Neg); + f.encoder().patchExpr(opcodeAt, Expr::I32Neg); *type = Type::Intish; return true; } if (operandType.isMaybeDouble()) { - f.patchOp(opcodeAt, Expr::F64Neg); + f.encoder().patchExpr(opcodeAt, Expr::F64Neg); *type = Type::Double; return true; } if (operandType.isMaybeFloat()) { - f.patchOp(opcodeAt, Expr::F32Neg); + f.encoder().patchExpr(opcodeAt, Expr::F32Neg); *type = Type::Floatish; return true; } @@ -5613,7 +5552,7 @@ CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type) ParseNode* operand = UnaryKid(expr); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type operandType; @@ -5622,7 +5561,7 @@ CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type) if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) { Expr opcode = operandType.isMaybeDouble() ? Expr::I32TruncSF64 : Expr::I32TruncSF32; - f.patchOp(opcodeAt, opcode); + f.encoder().patchExpr(opcodeAt, opcode); *type = Type::Signed; return true; } @@ -5630,7 +5569,7 @@ CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type) if (!operandType.isIntish()) return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars()); - f.patchOp(opcodeAt, Expr::Id); + f.encoder().patchExpr(opcodeAt, Expr::Id); *type = Type::Signed; return true; } @@ -5644,7 +5583,7 @@ CheckBitNot(FunctionValidator& f, ParseNode* neg, Type* type) if (operand->isKind(PNK_BITNOT)) return CheckCoerceToInt(f, operand, type); - if (!f.writeOp(Expr::I32BitNot)) + if (!f.encoder().writeExpr(Expr::I32BitNot)) return false; Type operandType; @@ -5667,7 +5606,9 @@ CheckComma(FunctionValidator& f, ParseNode* comma, Type* type) MOZ_ASSERT(comma->isKind(PNK_COMMA)); ParseNode* operands = ListHead(comma); - if (!f.writeOp(Expr::Block) || !f.writeVarU32(ListLength(comma))) + if (!f.encoder().writeExpr(Expr::Block)) + return false; + if (!f.encoder().writeVarU32(ListLength(comma))) return false; ParseNode* pn = operands; @@ -5684,7 +5625,7 @@ CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type) { MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL)); - if (!f.writeOp(Expr::IfElse)) + if (!f.encoder().writeExpr(Expr::IfElse)) return false; ParseNode* cond = TernaryKid1(ternary); @@ -5761,7 +5702,7 @@ CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type) ParseNode* rhs = MultiplyRight(star); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; Type lhsType; @@ -5775,19 +5716,19 @@ CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type) if (lhsType.isInt() && rhsType.isInt()) { if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs)) return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal"); - f.patchOp(opcodeAt, Expr::I32Mul); + f.encoder().patchExpr(opcodeAt, Expr::I32Mul); *type = Type::Intish; return true; } if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { - f.patchOp(opcodeAt, Expr::F64Mul); + f.encoder().patchExpr(opcodeAt, Expr::F64Mul); *type = Type::Double; return true; } if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { - f.patchOp(opcodeAt, Expr::F32Mul); + f.encoder().patchExpr(opcodeAt, Expr::F32Mul); *type = Type::Floatish; return true; } @@ -5808,7 +5749,7 @@ CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAd unsigned lhsNumAddOrSub, rhsNumAddOrSub; size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) { @@ -5838,13 +5779,13 @@ CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAd return f.fail(expr, "too many + or - without intervening coercion"); if (lhsType.isInt() && rhsType.isInt()) { - f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? Expr::I32Add : Expr::I32Sub); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::I32Add : Expr::I32Sub); *type = Type::Intish; } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { - f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F64Add : Expr::F64Sub); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F64Add : Expr::F64Sub); *type = Type::Double; } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { - f.patchOp(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F32Add : Expr::F32Sub); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F32Add : Expr::F32Sub); *type = Type::Floatish; } else { return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s", @@ -5862,7 +5803,7 @@ CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type) MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD)); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; ParseNode* lhs = DivOrModLeft(expr); @@ -5875,14 +5816,14 @@ CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type) return false; if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) { - f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? Expr::F64Div : Expr::F64Mod); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::F64Div : Expr::F64Mod); *type = Type::Double; return true; } if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) { if (expr->isKind(PNK_DIV)) - f.patchOp(opcodeAt, Expr::F32Div); + f.encoder().patchExpr(opcodeAt, Expr::F32Div); else return f.fail(expr, "modulo cannot receive float arguments"); *type = Type::Floatish; @@ -5890,13 +5831,13 @@ CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type) } if (lhsType.isSigned() && rhsType.isSigned()) { - f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivS : Expr::I32RemS); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivS : Expr::I32RemS); *type = Type::Intish; return true; } if (lhsType.isUnsigned() && rhsType.isUnsigned()) { - f.patchOp(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivU : Expr::I32RemU); + f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivU : Expr::I32RemU); *type = Type::Intish; return true; } @@ -5912,7 +5853,7 @@ CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type) comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE)); size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; ParseNode* lhs = ComparisonLeft(comp); @@ -5978,7 +5919,7 @@ CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type) MOZ_CRASH("unexpected type"); } - f.patchOp(opcodeAt, stmt); + f.encoder().patchExpr(opcodeAt, stmt); *type = Type::Int; return true; } @@ -6013,7 +5954,7 @@ CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type) if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) { if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL)) - return CheckCoercedCall(f, lhs, ExprType::I32, type); + return CheckCoercedCall(f, lhs, Type::Int, type); Type lhsType; if (!CheckExpr(f, lhs, &lhsType)) @@ -6024,12 +5965,12 @@ CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type) } switch (bitwise->getKind()) { - case PNK_BITOR: if (!f.writeOp(Expr::I32Or)) return false; break; - case PNK_BITAND: if (!f.writeOp(Expr::I32And)) return false; break; - case PNK_BITXOR: if (!f.writeOp(Expr::I32Xor)) return false; break; - case PNK_LSH: if (!f.writeOp(Expr::I32Shl)) return false; break; - case PNK_RSH: if (!f.writeOp(Expr::I32ShrS)) return false; break; - case PNK_URSH: if (!f.writeOp(Expr::I32ShrU)) return false; break; + case PNK_BITOR: if (!f.encoder().writeExpr(Expr::I32Or)) return false; break; + case PNK_BITAND: if (!f.encoder().writeExpr(Expr::I32And)) return false; break; + case PNK_BITXOR: if (!f.encoder().writeExpr(Expr::I32Xor)) return false; break; + case PNK_LSH: if (!f.encoder().writeExpr(Expr::I32Shl)) return false; break; + case PNK_RSH: if (!f.encoder().writeExpr(Expr::I32ShrS)) return false; break; + case PNK_URSH: if (!f.encoder().writeExpr(Expr::I32ShrU)) return false; break; default: MOZ_CRASH("not a bitwise op"); } @@ -6104,7 +6045,7 @@ CheckAsExprStatement(FunctionValidator& f, ParseNode* expr) { Type ignored; if (expr->isKind(PNK_CALL)) - return CheckCoercedCall(f, expr, ExprType::Void, &ignored); + return CheckCoercedCall(f, expr, Type::Void, &ignored); return CheckExpr(f, expr, &ignored); } @@ -6114,7 +6055,7 @@ CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt) MOZ_ASSERT(exprStmt->isKind(PNK_SEMI)); ParseNode* expr = UnaryKid(exprStmt); if (!expr) - return f.writeOp(Expr::Nop); + return f.encoder().writeExpr(Expr::Nop); return CheckAsExprStatement(f, expr); } @@ -6123,26 +6064,6 @@ enum class InterruptCheckPosition { Loop }; -static bool -MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn) -{ - if (f.m().mg().args().useSignalHandlersForInterrupt) - return true; - - switch (pos) { - case InterruptCheckPosition::Head: - if (!f.writeOp(Expr::InterruptCheckHead)) - return false; - break; - case InterruptCheckPosition::Loop: - if (!f.writeOp(Expr::InterruptCheckLoop)) - return false; - break; - } - - return true; -} - static bool CheckWhile(FunctionValidator& f, ParseNode* whileStmt) { @@ -6150,7 +6071,7 @@ CheckWhile(FunctionValidator& f, ParseNode* whileStmt) ParseNode* cond = BinaryLeft(whileStmt); ParseNode* body = BinaryRight(whileStmt); - if (!f.writeOp(Expr::While)) + if (!f.encoder().writeExpr(Expr::While)) return false; Type condType; @@ -6159,8 +6080,7 @@ CheckWhile(FunctionValidator& f, ParseNode* whileStmt) if (!condType.isInt()) return f.failf(cond, "%s is not a subtype of int", condType.toChars()); - return MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, whileStmt) && - CheckStatement(f, body); + return CheckStatement(f, body); } static bool @@ -6179,7 +6099,7 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt) Expr stmt = maybeInit ? (maybeInc ? Expr::ForInitInc : Expr::ForInitNoInc) : (maybeInc ? Expr::ForNoInitInc : Expr::ForNoInitNoInc); - if (!f.writeOp(stmt)) + if (!f.encoder().writeExpr(stmt)) return false; if (maybeInit && !CheckAsExprStatement(f, maybeInit)) @@ -6195,9 +6115,6 @@ CheckFor(FunctionValidator& f, ParseNode* forStmt) return false; } - if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, forStmt)) - return false; - if (!CheckStatement(f, body)) return false; @@ -6214,10 +6131,7 @@ CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt) ParseNode* body = BinaryLeft(whileStmt); ParseNode* cond = BinaryRight(whileStmt); - if (!f.writeOp(Expr::DoWhile)) - return false; - - if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Loop, cond)) + if (!f.encoder().writeExpr(Expr::DoWhile)) return false; if (!CheckStatement(f, body)) @@ -6239,14 +6153,14 @@ CheckLabel(FunctionValidator& f, ParseNode* labeledStmt) PropertyName* label = LabeledStatementLabel(labeledStmt); ParseNode* stmt = LabeledStatementStatement(labeledStmt); - if (!f.writeOp(Expr::Label)) + if (!f.encoder().writeExpr(Expr::Label)) return false; uint32_t labelId; if (!f.addLabel(label, &labelId)) return false; - if (!f.writeU32(labelId)) + if (!f.encoder().writeVarU32(labelId)) return false; if (!CheckStatement(f, stmt)) @@ -6261,7 +6175,7 @@ CheckIf(FunctionValidator& f, ParseNode* ifStmt) { recurse: size_t opcodeAt; - if (!f.tempOp(&opcodeAt)) + if (!f.encoder().writePatchableExpr(&opcodeAt)) return false; MOZ_ASSERT(ifStmt->isKind(PNK_IF)); @@ -6279,9 +6193,9 @@ CheckIf(FunctionValidator& f, ParseNode* ifStmt) return false; if (!elseStmt) { - f.patchOp(opcodeAt, Expr::If); + f.encoder().patchExpr(opcodeAt, Expr::If); } else { - f.patchOp(opcodeAt, Expr::IfElse); + f.encoder().patchExpr(opcodeAt, Expr::IfElse); if (elseStmt->isKind(PNK_IF)) { ifStmt = elseStmt; @@ -6374,10 +6288,10 @@ PatchSwitch(FunctionValidator& f, size_t highAt, int32_t high, size_t numCasesAt, uint32_t numCases) { - f.patchU8(hasDefaultAt, uint8_t(hasDefault)); - f.patch32(lowAt, low); - f.patch32(highAt, high); - f.patch32(numCasesAt, numCases); + f.encoder().patchU8(hasDefaultAt, uint8_t(hasDefault)); + f.encoder().patchVarU32(lowAt, low); + f.encoder().patchVarU32(highAt, high); + f.encoder().patchVarU32(numCasesAt, numCases); } static bool @@ -6385,23 +6299,23 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) { MOZ_ASSERT(switchStmt->isKind(PNK_SWITCH)); - if (!f.writeOp(Expr::TableSwitch)) + if (!f.encoder().writeExpr(Expr::TableSwitch)) return false; // Has default size_t hasDefaultAt; - if (!f.tempU8(&hasDefaultAt)) + if (!f.encoder().writePatchableU8(&hasDefaultAt)) return false; // Low / High / Num cases size_t lowAt; - if (!f.temp32(&lowAt)) + if (!f.encoder().writePatchableVarU32(&lowAt)) return false; size_t highAt; - if (!f.temp32(&highAt)) + if (!f.encoder().writePatchableVarU32(&highAt)) return false; size_t numCasesAt; - if (!f.temp32(&numCasesAt)) + if (!f.encoder().writePatchableVarU32(&numCasesAt)) return false; ParseNode* switchExpr = BinaryLeft(switchStmt); @@ -6445,7 +6359,7 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) cases[caseIndex] = true; numCases += 1; - if (!f.writeI32(caseValue)) + if (!f.encoder().writeVarU32(caseValue)) return false; if (!CheckStatement(f, CaseBody(stmt))) @@ -6464,14 +6378,14 @@ CheckSwitch(FunctionValidator& f, ParseNode* switchStmt) } static bool -CheckReturnType(FunctionValidator& f, ParseNode* usepn, ExprType ret) +CheckReturnType(FunctionValidator& f, ParseNode* usepn, Type ret) { if (!f.hasAlreadyReturned()) { - f.setReturnedType(ret); + f.setReturnedType(ret.canonicalToExprType()); return true; } - if (f.returnedType() != ret) { + if (f.returnedType() != ret.canonicalToExprType()) { return f.failf(usepn, "%s incompatible with previous return of type %s", Type::ret(ret).toChars(), Type::ret(f.returnedType()).toChars()); } @@ -6484,35 +6398,20 @@ CheckReturn(FunctionValidator& f, ParseNode* returnStmt) { ParseNode* expr = ReturnExpr(returnStmt); - if (!f.writeOp(Expr::Return)) + if (!f.encoder().writeExpr(Expr::Return)) return false; if (!expr) - return CheckReturnType(f, returnStmt, ExprType::Void); + return CheckReturnType(f, returnStmt, Type::Void); Type type; if (!CheckExpr(f, expr, &type)) return false; - ExprType ret; - if (type.isSigned()) - ret = ExprType::I32; - else if (type.isFloat()) - ret = ExprType::F32; - else if (type.isDouble()) - ret = ExprType::F64; - else if (type.isInt32x4()) - ret = ExprType::I32x4; - else if (type.isFloat32x4()) - ret = ExprType::F32x4; - else if (type.isBool32x4()) - ret = ExprType::B32x4; - else if (type.isVoid()) - ret = ExprType::Void; - else + if (!type.isReturnType()) return f.failf(expr, "%s is not a valid return type", type.toChars()); - return CheckReturnType(f, expr, ret); + return CheckReturnType(f, expr, Type::canonicalize(type)); } static bool @@ -6520,7 +6419,9 @@ CheckStatementList(FunctionValidator& f, ParseNode* stmtList) { MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); - if (!f.writeOp(Expr::Block) || !f.writeVarU32(ListLength(stmtList))) + if (!f.encoder().writeExpr(Expr::Block)) + return false; + if (!f.encoder().writeVarU32(ListLength(stmtList))) return false; for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) { @@ -6536,15 +6437,15 @@ CheckBreakOrContinue(FunctionValidator& f, PropertyName* maybeLabel, Expr withoutLabel, Expr withLabel) { if (!maybeLabel) - return f.writeOp(withoutLabel); + return f.encoder().writeExpr(withoutLabel); - if (!f.writeOp(withLabel)) + if (!f.encoder().writeExpr(withLabel)) return false; uint32_t labelId = f.lookupLabel(maybeLabel); MOZ_ASSERT(labelId != uint32_t(-1)); - return f.writeU32(labelId); + return f.encoder().writeVarU32(labelId); } static bool @@ -6665,23 +6566,28 @@ CheckFunction(ModuleValidator& m) if (!CheckArguments(f, &stmtIter, &args)) return false; - if (!MaybeAddInterruptCheck(f, InterruptCheckPosition::Head, fn)) + uint32_t numStmts = 0; + + size_t numStmtsAt; + if (!f.encoder().writePatchableVarU32(&numStmtsAt)) return false; - if (!CheckVariables(f, &stmtIter)) + if (!CheckVariables(f, &stmtIter, &numStmts)) return false; ParseNode* lastNonEmptyStmt = nullptr; - for (; stmtIter; stmtIter = NextNode(stmtIter)) { + for (; stmtIter; stmtIter = NextNonEmptyStatement(stmtIter)) { + numStmts++; + lastNonEmptyStmt = stmtIter; if (!CheckStatement(f, stmtIter)) return false; - if (!IsEmptyStatement(stmtIter)) - lastNonEmptyStmt = stmtIter; } if (!CheckFinalReturn(f, lastNonEmptyStmt)) return false; + f.encoder().patchVarU32(numStmtsAt, numStmts); + ModuleValidator::Func* func = nullptr; if (!CheckFunctionSignature(m, fn, Sig(Move(args), f.returnedType()), FunctionName(fn), &func)) return false; diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index 4a4be305ce..01de9c7302 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -179,7 +179,7 @@ static bool DecodeCall(FunctionDecoder& f, ExprType expected) { uint32_t funcIndex; - if (!f.d().readU32(&funcIndex)) + if (!f.d().readVarU32(&funcIndex)) return f.fail("unable to read import index"); if (funcIndex >= f.mg().numFuncSigs()) @@ -192,7 +192,7 @@ static bool DecodeCallImport(FunctionDecoder& f, ExprType expected) { uint32_t importIndex; - if (!f.d().readU32(&importIndex)) + if (!f.d().readVarU32(&importIndex)) return f.fail("unable to read import index"); if (importIndex >= f.mg().numImports()) @@ -205,7 +205,7 @@ static bool DecodeCallIndirect(FunctionDecoder& f, ExprType expected) { uint32_t sigIndex; - if (!f.d().readU32(&sigIndex)) + if (!f.d().readVarU32(&sigIndex)) return f.fail("unable to read indirect call signature index"); if (sigIndex >= f.mg().numSigs()) @@ -239,7 +239,7 @@ static bool DecodeConstF32(FunctionDecoder& f, ExprType expected) { float value; - if (!f.d().readF32(&value)) + if (!f.d().readFixedF32(&value)) return f.fail("unable to read f32.const immediate"); if (IsNaN(value)) { const float jsNaN = (float)JS::GenericNaN(); @@ -254,7 +254,7 @@ static bool DecodeConstF64(FunctionDecoder& f, ExprType expected) { double value; - if (!f.d().readF64(&value)) + if (!f.d().readFixedF64(&value)) return f.fail("unable to read f64.const immediate"); if (IsNaN(value)) { const double jsNaN = JS::GenericNaN(); @@ -322,7 +322,7 @@ static bool DecodeUnaryOperator(FunctionDecoder& f, ExprType expected, ExprType type) { return CheckType(f, type, expected) && - DecodeExpr(f, expected); + DecodeExpr(f, type); } static bool @@ -627,8 +627,23 @@ DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator const uint8_t* bodyBegin = d.currentPosition(); FunctionDecoder f(cx, d, mg, fg, funcIndex); - if (!DecodeExpr(f, f.ret())) - return false; + + uint32_t numExprs; + if (!d.readVarU32(&numExprs)) + return Fail(cx, d, "expected number of function body expressions"); + + if (numExprs) { + for (size_t i = 0; i < numExprs - 1; i++) { + if (!DecodeExpr(f, ExprType::Void)) + return false; + } + + if (!DecodeExpr(f, f.ret())) + return false; + } else { + if (!CheckType(f, ExprType::Void, f.ret())) + return false; + } const uint8_t* bodyEnd = d.currentPosition(); uintptr_t bodyLength = bodyEnd - bodyBegin; @@ -665,7 +680,7 @@ typedef HashSet SigSet; static bool DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) { - if (!d.readCStringIf(SigSection)) + if (!d.readCStringIf(SigLabel)) return true; uint32_t sectionStart; @@ -743,7 +758,7 @@ DecodeSignatureIndex(JSContext* cx, Decoder& d, const ModuleGeneratorData& init, static bool DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) { - if (!d.readCStringIf(DeclSection)) + if (!d.readCStringIf(DeclLabel)) return true; uint32_t sectionStart; @@ -774,7 +789,7 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) static bool DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) { - if (!d.readCStringIf(TableSection)) + if (!d.readCStringIf(TableLabel)) return true; uint32_t sectionStart; @@ -834,9 +849,6 @@ DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) static bool DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames) { - if (!d.readCStringIf(FuncSubsection)) - return Fail(cx, d, "expected 'func' tag"); - const DeclaredSig* sig; if (!DecodeSignatureIndex(cx, d, *init, &sig)) return false; @@ -861,21 +873,20 @@ DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVec static bool DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames) { - if (!d.readCStringIf(ImportSection)) + if (!d.readCStringIf(ImportLabel)) return true; uint32_t sectionStart; if (!d.startSection(§ionStart)) return Fail(cx, d, "expected import section byte size"); - uint32_t numImports; - if (!d.readVarU32(&numImports)) - return Fail(cx, d, "expected number of imports"); + for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) { + if (i >= MaxImports) + return Fail(cx, d, "too many imports"); - if (numImports > MaxImports) - return Fail(cx, d, "too many imports"); + if (!d.readCStringIf(FuncLabel)) + return Fail(cx, d, "expected 'func' import subsection"); - for (uint32_t i = 0; i < numImports; i++) { if (!DecodeImport(cx, d, init, importNames)) return false; } @@ -890,14 +901,14 @@ static bool DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg, MutableHandle heap) { - if (!d.readCStringIf(MemorySection)) + if (!d.readCStringIf(MemoryLabel)) return true; uint32_t sectionStart; if (!d.startSection(§ionStart)) return Fail(cx, d, "expected memory section byte size"); - if (!d.readCStringIf(FieldInitial)) + if (!d.readCStringIf(InitialLabel)) return Fail(cx, d, "expected memory section initial field"); uint32_t initialHeapSize; @@ -910,6 +921,9 @@ DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg, if (initialHeapSize > INT32_MAX) return Fail(cx, d, "initial memory size too big"); + if (!d.readCStringIf(EndLabel)) + return Fail(cx, d, "expected end field of memory section"); + if (!d.finishSection(sectionStart)) return Fail(cx, d, "memory section byte size mismatch"); @@ -978,33 +992,29 @@ DecodeMemoryExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, CStringSet* d static bool DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg) { - if (!d.readCStringIf(ExportSection)) + if (!d.readCStringIf(ExportLabel)) return true; uint32_t sectionStart; if (!d.startSection(§ionStart)) return Fail(cx, d, "expected export section byte size"); - uint32_t numExports; - if (!d.readVarU32(&numExports)) - return Fail(cx, d, "expected number of exports"); - - if (numExports > MaxExports) - return Fail(cx, d, "too many exports"); - CStringSet dupSet(cx); - if (!dupSet.init(numExports)) + if (!dupSet.init()) return false; - for (uint32_t i = 0; i < numExports; i++) { - if (d.readCStringIf(FuncSubsection)) { + for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) { + if (i >= MaxExports) + return Fail(cx, d, "too many exports"); + + if (d.readCStringIf(FuncLabel)) { if (!DecodeFunctionExport(cx, d, mg, &dupSet)) return false; - } else if (d.readCStringIf(MemorySubsection)) { + } else if (d.readCStringIf(MemoryLabel)) { if (!DecodeMemoryExport(cx, d, mg, &dupSet)) return false; } else { - return Fail(cx, d, "unknown export type"); + return Fail(cx, d, "unexpected export subsection"); } } @@ -1015,7 +1025,7 @@ DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg) } static bool -DecodeFunc(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) +DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) { int64_t before = PRMJ_Now(); @@ -1023,9 +1033,6 @@ DecodeFunc(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) if (!mg.startFuncDef(d.currentOffset(), &fg)) return false; - if (!d.readCStringIf(FuncSubsection)) - return Fail(cx, d, "expected 'func' tag"); - uint32_t sectionStart; if (!d.startSection(§ionStart)) return Fail(cx, d, "expected func section byte size"); @@ -1061,35 +1068,23 @@ DecodeFunc(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex) } static bool -DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg) +DecodeFunctionSections(JSContext* cx, Decoder& d, ModuleGenerator& mg) { if (!mg.startFuncDefs()) return false; uint32_t funcIndex = 0; - while (d.readCStringIf(CodeSection)) { - uint32_t sectionStart; - if (!d.startSection(§ionStart)) - return Fail(cx, d, "expected code section byte size"); - uint32_t numFuncs; - if (!d.readVarU32(&numFuncs)) - return Fail(cx, d, "expected number of functions"); - - if (funcIndex + numFuncs > mg.numFuncSigs()) + for (; d.readCStringIf(FuncLabel); funcIndex++) { + if (funcIndex >= mg.numFuncSigs()) return Fail(cx, d, "more function definitions than declarations"); - for (uint32_t i = 0; i < numFuncs; i++) { - if (!DecodeFunc(cx, d, mg, funcIndex++)) - return false; - } - - if (!d.finishSection(sectionStart)) - return Fail(cx, d, "code section byte size mismatch"); + if (!DecodeFunctionSection(cx, d, mg, funcIndex)) + return false; } - if (funcIndex != mg.numFuncSigs()) - return Fail(cx, d, "different number of definitions than declarations"); + if (funcIndex < mg.numFuncSigs()) + return Fail(cx, d, "fewer function definitions than declarations"); if (!mg.finishFuncDefs()) return false; @@ -1100,7 +1095,7 @@ DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg) static bool DecodeDataSection(JSContext* cx, Decoder& d, Handle heap) { - if (!d.readCStringIf(DataSection)) + if (!d.readCStringIf(DataLabel)) return true; if (!heap) @@ -1110,16 +1105,12 @@ DecodeDataSection(JSContext* cx, Decoder& d, Handle heap) if (!d.startSection(§ionStart)) return Fail(cx, d, "expected data section byte size"); - uint32_t numSegments; - if (!d.readVarU32(&numSegments)) - return Fail(cx, d, "expected number of data segments"); - uint8_t* const heapBase = heap->dataPointer(); uint32_t const heapLength = heap->byteLength(); uint32_t prevEnd = 0; - for (uint32_t i = 0; i < numSegments; i++) { - if (!d.readCStringIf(SegmentSubsection)) + for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) { + if (!d.readCStringIf(SegmentLabel)) return Fail(cx, d, "expected segment tag"); uint32_t dstOffset; @@ -1137,7 +1128,7 @@ DecodeDataSection(JSContext* cx, Decoder& d, Handle heap) return Fail(cx, d, "data segment does not fit in memory"); const uint8_t* src; - if (!d.readData(numBytes, &src)) + if (!d.readRawData(numBytes, &src)) return Fail(cx, d, "data segment shorter than declared"); memcpy(heapBase + dstOffset, src, numBytes); @@ -1157,13 +1148,14 @@ DecodeUnknownSection(JSContext* cx, Decoder& d) if (!sectionName) return Fail(cx, d, "failed to read section name"); - if (!strcmp(sectionName.get(), SigSection) || - !strcmp(sectionName.get(), ImportSection) || - !strcmp(sectionName.get(), DeclSection) || - !strcmp(sectionName.get(), ExportSection) || - !strcmp(sectionName.get(), CodeSection) || - !strcmp(sectionName.get(), DataSection) || - !strcmp(sectionName.get(), TableSection)) + if (!strcmp(sectionName.get(), SigLabel) || + !strcmp(sectionName.get(), ImportLabel) || + !strcmp(sectionName.get(), DeclLabel) || + !strcmp(sectionName.get(), TableLabel) || + !strcmp(sectionName.get(), MemoryLabel) || + !strcmp(sectionName.get(), ExportLabel) || + !strcmp(sectionName.get(), FuncLabel) || + !strcmp(sectionName.get(), DataLabel)) { return Fail(cx, d, "known section out of order"); } @@ -1182,13 +1174,13 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len Decoder d(bytes, bytes + length); uint32_t u32; - if (!d.readU32(&u32) || u32 != MagicNumber) + if (!d.readFixedU32(&u32) || u32 != MagicNumber) return Fail(cx, d, "failed to match magic number"); - if (!d.readU32(&u32) || u32 != EncodingVersion) + if (!d.readFixedU32(&u32) || u32 != EncodingVersion) return Fail(cx, d, "failed to match binary version"); - UniqueModuleGeneratorData init = MakeUnique(); + UniqueModuleGeneratorData init = js::MakeUnique(cx); if (!init) return false; @@ -1214,7 +1206,7 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len if (!DecodeExportsSection(cx, d, mg)) return false; - if (!DecodeCodeSection(cx, d, mg)) + if (!DecodeFunctionSections(cx, d, mg)) return false; if (!DecodeDataSection(cx, d, heap)) @@ -1222,7 +1214,7 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len CacheableCharsVector funcNames; - while (!d.readCStringIf(EndSection)) { + while (!d.readCStringIf(EndLabel)) { if (!DecodeUnknownSection(cx, d)) return false; } @@ -1366,7 +1358,6 @@ wasm::Eval(JSContext* cx, Handle code, return true; } - static bool WasmEval(JSContext* cx, unsigned argc, Value* vp) { diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 0f8850f74a..515d6478c4 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -42,24 +42,18 @@ static const unsigned MaxArgsPerFunc = 4 * 1024; static const uint32_t MagicNumber = 0x6d736100; // "\0asm" static const uint32_t EncodingVersion = -1; // experimental -// Module section names: -static const char SigSection[] = "sig"; -static const char ImportSection[] = "import"; -static const char DeclSection[] = "decl"; -static const char TableSection[] = "table"; -static const char MemorySection[] = "memory"; -static const char ExportSection[] = "export"; -static const char CodeSection[] = "code"; -static const char DataSection[] = "data"; -static const char EndSection[] = ""; - -// Subsection names: -static const char FuncSubsection[] = "func"; -static const char MemorySubsection[] = "memory"; -static const char SegmentSubsection[] = "segment"; - -// Field names: -static const char FieldInitial[] = "initial"; +// Names: +static const char SigLabel[] = "sig"; +static const char ImportLabel[] = "import"; +static const char DeclLabel[] = "decl"; +static const char TableLabel[] = "table"; +static const char MemoryLabel[] = "memory"; +static const char ExportLabel[] = "export"; +static const char FuncLabel[] = "func"; +static const char DataLabel[] = "data"; +static const char SegmentLabel[] = "segment"; +static const char InitialLabel[] = "initial"; +static const char EndLabel[] = ""; enum class Expr : uint16_t { @@ -82,7 +76,6 @@ enum class Expr : uint16_t CallImport, // Constants and calls - I8Const, I32Const, I64Const, F64Const, @@ -262,9 +255,6 @@ enum class Expr : uint16_t Id, - InterruptCheckHead, - InterruptCheckLoop, - I32Min, I32Max, @@ -283,6 +273,15 @@ enum class Expr : uint16_t I32x4Constructor, I32x4Const, #undef _ + // Unsigned I32x4 operations. These are the SIMD.Uint32x4 operations that + // behave differently from their SIMD.Int32x4 counterparts. + I32x4shiftRightByScalarU, + I32x4lessThanU, + I32x4lessThanOrEqualU, + I32x4greaterThanU, + I32x4greaterThanOrEqualU, + I32x4fromFloat32x4U, + #define _(OP) SIMD_OPCODE(F32x4, OP) FORALL_FLOAT32X4_ASMJS_OP(_) F32x4Constructor, @@ -340,56 +339,67 @@ class Encoder Bytecode& bytecode_; template - MOZ_WARN_UNUSED_RESULT - bool write(T v, size_t* offset) { - if (offset) - *offset = bytecode_.length(); - return bytecode_.append(reinterpret_cast(&v), sizeof(T)); - } - - template - MOZ_WARN_UNUSED_RESULT - bool writeEnum(T v, size_t* offset) { - // For now, just write a u16 instead of a variable-length integer. - // Variable-length is somewhat annoying at the moment due to the - // pre-order encoding and back-patching; let's see if we switch to - // post-order first. - static_assert(mozilla::IsEnum::value, "is an enum"); - MOZ_ASSERT(uint64_t(v) < IntT(-1)); - MOZ_ASSERT(v != T::Limit); - return write(IntT(v), offset); - } - - template - void patchEnum(size_t pc, T v) { - // See writeEnum comment. - static_assert(mozilla::IsEnum::value, "is an enum"); - MOZ_ASSERT(uint64_t(v) < UINT16_MAX); - memcpy(&bytecode_[pc], &v, sizeof(IntT)); - } - - template - static const T load(const uint8_t* p) { - T t; - memcpy(&t, p, sizeof(T)); - return t; + MOZ_WARN_UNUSED_RESULT bool write(const T& v) { + return bytecode_.append(reinterpret_cast(&v), sizeof(T)); } template - MOZ_WARN_UNUSED_RESULT bool - writeVarU(UInt i) { + MOZ_WARN_UNUSED_RESULT bool writeVarU(UInt i) { do { uint8_t byte = i & 0x7F; i >>= 7; if (i != 0) byte |= 0x80; - if (!writeU8(byte)) + if (!bytecode_.append(byte)) return false; } while(i != 0); return true; } - static const uint32_t BadSectionLength = uint32_t(-1); + template + MOZ_WARN_UNUSED_RESULT bool writeEnum(T v) { + static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits"); + MOZ_ASSERT(uint32_t(v) < uint32_t(T::Limit)); + return writeVarU32(uint32_t(v)); + } + + void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) { + do { + uint8_t assertByte = assertBits & 0x7f; + uint8_t patchByte = patchBits & 0x7f; + assertBits >>= 7; + patchBits >>= 7; + if (assertBits != 0) { + assertByte |= 0x80; + patchByte |= 0x80; + } + MOZ_ASSERT(assertByte == bytecode_[offset]); + bytecode_[offset] = patchByte; + offset++; + } while(assertBits != 0); + } + + uint32_t varU32ByteLength(size_t offset) const { + size_t start = offset; + while (bytecode_[offset] & 0x80) + offset++; + return offset - start + 1; + } + static const uint32_t EnumSentinel = 0x3fff; + + template + MOZ_WARN_UNUSED_RESULT bool writePatchableEnum(size_t* offset) { + static_assert(uint32_t(T::Limit) <= EnumSentinel, "reserve enough bits"); + *offset = bytecode_.length(); + return writeVarU32(EnumSentinel); + } + + template + void patchEnum(size_t offset, T v) { + static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits"); + MOZ_ASSERT(uint32_t(v) < uint32_t(T::Limit)); + return patchVarU32(offset, uint32_t(v), EnumSentinel); + } public: explicit Encoder(Bytecode& bytecode) @@ -401,102 +411,97 @@ class Encoder size_t bytecodeOffset() const { return bytecode_.length(); } bool empty() const { return bytecodeOffset() == 0; } - MOZ_WARN_UNUSED_RESULT bool - writeVarU32(uint32_t i) { - return writeVarU(i); + // Fixed-size encoding operations simply copy the literal bytes (without + // attempting to align). + + MOZ_WARN_UNUSED_RESULT bool writeFixedU32(uint32_t i) { + return write(i); + } + MOZ_WARN_UNUSED_RESULT bool writeFixedF32(float f) { + return write(f); + } + MOZ_WARN_UNUSED_RESULT bool writeFixedF64(double d) { + return write(d); + } + MOZ_WARN_UNUSED_RESULT bool writeFixedI32x4(const Val::I32x4& i32x4) { + return write(i32x4); + } + MOZ_WARN_UNUSED_RESULT bool writeFixedF32x4(const Val::F32x4& f32x4) { + return write(f32x4); } - MOZ_WARN_UNUSED_RESULT bool - writeVarU64(uint64_t i) { - return writeVarU(i); + // Variable-length encodings that all use LEB128. + + MOZ_WARN_UNUSED_RESULT bool writeVarU32(uint32_t i) { + return writeVarU(i); + } + MOZ_WARN_UNUSED_RESULT bool writeVarU64(uint64_t i) { + return writeVarU(i); + } + MOZ_WARN_UNUSED_RESULT bool writeExpr(Expr expr) { + return writeEnum(expr); + } + MOZ_WARN_UNUSED_RESULT bool writeValType(ValType type) { + return writeEnum(type); + } + MOZ_WARN_UNUSED_RESULT bool writeExprType(ExprType type) { + return writeEnum(type); } - MOZ_WARN_UNUSED_RESULT bool - writeExpr(Expr expr, size_t* offset = nullptr) { return writeEnum(expr, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeValType(ValType type, size_t* offset = nullptr) { return writeEnum(type, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeExprType(ExprType type, size_t* offset = nullptr) { return writeEnum(type, offset); } - - MOZ_WARN_UNUSED_RESULT bool - writeU8(uint8_t i, size_t* offset = nullptr) { return write(i, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeI32(int32_t i, size_t* offset = nullptr) { return write(i, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeU32(uint32_t i, size_t* offset = nullptr) { return write(i, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeF32(float f, size_t* offset = nullptr) { return write(f, offset); } - MOZ_WARN_UNUSED_RESULT bool - writeF64(double d, size_t* offset = nullptr) { return write(d, offset); } - - MOZ_WARN_UNUSED_RESULT bool - writeI32X4(const int32_t* i4, size_t* offset = nullptr) { - if (!writeI32(i4[0], offset)) - return false; - for (size_t i = 1; i < 4; i++) { - if (!writeI32(i4[i])) - return false; - } - return true; - } - MOZ_WARN_UNUSED_RESULT bool - writeF32X4(const float* f4, size_t* offset = nullptr) { - if (!writeF32(f4[0], offset)) - return false; - for (size_t i = 1; i < 4; i++) { - if (!writeF32(f4[i])) - return false; - } - return true; - } + // C-strings are written in UTF8 and null-terminated while raw data can + // contain nulls and instead has an explicit byte length. MOZ_WARN_UNUSED_RESULT bool writeCString(const char* cstr) { - MOZ_ASSERT(cstr); return bytecode_.append(reinterpret_cast(cstr), strlen(cstr) + 1); } - - MOZ_WARN_UNUSED_RESULT bool writeData(const uint8_t* bytes, uint32_t numBytes) { - MOZ_ASSERT(bytes); + MOZ_WARN_UNUSED_RESULT bool writeRawData(const uint8_t* bytes, uint32_t numBytes) { return bytecode_.append(bytes, numBytes); } + // A "section" is a contiguous region of bytes that stores its own size so + // that it may be trivially skipped without examining the contents. Sections + // require backpatching since the size of the section is only known at the + // end while the size's uint32 must be stored at the beginning. + MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) { - if (!writeU32(BadSectionLength)) - return false; - *offset = bytecode_.length(); - return true; + return writePatchableVarU32(offset); } void finishSection(size_t offset) { - uint8_t* patchAt = bytecode_.begin() + offset - sizeof(uint32_t); - MOZ_ASSERT(patchAt <= bytecode_.end() - sizeof(uint32_t)); - MOZ_ASSERT(load(patchAt) == BadSectionLength); - uint32_t numBytes = bytecode_.length() - offset; - memcpy(patchAt, &numBytes, sizeof(uint32_t)); + return patchVarU32(offset, bytecode_.length() - offset - varU32ByteLength(offset)); } -#ifdef DEBUG - bool pcIsPatchable(size_t pc, unsigned size) const { - bool patchable = true; - for (unsigned i = 0; patchable && i < size; i++) - patchable &= Expr(bytecode_[pc]) == Expr::Unreachable; - return patchable; + // Patching is necessary due to the combination of a preorder encoding and a + // single-pass algorithm that only knows the precise opcode after visiting + // children. Switching to a postorder encoding will remove these methods: + + MOZ_WARN_UNUSED_RESULT bool writePatchableVarU32(size_t* offset) { + *offset = bytecode_.length(); + return writeVarU32(UINT32_MAX); } -#endif - void patchU8(size_t pc, uint8_t i) { - MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t))); - bytecode_[pc] = i; + void patchVarU32(size_t offset, uint32_t patchBits) { + return patchVarU32(offset, patchBits, UINT32_MAX); } - void patchExpr(size_t pc, Expr expr) { - // See comment in writeEnum - MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint16_t))); - patchEnum(pc, expr); + + MOZ_WARN_UNUSED_RESULT bool writePatchableExpr(size_t* offset) { + return writePatchableEnum(offset); } - template - void patch32(size_t pc, T i) { - static_assert(sizeof(T) == sizeof(uint32_t), - "patch32 must be used with 32-bits wide types"); - MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t))); - memcpy(&bytecode_[pc], &i, sizeof(uint32_t)); + void patchExpr(size_t offset, Expr expr) { + patchEnum(offset, expr); + } + + // Temporary encoding forms which should be removed as part of the + // conversion to wasm: + + MOZ_WARN_UNUSED_RESULT bool writeU8(uint8_t i) { + return write(i); + } + MOZ_WARN_UNUSED_RESULT bool writePatchableU8(size_t* offset) { + *offset = bytecode_.length(); + return bytecode_.append(0xff); + } + void patchU8(size_t offset, uint8_t i) { + MOZ_ASSERT(bytecode_[offset] == 0xff); + bytecode_[offset] = i; } }; @@ -521,67 +526,52 @@ class Decoder return true; } - template + template MOZ_WARN_UNUSED_RESULT bool readEnum(T* out) { - static_assert(mozilla::IsEnum::value, "is an enum"); - // See Encoder::writeEnum. - IntT i; - if (!read(&i) || i >= IntT(T::Limit)) + static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits"); + uint32_t u32; + if (!readVarU32(&u32) || u32 >= uint32_t(T::Limit)) return false; if (out) - *out = T(i); + *out = T(u32); return true; } template - T uncheckedPeek() const { + T uncheckedRead() { MOZ_ASSERT(bytesRemain() >= sizeof(T)); T ret; memcpy(&ret, cur_, sizeof(T)); - return ret; - } - - template - T uncheckedPeekEnum() const { - // See Encoder::writeEnum. - static_assert(mozilla::IsEnum::value, "is an enum"); - return (T)uncheckedPeek(); - } - - template - T uncheckedRead() { - T ret = uncheckedPeek(); cur_ += sizeof(T); return ret; } - template + template T uncheckedReadEnum() { - // See Encoder::writeEnum. - static_assert(mozilla::IsEnum::value, "is an enum"); - return (T)uncheckedRead(); + static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits"); + return (T)uncheckedReadVarU32(); } template MOZ_WARN_UNUSED_RESULT bool readVarU(UInt* out = nullptr) { - static const unsigned numBits = sizeof(UInt) * CHAR_BIT; - static const unsigned remainderBits = numBits % 7; - static const unsigned numBitsInSevens = numBits - remainderBits; + const unsigned numBits = sizeof(UInt) * CHAR_BIT; + const unsigned remainderBits = numBits % 7; + const unsigned numBitsInSevens = numBits - remainderBits; UInt u = 0; uint8_t byte; UInt shift = 0; do { - if (!readU8(&byte)) + if (!readFixedU8(&byte)) return false; if (!(byte & 0x80)) { if (out) - *out = u | UInt(byte & 0x7F) << shift; + *out = u | UInt(byte) << shift; return true; } u |= UInt(byte & 0x7F) << shift; shift += 7; } while (shift != numBitsInSevens); - if (!readU8(&byte) || (byte & (unsigned(-1) << remainderBits))) + if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) return false; if (out) *out = u | UInt(byte) << numBitsInSevens; @@ -613,58 +603,50 @@ class Decoder size_t currentOffset() const { return cur_ - beg_; } - size_t offsetOfLastExpr() const { - return currentOffset() - sizeof(uint16_t); - } void assertCurrentIs(const DebugOnly offset) const { MOZ_ASSERT(currentOffset() == offset); } - // The fallible unpacking API should be used when we're not assuming - // anything about the bytecode, in particular if it is well-formed. - MOZ_WARN_UNUSED_RESULT bool readU8 (uint8_t* i = nullptr) { return read(i); } - MOZ_WARN_UNUSED_RESULT bool readI32(int32_t* i = nullptr) { return read(i); } - MOZ_WARN_UNUSED_RESULT bool readF32(float* f = nullptr) { return read(f); } - MOZ_WARN_UNUSED_RESULT bool readU32(uint32_t* u = nullptr) { return read(u); } - MOZ_WARN_UNUSED_RESULT bool readF64(double* d = nullptr) { return read(d); } + // Fixed-size encoding operations simply copy the literal bytes (without + // attempting to align). - MOZ_WARN_UNUSED_RESULT bool readI32X4(jit::SimdConstant* c = nullptr) { - int32_t v[4] = { 0, 0, 0, 0 }; - for (size_t i = 0; i < 4; i++) { - if (!readI32(&v[i])) - return false; - } - if (c) - *c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); - return true; + MOZ_WARN_UNUSED_RESULT bool readFixedU32(uint32_t* u = nullptr) { + return read(u); } - MOZ_WARN_UNUSED_RESULT bool readF32X4(jit::SimdConstant* c = nullptr) { - float v[4] = { 0., 0., 0., 0. }; - for (size_t i = 0; i < 4; i++) { - if (!readF32(&v[i])) - return false; - } - if (c) - *c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); - return true; + MOZ_WARN_UNUSED_RESULT bool readFixedF32(float* f = nullptr) { + return read(f); } + MOZ_WARN_UNUSED_RESULT bool readFixedF64(double* d = nullptr) { + return read(d); + } + MOZ_WARN_UNUSED_RESULT bool readFixedI32x4(Val::I32x4* i32x4 = nullptr) { + return read(i32x4); + } + MOZ_WARN_UNUSED_RESULT bool readFixedF32x4(Val::F32x4* f32x4 = nullptr) { + return read(f32x4); + } + + // Variable-length encodings that all use LEB128. MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* out = nullptr) { - return readVarU(out); + return readVarU(out); } MOZ_WARN_UNUSED_RESULT bool readVarU64(uint64_t* out = nullptr) { - return readVarU(out); + return readVarU(out); } MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr = nullptr) { - return readEnum(expr); + return readEnum(expr); } MOZ_WARN_UNUSED_RESULT bool readValType(ValType* type = nullptr) { - return readEnum(type); + return readEnum(type); } MOZ_WARN_UNUSED_RESULT bool readExprType(ExprType* type = nullptr) { - return readEnum(type); + return readEnum(type); } + // C-strings are written in UTF8 and null-terminated while raw data can + // contain nulls and instead has an explicit byte length. + MOZ_WARN_UNUSED_RESULT UniqueChars readCString() { const char* begin = reinterpret_cast(cur_); for (; cur_ != end_; cur_++) { @@ -686,8 +668,7 @@ class Decoder } return false; } - - MOZ_WARN_UNUSED_RESULT bool readData(uint32_t numBytes, const uint8_t** bytes = nullptr) { + MOZ_WARN_UNUSED_RESULT bool readRawData(uint32_t numBytes, const uint8_t** bytes = nullptr) { if (bytes) *bytes = cur_; if (bytesRemain() < numBytes) @@ -696,73 +677,77 @@ class Decoder return true; } - MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* offset) { + // See "section" description in Encoder. + + MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* startOffset) { + *startOffset = currentOffset(); uint32_t unused; - if (!readU32(&unused)) - return false; - *offset = currentOffset(); - return true; + return readVarU32(&unused); } - MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t offset) { - const uint8_t* start = beg_ + offset; - uint32_t numBytes; - memcpy(&numBytes, start - sizeof(uint32_t), sizeof(uint32_t)); - return numBytes == uintptr_t(cur_ - start); + MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t startOffset) { + uint32_t currentOffset = cur_ - beg_; + cur_ = beg_ + startOffset; + uint32_t numBytes = uncheckedReadVarU32(); + uint32_t afterNumBytes = cur_ - beg_; + cur_ = beg_ + currentOffset; + return numBytes == (currentOffset - afterNumBytes); } MOZ_WARN_UNUSED_RESULT bool skipSection() { uint32_t numBytes; - if (!readU32(&numBytes)) - return false; - if (bytesRemain() < numBytes) + if (!readVarU32(&numBytes) || bytesRemain() < numBytes) return false; cur_ += numBytes; return true; } - // The infallible unpacking API should be used when we are sure that the - // bytecode is well-formed. - uint8_t uncheckedReadU8 () { return uncheckedRead(); } - int32_t uncheckedReadI32() { return uncheckedRead(); } - float uncheckedReadF32() { return uncheckedRead(); } - uint32_t uncheckedReadU32() { return uncheckedRead(); } - double uncheckedReadF64() { return uncheckedRead(); } + // The infallible "unchecked" decoding functions can be used when we are + // sure that the bytecode is well-formed (by construction or due to previous + // validation). - jit::SimdConstant uncheckedReadI32X4() { - int32_t v[4] = { 0, 0, 0, 0 }; - for (size_t i = 0; i < 4; i++) - v[i] = uncheckedReadI32(); - return jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + uint32_t uncheckedReadFixedU32() { + return uncheckedRead(); } - jit::SimdConstant uncheckedReadF32X4() { - float v[4] = { 0., 0., 0., 0. }; - for (size_t i = 0; i < 4; i++) - v[i] = uncheckedReadF32(); - return jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]); + float uncheckedReadFixedF32() { + return uncheckedRead(); + } + double uncheckedReadFixedF64() { + return uncheckedRead(); } - uint32_t uncheckedReadVarU32() { uint32_t decoded = 0; uint32_t shift = 0; - uint8_t byte; do { - byte = uncheckedReadU8(); - if (!(byte & 0x80)) { - decoded |= uint32_t(byte & 0x7F) << shift; - return decoded; - } - decoded |= uint32_t(byte & 0x7F) << shift; + uint8_t byte = *cur_++; + if (!(byte & 0x80)) + return decoded | (uint32_t(byte) << shift); + decoded |= uint32_t(byte & 0x7f) << shift; shift += 7; } while (shift != 28); - byte = uncheckedReadU8(); - MOZ_ASSERT(!(byte & 0xF0)); - decoded |= uint32_t(byte) << 28; - return decoded; + uint8_t byte = *cur_++; + MOZ_ASSERT(!(byte & 0xf0)); + return decoded | (uint32_t(byte) << 28); } Expr uncheckedReadExpr() { - return uncheckedReadEnum(); + return uncheckedReadEnum(); } - Expr uncheckedPeekExpr() const { - return uncheckedPeekEnum(); + Expr uncheckedPeekExpr() { + const uint8_t* before = cur_; + Expr ret = uncheckedReadEnum(); + cur_ = before; + return ret; + } + + // Temporary encoding forms which should be removed as part of the + // conversion to wasm: + + MOZ_WARN_UNUSED_RESULT bool readFixedU8(uint8_t* i = nullptr) { + return read(i); + } + MOZ_WARN_UNUSED_RESULT bool readFixedI32(int32_t* i = nullptr) { + return read(i); + } + uint8_t uncheckedReadFixedU8() { + return uncheckedRead(); } }; diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index c27aeecd93..f0adadb567 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -120,7 +120,7 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, UniqueChars filename) return false; module_->globalBytes = InitialGlobalDataBytes; - module_->compileArgs = CompileArgs(cx_); + module_->compileArgs = shared->args; module_->kind = shared->kind; module_->heapUsage = HeapUsage::None; module_->filename = Move(filename); @@ -767,7 +767,7 @@ ModuleGenerator::startFuncDefs() return false; JSRuntime* rt = cx_->compartment()->runtimeFromAnyThread(); for (size_t i = 0; i < numTasks; i++) - tasks_.infallibleEmplaceBack(rt, args(), *threadView_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); + tasks_.infallibleEmplaceBack(rt, *threadView_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE); if (!freeTasks_.reserve(numTasks)) return false; diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index df80fcc816..fcbfca316b 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -96,6 +96,7 @@ typedef Vector AsmJSGlobalVariableVec struct ModuleGeneratorData { + CompileArgs args; ModuleKind kind; uint32_t numTableElems; @@ -109,8 +110,8 @@ struct ModuleGeneratorData return funcSigs[funcIndex] - sigs.begin(); } - explicit ModuleGeneratorData(ModuleKind kind = ModuleKind::Wasm) - : kind(kind), numTableElems(0) + explicit ModuleGeneratorData(ExclusiveContext* cx, ModuleKind kind = ModuleKind::Wasm) + : args(cx), kind(kind), numTableElems(0) {} }; @@ -129,6 +130,9 @@ class ModuleGeneratorThreadView explicit ModuleGeneratorThreadView(const ModuleGeneratorData& shared) : shared_(shared) {} + CompileArgs args() const { + return shared_.args; + } bool isAsmJS() const { return shared_.kind == ModuleKind::AsmJS; } diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index ad6c4006b9..ead29d0169 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -145,6 +145,8 @@ class FunctionCompiler return false; } + addInterruptCheck(); + return true; } @@ -255,7 +257,7 @@ class FunctionCompiler MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type()); MOZ_ASSERT(lhs->type() == type); - MSimdBinaryArith* ins = MSimdBinaryArith::NewAsmJS(alloc(), lhs, rhs, op); + auto* ins = MSimdBinaryArith::New(alloc(), lhs, rhs, op); curBlock_->add(ins); return ins; } @@ -268,18 +270,27 @@ class FunctionCompiler MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type()); MOZ_ASSERT(lhs->type() == type); - MSimdBinaryBitwise* ins = MSimdBinaryBitwise::NewAsmJS(alloc(), lhs, rhs, op); + auto* ins = MSimdBinaryBitwise::New(alloc(), lhs, rhs, op); curBlock_->add(ins); return ins; } + MDefinition* binarySimdComp(MDefinition* lhs, MDefinition* rhs, MSimdBinaryComp::Operation op, + SimdSign sign) + { + if (inDeadCode()) + return nullptr; + + return MSimdBinaryComp::AddLegalized(alloc(), curBlock_, lhs, rhs, op, sign); + } + template MDefinition* binarySimd(MDefinition* lhs, MDefinition* rhs, typename T::Operation op) { if (inDeadCode()) return nullptr; - T* ins = T::NewAsmJS(alloc(), lhs, rhs, op); + T* ins = T::New(alloc(), lhs, rhs, op); curBlock_->add(ins); return ins; } @@ -353,19 +364,29 @@ class FunctionCompiler return ins; } - template - MDefinition* convertSimd(MDefinition* vec, MIRType from, MIRType to) + // fromXXXBits() + MDefinition* bitcastSimd(MDefinition* vec, MIRType from, MIRType to) { if (inDeadCode()) return nullptr; MOZ_ASSERT(vec->type() == from); MOZ_ASSERT(IsSimdType(from) && IsSimdType(to) && from != to); - T* ins = T::NewAsmJS(alloc(), vec, to); + auto* ins = MSimdReinterpretCast::New(alloc(), vec, to); curBlock_->add(ins); return ins; } + // Int <--> Float conversions. + MDefinition* convertSimd(MDefinition* vec, MIRType from, MIRType to, SimdSign sign) + { + if (inDeadCode()) + return nullptr; + + MOZ_ASSERT(IsSimdType(from) && IsSimdType(to) && from != to); + return MSimdConvert::AddLegalized(alloc(), curBlock_, vec, to, sign); + } + MDefinition* splatSimd(MDefinition* v, MIRType type) { if (inDeadCode()) @@ -590,6 +611,9 @@ class FunctionCompiler void addInterruptCheck() { + if (mg_.args().useSignalHandlersForInterrupt) + return; + if (inDeadCode()) return; @@ -604,14 +628,14 @@ class FunctionCompiler curBlock_->add(MAsmJSInterruptCheck::New(alloc())); } - MDefinition* extractSimdElement(SimdLane lane, MDefinition* base, MIRType type) + MDefinition* extractSimdElement(SimdLane lane, MDefinition* base, MIRType type, SimdSign sign) { if (inDeadCode()) return nullptr; MOZ_ASSERT(IsSimdType(base->type())); MOZ_ASSERT(!IsSimdType(type)); - MSimdExtractElement* ins = MSimdExtractElement::NewAsmJS(alloc(), base, type, lane); + auto* ins = MSimdExtractElement::New(alloc(), base, type, lane, sign); curBlock_->add(ins); return ins; } @@ -1208,21 +1232,31 @@ class FunctionCompiler /************************************************************ DECODING ***/ - uint8_t readU8() { return decoder_.uncheckedReadU8(); } - uint32_t readU32() { return decoder_.uncheckedReadU32(); } + uint8_t readU8() { return decoder_.uncheckedReadFixedU8(); } uint32_t readVarU32() { return decoder_.uncheckedReadVarU32(); } - int32_t readI32() { return decoder_.uncheckedReadI32(); } - float readF32() { return decoder_.uncheckedReadF32(); } - double readF64() { return decoder_.uncheckedReadF64(); } - SimdConstant readI32X4() { return decoder_.uncheckedReadI32X4(); } - SimdConstant readF32X4() { return decoder_.uncheckedReadF32X4(); } - Expr readOpcode() { return decoder_.uncheckedReadExpr(); } - Expr peekOpcode() { return decoder_.uncheckedPeekExpr(); } + float readF32() { return decoder_.uncheckedReadFixedF32(); } + double readF64() { return decoder_.uncheckedReadFixedF64(); } + Expr readExpr() { return decoder_.uncheckedReadExpr(); } + Expr peakExpr() { return decoder_.uncheckedPeekExpr(); } - uint32_t readCallSiteLineOrBytecode() { + SimdConstant readI32X4() { + Val::I32x4 i32x4; + JS_ALWAYS_TRUE(decoder_.readFixedI32x4(&i32x4)); + return SimdConstant::CreateX4(i32x4); + } + SimdConstant readF32X4() { + Val::F32x4 f32x4; + JS_ALWAYS_TRUE(decoder_.readFixedF32x4(&f32x4)); + return SimdConstant::CreateX4(f32x4); + } + + uint32_t currentOffset() const { + return decoder_.currentOffset(); + } + uint32_t readCallSiteLineOrBytecode(uint32_t callOffset) { if (!func_.callSiteLineNums().empty()) return func_.callSiteLineNums()[lastReadCallSite_++]; - return decoder_.offsetOfLastExpr(); + return callOffset; } bool done() const { return decoder_.done(); } @@ -1597,10 +1631,10 @@ EmitCallArgs(FunctionCompiler& f, const Sig& sig, FunctionCompiler::Call* call) } static bool -EmitCall(FunctionCompiler& f, ExprType ret, MDefinition** def) +EmitCall(FunctionCompiler& f, uint32_t callOffset, ExprType ret, MDefinition** def) { - uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - uint32_t funcIndex = f.readU32(); + uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); + uint32_t funcIndex = f.readVarU32(); const Sig& sig = f.mg().funcSig(funcIndex); MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret != ExprType::Void, sig.ret() == ret); @@ -1613,10 +1647,10 @@ EmitCall(FunctionCompiler& f, ExprType ret, MDefinition** def) } static bool -EmitCallIndirect(FunctionCompiler& f, ExprType ret, MDefinition** def) +EmitCallIndirect(FunctionCompiler& f, uint32_t callOffset, ExprType ret, MDefinition** def) { - uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - uint32_t sigIndex = f.readU32(); + uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); + uint32_t sigIndex = f.readVarU32(); const Sig& sig = f.mg().sig(sigIndex); MOZ_ASSERT_IF(!IsVoid(sig.ret()) && ret != ExprType::Void, sig.ret() == ret); @@ -1634,10 +1668,10 @@ EmitCallIndirect(FunctionCompiler& f, ExprType ret, MDefinition** def) } static bool -EmitCallImport(FunctionCompiler& f, ExprType ret, MDefinition** def) +EmitCallImport(FunctionCompiler& f, uint32_t callOffset, ExprType ret, MDefinition** def) { - uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - uint32_t importIndex = f.readU32(); + uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); + uint32_t importIndex = f.readVarU32(); const ImportModuleGeneratorData& import = f.mg().import(importIndex); const Sig& sig = *import.sig; @@ -1651,11 +1685,11 @@ EmitCallImport(FunctionCompiler& f, ExprType ret, MDefinition** def) } static bool -EmitF32MathBuiltinCall(FunctionCompiler& f, Expr f32, MDefinition** def) +EmitF32MathBuiltinCall(FunctionCompiler& f, uint32_t callOffset, Expr f32, MDefinition** def) { MOZ_ASSERT(f32 == Expr::F32Ceil || f32 == Expr::F32Floor); - uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); + uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); FunctionCompiler::Call call(f, lineOrBytecode); f.startCallArgs(&call); @@ -1671,9 +1705,9 @@ EmitF32MathBuiltinCall(FunctionCompiler& f, Expr f32, MDefinition** def) } static bool -EmitF64MathBuiltinCall(FunctionCompiler& f, Expr f64, MDefinition** def) +EmitF64MathBuiltinCall(FunctionCompiler& f, uint32_t callOffset, Expr f64, MDefinition** def) { - uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); + uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset); FunctionCompiler::Call call(f, lineOrBytecode); f.startCallArgs(&call); @@ -1759,7 +1793,7 @@ EmitSimdBinary(FunctionCompiler& f, ExprType type, OpKind op, MDefinition** def) static bool EmitSimdBinaryComp(FunctionCompiler& f, ExprType type, MSimdBinaryComp::Operation op, - MDefinition** def) + SimdSign sign, MDefinition** def) { MDefinition* lhs; if (!EmitExpr(f, type, &lhs)) @@ -1767,7 +1801,7 @@ EmitSimdBinaryComp(FunctionCompiler& f, ExprType type, MSimdBinaryComp::Operatio MDefinition* rhs; if (!EmitExpr(f, type, &rhs)) return false; - *def = f.binarySimd(lhs, rhs, op); + *def = f.binarySimdComp(lhs, rhs, op, sign); return true; } @@ -1802,7 +1836,7 @@ SimdToLaneType(ExprType type) } static bool -EmitExtractLane(FunctionCompiler& f, ExprType type, MDefinition** def) +EmitExtractLane(FunctionCompiler& f, ExprType type, SimdSign sign, MDefinition** def) { MDefinition* vec; if (!EmitExpr(f, type, &vec)) @@ -1818,11 +1852,11 @@ EmitExtractLane(FunctionCompiler& f, ExprType type, MDefinition** def) } MOZ_ASSERT(laneDef->isConstant()); - int32_t laneLit = laneDef->toConstant()->value().toInt32(); + int32_t laneLit = laneDef->toConstant()->toInt32(); MOZ_ASSERT(laneLit < 4); SimdLane lane = SimdLane(laneLit); - *def = f.extractSimdElement(lane, vec, ToMIRType(SimdToLaneType(type))); + *def = f.extractSimdElement(lane, vec, ToMIRType(SimdToLaneType(type)), sign); return true; } @@ -1853,7 +1887,7 @@ EmitSimdReplaceLane(FunctionCompiler& f, ExprType simdType, MDefinition** def) SimdLane lane; if (laneDef) { MOZ_ASSERT(laneDef->isConstant()); - int32_t laneLit = laneDef->toConstant()->value().toInt32(); + int32_t laneLit = laneDef->toConstant()->toInt32(); MOZ_ASSERT(laneLit < 4); lane = SimdLane(laneLit); } else { @@ -1871,14 +1905,24 @@ EmitSimdReplaceLane(FunctionCompiler& f, ExprType simdType, MDefinition** def) return true; } -template inline bool -EmitSimdCast(FunctionCompiler& f, ExprType fromType, ExprType toType, MDefinition** def) +EmitSimdBitcast(FunctionCompiler& f, ExprType fromType, ExprType toType, MDefinition** def) { MDefinition* in; if (!EmitExpr(f, fromType, &in)) return false; - *def = f.convertSimd(in, ToMIRType(fromType), ToMIRType(toType)); + *def = f.bitcastSimd(in, ToMIRType(fromType), ToMIRType(toType)); + return true; +} + +inline bool +EmitSimdConvert(FunctionCompiler& f, ExprType fromType, ExprType toType, SimdSign sign, + MDefinition** def) +{ + MDefinition* in; + if (!EmitExpr(f, fromType, &in)) + return false; + *def = f.convertSimd(in, ToMIRType(fromType), ToMIRType(toType), sign); return true; } @@ -2259,13 +2303,13 @@ EmitBitwise(FunctionCompiler& f, MDefinition** def) } static bool -EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, MDefinition** def) +EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, SimdSign sign, MDefinition** def) { switch (op) { case SimdOperation::Constructor: return EmitSimdCtor(f, type, def); case SimdOperation::Fn_extractLane: - return EmitExtractLane(f, type, def); + return EmitExtractLane(f, type, sign, def); case SimdOperation::Fn_replaceLane: return EmitSimdReplaceLane(f, type, def); case SimdOperation::Fn_check: @@ -2308,16 +2352,14 @@ EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, MDefinition** d case SimdOperation::Fn_shiftLeftByScalar: return EmitSimdShift(f, type, MSimdShift::lsh, def); case SimdOperation::Fn_shiftRightByScalar: - return EmitSimdShift(f, type, - type == ExprType::I32x4 ? MSimdShift::rsh : MSimdShift::ursh, - def); + return EmitSimdShift(f, type, MSimdShift::rshForSign(sign), def); case SimdOperation::Fn_shiftRightArithmeticByScalar: return EmitSimdShift(f, type, MSimdShift::rsh, def); case SimdOperation::Fn_shiftRightLogicalByScalar: return EmitSimdShift(f, type, MSimdShift::ursh, def); #define _CASE(OP) \ case SimdOperation::Fn_##OP: \ - return EmitSimdBinaryComp(f, type, MSimdBinaryComp::OP, def); + return EmitSimdBinaryComp(f, type, MSimdBinaryComp::OP, sign, def); FOREACH_COMP_SIMD_OP(_CASE) #undef _CASE case SimdOperation::Fn_and: @@ -2333,41 +2375,26 @@ EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, MDefinition** d FOREACH_FLOAT_SIMD_BINOP(_CASE) #undef _CASE case SimdOperation::Fn_fromFloat32x4: - return EmitSimdCast(f, ExprType::F32x4, type, def); + return EmitSimdConvert(f, ExprType::F32x4, type, sign, def); case SimdOperation::Fn_fromInt32x4: - return EmitSimdCast(f, ExprType::I32x4, type, def); - case SimdOperation::Fn_fromInt32x4Bits: - return EmitSimdCast(f, ExprType::I32x4, type, def); - case SimdOperation::Fn_fromFloat32x4Bits: - return EmitSimdCast(f, ExprType::F32x4, type, def); + return EmitSimdConvert(f, ExprType::I32x4, type, SimdSign::Signed, def); case SimdOperation::Fn_fromUint32x4: + return EmitSimdConvert(f, ExprType::I32x4, type, SimdSign::Unsigned, def); + case SimdOperation::Fn_fromInt32x4Bits: + case SimdOperation::Fn_fromUint32x4Bits: + return EmitSimdBitcast(f, ExprType::I32x4, type, def); + case SimdOperation::Fn_fromFloat32x4Bits: + return EmitSimdBitcast(f, ExprType::F32x4, type, def); case SimdOperation::Fn_fromInt8x16Bits: case SimdOperation::Fn_fromInt16x8Bits: case SimdOperation::Fn_fromUint8x16Bits: case SimdOperation::Fn_fromUint16x8Bits: - case SimdOperation::Fn_fromUint32x4Bits: case SimdOperation::Fn_fromFloat64x2Bits: MOZ_CRASH("NYI"); } MOZ_CRASH("unexpected opcode"); } -static bool -EmitInterruptCheck(FunctionCompiler& f) -{ - f.addInterruptCheck(); - return true; -} - -static bool -EmitInterruptCheckLoop(FunctionCompiler& f) -{ - if (!EmitInterruptCheck(f)) - return false; - MDefinition* _; - return EmitExprStmt(f, &_); -} - static bool EmitWhile(FunctionCompiler& f, const LabelVector* maybeLabels) { @@ -2385,6 +2412,8 @@ EmitWhile(FunctionCompiler& f, const LabelVector* maybeLabels) if (!f.branchAndStartLoopBody(condDef, &afterLoop)) return false; + f.addInterruptCheck(); + MDefinition* _; if (!EmitExprStmt(f, &_)) return false; @@ -2420,6 +2449,8 @@ EmitFor(FunctionCompiler& f, Expr expr, const LabelVector* maybeLabels) if (!f.branchAndStartLoopBody(condDef, &afterLoop)) return false; + f.addInterruptCheck(); + MDefinition* _; if (!EmitExprStmt(f, &_)) return false; @@ -2445,6 +2476,8 @@ EmitDoWhile(FunctionCompiler& f, const LabelVector* maybeLabels) if (!f.startPendingLoop(headId, &loopEntry)) return false; + f.addInterruptCheck(); + MDefinition* _; if (!EmitExprStmt(f, &_)) return false; @@ -2462,7 +2495,7 @@ EmitDoWhile(FunctionCompiler& f, const LabelVector* maybeLabels) static bool EmitLabel(FunctionCompiler& f, LabelVector* maybeLabels) { - uint32_t labelId = f.readU32(); + uint32_t labelId = f.readVarU32(); if (maybeLabels) { if (!maybeLabels->append(labelId)) @@ -2523,10 +2556,10 @@ EmitIfElse(FunctionCompiler& f, bool hasElse, ExprType expected, MDefinition** d f.switchToElse(elseOrJoinBlock); - Expr nextStmt = f.peekOpcode(); + Expr nextStmt = f.peakExpr(); if (nextStmt == Expr::If || nextStmt == Expr::IfElse) { hasElse = nextStmt == Expr::IfElse; - JS_ALWAYS_TRUE(f.readOpcode() == nextStmt); + JS_ALWAYS_TRUE(f.readExpr() == nextStmt); goto recurse; } @@ -2553,9 +2586,9 @@ static bool EmitTableSwitch(FunctionCompiler& f) { bool hasDefault = f.readU8(); - int32_t low = f.readI32(); - int32_t high = f.readI32(); - uint32_t numCases = f.readU32(); + int32_t low = f.readVarU32(); + int32_t high = f.readVarU32(); + uint32_t numCases = f.readVarU32(); MDefinition* exprDef; if (!EmitExpr(f, ExprType::I32, &exprDef)) @@ -2574,7 +2607,7 @@ EmitTableSwitch(FunctionCompiler& f) return false; while (numCases--) { - int32_t caseValue = f.readI32(); + int32_t caseValue = f.readVarU32(); MOZ_ASSERT(caseValue >= low && caseValue <= high); unsigned caseIndex = caseValue - low; if (!f.startSwitchCase(switchBlock, &cases[caseIndex])) @@ -2635,7 +2668,7 @@ EmitContinue(FunctionCompiler& f, bool hasLabel) { if (!hasLabel) return f.addContinue(nullptr); - uint32_t labelId = f.readU32(); + uint32_t labelId = f.readVarU32(); return f.addContinue(&labelId); } @@ -2644,7 +2677,7 @@ EmitBreak(FunctionCompiler& f, bool hasLabel) { if (!hasLabel) return f.addBreak(nullptr); - uint32_t labelId = f.readU32(); + uint32_t labelId = f.readVarU32(); return f.addBreak(&labelId); } @@ -2654,8 +2687,11 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may if (!f.mirGen().ensureBallast()) return false; - switch (Expr op = f.readOpcode()) { + uint32_t exprOffset = f.currentOffset(); + + switch (Expr op = f.readExpr()) { case Expr::Nop: + *def = nullptr; return true; case Expr::Block: return EmitBlock(f, type, def); @@ -2686,18 +2722,14 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may case Expr::Return: return EmitRet(f); case Expr::Call: - return EmitCall(f, type, def); + return EmitCall(f, exprOffset, type, def); case Expr::CallIndirect: - return EmitCallIndirect(f, type, def); + return EmitCallIndirect(f, exprOffset, type, def); case Expr::CallImport: - return EmitCallImport(f, type, def); + return EmitCallImport(f, exprOffset, type, def); case Expr::AtomicsFence: f.memoryBarrier(MembarFull); return true; - case Expr::InterruptCheckHead: - return EmitInterruptCheck(f); - case Expr::InterruptCheckLoop: - return EmitInterruptCheckLoop(f); // Common case Expr::GetLocal: return EmitGetLocal(f, type, def); @@ -2828,7 +2860,7 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may return EmitUnaryMir(f, ExprType::F32, def); case Expr::F32Ceil: case Expr::F32Floor: - return EmitF32MathBuiltinCall(f, op, def); + return EmitF32MathBuiltinCall(f, exprOffset, op, def); case Expr::F32DemoteF64: return EmitUnary(f, ExprType::F64, def); case Expr::F32ConvertSI32: @@ -2876,7 +2908,7 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may case Expr::F64Log: case Expr::F64Pow: case Expr::F64Atan2: - return EmitF64MathBuiltinCall(f, op, def); + return EmitF64MathBuiltinCall(f, exprOffset, op, def); case Expr::F64PromoteF32: return EmitUnary(f, ExprType::F32, def); case Expr::F64ConvertSI32: @@ -2890,17 +2922,18 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may case Expr::F64StoreMemF32: return EmitStoreWithCoercion(f, Scalar::Float64, Scalar::Float32, def); // SIMD -#define CASE(TYPE, OP) \ +#define CASE(TYPE, OP, SIGN) \ case Expr::TYPE##OP: \ - return EmitSimdOp(f, ExprType::TYPE, SimdOperation::Fn_##OP, def); -#define I32CASE(OP) CASE(I32x4, OP) -#define F32CASE(OP) CASE(F32x4, OP) -#define B32CASE(OP) CASE(B32x4, OP) + return EmitSimdOp(f, ExprType::TYPE, SimdOperation::Fn_##OP, SIGN, def); +#define I32CASE(OP) CASE(I32x4, OP, SimdSign::Signed) +#define F32CASE(OP) CASE(F32x4, OP, SimdSign::NotApplicable) +#define B32CASE(OP) CASE(B32x4, OP, SimdSign::NotApplicable) #define ENUMERATE(TYPE, FORALL, DO) \ case Expr::TYPE##Const: \ return EmitLiteral(f, ExprType::TYPE, def); \ case Expr::TYPE##Constructor: \ - return EmitSimdOp(f, ExprType::TYPE, SimdOperation::Constructor, def); \ + return EmitSimdOp(f, ExprType::TYPE, SimdOperation::Constructor, \ + SimdSign::NotApplicable, def); \ FORALL(DO) ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32CASE) @@ -2912,12 +2945,30 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may #undef F32CASE #undef B32CASE #undef ENUMERATE + // SIMD unsigned integer operations. + case Expr::I32x4shiftRightByScalarU: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_shiftRightByScalar, + SimdSign::Unsigned, def); + case Expr::I32x4lessThanU: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_lessThan, SimdSign::Unsigned, def); + case Expr::I32x4lessThanOrEqualU: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_lessThanOrEqual, + SimdSign::Unsigned, def); + case Expr::I32x4greaterThanU: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_greaterThan, SimdSign::Unsigned, + def); + case Expr::I32x4greaterThanOrEqualU: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_greaterThanOrEqual, + SimdSign::Unsigned, def); + case Expr::I32x4fromFloat32x4U: + return EmitSimdOp(f, ExprType::I32x4, SimdOperation::Fn_fromFloat32x4, + SimdSign::Unsigned, def); + // Future opcodes case Expr::Loop: case Expr::Select: case Expr::Br: case Expr::BrIf: - case Expr::I8Const: case Expr::I32Ctz: case Expr::I32Popcnt: case Expr::F32CopySign: @@ -3010,7 +3061,7 @@ wasm::IonCompileFunction(IonCompileTask* task) CompileInfo compileInfo(func.numLocals()); MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo, IonOptimizations.get(OptimizationLevel::AsmJS), - task->args().useSignalHandlersForOOB); + task->mg().args().useSignalHandlersForOOB); // Build MIR graph { @@ -3019,17 +3070,17 @@ wasm::IonCompileFunction(IonCompileTask* task) return false; MDefinition* last = nullptr; - if (f.mg().isAsmJS()) { - while (!f.done()) { - if (!EmitExprStmt(f, &last)) + if (uint32_t numExprs = f.readVarU32()) { + for (uint32_t i = 0; i < numExprs - 1; i++) { + if (!EmitExpr(f, ExprType::Void, &last)) return false; } - } else { + if (!EmitExpr(f, f.sig().ret(), &last)) return false; - MOZ_ASSERT(f.done()); } + MOZ_ASSERT(f.done()); MOZ_ASSERT(IsVoid(f.sig().ret()) || f.inDeadCode() || last); if (IsVoid(f.sig().ret())) diff --git a/js/src/asmjs/WasmIonCompile.h b/js/src/asmjs/WasmIonCompile.h index b3d49b2c51..fe7a70b84a 100644 --- a/js/src/asmjs/WasmIonCompile.h +++ b/js/src/asmjs/WasmIonCompile.h @@ -67,7 +67,6 @@ class FuncCompileResults class IonCompileTask { JSRuntime* const runtime_; - const CompileArgs args_; ModuleGeneratorThreadView& mg_; LifoAlloc lifo_; UniqueFuncBytecode func_; @@ -77,13 +76,8 @@ class IonCompileTask IonCompileTask& operator=(const IonCompileTask&) = delete; public: - IonCompileTask(JSRuntime* rt, CompileArgs args, ModuleGeneratorThreadView& mg, - size_t defaultChunkSize) - : runtime_(rt), - args_(args), - mg_(mg), - lifo_(defaultChunkSize), - func_(nullptr) + IonCompileTask(JSRuntime* rt, ModuleGeneratorThreadView& mg, size_t defaultChunkSize) + : runtime_(rt), mg_(mg), lifo_(defaultChunkSize), func_(nullptr) {} JSRuntime* runtime() const { return runtime_; @@ -91,9 +85,6 @@ class IonCompileTask LifoAlloc& lifo() { return lifo_; } - CompileArgs args() const { - return args_; - } ModuleGeneratorThreadView& mg() const { return mg_; } diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index 2cc1c50642..685b96604a 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -34,6 +34,7 @@ using namespace js; using namespace js::wasm; + using mozilla::BitwiseCast; using mozilla::CountLeadingZeroes32; using mozilla::CheckedInt; @@ -337,17 +338,17 @@ class WasmAstFunc : public WasmAstNode { const uint32_t sigIndex_; WasmAstValTypeVector varTypes_; - WasmAstExpr* const maybeBody_; + WasmAstExprVector body_; public: - WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExpr* maybeBody) + WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExprVector&& body) : sigIndex_(sigIndex), varTypes_(Move(varTypes)), - maybeBody_(maybeBody) + body_(Move(body)) {} uint32_t sigIndex() const { return sigIndex_; } const WasmAstValTypeVector& varTypes() const { return varTypes_; } - WasmAstExpr* maybeBody() const { return maybeBody_; } + const WasmAstExprVector& body() const { return body_; } }; class WasmAstImport : public WasmAstNode @@ -2421,9 +2422,9 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module) } } - WasmAstExpr* maybeBody = nullptr; + WasmAstExprVector body(c.lifo); - while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) { + while (c.ts.getIf(WasmToken::OpenParen)) { WasmToken token = c.ts.get(); switch (token.kind()) { case WasmToken::Local: @@ -2440,8 +2441,8 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module) break; default: c.ts.unget(token); - maybeBody = ParseExprInsideParens(c); - if (!maybeBody) + WasmAstExpr* expr = ParseExprInsideParens(c); + if (!expr || !body.append(expr)) return nullptr; break; } @@ -2454,7 +2455,7 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module) return nullptr; } - return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody); + return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), Move(body)); } static bool @@ -2720,7 +2721,7 @@ EncodeCall(Encoder& e, WasmAstCall& c) if (!e.writeExpr(c.expr())) return false; - if (!e.writeU32(c.index())) + if (!e.writeVarU32(c.index())) return false; if (!EncodeArgs(e, c.args())) @@ -2735,7 +2736,7 @@ EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c) if (!e.writeExpr(Expr::CallIndirect)) return false; - if (!e.writeU32(c.sigIndex())) + if (!e.writeVarU32(c.sigIndex())) return false; if (!EncodeExpr(e, *c.index())) @@ -2759,10 +2760,10 @@ EncodeConst(Encoder& e, WasmAstConst& c) e.writeVarU64(c.val().i64()); case ValType::F32: return e.writeExpr(Expr::F32Const) && - e.writeF32(c.val().f32()); + e.writeFixedF32(c.val().f32()); case ValType::F64: return e.writeExpr(Expr::F64Const) && - e.writeF64(c.val().f64()); + e.writeFixedF64(c.val().f64()); default: break; } @@ -2892,7 +2893,7 @@ EncodeSignatureSection(Encoder& e, WasmAstModule& module) if (module.sigs().empty()) return true; - if (!e.writeCString(SigSection)) + if (!e.writeCString(SigLabel)) return false; size_t offset; @@ -2925,7 +2926,7 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module) if (module.funcs().empty()) return true; - if (!e.writeCString(DeclSection)) + if (!e.writeCString(DeclLabel)) return false; size_t offset; @@ -2954,9 +2955,6 @@ EncodeCString(Encoder& e, TwoByteChars twoByteChars) static bool EncodeImport(Encoder& e, WasmAstImport& imp) { - if (!e.writeCString(FuncSubsection)) - return false; - if (!e.writeVarU32(imp.sigIndex())) return false; @@ -2975,21 +2973,23 @@ EncodeImportSection(Encoder& e, WasmAstModule& module) if (module.imports().empty()) return true; - if (!e.writeCString(ImportSection)) + if (!e.writeCString(ImportLabel)) return false; size_t offset; if (!e.startSection(&offset)) return false; - if (!e.writeVarU32(module.imports().length())) - return false; - for (WasmAstImport* imp : module.imports()) { + if (!e.writeCString(FuncLabel)) + return false; if (!EncodeImport(e, *imp)) return false; } + if (!e.writeCString(EndLabel)) + return false; + e.finishSection(offset); return true; } @@ -3000,7 +3000,7 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module) if (!module.maybeMemory()) return true; - if (!e.writeCString(MemorySection)) + if (!e.writeCString(MemoryLabel)) return false; size_t offset; @@ -3009,12 +3009,15 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module) WasmAstMemory& memory = *module.maybeMemory(); - if (!e.writeCString(FieldInitial)) + if (!e.writeCString(InitialLabel)) return false; if (!e.writeVarU32(memory.initialSize())) return false; + if (!e.writeCString(EndLabel)) + return false; + e.finishSection(offset); return true; } @@ -3022,9 +3025,6 @@ EncodeMemorySection(Encoder& e, WasmAstModule& module) static bool EncodeFunctionExport(Encoder& e, WasmAstExport& exp) { - if (!e.writeCString(FuncSubsection)) - return false; - if (!e.writeVarU32(exp.funcIndex())) return false; @@ -3037,9 +3037,6 @@ EncodeFunctionExport(Encoder& e, WasmAstExport& exp) static bool EncodeMemoryExport(Encoder& e, WasmAstExport& exp) { - if (!e.writeCString(MemorySubsection)) - return false; - if (!EncodeCString(e, exp.name())) return false; @@ -3052,29 +3049,33 @@ EncodeExportSection(Encoder& e, WasmAstModule& module) if (module.exports().empty()) return true; - if (!e.writeCString(ExportSection)) + if (!e.writeCString(ExportLabel)) return false; size_t offset; if (!e.startSection(&offset)) return false; - if (!e.writeVarU32(module.exports().length())) - return false; - for (WasmAstExport* exp : module.exports()) { switch (exp->kind()) { case WasmAstExportKind::Func: + if (!e.writeCString(FuncLabel)) + return false; if (!EncodeFunctionExport(e, *exp)) return false; break; case WasmAstExportKind::Memory: + if (!e.writeCString(MemoryLabel)) + return false; if (!EncodeMemoryExport(e, *exp)) return false; break; } } + if (!e.writeCString(EndLabel)) + return false; + e.finishSection(offset); return true; } @@ -3085,7 +3086,7 @@ EncodeTableSection(Encoder& e, WasmAstModule& module) if (!module.maybeTable()) return true; - if (!e.writeCString(TableSection)) + if (!e.writeCString(TableLabel)) return false; size_t offset; @@ -3105,9 +3106,9 @@ EncodeTableSection(Encoder& e, WasmAstModule& module) } static bool -EncodeFunc(Encoder& e, WasmAstFunc& func) +EncodeFunctionSection(Encoder& e, WasmAstFunc& func) { - if (!e.writeCString(FuncSubsection)) + if (!e.writeCString(FuncLabel)) return false; size_t offset; @@ -3122,11 +3123,11 @@ EncodeFunc(Encoder& e, WasmAstFunc& func) return false; } - if (func.maybeBody()) { - if (!EncodeExpr(e, *func.maybeBody())) - return false; - } else { - if (!e.writeExpr(Expr::Nop)) + if (!e.writeVarU32(func.body().length())) + return false; + + for (WasmAstExpr* expr : func.body()) { + if (!EncodeExpr(e, *expr)) return false; } @@ -3135,37 +3136,9 @@ EncodeFunc(Encoder& e, WasmAstFunc& func) return true; } -static bool -EncodeCodeSection(Encoder& e, WasmAstModule& module) -{ - if (module.funcs().empty()) - return true; - - if (!e.writeCString(CodeSection)) - return false; - - size_t offset; - if (!e.startSection(&offset)) - return false; - - if (!e.writeVarU32(module.funcs().length())) - return false; - - for (WasmAstFunc* func : module.funcs()) { - if (!EncodeFunc(e, *func)) - return false; - } - - e.finishSection(offset); - return true; -} - static bool EncodeDataSegment(Encoder& e, WasmAstSegment& segment) { - if (!e.writeCString(SegmentSubsection)) - return false; - if (!e.writeVarU32(segment.offset())) return false; @@ -3186,7 +3159,7 @@ EncodeDataSegment(Encoder& e, WasmAstSegment& segment) if (!e.writeVarU32(bytes.length())) return false; - if (!e.writeData(bytes.begin(), bytes.length())) + if (!e.writeRawData(bytes.begin(), bytes.length())) return false; return true; @@ -3200,21 +3173,23 @@ EncodeDataSection(Encoder& e, WasmAstModule& module) const WasmAstSegmentVector& segments = module.maybeMemory()->segments(); - if (!e.writeCString(DataSection)) + if (!e.writeCString(DataLabel)) return false; size_t offset; if (!e.startSection(&offset)) return false; - if (!e.writeVarU32(segments.length())) - return false; - for (WasmAstSegment* segment : segments) { + if (!e.writeCString(SegmentLabel)) + return false; if (!EncodeDataSegment(e, *segment)) return false; } + if (!e.writeCString(EndLabel)) + return false; + e.finishSection(offset); return true; } @@ -3228,10 +3203,10 @@ EncodeModule(WasmAstModule& module) Encoder e(*bytecode); - if (!e.writeU32(MagicNumber)) + if (!e.writeFixedU32(MagicNumber)) return nullptr; - if (!e.writeU32(EncodingVersion)) + if (!e.writeFixedU32(EncodingVersion)) return nullptr; if (!EncodeSignatureSection(e, module)) @@ -3252,13 +3227,15 @@ EncodeModule(WasmAstModule& module) if (!EncodeExportSection(e, module)) return nullptr; - if (!EncodeCodeSection(e, module)) - return nullptr; + for (WasmAstFunc* func : module.funcs()) { + if (!EncodeFunctionSection(e, *func)) + return nullptr; + } if (!EncodeDataSection(e, module)) return nullptr; - if (!e.writeCString(EndSection)) + if (!e.writeCString(EndLabel)) return nullptr; return Move(bytecode); diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 1865ee1c43..72dde4ee30 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -741,9 +741,13 @@ _(fromFloat32x4) \ _(fromFloat32x4Bits) \ _(fromInt32x4) \ - _(fromInt32x4Bits) + _(fromInt32x4Bits) \ + _(fromUint32x4) \ + _(fromUint32x4Bits) -// All operations on Int32x4 in the asm.js world +// All operations on Int32x4 or Uint32x4 in the asm.js world. +// Note: this does not include conversions and casts to/from Uint32x4 because +// this list is shared between Int32x4 and Uint32x4. #define FORALL_INT32X4_ASMJS_OP(_) \ FORALL_INT_SIMD_OP(_) \ FOREACH_MEMORY_X4_SIMD_OP(_) \ @@ -755,27 +759,9 @@ FORALL_FLOAT_SIMD_OP(_) \ FOREACH_MEMORY_X4_SIMD_OP(_) \ _(fromInt32x4) \ - _(fromInt32x4Bits) - -// TODO: remove when all SIMD calls are inlined (bug 1112155) -#define ION_COMMONX4_SIMD_OP(_) \ - FOREACH_NUMERIC_SIMD_BINOP(_) \ - _(extractLane) \ - _(replaceLane) \ - _(select) \ - _(splat) \ - _(neg) \ - _(swizzle) \ - _(shuffle) \ - _(load) \ - _(load1) \ - _(load2) \ - _(load3) \ - _(store) \ - _(store1) \ - _(store2) \ - _(store3) \ - _(check) + _(fromInt32x4Bits) \ + _(fromUint32x4) \ + _(fromUint32x4Bits) namespace js { diff --git a/js/src/jit-test/tests/wasm/basic-float.js b/js/src/jit-test/tests/wasm/basic-float.js index 79a758b78e..e5d0f6763e 100644 --- a/js/src/jit-test/tests/wasm/basic-float.js +++ b/js/src/jit-test/tests/wasm/basic-float.js @@ -64,6 +64,7 @@ assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f64) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("i32", "f64")); assertErrorMessage(() => wasmEvalText('(module (func (param f64) (result i32) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("f64", "i32")); assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i32) (f64.sqrt (get_local 0))))'), TypeError, mismatchError("f64", "i32")); +assertErrorMessage(() => wasmEvalText('(module (func (f32.sqrt (nop))))'), TypeError, mismatchError("void", "f32")); assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result f32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32")); assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result f32) (f32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32")); diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index d5ed26fbaa..b1de3f9bb3 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -238,6 +238,8 @@ assertErrorMessage(() => wasmEvalText('(module (func (result f32) (block (i32.co assertEq(wasmEvalText('(module (func (result i32) (block (i32.const 13) (block (i32.const 42)))) (export "" 0))')(), 42); assertErrorMessage(() => wasmEvalText('(module (func (result f32) (param f32) (block (get_local 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32")); +assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (i32.const 42)) (get_local 0)) (export "" 0))')(), 42); + // ---------------------------------------------------------------------------- // calls diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index c46b49e1ea..b9060c385b 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -13,14 +13,13 @@ const ver2 = 0xff; const ver3 = 0xff; // Section names -const sigSectionStr = "sig"; -const declSectionStr = "decl"; -const importSectionStr = "import"; -const exportSectionStr = "export"; -const codeSectionStr = "code"; -const dataSectionStr = "data"; -const funcSubsectionStr = "func"; -const tableStr = "table"; +const sigLabel = "sig"; +const declLabel = "decl"; +const tableLabel = "table"; +const importLabel = "import"; +const exportLabel = "export"; +const funcLabel = "func"; +const dataLabel = "data"; const magicError = /failed to match magic number/; const versionError = /failed to match binary version/; @@ -73,17 +72,11 @@ function cstring(name) { return (name + '\0').split('').map(c => c.charCodeAt(0)); } -function sectionLength(length) { - var i32 = new Uint32Array(1); - i32[0] = length; - return new Uint8Array(i32.buffer); -} - function moduleWithSections(sectionArray) { var bytes = moduleHeaderThen(); for (let section of sectionArray) { bytes.push(...cstring(section.name)); - bytes.push(...sectionLength(section.body.length)); + bytes.push(...varU32(section.body.length)); bytes.push(...section.body); } bytes.push(0); @@ -99,7 +92,7 @@ function sigSection(sigs) { for (let arg of sig.args) body.push(...varU32(arg)); } - return { name: sigSectionStr, body }; + return { name: sigLabel, body }; } function declSection(decls) { @@ -107,33 +100,28 @@ function declSection(decls) { body.push(...varU32(decls.length)); for (let decl of decls) body.push(...varU32(decl)); - return { name: declSectionStr, body }; + return { name: declLabel, body }; } -function codeSection(funcs) { +function funcSection(func) { var body = []; - body.push(...varU32(funcs.length)); - for (let func of funcs) { - body.push(...cstring(funcSubsectionStr)); - var locals = varU32(func.locals.length); - for (let local of func.locals) - locals.push(...varU32(local)); - body.push(...sectionLength(locals.length + func.body.length)); - body = body.concat(locals, func.body); - } - return { name: codeSectionStr, body }; + var locals = varU32(func.locals.length); + for (let local of func.locals) + locals.push(...varU32(local)); + body = body.concat(locals, func.body); + return { name: funcLabel, body }; } function importSection(imports) { var body = []; - body.push(...varU32(imports.length)); for (let imp of imports) { - body.push(...cstring(funcSubsectionStr)); + body.push(...cstring(funcLabel)); body.push(...varU32(imp.sigIndex)); body.push(...cstring(imp.module)); body.push(...cstring(imp.func)); } - return { name: importSectionStr, body }; + body.push(0); + return { name: importLabel, body }; } function tableSection(elems) { @@ -141,24 +129,19 @@ function tableSection(elems) { body.push(...varU32(elems.length)); for (let i of elems) body.push(...varU32(i)); - return { name: tableStr, body }; + return { name: tableLabel, body }; } const v2vSig = {args:[], ret:VoidCode}; const i2vSig = {args:[I32Code], ret:VoidCode}; -const trivialSigSection = sigSection([v2vSig]); -const trivialDeclSection = declSection([0]); -const nopFunc = {locals:[], body:[0, 0]}; -const trivialCodeSection = codeSection([nopFunc]); +const v2vFunc = funcSection({locals:[], body:[0]}); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigSectionStr, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/); assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}, {args:[], ret:VoidCode}])]))), TypeError, /duplicate signature/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body: U32MAX_LEB, }]))), TypeError, /too many imports/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: exportSectionStr, body: U32MAX_LEB, }]))), TypeError, /too many exports/); -assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1]}]))), TypeError); -assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), TypeError); +assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1]}]))), TypeError); +assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1, 1, 0]}]))), TypeError); wasmEval(toBuf(moduleWithSections([sigSection([])]))); wasmEval(toBuf(moduleWithSections([sigSection([v2vSig])]))); @@ -169,28 +152,28 @@ assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[] assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/); -assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([1])]))), TypeError, /signature index out of range/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0])]))), TypeError, /different number of definitions than declarations/); -wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, trivialCodeSection]))); +assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([1])]))), TypeError, /signature index out of range/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /fewer function definitions than declarations/); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), v2vFunc]))); -assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body:[]}]))), TypeError); +assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), {name: importLabel, body:[]}]))), TypeError); assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])]))), TypeError, /signature index out of range/); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/); -wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([])]))); -wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}}); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([])]))); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}}); wasmEval(toBuf(moduleWithSections([ - trivialSigSection, + sigSection([v2vSig]), importSection([{sigIndex:0, module:"a", func:""}]), - trivialDeclSection, - trivialCodeSection])), {a:()=>{}}); + declSection([0]), + v2vFunc])), {a:()=>{}}); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataSectionStr, body: [], } ]))), TypeError, /data section requires a memory section/); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataLabel, body: [], } ]))), TypeError, /data section requires a memory section/); wasmEval(toBuf(moduleWithSections([tableSection([])]))); assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([tableSection([0])]))), TypeError, /table element out of range/); -wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0]), trivialCodeSection]))); -wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0,0]), trivialCodeSection]))); -assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, tableSection([0,1]), trivialCodeSection]))), TypeError, /table element out of range/); -wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0,0,0]), tableSection([0,1,0,2]), codeSection([nopFunc,nopFunc,nopFunc])]))); -wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), codeSection([nopFunc,nopFunc,nopFunc])]))); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), v2vFunc]))); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), v2vFunc]))); +assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), v2vFunc]))), TypeError, /table element out of range/); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), v2vFunc, v2vFunc, v2vFunc]))); +wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), v2vFunc, v2vFunc, v2vFunc]))); diff --git a/js/src/jit/AlignmentMaskAnalysis.cpp b/js/src/jit/AlignmentMaskAnalysis.cpp index 33a3853ef8..22df372ff4 100644 --- a/js/src/jit/AlignmentMaskAnalysis.cpp +++ b/js/src/jit/AlignmentMaskAnalysis.cpp @@ -59,8 +59,8 @@ AnalyzeAsmHeapAddress(MDefinition* ptr, MIRGraph& graph) if (!op1->isConstant()) return; - uint32_t i = op1->toConstant()->value().toInt32(); - uint32_t m = rhs->toConstant()->value().toInt32(); + uint32_t i = op1->toConstant()->toInt32(); + uint32_t m = rhs->toConstant()->toInt32(); if (!IsAlignmentMask(m) || (i & m) != i) return; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index cef9553d61..e0f34a8abc 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -2414,7 +2414,7 @@ CodeGenerator::visitStackArgT(LStackArgT* lir) else if (arg->isRegister()) masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest); else - masm.storeValue(*(arg->toConstant()), dest); + masm.storeValue(arg->toConstant()->toJSValue(), dest); } void @@ -2549,7 +2549,7 @@ CodeGenerator::visitStoreSlotT(LStoreSlotT* lir) } else { ConstantOrRegister value; if (lir->value()->isConstant()) - value = ConstantOrRegister(*lir->value()->toConstant()); + value = ConstantOrRegister(lir->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value())); masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType()); @@ -2740,7 +2740,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins) ConstantOrRegister value; if (ins->mir()->value()->isConstant()) - value = ConstantOrRegister(ins->mir()->value()->toConstant()->value()); + value = ConstantOrRegister(ins->mir()->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(ins->value())); @@ -7099,7 +7099,7 @@ static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, MIRType valueType) { if (value->isConstant()) - return ConstantOrRegister(*value->toConstant()); + return ConstantOrRegister(value->toConstant()->toJSValue()); return TypedOrValueRegister(valueType, ToAnyRegister(value)); } @@ -7291,7 +7291,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); if (store->value()->isConstant()) - value = ConstantOrRegister(*store->value()->toConstant()); + value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); unboxedType = store->mir()->unboxedType(); @@ -7388,7 +7388,7 @@ StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAlloca if (preBarrier) masm.patchableCallPreBarrier(address, type); if (value->isConstant()) { - Value v = *value->toConstant(); + Value v = value->toConstant()->toJSValue(); if (v.isMarkable()) { masm.storePtr(ImmGCPtr(v.toGCThing()), address); } else { @@ -7656,7 +7656,7 @@ CodeGenerator::visitArrayPushT(LArrayPushT* lir) Register length = ToRegister(lir->output()); ConstantOrRegister value; if (lir->value()->isConstant()) - value = ConstantOrRegister(*lir->value()->toConstant()); + value = ConstantOrRegister(lir->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value())); emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length); @@ -8824,7 +8824,7 @@ CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) masm.storeObjectOrNull(nvalue, address); } else { ConstantOrRegister nvalue = value->isConstant() - ? ConstantOrRegister(*value->toConstant()) + ? ConstantOrRegister(value->toConstant()->toJSValue()) : TypedOrValueRegister(valueType, ToAnyRegister(value)); masm.storeConstantOrRegister(nvalue, address); } @@ -8894,7 +8894,7 @@ CodeGenerator::toConstantOrRegister(LInstruction* lir, size_t n, MIRType type) const LAllocation* value = lir->getOperand(n); if (value->isConstant()) - return ConstantOrRegister(*value->toConstant()); + return ConstantOrRegister(value->toConstant()->toJSValue()); return TypedOrValueRegister(type, ToAnyRegister(value)); } diff --git a/js/src/jit/EffectiveAddressAnalysis.cpp b/js/src/jit/EffectiveAddressAnalysis.cpp index 1ae27643f0..790a761839 100644 --- a/js/src/jit/EffectiveAddressAnalysis.cpp +++ b/js/src/jit/EffectiveAddressAnalysis.cpp @@ -27,11 +27,10 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* lsh) if (!shiftValue) return; - Value shiftv = shiftValue->value(); - if (!shiftv.isInt32() || !IsShiftInScaleRange(shiftv.toInt32())) + if (shiftValue->type() != MIRType_Int32 || !IsShiftInScaleRange(shiftValue->toInt32())) return; - Scale scale = ShiftToScale(shiftv.toInt32()); + Scale scale = ShiftToScale(shiftValue->toInt32()); int32_t displacement = 0; MInstruction* last = lsh; @@ -51,7 +50,7 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* lsh) MDefinition* other = add->getOperand(1 - add->indexOf(*use)); if (MConstant* otherConst = other->maybeConstantValue()) { - displacement += otherConst->value().toInt32(); + displacement += otherConst->toInt32(); } else { if (base) break; @@ -85,7 +84,7 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* lsh) return; uint32_t bitsClearedByShift = elemSize - 1; - uint32_t bitsClearedByMask = ~uint32_t(otherConst->value().toInt32()); + uint32_t bitsClearedByMask = ~uint32_t(otherConst->toInt32()); if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask) return; @@ -143,7 +142,7 @@ EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins) // is always the address mode immediate. This also allows it to avoid // a situation where the sum of a constant pointer value and a non-zero // offset doesn't actually fit into the address mode immediate. - int32_t imm = ptr->toConstant()->value().toInt32(); + int32_t imm = ptr->toConstant()->toInt32(); if (imm != 0 && tryAddDisplacement(ins, imm)) { MInstruction* zero = MConstant::New(graph_.alloc(), Int32Value(0)); ins->block()->insertBefore(ins, zero); @@ -158,7 +157,7 @@ EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins) if (op0->isConstant()) mozilla::Swap(op0, op1); if (op1->isConstant()) { - int32_t imm = op1->toConstant()->value().toInt32(); + int32_t imm = op1->toConstant()->toInt32(); if (tryAddDisplacement(ins, imm)) ins->replacePtr(op0); } diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 787dee862b..82679a84bf 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2708,11 +2708,8 @@ jit::ExtractLinearSum(MDefinition* ins) if (ins->type() != MIRType_Int32) return SimpleLinearSum(ins, 0); - if (ins->isConstant()) { - const Value& v = ins->toConstant()->value(); - MOZ_ASSERT(v.isInt32()); - return SimpleLinearSum(nullptr, v.toInt32()); - } + if (ins->isConstant()) + return SimpleLinearSum(nullptr, ins->toConstant()->toInt32()); if (ins->isAdd() || ins->isSub()) { MDefinition* lhs = ins->getOperand(0); @@ -3333,7 +3330,7 @@ LinearSum::add(MDefinition* term, int32_t scale) return true; if (MConstant* termConst = term->maybeConstantValue()) { - int32_t constant = termConst->value().toInt32(); + int32_t constant = termConst->toInt32(); if (!SafeMul(constant, scale, &constant)) return false; return add(constant); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index b6554e66a4..6e0a72b0bb 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3685,10 +3685,9 @@ bool IonBuilder::improveTypesAtTypeOfCompare(MCompare* ins, bool trueBranch, MTest* test) { MTypeOf* typeOf = ins->lhs()->isTypeOf() ? ins->lhs()->toTypeOf() : ins->rhs()->toTypeOf(); - MConstant* constantDef = ins->lhs()->isConstant() ? ins->lhs()->toConstant() : ins->rhs()->toConstant(); - const Value* constant = constantDef->vp(); + MConstant* constant = ins->lhs()->isConstant() ? ins->lhs()->toConstant() : ins->rhs()->toConstant(); - if (!constant->isString()) + if (constant->type() != MIRType_String) return true; bool equal = ins->jsop() == JSOP_EQ || ins->jsop() == JSOP_STRICTEQ; @@ -4851,7 +4850,7 @@ IonBuilder::arithTrySharedStub(bool* emitted, JSOp op, case JSOP_NEG: case JSOP_BITNOT: MOZ_ASSERT_IF(op == JSOP_MUL, - left->maybeConstantValue() && left->maybeConstantValue()->value().toInt32() == -1); + left->maybeConstantValue() && left->maybeConstantValue()->toInt32() == -1); MOZ_ASSERT_IF(op != JSOP_MUL, !left); stub = MUnarySharedStub::New(alloc(), right); @@ -6861,7 +6860,7 @@ IonBuilder::jsop_eval(uint32_t argc) string->getOperand(1)->type() == MIRType_String && string->getOperand(1)->maybeConstantValue()) { - JSAtom* atom = &string->getOperand(1)->maybeConstantValue()->value().toString()->asAtom(); + JSAtom* atom = &string->getOperand(1)->maybeConstantValue()->toString()->asAtom(); if (StringEqualsAscii(atom, "()")) { MDefinition* name = string->getOperand(0); @@ -8282,7 +8281,7 @@ IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name) // Pop the bound object on the stack. MDefinition* obj = current->pop(); - MOZ_ASSERT(&obj->toConstant()->value().toObject() == staticObject); + MOZ_ASSERT(&obj->toConstant()->toObject() == staticObject); if (NeedsPostBarrier(value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); @@ -8953,7 +8952,7 @@ IonBuilder::getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* inde MConstant* indexConst = index->maybeConstantValue(); jsid id; - if (!indexConst || !ValueToIdPure(indexConst->value(), &id)) + if (!indexConst || !ValueToIdPure(indexConst->toJSValue(), &id)) return true; if (id != IdToTypeId(id)) @@ -9223,7 +9222,7 @@ IonBuilder::getElemTryArgumentsInlined(bool* emitted, MDefinition* obj, MDefinit if (indexConst && indexConst->type() == MIRType_Int32) { MOZ_ASSERT(inliningDepth_ > 0); - int32_t id = indexConst->value().toInt32(); + int32_t id = indexConst->toInt32(); index->setImplicitlyUsedUnchecked(); if (id < (int32_t)inlineCallInfo_->argc() && id >= 0) @@ -9465,7 +9464,7 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition* obj, if (MConstant* objConst = obj->maybeConstantValue()) { if (objConst->type() == MIRType_Object) - tarr = &objConst->value().toObject(); + tarr = &objConst->toObject(); } else if (TemporaryTypeSet* types = obj->resultTypeSet()) { tarr = types->maybeSingleton(); } @@ -9527,7 +9526,7 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id, // absolute offset being accessed. if (MConstant* idConst = id->maybeConstantValue()) { if (idConst->type() == MIRType_Int32) { - int32_t index = idConst->value().toInt32(); + int32_t index = idConst->toInt32(); MConstant* offset = MConstant::New(alloc(), Int32Value(index << TypedArrayShift(viewType))); current->add(offset); return offset; @@ -9536,15 +9535,16 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id, if (!id->isRsh() || id->isEffectful()) return nullptr; - if (!id->getOperand(1)->maybeConstantValue()) + + MConstant* shiftAmount = id->toRsh()->rhs()->maybeConstantValue(); + if (!shiftAmount || shiftAmount->type() != MIRType_Int32) return nullptr; - const Value& value = id->getOperand(1)->maybeConstantValue()->value(); - if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType)) + if (uint32_t(shiftAmount->toInt32()) != TypedArrayShift(viewType)) return nullptr; // Instead of shifting, mask off the low bits of the index so that // a non-scaled access on the typed array can be performed. - MConstant* mask = MConstant::New(alloc(), Int32Value(~((1 << value.toInt32()) - 1))); + MConstant* mask = MConstant::New(alloc(), Int32Value(~((1 << shiftAmount->toInt32()) - 1))); MBitAnd* ptr = MBitAnd::New(alloc(), id->getOperand(0), mask); ptr->infer(nullptr, nullptr); @@ -13358,8 +13358,9 @@ IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id) MOZ_ASSERT(!*emitted); + MConstant* idConst = id->maybeConstantValue(); jsid propId; - if (!id->maybeConstantValue() || !ValueToIdPure(id->maybeConstantValue()->value(), &propId)) + if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId)) return true; if (propId != IdToTypeId(propId)) diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 517c389fae..fac2269d99 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -69,7 +69,7 @@ class LAllocation : public TempObject public: enum Kind { - CONSTANT_VALUE, // Constant js::Value. + CONSTANT_VALUE, // MConstant*. CONSTANT_INDEX, // Constant arbitrary index. USE, // Use of a virtual register, with physical allocation policy. GPR, // General purpose register. @@ -107,10 +107,10 @@ class LAllocation : public TempObject MOZ_ASSERT(isBogus()); } - // The value pointer must be rooted in MIR and have its low bits cleared. - explicit LAllocation(const Value* vp) { - MOZ_ASSERT(vp); - bits_ = uintptr_t(vp); + // The MConstant pointer must have its low bits cleared. + explicit LAllocation(const MConstant* c) { + MOZ_ASSERT(c); + bits_ = uintptr_t(c); MOZ_ASSERT((bits_ & (KIND_MASK << KIND_SHIFT)) == 0); bits_ |= CONSTANT_VALUE << KIND_SHIFT; } @@ -166,9 +166,9 @@ class LAllocation : public TempObject inline const LConstantIndex* toConstantIndex() const; inline AnyRegister toRegister() const; - const Value* toConstant() const { + const MConstant* toConstant() const { MOZ_ASSERT(isConstantValue()); - return reinterpret_cast(bits_ & ~(KIND_MASK << KIND_SHIFT)); + return reinterpret_cast(bits_ & ~(KIND_MASK << KIND_SHIFT)); } bool operator ==(const LAllocation& other) const { diff --git a/js/src/jit/LoopUnroller.cpp b/js/src/jit/LoopUnroller.cpp index 4792ca9ef9..bf1e577f00 100644 --- a/js/src/jit/LoopUnroller.cpp +++ b/js/src/jit/LoopUnroller.cpp @@ -68,7 +68,7 @@ LoopUnroller::getReplacementDefinition(MDefinition* def) // the block itself. MOZ_ASSERT(def->isConstant()); - MConstant* constant = MConstant::New(alloc, def->toConstant()->value()); + MConstant* constant = new(alloc) MConstant(*def->toConstant()); oldPreheader->insertBefore(*oldPreheader->begin(), constant); return constant; } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index ac248e13ca..b7166f1783 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1596,7 +1596,7 @@ LIRGenerator::visitMul(MMul* ins) // If our RHS is a constant -1 and we don't have to worry about // overflow, we can optimize to an LNegI. - if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->value().toInt32() == -1) + if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->toInt32() == -1) defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0); else lowerMulI(ins, lhs, rhs); @@ -1605,7 +1605,7 @@ LIRGenerator::visitMul(MMul* ins) ReorderCommutative(&lhs, &rhs, ins); // If our RHS is a constant -1.0, we can optimize to an LNegD. - if (rhs->isConstant() && rhs->toConstant()->value().toDouble() == -1.0) + if (rhs->isConstant() && rhs->toConstant()->toDouble() == -1.0) defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0); else lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs); @@ -1614,7 +1614,7 @@ LIRGenerator::visitMul(MMul* ins) ReorderCommutative(&lhs, &rhs, ins); // We apply the same optimizations as for doubles - if (rhs->isConstant() && rhs->toConstant()->value().toDouble() == -1.0) + if (rhs->isConstant() && rhs->toConstant()->toFloat32() == -1.0f) defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0); else lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs); @@ -2477,6 +2477,17 @@ LIRGenerator::visitMonitorTypes(MMonitorTypes* ins) add(lir, ins); } +// Returns true iff |def| is a constant that's either not a GC thing or is not +// allocated in the nursery. +static bool +IsNonNurseryConstant(MDefinition* def) +{ + if (!def->isConstant()) + return false; + Value v = def->toConstant()->toJSValue(); + return !v.isMarkable() || !IsInsideNursery(v.toGCThing()); +} + void LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) { @@ -2486,9 +2497,7 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins) // object is tenured, and does not need to be tested for being in the // nursery. Ensure that assumption holds by lowering constant nursery // objects to a register. - bool useConstantObject = - ins->object()->isConstant() && - !IsInsideNursery(&ins->object()->toConstant()->value().toObject()); + bool useConstantObject = IsNonNurseryConstant(ins->object()); switch (ins->value()->type()) { case MIRType_Object: @@ -2534,7 +2543,7 @@ LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins) // objects to a register. bool useConstantObject = ins->object()->isConstant() && - !IsInsideNursery(&ins->object()->toConstant()->value().toObject()); + !IsInsideNursery(&ins->object()->toConstant()->toObject()); switch (ins->value()->type()) { case MIRType_Object: @@ -3643,6 +3652,7 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins) // If this is a SETPROP, the id is a constant string. Allow passing it as a // constant to reduce register allocation pressure. bool useConstId = id->type() == MIRType_String || id->type() == MIRType_Symbol; + bool useConstValue = IsNonNurseryConstant(ins->value()); // Set the performs-call flag so that we don't omit the overrecursed check. // This is necessary because the cache can attach a scripted setter stub @@ -3665,7 +3675,7 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins) LInstruction* lir = new(alloc()) LSetPropertyCache(useRegister(ins->object()), temp(), tempToUnboxIndex, tempD, tempF32); useBoxOrTypedOrConstant(lir, LSetPropertyCache::Id, id, useConstId); - useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), /* useConstant = */ true); + useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), useConstValue); add(lir, ins); assignSafepoint(lir, ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 89cc3d1f09..5d6e857e95 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -459,7 +459,7 @@ IonBuilder::inlineArray(CallInfo& callInfo) trackOptimizationOutcome(TrackedOutcome::ArrayRange); // Negative lengths generate a RangeError, unhandled by the inline path. - initLength = arg->toConstant()->value().toInt32(); + initLength = arg->toConstant()->toInt32(); if (initLength > NativeObject::MAX_DENSE_ELEMENTS_COUNT) return InliningStatus_NotInlined; MOZ_ASSERT(initLength <= INT32_MAX); @@ -1276,7 +1276,7 @@ IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outp // Optimize some constant powers. if (rhs->isConstant()) { - double pow = rhs->toConstant()->value().toNumber(); + double pow = rhs->toConstant()->toNumber(); // 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()->value().toDouble(); + double cte = arg->toConstant()->toNumber(); // min(int32, cte >= INT32_MAX) = int32 if (cte >= INT32_MAX && !max) break; @@ -1565,17 +1565,14 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo) if (!callInfo.getArg(0)->isConstant()) return InliningStatus_NotInlined; - const js::Value* argval = callInfo.getArg(0)->toConstant()->vp(); - if (!argval->isString()) + MConstant* argval = callInfo.getArg(0)->toConstant(); + if (argval->type() != MIRType_String) return InliningStatus_NotInlined; - const js::Value* strval = callInfo.thisArg()->toConstant()->vp(); - if (!strval->isString()) + MConstant* strval = callInfo.thisArg()->toConstant(); + if (strval->type() != MIRType_String) return InliningStatus_NotInlined; - MOZ_ASSERT(callInfo.getArg(0)->type() == MIRType_String); - MOZ_ASSERT(callInfo.thisArg()->type() == MIRType_String); - // Check if exist a template object in stub. JSString* stringThis = nullptr; JSString* stringArg = nullptr; @@ -1755,10 +1752,10 @@ IonBuilder::inlineConstantCharCodeAt(CallInfo& callInfo) return InliningStatus_NotInlined; } - const js::Value* strval = callInfo.thisArg()->maybeConstantValue()->vp(); - const js::Value* idxval = callInfo.getArg(0)->maybeConstantValue()->vp(); + MConstant* strval = callInfo.thisArg()->maybeConstantValue(); + MConstant* idxval = callInfo.getArg(0)->maybeConstantValue(); - if (!strval->isString() || !idxval->isInt32()) + if (strval->type() != MIRType_String || idxval->type() != MIRType_Int32) return InliningStatus_NotInlined; JSString* str = strval->toString(); @@ -2423,7 +2420,7 @@ IonBuilder::inlineUnsafeSetReservedSlot(CallInfo& callInfo) MDefinition* arg = callInfo.getArg(1); if (!arg->isConstant()) return InliningStatus_NotInlined; - uint32_t slot = arg->toConstant()->value().toPrivateUint32(); + uint32_t slot = uint32_t(arg->toConstant()->toInt32()); callInfo.setImplicitlyUsedUnchecked(); @@ -2454,7 +2451,7 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueTy MDefinition* arg = callInfo.getArg(1); if (!arg->isConstant()) return InliningStatus_NotInlined; - uint32_t slot = arg->toConstant()->value().toPrivateUint32(); + uint32_t slot = uint32_t(arg->toConstant()->toInt32()); callInfo.setImplicitlyUsedUnchecked(); @@ -2642,7 +2639,7 @@ IonBuilder::inlineAssertFloat32(CallInfo& callInfo) MOZ_ASSERT(secondArg->type() == MIRType_Boolean); MOZ_ASSERT(secondArg->isConstant()); - bool mustBeFloat32 = secondArg->toConstant()->value().toBoolean(); + bool mustBeFloat32 = secondArg->toConstant()->toBoolean(); current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32)); MConstant* undefined = MConstant::New(alloc(), UndefinedValue()); @@ -2672,7 +2669,7 @@ IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo) MOZ_ASSERT(secondArg->type() == MIRType_Boolean); MOZ_ASSERT(secondArg->isConstant()); - bool mustBeRecovered = secondArg->toConstant()->value().toBoolean(); + bool mustBeRecovered = secondArg->toConstant()->toBoolean(); MAssertRecoveredOnBailout* assert = MAssertRecoveredOnBailout::New(alloc(), callInfo.getArg(0), mustBeRecovered); current->add(assert); @@ -3505,7 +3502,7 @@ IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType MDefinition* arg = callInfo.getArg(1); if (!arg->isConstant() || arg->type() != MIRType_Int32) return InliningStatus_NotInlined; - int32_t lane = arg->toConstant()->value().toInt32(); + int32_t lane = arg->toConstant()->toInt32(); if (lane < 0 || lane >= 4) return InliningStatus_NotInlined; @@ -3539,7 +3536,7 @@ IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType if (!arg->isConstant() || arg->type() != MIRType_Int32) return InliningStatus_NotInlined; - int32_t lane = arg->toConstant()->value().toInt32(); + int32_t lane = arg->toConstant()->toInt32(); if (lane < 0 || lane >= 4) return InliningStatus_NotInlined; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 1ad514fc2b..1592851bdd 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -90,49 +90,49 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt if (!left->isConstant() || !right->isConstant()) return nullptr; - Value lhs = left->toConstant()->value(); - Value rhs = right->toConstant()->value(); + MConstant* lhs = left->toConstant(); + MConstant* rhs = right->toConstant(); Value ret = UndefinedValue(); switch (ins->op()) { case MDefinition::Op_BitAnd: - ret = Int32Value(lhs.toInt32() & rhs.toInt32()); + ret = Int32Value(lhs->toInt32() & rhs->toInt32()); break; case MDefinition::Op_BitOr: - ret = Int32Value(lhs.toInt32() | rhs.toInt32()); + ret = Int32Value(lhs->toInt32() | rhs->toInt32()); break; case MDefinition::Op_BitXor: - ret = Int32Value(lhs.toInt32() ^ rhs.toInt32()); + ret = Int32Value(lhs->toInt32() ^ rhs->toInt32()); break; case MDefinition::Op_Lsh: - ret = Int32Value(uint32_t(lhs.toInt32()) << (rhs.toInt32() & 0x1F)); + ret = Int32Value(uint32_t(lhs->toInt32()) << (rhs->toInt32() & 0x1F)); break; case MDefinition::Op_Rsh: - ret = Int32Value(lhs.toInt32() >> (rhs.toInt32() & 0x1F)); + ret = Int32Value(lhs->toInt32() >> (rhs->toInt32() & 0x1F)); break; case MDefinition::Op_Ursh: - ret.setNumber(uint32_t(lhs.toInt32()) >> (rhs.toInt32() & 0x1F)); + ret.setNumber(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F)); break; case MDefinition::Op_Add: - ret.setNumber(lhs.toNumber() + rhs.toNumber()); + ret.setNumber(lhs->toNumber() + rhs->toNumber()); break; case MDefinition::Op_Sub: - ret.setNumber(lhs.toNumber() - rhs.toNumber()); + ret.setNumber(lhs->toNumber() - rhs->toNumber()); break; case MDefinition::Op_Mul: - ret.setNumber(lhs.toNumber() * rhs.toNumber()); + ret.setNumber(lhs->toNumber() * rhs->toNumber()); break; case MDefinition::Op_Div: if (ins->toDiv()->isUnsigned()) - ret.setInt32(rhs.isInt32(0) ? 0 : uint32_t(lhs.toInt32()) / uint32_t(rhs.toInt32())); + 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->toNumber(), rhs->toNumber())); break; case MDefinition::Op_Mod: if (ins->toMod()->isUnsigned()) - ret.setInt32(rhs.isInt32(0) ? 0 : uint32_t(lhs.toInt32()) % uint32_t(rhs.toInt32())); + 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->toNumber(), rhs->toNumber())); break; default: MOZ_CRASH("NYI"); @@ -165,10 +165,8 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins) if (!right->isConstant()) return nullptr; - Value rhs = right->toConstant()->value(); - int32_t num; - if (!mozilla::NumberIsInt32(rhs.toNumber(), &num)) + if (!mozilla::NumberIsInt32(right->toConstant()->toNumber(), &num)) return nullptr; // check if rhs is a power of two @@ -790,7 +788,7 @@ MConstant::congruentTo(const MDefinition* ins) const { if (!ins->isConstant()) return false; - return ins->toConstant()->value() == value(); + return ins->toConstant()->value_ == value_; } void @@ -806,23 +804,23 @@ MConstant::printOpcode(GenericPrinter& out) const out.printf("null"); break; case MIRType_Boolean: - out.printf(value().toBoolean() ? "true" : "false"); + out.printf(toBoolean() ? "true" : "false"); break; case MIRType_Int32: - out.printf("0x%x", value().toInt32()); + out.printf("0x%x", toInt32()); break; case MIRType_Double: - out.printf("%.16g", value().toDouble()); + out.printf("%.16g", toDouble()); break; case MIRType_Float32: { - float val = value().toDouble(); + float val = toFloat32(); out.printf("%.16g", val); break; } case MIRType_Object: - if (value().toObject().is()) { - JSFunction* fun = &value().toObject().as(); + if (toObject().is()) { + JSFunction* fun = &toObject().as(); if (fun->displayAtom()) { out.put("function "); EscapedStringPrinter(out, fun->displayAtom(), 0); @@ -837,14 +835,13 @@ MConstant::printOpcode(GenericPrinter& out) const out.printf(" at %p", (void*) fun); break; } - out.printf("object %p (%s)", (void*)&value().toObject(), - value().toObject().getClass()->name); + out.printf("object %p (%s)", (void*)&toObject(), toObject().getClass()->name); break; case MIRType_Symbol: - out.printf("symbol at %p", (void*)value().toSymbol()); + out.printf("symbol at %p", (void*)toSymbol()); break; case MIRType_String: - out.printf("string %p", (void*)value().toString()); + out.printf("string %p", (void*)toString()); break; case MIRType_MagicOptimizedArguments: out.printf("magic lazyargs"); @@ -873,12 +870,19 @@ MConstant::canProduceFloat32() const return false; if (type() == MIRType_Int32) - return IsFloat32Representable(static_cast(value_.toInt32())); + return IsFloat32Representable(static_cast(toInt32())); if (type() == MIRType_Double) - return IsFloat32Representable(value_.toDouble()); + return IsFloat32Representable(toDouble()); + MOZ_ASSERT(type() == MIRType_Float32); return true; } +Value +MConstant::toJSValue() const +{ + return value_; +} + bool MConstant::valueToBoolean(bool* res) const { @@ -944,14 +948,14 @@ MSimdValueX4::foldsTo(TempAllocator& alloc) case MIRType_Int32x4: { int32_t a[4]; for (size_t i = 0; i < 4; ++i) - a[i] = getOperand(i)->toConstant()->value().toInt32(); + a[i] = getOperand(i)->toConstant()->toInt32(); cst = SimdConstant::CreateX4(a); break; } case MIRType_Float32x4: { float a[4]; for (size_t i = 0; i < 4; ++i) - a[i] = getOperand(i)->toConstant()->value().toNumber(); + a[i] = getOperand(i)->toConstant()->toNumber(); cst = SimdConstant::CreateX4(a); break; } @@ -982,12 +986,12 @@ MSimdSplatX4::foldsTo(TempAllocator& alloc) break; } case MIRType_Int32x4: { - int32_t v = op->toConstant()->value().toInt32(); + int32_t v = op->toConstant()->toInt32(); cst = SimdConstant::SplatX4(v); break; } case MIRType_Float32x4: { - float v = op->toConstant()->value().toNumber(); + float v = op->toConstant()->toNumber(); cst = SimdConstant::SplatX4(v); break; } @@ -1035,7 +1039,7 @@ MSimdGeneralShuffle::foldsTo(TempAllocator& alloc) for (size_t i = 0; i < numLanes(); i++) { if (!lane(i)->isConstant() || lane(i)->type() != MIRType_Int32) return this; - int32_t temp = lane(i)->toConstant()->value().toInt32(); + int32_t temp = lane(i)->toConstant()->toInt32(); if (temp < 0 || uint32_t(temp) >= numLanes() * numVectors()) return this; lanes[i] = uint32_t(temp); @@ -1315,14 +1319,10 @@ MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) { MDefinition* input = getOperand(0); - if (!input->isConstant()) + if (!input->isConstant() || !input->toConstant()->isNumber()) return this; - Value val = input->toConstant()->value(); - if (!val.isNumber()) - return this; - - double in = val.toNumber(); + double in = input->toConstant()->toNumber(); double out; switch (function_) { case Log: @@ -1410,14 +1410,11 @@ MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) { MDefinition* input = getOperand(0); - if (!input->isConstant()) + if (!input->isConstant() || input->type() != MIRType_Int32) return this; - Value val = input->toConstant()->value(); - if (!val.isInt32()) - return this; - - return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfree(val.toInt32()))); + int32_t i = input->toConstant()->toInt32(); + return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfree(i))); } MParameter* @@ -1603,8 +1600,7 @@ MDefinition* MStringLength::foldsTo(TempAllocator& alloc) { if (type() == MIRType_Int32 && string()->isConstant()) { - Value value = string()->toConstant()->value(); - JSAtom* atom = &value.toString()->asAtom(); + JSAtom* atom = &string()->toConstant()->toString()->asAtom(); return MConstant::New(alloc, Int32Value(atom->length())); } @@ -1614,10 +1610,10 @@ MStringLength::foldsTo(TempAllocator& alloc) MDefinition* MConcat::foldsTo(TempAllocator& alloc) { - if (lhs()->isConstant() && lhs()->toConstant()->value().toString()->empty()) + if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) return rhs(); - if (rhs()->isConstant() && rhs()->toConstant()->value().toString()->empty()) + if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) return lhs(); return this; @@ -1894,7 +1890,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->vp()->toNumber() == 0) { + if (testArg->type() == MIRType_Int32 && c->toNumber() == 0) { // When folding to the constant we need to hoist it. if (trueDef == c && !c->block()->dominates(block())) c->block()->moveBefore(pred->lastIns(), c); @@ -1905,7 +1901,7 @@ MPhi::foldsTernary() // - fold testArg ? testArg : "" to testArg // - fold testArg ? "" : testArg to "" if (testArg->type() == MIRType_String && - c->vp()->toString() == GetJitContext()->runtime->emptyString()) + c->toString() == GetJitContext()->runtime->emptyString()) { // When folding to the constant we need to hoist it. if (trueDef == c && !c->block()->dominates(block())) @@ -2270,7 +2266,7 @@ IsConstant(MDefinition* def, double v) if (!def->isConstant()) return false; - return NumbersAreIdentical(def->toConstant()->value().toNumber(), v); + return NumbersAreIdentical(def->toConstant()->toNumber(), v); } MDefinition* @@ -2387,7 +2383,7 @@ CanProduceNegativeZero(MDefinition* def) // and changing types. switch (def->op()) { case MDefinition::Op_Constant: - if (def->type() == MIRType_Double && def->toConstant()->value().toDouble() == -0.0) + if (def->type() == MIRType_Double && def->toConstant()->toDouble() == -0.0) return true; MOZ_FALLTHROUGH; case MDefinition::Op_BitAnd: @@ -2684,13 +2680,11 @@ MMinMax::foldsTo(TempAllocator& alloc) // Directly apply math utility to compare the rhs() and lhs() when // they are both constants. if (lhs()->isConstant() && rhs()->isConstant()) { - Value lval = lhs()->toConstant()->value(); - Value rval = rhs()->toConstant()->value(); - if (!lval.isNumber() || !rval.isNumber()) + if (!lhs()->toConstant()->isNumber() || !rhs()->toConstant()->isNumber()) return this; - double lnum = lval.toNumber(); - double rnum = rval.toNumber(); + double lnum = lhs()->toConstant()->toNumber(); + double rnum = rhs()->toConstant()->toNumber(); double result; if (isMax()) result = js::math_max_impl(lnum, rnum); @@ -2713,12 +2707,11 @@ MMinMax::foldsTo(TempAllocator& alloc) } MDefinition* operand = lhs()->isConstant() ? rhs() : lhs(); - const js::Value& val = - lhs()->isConstant() ? lhs()->toConstant()->value() : rhs()->toConstant()->value(); + MConstant* constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant(); if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) { // min(int32, cte >= INT32_MAX) = int32 - if (val.isDouble() && val.toDouble() >= INT32_MAX && !isMax()) { + if (constant->isNumber() && constant->toNumber() >= INT32_MAX && !isMax()) { MLimitedTruncate* limit = MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate); block()->insertBefore(this, limit); @@ -2727,7 +2720,7 @@ MMinMax::foldsTo(TempAllocator& alloc) } // max(int32, cte <= INT32_MIN) = int32 - if (val.isDouble() && val.toDouble() <= INT32_MIN && isMax()) { + if (constant->isNumber() && constant->toNumber() <= INT32_MIN && isMax()) { MLimitedTruncate* limit = MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate); block()->insertBefore(this, limit); @@ -2787,26 +2780,25 @@ MDiv::analyzeEdgeCasesForward() MOZ_ASSERT(rhs()->type() == MIRType_Int32); // Try removing divide by zero check - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) + if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) canBeDivideByZero_ = false; // If lhs is a constant int != INT32_MIN, then // negative overflow check can be skipped. - if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN)) + if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN)) canBeNegativeOverflow_ = false; // If rhs is a constant int != -1, likewise. - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1)) + if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) canBeNegativeOverflow_ = false; // If lhs is != 0, then negative zero check can be skipped. - if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0)) + if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) setCanBeNegativeZero(false); // If rhs is >= 0, likewise. - if (rhs()->isConstant()) { - const js::Value& val = rhs()->toConstant()->value(); - if (val.isInt32() && val.toInt32() >= 0) + if (rhs()->isConstant() && rhs()->type() == MIRType_Int32) { + if (rhs()->toConstant()->toInt32() >= 0) setCanBeNegativeZero(false); } } @@ -2843,11 +2835,11 @@ MMod::analyzeEdgeCasesForward() if (specialization_ != MIRType_Int32) return; - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) + if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) canBeDivideByZero_ = false; if (rhs()->isConstant()) { - int32_t n = rhs()->toConstant()->value().toInt32(); + int32_t n = rhs()->toConstant()->toInt32(); if (n > 0 && !IsPowerOfTwo(n)) canBePowerOfTwoDivisor_ = false; } @@ -2933,16 +2925,14 @@ MMul::analyzeEdgeCasesForward() return; // If lhs is > 0, no need for negative zero check. - if (lhs()->isConstant()) { - const js::Value& val = lhs()->toConstant()->value(); - if (val.isInt32() && val.toInt32() > 0) + if (lhs()->isConstant() && lhs()->type() == MIRType_Int32) { + if (lhs()->toConstant()->toInt32() > 0) setCanBeNegativeZero(false); } // If rhs is > 0, likewise. - if (rhs()->isConstant()) { - const js::Value& val = rhs()->toConstant()->value(); - if (val.isInt32() && val.toInt32() > 0) + if (rhs()->isConstant() && rhs()->type() == MIRType_Int32) { + if (rhs()->toConstant()->toInt32() > 0) setCanBeNegativeZero(false); } } @@ -3044,12 +3034,12 @@ MustBeUInt32(MDefinition* def, MDefinition** pwrapped) MDefinition* rhs = def->toUrsh()->rhs(); return !def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() && - rhs->maybeConstantValue()->value().isInt32(0); + rhs->maybeConstantValue()->isInt32(0); } if (MConstant* defConst = def->maybeConstantValue()) { *pwrapped = defConst; - return defConst->value().isInt32(0); + return defConst->isInt32(0); } return false; @@ -3198,7 +3188,7 @@ MBitNot::foldsTo(TempAllocator& alloc) MDefinition* input = getOperand(0); if (input->isConstant()) { - js::Value v = Int32Value(~(input->toConstant()->value().toInt32())); + js::Value v = Int32Value(~(input->toConstant()->toInt32())); return MConstant::New(alloc, v); } @@ -3517,7 +3507,6 @@ MToInt32::foldsTo(TempAllocator& alloc) // Fold this operation if the input operand is constant. if (input->isConstant()) { - Value val = input->toConstant()->value(); DebugOnly convert = conversion(); switch (input->type()) { case MIRType_Null: @@ -3526,14 +3515,14 @@ MToInt32::foldsTo(TempAllocator& alloc) case MIRType_Boolean: MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any || convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly); - return MConstant::New(alloc, Int32Value(val.toBoolean())); + return MConstant::New(alloc, Int32Value(input->toConstant()->toBoolean())); case MIRType_Int32: - return MConstant::New(alloc, Int32Value(val.toInt32())); + return MConstant::New(alloc, Int32Value(input->toConstant()->toInt32())); case MIRType_Float32: case MIRType_Double: int32_t ival; // Only the value within the range of Int32 can be substituted as constant. - if (mozilla::NumberEqualsInt32(val.toNumber(), &ival)) + if (mozilla::NumberEqualsInt32(input->toConstant()->toNumber(), &ival)) return MConstant::New(alloc, Int32Value(ival)); break; default: @@ -3564,8 +3553,7 @@ MTruncateToInt32::foldsTo(TempAllocator& alloc) return input; if (input->type() == MIRType_Double && input->isConstant()) { - const Value& v = input->toConstant()->value(); - int32_t ret = ToInt32(v.toDouble()); + int32_t ret = ToInt32(input->toConstant()->toDouble()); return MConstant::New(alloc, Int32Value(ret)); } @@ -3582,12 +3570,9 @@ MToDouble::foldsTo(TempAllocator& alloc) if (input->type() == MIRType_Double) return input; - if (input->isConstant()) { - const Value& v = input->toConstant()->value(); - if (v.isNumber()) { - double out = v.toNumber(); - return MConstant::New(alloc, DoubleValue(out)); - } + if (input->isConstant() && input->toConstant()->isNumber()) { + double out = input->toConstant()->toNumber(); + return MConstant::New(alloc, DoubleValue(out)); } return this; @@ -3607,14 +3592,11 @@ MToFloat32::foldsTo(TempAllocator& alloc) if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32) return input->toToDouble()->input(); - if (input->isConstant()) { - const Value& v = input->toConstant()->value(); - if (v.isNumber()) { - float out = v.toNumber(); - MConstant* c = MConstant::New(alloc, DoubleValue(out)); - c->setResultType(MIRType_Float32); - return c; - } + 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; } return this; } @@ -3635,13 +3617,8 @@ MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) { if (MConstant* inputConst = input()->maybeConstantValue()) { - const Value& v = inputConst->value(); - if (v.isDouble()) { - int32_t clamped = ClampDoubleToUint8(v.toDouble()); - return MConstant::New(alloc, Int32Value(clamped)); - } - if (v.isInt32()) { - int32_t clamped = ClampIntForUint8Array(v.toInt32()); + if (inputConst->isNumber()) { + int32_t clamped = ClampDoubleToUint8(inputConst->toNumber()); return MConstant::New(alloc, Int32Value(clamped)); } } @@ -3696,9 +3673,9 @@ MCompare::tryFoldTypeOf(bool* result) return false; MTypeOf* typeOf = lhs()->isTypeOf() ? lhs()->toTypeOf() : rhs()->toTypeOf(); - const Value* constant = lhs()->isConstant() ? lhs()->toConstant()->vp() : rhs()->toConstant()->vp(); + MConstant* constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant(); - if (!constant->isString()) + if (constant->type() != MIRType_String) return false; if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE && @@ -3840,8 +3817,8 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) MDefinition* operand = left->isConstant() ? right : left; MConstant* constant = left->isConstant() ? left->toConstant() : right->toConstant(); - MOZ_ASSERT(constant->value().isDouble()); - double cte = constant->value().toDouble(); + MOZ_ASSERT(constant->type() == MIRType_Double); + double cte = constant->toDouble(); if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) { bool replaced = false; @@ -3914,14 +3891,14 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) if (!left->isConstant() || !right->isConstant()) return false; - Value lhs = left->toConstant()->value(); - Value rhs = right->toConstant()->value(); + MConstant* lhs = left->toConstant(); + MConstant* rhs = right->toConstant(); // Fold away some String equality comparisons. - if (lhs.isString() && rhs.isString()) { + if (lhs->type() == MIRType_String && rhs->type() == MIRType_String) { int32_t comp = 0; // Default to equal. if (left != right) - comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom()); + comp = CompareAtoms(&lhs->toString()->asAtom(), &rhs->toString()->asAtom()); switch (jsop_) { case JSOP_LT: @@ -3952,8 +3929,8 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) } if (compareType_ == Compare_UInt32) { - uint32_t lhsUint = uint32_t(lhs.toInt32()); - uint32_t rhsUint = uint32_t(rhs.toInt32()); + uint32_t lhsUint = uint32_t(lhs->toInt32()); + uint32_t rhsUint = uint32_t(rhs->toInt32()); switch (jsop_) { case JSOP_LT: @@ -3983,29 +3960,29 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) return true; } - if (!lhs.isNumber() || !rhs.isNumber()) + if (!lhs->isNumber() || !rhs->isNumber()) return false; switch (jsop_) { case JSOP_LT: - *result = (lhs.toNumber() < rhs.toNumber()); + *result = (lhs->toNumber() < rhs->toNumber()); break; case JSOP_LE: - *result = (lhs.toNumber() <= rhs.toNumber()); + *result = (lhs->toNumber() <= rhs->toNumber()); break; case JSOP_GT: - *result = (lhs.toNumber() > rhs.toNumber()); + *result = (lhs->toNumber() > rhs->toNumber()); break; case JSOP_GE: - *result = (lhs.toNumber() >= rhs.toNumber()); + *result = (lhs->toNumber() >= rhs->toNumber()); break; case JSOP_STRICTEQ: // Fall through. case JSOP_EQ: - *result = (lhs.toNumber() == rhs.toNumber()); + *result = (lhs->toNumber() == rhs->toNumber()); break; case JSOP_STRICTNE: // Fall through. case JSOP_NE: - *result = (lhs.toNumber() != rhs.toNumber()); + *result = (lhs->toNumber() != rhs->toNumber()); break; default: return false; @@ -4441,7 +4418,7 @@ MAsmJSLoadHeap::mightAlias(const MDefinition* def) const if (!ptr()->isConstant() || !store->ptr()->isConstant()) return true; const MConstant* otherPtr = store->ptr()->toConstant(); - return ptr()->toConstant()->value() == otherPtr->value(); + return ptr()->toConstant()->equals(otherPtr); } return true; } @@ -4947,11 +4924,8 @@ MGetPropertyCache::updateForReplacement(MDefinition* ins) MDefinition* MAsmJSUnsignedToDouble::foldsTo(TempAllocator& alloc) { - if (input()->isConstant()) { - const Value& v = input()->toConstant()->value(); - if (v.isInt32()) - return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32()))); - } + if (input()->isConstant() && input()->type() == MIRType_Int32) + return MConstant::New(alloc, DoubleValue(uint32_t(input()->toConstant()->toInt32()))); return this; } @@ -4959,13 +4933,10 @@ MAsmJSUnsignedToDouble::foldsTo(TempAllocator& alloc) MDefinition* MAsmJSUnsignedToFloat32::foldsTo(TempAllocator& alloc) { - if (input()->isConstant()) { - const Value& v = input()->toConstant()->value(); - if (v.isInt32()) { - double dval = double(uint32_t(v.toInt32())); - if (IsFloat32Representable(dval)) - return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); - } + if (input()->isConstant() && input()->type() == MIRType_Int32) { + double dval = double(uint32_t(input()->toConstant()->toInt32())); + if (IsFloat32Representable(dval)) + return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); } return this; @@ -5010,7 +4981,7 @@ MDefinition* MClz::foldsTo(TempAllocator& alloc) { if (num()->isConstant()) { - int32_t n = num()->toConstant()->value().toInt32(); + int32_t n = num()->toConstant()->toInt32(); if (n == 0) return MConstant::New(alloc, Int32Value(32)); return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n))); @@ -5023,8 +4994,8 @@ MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) { if (index()->isConstant() && length()->isConstant()) { - uint32_t len = length()->toConstant()->value().toInt32(); - uint32_t idx = index()->toConstant()->value().toInt32(); + uint32_t len = length()->toConstant()->toInt32(); + uint32_t idx = index()->toConstant()->toInt32(); if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) return index(); } @@ -5044,9 +5015,8 @@ MTableSwitch::foldsTo(TempAllocator& alloc) return MGoto::New(alloc, getDefault()); if (MConstant* opConst = op->maybeConstantValue()) { - Value v = opConst->value(); - if (v.isInt32()) { - int32_t i = v.toInt32() - low_; + if (op->type() == MIRType_Int32) { + int32_t i = opConst->toInt32() - low_; MBasicBlock* target; if (size_t(i) < numCases()) target = getCase(size_t(i)); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 8f129b73fa..6b499642cc 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1367,13 +1367,6 @@ class MConstant : public MNullaryInstruction static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type); static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v); - const js::Value& value() const { - return value_; - } - const js::Value* vp() const { - return &value_; - } - // Try to convert this constant to boolean, similar to js::ToBoolean. // Returns false if the type is MIRType_Magic*. bool valueToBoolean(bool* res) const; @@ -1413,6 +1406,65 @@ class MConstant : public MNullaryInstruction bool canProduceFloat32() const override; ALLOW_CLONE(MConstant) + + bool equals(const MConstant* other) { + return value_ == other->value_; + } + + bool toBoolean() const { + MOZ_ASSERT(type() == MIRType_Boolean); + return value_.toBoolean(); + } + int32_t toInt32() const { + MOZ_ASSERT(type() == MIRType_Int32); + return value_.toInt32(); + } + bool isInt32(int32_t i) const { + return type() == MIRType_Int32 && value_.toInt32() == i; + } + double toDouble() const { + MOZ_ASSERT(type() == MIRType_Double); + return value_.toDouble(); + } + float toFloat32() const { + MOZ_ASSERT(type() == MIRType_Float32); + return value_.toDouble(); + } + JSString* toString() const { + MOZ_ASSERT(type() == MIRType_String); + return value_.toString(); + } + JS::Symbol* toSymbol() const { + MOZ_ASSERT(type() == MIRType_Symbol); + return value_.toSymbol(); + } + JSObject& toObject() const { + MOZ_ASSERT(type() == MIRType_Object); + return value_.toObject(); + } + JSObject* toObjectOrNull() const { + if (type() == MIRType_Object) + return &value_.toObject(); + MOZ_ASSERT(type() == MIRType_Null); + return nullptr; + } + + bool isNumber() const { + return IsNumberType(type()); + } + double toNumber() const { + if (type() == MIRType_Int32) + return toInt32(); + if (type() == MIRType_Double) + return toDouble(); + MOZ_ASSERT(type() == MIRType_Float32); + return toFloat32(); + } + + // Convert this constant to a js::Value. Float32 constants will be stored + // as DoubleValue and NaNs are canonicalized. Callers must be careful: not + // all constants can be represented by js::Value (wasm supports int64). + Value toJSValue() const; }; // Generic constructor of SIMD valuesX4. @@ -1567,11 +1619,6 @@ class MSimdConvert public: INSTRUCTION_HEADER(SimdConvert) - static MSimdConvert* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType toType) - { - // AsmJS only has signed integer vectors for now. - return new(alloc) MSimdConvert(obj, toType, SimdSign::Signed); - } static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign) { @@ -1619,10 +1666,6 @@ class MSimdReinterpretCast public: INSTRUCTION_HEADER(SimdReinterpretCast) - static MSimdReinterpretCast* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType toType) - { - return new(alloc) MSimdReinterpretCast(obj, toType); - } static MSimdReinterpretCast* New(TempAllocator& alloc, MDefinition* obj, MIRType toType) { @@ -1680,15 +1723,6 @@ class MSimdExtractElement public: INSTRUCTION_HEADER(SimdExtractElement) - static MSimdExtractElement* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType type, - SimdLane lane) - { - // Only signed integer types in AsmJS so far. - SimdSign sign = - IsIntegerSimdType(obj->type()) ? SimdSign::Signed : SimdSign::NotApplicable; - return new (alloc) MSimdExtractElement(obj, type, lane, sign); - } - static MSimdExtractElement* New(TempAllocator& alloc, MDefinition* obj, MIRType scalarType, SimdLane lane, SimdSign sign) { @@ -2170,14 +2204,6 @@ class MSimdBinaryComp public: INSTRUCTION_HEADER(SimdBinaryComp) - static MSimdBinaryComp* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, - Operation op) - { - // AsmJS only has signed vectors for now. - SimdSign sign = - IsIntegerSimdType(left->type()) ? SimdSign::Signed : SimdSign::NotApplicable; - return new (alloc) MSimdBinaryComp(left, right, op, sign); - } static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op, SimdSign sign) @@ -2275,12 +2301,6 @@ class MSimdBinaryArith return new(alloc) MSimdBinaryArith(left, right, op); } - static MSimdBinaryArith* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, - Operation op) - { - return New(alloc, left, right, op); - } - AliasSet getAliasSet() const override { return AliasSet::None(); } @@ -2340,12 +2360,6 @@ class MSimdBinaryBitwise return new(alloc) MSimdBinaryBitwise(left, right, op); } - static MSimdBinaryBitwise* NewAsmJS(TempAllocator& alloc, MDefinition* left, - MDefinition* right, Operation op) - { - return new(alloc) MSimdBinaryBitwise(left, right, op); - } - AliasSet getAliasSet() const override { return AliasSet::None(); } @@ -2388,12 +2402,6 @@ class MSimdShift public: INSTRUCTION_HEADER(SimdShift) - static MSimdShift* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, - Operation op) - { - MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32); - return new(alloc) MSimdShift(left, right, op); - } static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op) @@ -3031,7 +3039,7 @@ class MNewArray } JSObject* templateObject() const { - return getOperand(0)->toConstant()->value().toObjectOrNull(); + return getOperand(0)->toConstant()->toObjectOrNull(); } gc::InitialHeap initialHeap() const { @@ -3179,7 +3187,7 @@ class MNewObject // making it emittedAtUses, we do not produce register allocations for // it and inline its content inside the code produced by the // CodeGenerator. - if (templateConst->toConstant()->value().isObject()) + if (templateConst->toConstant()->type() == MIRType_Object) templateConst->setEmittedAtUses(); } @@ -3202,7 +3210,7 @@ class MNewObject } JSObject* templateObject() const { - return getOperand(0)->toConstant()->value().toObjectOrNull(); + return getOperand(0)->toConstant()->toObjectOrNull(); } gc::InitialHeap initialHeap() const { @@ -4752,7 +4760,7 @@ class MCreateThisWithTemplate // Template for |this|, provided by TI. JSObject* templateObject() const { - return &getOperand(0)->toConstant()->value().toObject(); + return &getOperand(0)->toConstant()->toObject(); } gc::InitialHeap initialHeap() const { @@ -6879,7 +6887,7 @@ class MStringSplit return getOperand(1); } JSObject* templateObject() const { - return &getOperand(2)->toConstant()->value().toObject(); + return &getOperand(2)->toConstant()->toObject(); } ObjectGroup* group() const { return templateObject()->group(); @@ -7874,7 +7882,7 @@ class MLambda const LambdaFunctionInfo info_; MLambda(CompilerConstraintList* constraints, MDefinition* scopeChain, MConstant* cst) - : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as()) + : MBinaryInstruction(scopeChain, cst), info_(&cst->toObject().as()) { setResultType(MIRType_Object); if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun)) diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index b54a98ad44..422854190a 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -618,7 +618,7 @@ MBasicBlock::linkOsrValues(MStart* start) cloneRp = def->toOsrReturnValue(); } else if (info().hasArguments() && i == info().argsObjSlot()) { MOZ_ASSERT(def->isConstant() || def->isOsrArgumentsObject()); - MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); + MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->type() == MIRType_Undefined); if (def->isOsrArgumentsObject()) cloneRp = def->toOsrArgumentsObject(); } else { @@ -628,7 +628,7 @@ MBasicBlock::linkOsrValues(MStart* start) // A constant Undefined can show up here for an argument slot when // the function has an arguments object, but the argument in // question is stored on the scope chain. - MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); + MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->type() == MIRType_Undefined); if (def->isOsrValue()) cloneRp = def->toOsrValue(); diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 942baa8582..43461cf4f7 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -182,12 +182,12 @@ RangeAnalysis::addBetaNodes() MConstant* leftConst = left->maybeConstantValue(); MConstant* rightConst = right->maybeConstantValue(); - if (leftConst && leftConst->value().isNumber()) { - bound = leftConst->value().toNumber(); + if (leftConst && leftConst->isNumber()) { + bound = leftConst->toNumber(); val = right; jsop = ReverseCompareOp(jsop); - } else if (rightConst && rightConst->value().isNumber()) { - bound = rightConst->value().toNumber(); + } else if (rightConst && rightConst->isNumber()) { + bound = rightConst->toNumber(); val = left; } else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) { MDefinition* smaller = nullptr; @@ -1275,11 +1275,11 @@ MBeta::computeRange(TempAllocator& alloc) void MConstant::computeRange(TempAllocator& alloc) { - if (value().isNumber()) { - double d = value().toNumber(); + if (isNumber()) { + double d = toNumber(); setRange(Range::NewDoubleSingletonRange(alloc, d)); - } else if (value().isBoolean()) { - bool b = value().toBoolean(); + } else if (type() == MIRType_Boolean) { + bool b = toBoolean(); setRange(Range::NewInt32Range(alloc, b, b)); } } @@ -1348,7 +1348,7 @@ MLsh::computeRange(TempAllocator& alloc) MConstant* rhsConst = getOperand(1)->maybeConstantValue(); if (rhsConst && rhsConst->type() == MIRType_Int32) { - int32_t c = rhsConst->value().toInt32(); + int32_t c = rhsConst->toInt32(); setRange(Range::lsh(alloc, &left, c)); return; } @@ -1366,7 +1366,7 @@ MRsh::computeRange(TempAllocator& alloc) MConstant* rhsConst = getOperand(1)->maybeConstantValue(); if (rhsConst && rhsConst->type() == MIRType_Int32) { - int32_t c = rhsConst->value().toInt32(); + int32_t c = rhsConst->toInt32(); setRange(Range::rsh(alloc, &left, c)); return; } @@ -1391,7 +1391,7 @@ MUrsh::computeRange(TempAllocator& alloc) MConstant* rhsConst = getOperand(1)->maybeConstantValue(); if (rhsConst && rhsConst->type() == MIRType_Int32) { - int32_t c = rhsConst->value().toInt32(); + int32_t c = rhsConst->toInt32(); setRange(Range::ursh(alloc, &left, c)); } else { setRange(Range::ursh(alloc, &left, &right)); @@ -3322,13 +3322,13 @@ MBinaryBitwiseInstruction::collectRangeInfoPreTrunc() Range rhsRange(rhs()); if (lhs()->isConstant() && lhs()->type() == MIRType_Int32 && - DoesMaskMatchRange(lhs()->toConstant()->value().toInt32(), rhsRange)) + DoesMaskMatchRange(lhs()->toConstant()->toInt32(), rhsRange)) { maskMatchesRightRange = true; } if (rhs()->isConstant() && rhs()->type() == MIRType_Int32 && - DoesMaskMatchRange(rhs()->toConstant()->value().toInt32(), lhsRange)) + DoesMaskMatchRange(rhs()->toConstant()->toInt32(), lhsRange)) { maskMatchesLeftRange = true; } diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index aa805a4e83..e2f38a925a 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -659,7 +659,7 @@ ObjectMemoryView::visitLambda(MLambda* ins) static size_t GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset) { - int32_t idx = index->toConstant()->value().toInt32(); + int32_t idx = index->toConstant()->toInt32(); MOZ_ASSERT(idx >= 0); MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData()); return idx * width + baseOffset - UnboxedPlainObject::offsetOfData(); @@ -781,13 +781,9 @@ IndexOf(MDefinition* ins, int32_t* res) if (indexDef->isToInt32()) indexDef = indexDef->toToInt32()->getOperand(0); MConstant* indexDefConst = indexDef->maybeConstantValue(); - if (!indexDefConst) + if (!indexDefConst || indexDefConst->type() != MIRType_Int32) return false; - - Value index = indexDefConst->value(); - if (!index.isInt32()) - return false; - *res = index.toInt32(); + *res = indexDefConst->toInt32(); return true; } @@ -1259,7 +1255,7 @@ ArrayMemoryView::visitSetInitializedLength(MSetInitializedLength* ins) return; } - int32_t initLengthValue = ins->index()->maybeConstantValue()->value().toInt32() + 1; + int32_t initLengthValue = ins->index()->maybeConstantValue()->toInt32() + 1; MConstant* initLength = MConstant::New(alloc_, Int32Value(initLengthValue)); ins->block()->insertBefore(ins, initLength); ins->block()->insertBefore(ins, state_); diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 5741117ae5..ed15a6fb51 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -65,7 +65,7 @@ LIRGeneratorARM::visitBox(MBox* box) } if (inner->isConstant()) { - defineBox(new(alloc()) LValue(inner->toConstant()->value()), box); + defineBox(new(alloc()) LValue(inner->toConstant()->toJSValue()), box); return; } @@ -243,7 +243,7 @@ LIRGeneratorARM::lowerDivI(MDiv* div) // Division instructions are slow. Division by constant denominators can be // rewritten to use other instructions. if (div->rhs()->isConstant()) { - int32_t rhs = div->rhs()->toConstant()->value().toInt32(); + int32_t rhs = div->rhs()->toConstant()->toInt32(); // Check for division by a positive power of two, which is an easy and // important case to optimize. Note that other optimizations are also // possible; division by negative powers of two can be optimized in a @@ -292,7 +292,7 @@ LIRGeneratorARM::lowerModI(MMod* mod) } if (mod->rhs()->isConstant()) { - int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); + int32_t rhs = mod->rhs()->toConstant()->toInt32(); int32_t shift = FloorLog2(rhs); if (rhs > 0 && 1 << shift == rhs) { LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift); @@ -468,8 +468,8 @@ LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) // For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed. if (ptr->isConstant() && !ins->needsBoundsCheck()) { // A bounds check is only skipped for a positive index. - MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0); + ptrAlloc = LAllocation(ptr->toConstant()); } else { ptrAlloc = useRegisterAtStart(ptr); } @@ -485,8 +485,8 @@ LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) LAllocation ptrAlloc; if (ptr->isConstant() && !ins->needsBoundsCheck()) { - MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0); + ptrAlloc = LAllocation(ptr->toConstant()); } else { ptrAlloc = useRegisterAtStart(ptr); } diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index a6a89e7725..fd31d58962 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -414,7 +414,7 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir, if (mir->isLambda()) { MConstant* constant = mir->toLambda()->functionOperand(); uint32_t cstIndex; - masm.propagateOOM(graph.addConstantToPool(constant->value(), &cstIndex)); + masm.propagateOOM(graph.addConstantToPool(constant->toJSValue(), &cstIndex)); alloc = RValueAllocation::RecoverInstruction(index, cstIndex); break; } @@ -440,7 +440,7 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir, if (payload->isConstant()) { MConstant* constant = mir->toConstant(); uint32_t index; - masm.propagateOOM(graph.addConstantToPool(constant->value(), &index)); + masm.propagateOOM(graph.addConstantToPool(constant->toJSValue(), &index)); alloc = RValueAllocation::ConstantPool(index); break; } @@ -466,7 +466,7 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir, if (payload->isConstant()) { MConstant* constant = mir->toConstant(); uint32_t index; - masm.propagateOOM(graph.addConstantToPool(constant->value(), &index)); + masm.propagateOOM(graph.addConstantToPool(constant->toJSValue(), &index)); alloc = RValueAllocation::ConstantPool(index); break; } diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 526f543203..4729bf1e5d 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -269,11 +269,10 @@ LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as) { MInstruction* replacement; if (def->type() != as->type()) { - Value v = as->toConstant()->value(); if (as->type() == MIRType_Int32) - replacement = MConstant::New(alloc(), BooleanValue(v.toInt32())); + replacement = MConstant::New(alloc(), BooleanValue(as->toConstant()->toInt32())); else - replacement = MConstant::New(alloc(), Int32Value(v.toBoolean())); + replacement = MConstant::New(alloc(), Int32Value(as->toConstant()->toBoolean())); def->block()->insertBefore(def->toInstruction(), replacement); emitAtUses(replacement->toInstruction()); } else { @@ -349,7 +348,7 @@ LAllocation LIRGeneratorShared::useOrConstant(MDefinition* mir) { if (mir->isConstant()) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return use(mir); } @@ -357,7 +356,7 @@ LAllocation LIRGeneratorShared::useOrConstantAtStart(MDefinition* mir) { if (mir->isConstant()) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return useAtStart(mir); } @@ -365,7 +364,7 @@ LAllocation LIRGeneratorShared::useRegisterOrConstant(MDefinition* mir) { if (mir->isConstant()) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return useRegister(mir); } @@ -373,14 +372,14 @@ LAllocation LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition* mir) { if (mir->isConstant()) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return useRegisterAtStart(mir); } LAllocation LIRGeneratorShared::useRegisterOrZeroAtStart(MDefinition* mir) { - if (mir->isConstant() && mir->toConstant()->value().isInt32(0)) + if (mir->isConstant() && mir->toConstant()->isInt32(0)) return LAllocation(); return useRegisterAtStart(mir); } @@ -389,7 +388,7 @@ LAllocation LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition* mir) { if (mir->isConstant() && mir->type() != MIRType_Double && mir->type() != MIRType_Float32) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return useRegister(mir); } @@ -450,7 +449,7 @@ LAllocation LIRGeneratorShared::useKeepaliveOrConstant(MDefinition* mir) { if (mir->isConstant()) - return LAllocation(mir->toConstant()->vp()); + return LAllocation(mir->toConstant()); return useKeepalive(mir); } @@ -630,7 +629,7 @@ LIRGeneratorShared::useBoxOrTypedOrConstant(LInstruction* lir, size_t n, MDefini } if (useConstant && mir->isConstant()) - lir->setOperand(n, LAllocation(mir->toConstant()->vp())); + lir->setOperand(n, LAllocation(mir->toConstant())); else lir->setOperand(n, useRegister(mir)); diff --git a/js/src/jit/shared/Lowering-shared.cpp b/js/src/jit/shared/Lowering-shared.cpp index 4e3286331c..1116fc2b4c 100644 --- a/js/src/jit/shared/Lowering-shared.cpp +++ b/js/src/jit/shared/Lowering-shared.cpp @@ -79,28 +79,27 @@ LIRGeneratorShared::visitConstant(MConstant* ins) return; } - const Value& v = ins->value(); switch (ins->type()) { case MIRType_Double: - define(new(alloc()) LDouble(v.toDouble()), ins); + define(new(alloc()) LDouble(ins->toDouble()), ins); break; case MIRType_Float32: - define(new(alloc()) LFloat32(v.toDouble()), ins); + define(new(alloc()) LFloat32(ins->toFloat32()), ins); break; case MIRType_Boolean: - define(new(alloc()) LInteger(v.toBoolean()), ins); + define(new(alloc()) LInteger(ins->toBoolean()), ins); break; case MIRType_Int32: - define(new(alloc()) LInteger(v.toInt32()), ins); + define(new(alloc()) LInteger(ins->toInt32()), ins); break; case MIRType_String: - define(new(alloc()) LPointer(v.toString()), ins); + define(new(alloc()) LPointer(ins->toString()), ins); break; case MIRType_Symbol: - define(new(alloc()) LPointer(v.toSymbol()), ins); + define(new(alloc()) LPointer(ins->toSymbol()), ins); break; case MIRType_Object: - define(new(alloc()) LPointer(&v.toObject()), ins); + define(new(alloc()) LPointer(&ins->toObject()), ins); break; default: // Constants of special types (undefined, null) should never flow into diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 50ef093964..443d3697e6 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -151,7 +151,7 @@ CodeGeneratorX64::visitCompareB(LCompareB* lir) // Load boxed boolean in ScratchReg. ScratchRegisterScope scratch(masm); if (rhs->isConstant()) - masm.moveValue(*rhs->toConstant(), scratch); + masm.moveValue(rhs->toConstant()->toJSValue(), scratch); else masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), scratch); @@ -173,7 +173,7 @@ CodeGeneratorX64::visitCompareBAndBranch(LCompareBAndBranch* lir) // Load boxed boolean in ScratchReg. ScratchRegisterScope scratch(masm); if (rhs->isConstant()) - masm.moveValue(*rhs->toConstant(), scratch); + masm.moveValue(rhs->toConstant()->toJSValue(), scratch); else masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), scratch); diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index af91a9f88e..eac6421be4 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -59,7 +59,7 @@ LIRGeneratorX64::visitBox(MBox* box) } if (opd->isConstant()) { - define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX)); + define(new(alloc()) LValue(opd->toConstant()->toJSValue()), box, LDefinition(LDefinition::BOX)); } else { LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type()); define(ins, box, LDefinition(LDefinition::BOX)); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 3864cdabb0..200df822a8 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -179,7 +179,7 @@ LIRGeneratorX86Shared::lowerDivI(MDiv* div) // Division instructions are slow. Division by constant denominators can be // rewritten to use other instructions. if (div->rhs()->isConstant()) { - int32_t rhs = div->rhs()->toConstant()->value().toInt32(); + int32_t rhs = div->rhs()->toConstant()->toInt32(); // Division by powers of two can be done by shifting, and division by // other numbers can be done by a reciprocal multiplication technique. @@ -226,7 +226,7 @@ LIRGeneratorX86Shared::lowerModI(MMod* mod) } if (mod->rhs()->isConstant()) { - int32_t rhs = mod->rhs()->toConstant()->value().toInt32(); + int32_t rhs = mod->rhs()->toConstant()->toInt32(); int32_t shift = FloorLog2(Abs(rhs)); if (rhs != 0 && uint32_t(1) << shift == Abs(rhs)) { LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegisterAtStart(mod->lhs()), shift); @@ -275,7 +275,7 @@ void LIRGeneratorX86Shared::lowerUDiv(MDiv* div) { if (div->rhs()->isConstant()) { - uint32_t rhs = div->rhs()->toConstant()->value().toInt32(); + uint32_t rhs = div->rhs()->toConstant()->toInt32(); int32_t shift = FloorLog2(rhs); LAllocation lhs = useRegisterAtStart(div->lhs()); @@ -306,7 +306,7 @@ void LIRGeneratorX86Shared::lowerUMod(MMod* mod) { if (mod->rhs()->isConstant()) { - uint32_t rhs = mod->rhs()->toConstant()->value().toInt32(); + uint32_t rhs = mod->rhs()->toConstant()->toInt32(); int32_t shift = FloorLog2(rhs); if (rhs != 0 && uint32_t(1) << shift == rhs) { diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index a45e2faca3..d16dcce143 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -62,7 +62,7 @@ LIRGeneratorX86::visitBox(MBox* box) } if (inner->isConstant()) { - defineBox(new(alloc()) LValue(inner->toConstant()->value()), box); + defineBox(new(alloc()) LValue(inner->toConstant()->toJSValue()), box); return; } diff --git a/js/src/jsapi-tests/testJitDCEinGVN.cpp b/js/src/jsapi-tests/testJitDCEinGVN.cpp index 11ddda11f5..9d6b13ab44 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()->value().toNumber() != 1.0)); + CHECK(!ins->isConstant() || (ins->toConstant()->toNumber() != 1.0)); } return true; } diff --git a/js/src/jsapi-tests/testJitFoldsTo.cpp b/js/src/jsapi-tests/testJitFoldsTo.cpp index aa829515be..ba5278c793 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()->value().toNumber() == 0.25); + CHECK(op->getOperand(1)->toConstant()->toNumber() == 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->value().toNumber(), 0.0)); + CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 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->value().toNumber(), 1.0)); + CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 1.0)); return true; } END_TEST(testJitFoldsTo_UnsignedMod) diff --git a/widget/cocoa/nsCocoaUtils.h b/widget/cocoa/nsCocoaUtils.h index ebf5db5a70..c8854ffc84 100644 --- a/widget/cocoa/nsCocoaUtils.h +++ b/widget/cocoa/nsCocoaUtils.h @@ -210,15 +210,17 @@ public: // in the bottom-left of the primary screen. Both nsRect and NSRect // contain width/height info, with no difference in their use. // This function does no scaling, so the Gecko coordinates are - // expected to be CSS pixels, which we treat as equal to Cocoa points. - static NSRect GeckoRectToCocoaRect(const nsIntRect &geckoRect); + // expected to be desktop pixels, which are equal to Cocoa points + // (by definition). + static NSRect GeckoRectToCocoaRect(const mozilla::DesktopIntRect &geckoRect); // Converts aGeckoRect in dev pixels to points in Cocoa coordinates - static NSRect GeckoRectToCocoaRectDevPix(const nsIntRect &aGeckoRect, - CGFloat aBackingScale); + static NSRect + GeckoRectToCocoaRectDevPix(const mozilla::LayoutDeviceIntRect &aGeckoRect, + CGFloat aBackingScale); // See explanation for geckoRectToCocoaRect, guess what this does... - static nsIntRect CocoaRectToGeckoRect(const NSRect &cocoaRect); + static mozilla::DesktopIntRect CocoaRectToGeckoRect(const NSRect &cocoaRect); static mozilla::LayoutDeviceIntRect CocoaRectToGeckoRectDevPix( const NSRect& aCocoaRect, CGFloat aBackingScale); diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index 4c4089e5c0..5f24b165fe 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -65,7 +65,7 @@ nsCocoaUtils::FlippedScreenY(float y) return MenuBarScreenHeight() - y; } -NSRect nsCocoaUtils::GeckoRectToCocoaRect(const nsIntRect &geckoRect) +NSRect nsCocoaUtils::GeckoRectToCocoaRect(const DesktopIntRect &geckoRect) { // We only need to change the Y coordinate by starting with the primary screen // height and subtracting the gecko Y coordinate of the bottom of the rect. @@ -75,8 +75,9 @@ NSRect nsCocoaUtils::GeckoRectToCocoaRect(const nsIntRect &geckoRect) geckoRect.height); } -NSRect nsCocoaUtils::GeckoRectToCocoaRectDevPix(const nsIntRect &aGeckoRect, - CGFloat aBackingScale) +NSRect +nsCocoaUtils::GeckoRectToCocoaRectDevPix(const LayoutDeviceIntRect &aGeckoRect, + CGFloat aBackingScale) { return NSMakeRect(aGeckoRect.x / aBackingScale, MenuBarScreenHeight() - aGeckoRect.YMost() / aBackingScale, @@ -84,12 +85,12 @@ NSRect nsCocoaUtils::GeckoRectToCocoaRectDevPix(const nsIntRect &aGeckoRect, aGeckoRect.height / aBackingScale); } -nsIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect) +DesktopIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect) { // We only need to change the Y coordinate by starting with the primary screen // height and subtracting both the cocoa y origin and the height of the // cocoa rect. - nsIntRect rect; + DesktopIntRect rect; rect.x = NSToIntRound(cocoaRect.origin.x); rect.y = NSToIntRound(FlippedScreenY(cocoaRect.origin.y + cocoaRect.size.height)); rect.width = NSToIntRound(cocoaRect.origin.x + cocoaRect.size.width) - rect.x; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 1f670fce44..42715c39d1 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -188,8 +188,7 @@ FindTargetScreenForRect(const DesktopIntRect& aRect) int largestIntersectArea = 0; while (NSScreen *screen = [screenEnum nextObject]) { DesktopIntRect screenRect = - DesktopIntRect::FromUnknownRect( - nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame])); + nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]); screenRect = screenRect.Intersect(aRect); int area = screenRect.width * screenRect.height; if (area > largestIntersectArea) { @@ -210,7 +209,8 @@ FitRectToVisibleAreaForScreen(DesktopIntRect& aRect, NSScreen* aScreen) aScreen = FindTargetScreenForRect(aRect); } - nsIntRect screenBounds(nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame])); + DesktopIntRect screenBounds = + nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame]); if (aRect.width > screenBounds.width) { aRect.width = screenBounds.width; @@ -279,7 +279,7 @@ nsresult nsCocoaWindow::Create(nsIWidget* aParent, return NS_OK; nsresult rv = - CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds.ToUnknownRect()), + CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds), mBorderStyle, false); NS_ENSURE_SUCCESS(rv, rv); @@ -1439,8 +1439,7 @@ nsresult nsCocoaWindow::DoResize(double aX, double aY, [mWindow screen] : nullptr); // convert requested bounds into Cocoa coordinate system - NSRect newFrame = - nsCocoaUtils::GeckoRectToCocoaRect(newBounds.ToUnknownRect()); + NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds); NSRect frame = [mWindow frame]; BOOL isMoving = newFrame.origin.x != frame.origin.x || @@ -1576,8 +1575,7 @@ GetBackingScaleFactor(NSWindow* aWindow) // Then identify the screen it belongs to, and return its scale factor. NSScreen *screen = - FindTargetScreenForRect( - DesktopIntRect::FromUnknownRect(nsCocoaUtils::CocoaRectToGeckoRect(frame))); + FindTargetScreenForRect(nsCocoaUtils::CocoaRectToGeckoRect(frame)); return nsCocoaUtils::GetBackingScaleFactor(screen); } diff --git a/widget/cocoa/nsScreenCocoa.mm b/widget/cocoa/nsScreenCocoa.mm index 1b72bbf82e..16f819bb39 100644 --- a/widget/cocoa/nsScreenCocoa.mm +++ b/widget/cocoa/nsScreenCocoa.mm @@ -76,7 +76,7 @@ nsScreenCocoa::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth { NSRect frame = [mScreen frame]; - nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); + DesktopIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); *outX = r.x; *outY = r.y; @@ -91,7 +91,7 @@ nsScreenCocoa::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *out { NSRect frame = [mScreen visibleFrame]; - nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); + DesktopIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); *outX = r.x; *outY = r.y; diff --git a/widget/cocoa/nsScreenManagerCocoa.mm b/widget/cocoa/nsScreenManagerCocoa.mm index 731e05632e..9a0cbb9cc5 100644 --- a/widget/cocoa/nsScreenManagerCocoa.mm +++ b/widget/cocoa/nsScreenManagerCocoa.mm @@ -8,6 +8,8 @@ #include "nsCOMPtr.h" #include "nsCocoaUtils.h" +using namespace mozilla; + NS_IMPL_ISUPPORTS(nsScreenManagerCocoa, nsIScreenManager) nsScreenManagerCocoa::nsScreenManagerCocoa() @@ -60,13 +62,16 @@ nsScreenManagerCocoa::ScreenForId (uint32_t aId, nsIScreen **outScreen) } NS_IMETHODIMP -nsScreenManagerCocoa::ScreenForRect (int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, +nsScreenManagerCocoa::ScreenForRect (int32_t aX, int32_t aY, + int32_t aWidth, int32_t aHeight, nsIScreen **outScreen) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator]; - NSRect inRect = nsCocoaUtils::GeckoRectToCocoaRect(nsIntRect(aX, aY, aWidth, aHeight)); + NSRect inRect = + nsCocoaUtils::GeckoRectToCocoaRect(DesktopIntRect(aX, aY, + aWidth, aHeight)); NSScreen *screenWindowIsOn = [NSScreen mainScreen]; float greatestArea = 0; diff --git a/widget/gtk/gtk3drawing.c b/widget/gtk/gtk3drawing.c index 9c544f262f..3986e7ebb3 100644 --- a/widget/gtk/gtk3drawing.c +++ b/widget/gtk/gtk3drawing.c @@ -722,8 +722,6 @@ ensure_scrolled_window_widget() gint moz_gtk_init() { - GtkWidgetClass *entry_class; - if (is_initialized) return MOZ_GTK_SUCCESS; @@ -744,11 +742,6 @@ moz_gtk_init() notebook_has_tab_gap = TRUE; } - /* Add style property to GtkEntry. - * Adding the style property to the normal GtkEntry class means that it - * will work without issues inside GtkComboBox and for Spinbuttons. */ - entry_class = g_type_class_ref(GTK_TYPE_ENTRY); - return MOZ_GTK_SUCCESS; } @@ -1095,14 +1088,11 @@ moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, GtkTextDirection direction) { GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); - GtkStateType saved_state; GdkRectangle arrow_rect; gdouble arrow_angle; GtkStyleContext* style; GtkWidget *scrollbar; gint arrow_displacement_x, arrow_displacement_y; - const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? - "vscrollbar" : "hscrollbar"; ensure_scrollbar_widget(); @@ -1215,7 +1205,6 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); GtkStyleContext* style; GtkScrollbar *scrollbar; - GtkAdjustment *adj; GtkBorder margin; ensure_scrollbar_widget(); @@ -1648,7 +1637,7 @@ moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect, GtkTextDirection direction) { GdkRectangle arrow_rect, real_arrow_rect; - gint arrow_size, separator_width; + gint separator_width; gboolean wide_separators; GtkStyleContext* style; GtkRequisition arrow_req; @@ -2456,9 +2445,8 @@ moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, GtkStyleContext* style; gboolean wide_separators; gint separator_height; - gint paint_height; guint border_width; - gint x, y, w, h; + gint x, y, w; GtkBorder padding; ensure_menu_separator_widget(); @@ -2472,7 +2460,6 @@ moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, x = rect->x + border_width; y = rect->y + border_width; w = rect->width - border_width * 2; - h = rect->height - border_width * 2; gtk_style_context_save(style); gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR); @@ -3396,8 +3383,6 @@ gboolean moz_gtk_has_scrollbar_buttons(void) gint moz_gtk_shutdown() { - GtkWidgetClass *entry_class; - if (gTooltipWidget) gtk_widget_destroy(gTooltipWidget); /* This will destroy all of our widgets */ @@ -3453,9 +3438,6 @@ moz_gtk_shutdown() gVPanedWidget = NULL; gScrolledWindowWidget = NULL; - entry_class = g_type_class_peek(GTK_TYPE_ENTRY); - g_type_class_unref(entry_class); - is_initialized = FALSE; return MOZ_GTK_SUCCESS; diff --git a/widget/gtk/mozcontainer.c b/widget/gtk/mozcontainer.c index 34d2d006c6..9b596e4fb8 100644 --- a/widget/gtk/mozcontainer.c +++ b/widget/gtk/mozcontainer.c @@ -264,8 +264,6 @@ moz_container_size_allocate (GtkWidget *widget, MozContainer *container; GList *tmp_list; GtkAllocation tmp_allocation; - GtkRequisition tmp_requisition; - GtkWidget *tmp_child; g_return_if_fail (IS_MOZ_CONTAINER (widget)); @@ -385,13 +383,10 @@ moz_container_allocate_child (MozContainer *container, MozContainerChild *child) { GtkAllocation allocation; - GtkRequisition requisition; gtk_widget_get_allocation (child->widget, &allocation); allocation.x = child->x; allocation.y = child->y; - /* gtk_widget_get_child_requisition (child->widget, &requisition); */ - /* gtk_widget_size_request (child->widget, &requisition); */ gtk_widget_size_allocate (child->widget, &allocation); } diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 16e7c75ee2..5558331329 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -10,6 +10,7 @@ #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" +#include "mozilla/TimeStamp.h" #include "mozilla/TouchEvents.h" #include "mozilla/UniquePtrExtensions.h" #include @@ -1085,10 +1086,9 @@ nsWindow::Show(bool aState) NS_IMETHODIMP nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { - CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale() - : CSSToLayoutDeviceScale(1.0); - int32_t width = NSToIntRound(scale.scale * aWidth); - int32_t height = NSToIntRound(scale.scale * aHeight); + double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; + int32_t width = NSToIntRound(scale * aWidth); + int32_t height = NSToIntRound(scale * aHeight); ConstrainSize(&width, &height); // For top-level windows, aWidth and aHeight should possibly be @@ -1117,14 +1117,13 @@ NS_IMETHODIMP nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) { - CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale() - : CSSToLayoutDeviceScale(1.0); - int32_t width = NSToIntRound(scale.scale * aWidth); - int32_t height = NSToIntRound(scale.scale * aHeight); + double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; + int32_t width = NSToIntRound(scale * aWidth); + int32_t height = NSToIntRound(scale * aHeight); ConstrainSize(&width, &height); - int32_t x = NSToIntRound(scale.scale * aX); - int32_t y = NSToIntRound(scale.scale * aY); + int32_t x = NSToIntRound(scale * aX); + int32_t y = NSToIntRound(scale * aY); mBounds.x = x; mBounds.y = y; mBounds.SizeTo(width, height); @@ -1183,10 +1182,9 @@ nsWindow::Move(double aX, double aY) LOG(("nsWindow::Move [%p] %f %f\n", (void *)this, aX, aY)); - CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale() - : CSSToLayoutDeviceScale(1.0); - int32_t x = NSToIntRound(aX * scale.scale); - int32_t y = NSToIntRound(aY * scale.scale); + double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0; + int32_t x = NSToIntRound(aX * scale); + int32_t y = NSToIntRound(aY * scale); if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) { @@ -5349,6 +5347,18 @@ gboolean expose_event_cb(GtkWidget *widget, cairo_t *cr) { draw_window_of_widget(widget, gtk_widget_get_window(widget), cr); + + // A strong reference is already held during "draw" signal emission, + // but GTK+ 3.4 wants the object to live a little longer than that + // (bug 1225970). + g_object_ref(widget); + g_idle_add( + [](gpointer data) -> gboolean { + g_object_unref(data); + return G_SOURCE_REMOVE; + }, + widget); + return FALSE; } #endif //MOZ_WIDGET_GTK2 diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 458dcb8933..fee62a3a9a 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -100,6 +100,11 @@ public: virtual nsIWidget *GetParent() override; virtual float GetDPI() override; virtual double GetDefaultScaleInternal() override; + // Under Gtk, we manage windows using device pixels so no scaling is needed: + mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final + { + return mozilla::DesktopToLayoutDeviceScale(1.0); + } virtual nsresult SetParent(nsIWidget* aNewParent) override; NS_IMETHOD SetModal(bool aModal) override; virtual bool IsVisible() const override; diff --git a/widget/gtkxtbin/gtk2xtbin.c b/widget/gtkxtbin/gtk2xtbin.c index 90af5072e4..189478bada 100644 --- a/widget/gtkxtbin/gtk2xtbin.c +++ b/widget/gtkxtbin/gtk2xtbin.c @@ -40,7 +40,6 @@ static void gtk_xtbin_init (GtkXtBin *xtbin); static void gtk_xtbin_realize (GtkWidget *widget); static void gtk_xtbin_unrealize (GtkWidget *widget); static void gtk_xtbin_destroy (GtkObject *object); -static void gtk_xtbin_shutdown (GtkObject *object); /* Xt aware XEmbed */ static void xt_client_handle_xembed_message (Widget w, @@ -105,7 +104,6 @@ xt_event_dispatch (GSource* source_data, GSourceFunc call_back, gpointer user_data) { - XEvent event; XtAppContext ac; int i = 0;