mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1249482 - OdinMonkey: Remove the needsBoundsCheck flag from the frontend. r=luke (5004e664a0) - Bug 1249513 - OdinMonkey: MIPS: Clean up long jump patching in asm.js. r=luke (18c7f026f5) - fix misspatch of 1234985 (0713b005b4) - Bug 1248598 part 1 - Some code changes required for the i64.const instruction. r=luke (7bb3eaa57f) - Bug 1247855: Resolve named functions and locals (r=luke) (e06d418fd1) - Bug 1249524 - Baldr: add the other (param) declaration form to the text format (r=mbx) (9492f57377) - Bug 1249523 - Baldr: add return (r=mbx) (36a4eca6a3) - Bug 1249316 - Baldr: move testing functions back into TestingFunctions.cpp (r=jandem) (7daf99a017) - Bug 1249531 - Baldr: add text format and encoding support for control operators (r=mbx) (6dee433349) - Bug 1248598 part 2 - Reject wasm i64 imports/exports as JS doesn't have an int64 type. r=luke (72603cdb3d) - Bug 1246658 part 4 - Replace MConstant's js::Value with a custom union. r=luke (1c78b526d2) - Bug 1246658 part 5 - Support int64 constants, add MIRType_Int64. r=luk (db94c230c6) - Bug 1246658 part 1 followup - Fix a bug introduced by the refactoring. (ec3d444596) - Bug 1248859 - OdinMonkey: MIPS: Fix replace retargetWithOffset. r=arai (dd117fcbf5) - Bug 1249525 - Baldr: accept - and $ in text names (r=sunfish) (64e4e1ddf8) - Bug 1249525 - Baldr: accept integer cases in float constants (r=sunfish) (90a8fbb5d5) - Bug 1248859 - OdinMonkey: MIPS: Implement thunkWithPatch and re/patchThunk. r=luke (722240c9b7) - Bug 1248859 - OdinMonkey: MIPS: Refactor callWithPatch via reative branch. r=luke (0684904686) - Bug 1248503 - Improve log output for MSimdBox and MSimdUnbox. r=nbp (6b65608504) - Bug 1248503 - Fix initial heap assertion. r=nbp (bcf704df34) - Bug 1249525 - Baldr: change order of load/store immediates/subexpressions (r=sunfish) (c2ec5329e2) - Bug 1240055: IonMonkey: When spewing info about range analysis, also spew truncation info, r=nbp (cf477cffce) - fix misspatch in symbol visibility (039e111b31)
This commit is contained in:
+28
-88
@@ -1794,7 +1794,12 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
return false;
|
||||
}
|
||||
|
||||
return mg_.init(Move(genData), Move(filename));
|
||||
if (!mg_.init(Move(genData), Move(filename)))
|
||||
return false;
|
||||
|
||||
mg_.bumpMinHeapLength(module_->minHeapLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ExclusiveContext* cx() const { return cx_; }
|
||||
@@ -2090,8 +2095,10 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
if (len > uint64_t(INT32_MAX) + 1)
|
||||
return false;
|
||||
len = RoundUpToNextValidAsmJSHeapLength(len);
|
||||
if (len > module_->minHeapLength)
|
||||
if (len > module_->minHeapLength) {
|
||||
module_->minHeapLength = len;
|
||||
mg_.bumpMinHeapLength(len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3505,40 +3512,12 @@ IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn, uint32_t* u32)
|
||||
return IsLiteralInt(lit, u32);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldMaskedArrayIndex(FunctionValidator& f, ParseNode** indexExpr, int32_t* mask,
|
||||
NeedsBoundsCheck* needsBoundsCheck)
|
||||
{
|
||||
MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND));
|
||||
|
||||
ParseNode* indexNode = BitwiseLeft(*indexExpr);
|
||||
ParseNode* maskNode = BitwiseRight(*indexExpr);
|
||||
|
||||
uint32_t mask2;
|
||||
if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
|
||||
// Flag the access to skip the bounds check if the mask ensures that an
|
||||
// 'out of bounds' access can not occur based on the current heap length
|
||||
// constraint. The unsigned maximum of a masked index is the mask
|
||||
// itself, so check that the mask is not negative and compare the mask
|
||||
// to the known minimum heap length.
|
||||
if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength())
|
||||
*needsBoundsCheck = NO_BOUNDS_CHECK;
|
||||
*mask &= mask2;
|
||||
*indexExpr = indexNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int32_t NoMask = -1;
|
||||
|
||||
static bool
|
||||
CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
|
||||
Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask)
|
||||
Scalar::Type* viewType, int32_t* mask)
|
||||
{
|
||||
*needsBoundsCheck = NEEDS_BOUNDS_CHECK;
|
||||
|
||||
if (!viewName->isKind(PNK_NAME))
|
||||
return f.fail(viewName, "base of array access must be a typed array view name");
|
||||
|
||||
@@ -3555,7 +3534,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
|
||||
return f.fail(indexExpr, "constant index out of range");
|
||||
|
||||
*mask = NoMask;
|
||||
*needsBoundsCheck = NO_BOUNDS_CHECK;
|
||||
return f.writeInt32Lit(byteOffset);
|
||||
}
|
||||
|
||||
@@ -3577,9 +3555,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
|
||||
|
||||
ParseNode* pointerNode = BitwiseLeft(indexExpr);
|
||||
|
||||
if (pointerNode->isKind(PNK_BITAND))
|
||||
FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
|
||||
|
||||
Type pointerType;
|
||||
if (!CheckExpr(f, pointerNode, &pointerType))
|
||||
return false;
|
||||
@@ -3596,9 +3571,6 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
|
||||
|
||||
ParseNode* pointerNode = indexExpr;
|
||||
|
||||
if (pointerNode->isKind(PNK_BITAND))
|
||||
folded = FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
|
||||
|
||||
Type pointerType;
|
||||
if (!CheckExpr(f, pointerNode, &pointerType))
|
||||
return false;
|
||||
@@ -3617,13 +3589,13 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
|
||||
|
||||
static bool
|
||||
CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
|
||||
Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck, int32_t* mask)
|
||||
Scalar::Type* viewType, int32_t* mask)
|
||||
{
|
||||
size_t prepareAt;
|
||||
if (!f.encoder().writePatchableExpr(&prepareAt))
|
||||
return false;
|
||||
|
||||
if (!CheckArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask))
|
||||
if (!CheckArrayAccess(f, viewName, indexExpr, viewType, mask))
|
||||
return false;
|
||||
|
||||
// Don't generate the mask op if there is no need for it which could happen for
|
||||
@@ -3641,17 +3613,13 @@ static bool
|
||||
CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
|
||||
{
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
|
||||
size_t opcodeAt;
|
||||
size_t needsBoundsCheckAt;
|
||||
if (!f.encoder().writePatchableExpr(&opcodeAt))
|
||||
return false;
|
||||
if (!f.encoder().writePatchableU8(&needsBoundsCheckAt))
|
||||
return false;
|
||||
|
||||
if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &mask))
|
||||
return false;
|
||||
|
||||
switch (viewType) {
|
||||
@@ -3666,8 +3634,6 @@ CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
|
||||
default: MOZ_CRASH("unexpected scalar type");
|
||||
}
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
|
||||
switch (viewType) {
|
||||
case Scalar::Int8:
|
||||
case Scalar::Int16:
|
||||
@@ -3693,16 +3659,12 @@ static bool
|
||||
CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
|
||||
{
|
||||
size_t opcodeAt;
|
||||
size_t needsBoundsCheckAt;
|
||||
if (!f.encoder().writePatchableExpr(&opcodeAt))
|
||||
return false;
|
||||
if (!f.encoder().writePatchableU8(&needsBoundsCheckAt))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &mask))
|
||||
return false;
|
||||
|
||||
Type rhsType;
|
||||
@@ -3759,8 +3721,6 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
|
||||
default: MOZ_CRASH("unexpected scalar type");
|
||||
}
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
|
||||
*type = rhsType;
|
||||
return true;
|
||||
}
|
||||
@@ -3999,10 +3959,9 @@ CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* typ
|
||||
|
||||
static bool
|
||||
CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
|
||||
Scalar::Type* viewType, NeedsBoundsCheck* needsBoundsCheck,
|
||||
int32_t* mask)
|
||||
Scalar::Type* viewType, int32_t* mask)
|
||||
{
|
||||
if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, needsBoundsCheck, mask))
|
||||
if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, viewType, mask))
|
||||
return false;
|
||||
|
||||
// The global will be sane, CheckArrayAccess checks it.
|
||||
@@ -4038,10 +3997,9 @@ CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
}
|
||||
|
||||
static bool
|
||||
WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* needsBoundsCheckAt, size_t* viewTypeAt)
|
||||
WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt)
|
||||
{
|
||||
return f.encoder().writeExpr(opcode) &&
|
||||
f.encoder().writePatchableU8(needsBoundsCheckAt) &&
|
||||
f.encoder().writePatchableU8(viewTypeAt);
|
||||
}
|
||||
|
||||
@@ -4054,18 +4012,15 @@ CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
ParseNode* arrayArg = CallArgList(call);
|
||||
ParseNode* indexArg = NextNode(arrayArg);
|
||||
|
||||
size_t needsBoundsCheckAt;
|
||||
size_t viewTypeAt;
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, &needsBoundsCheckAt, &viewTypeAt))
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, &viewTypeAt))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
|
||||
return false;
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
|
||||
|
||||
*type = Type::Int;
|
||||
@@ -4082,15 +4037,13 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
ParseNode* indexArg = NextNode(arrayArg);
|
||||
ParseNode* valueArg = NextNode(indexArg);
|
||||
|
||||
size_t needsBoundsCheckAt;
|
||||
size_t viewTypeAt;
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsStore, &needsBoundsCheckAt, &viewTypeAt))
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsStore, &viewTypeAt))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
|
||||
return false;
|
||||
|
||||
Type rhsType;
|
||||
@@ -4100,7 +4053,6 @@ CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
if (!rhsType.isIntish())
|
||||
return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
|
||||
|
||||
*type = rhsType;
|
||||
@@ -4117,17 +4069,15 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op
|
||||
ParseNode* indexArg = NextNode(arrayArg);
|
||||
ParseNode* valueArg = NextNode(indexArg);
|
||||
|
||||
size_t needsBoundsCheckAt;
|
||||
size_t viewTypeAt;
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &needsBoundsCheckAt, &viewTypeAt))
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &viewTypeAt))
|
||||
return false;
|
||||
if (!f.encoder().writeU8(uint8_t(op)))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
|
||||
return false;
|
||||
|
||||
Type valueArgType;
|
||||
@@ -4137,7 +4087,6 @@ CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op
|
||||
if (!valueArgType.isIntish())
|
||||
return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
|
||||
|
||||
*type = Type::Int;
|
||||
@@ -4171,15 +4120,13 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
ParseNode* oldValueArg = NextNode(indexArg);
|
||||
ParseNode* newValueArg = NextNode(oldValueArg);
|
||||
|
||||
size_t needsBoundsCheckAt;
|
||||
size_t viewTypeAt;
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsCompareExchange, &needsBoundsCheckAt, &viewTypeAt))
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsCompareExchange, &viewTypeAt))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
|
||||
return false;
|
||||
|
||||
Type oldValueArgType;
|
||||
@@ -4196,7 +4143,6 @@ CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
if (!newValueArgType.isIntish())
|
||||
return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
|
||||
|
||||
*type = Type::Int;
|
||||
@@ -4213,15 +4159,13 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
ParseNode* indexArg = NextNode(arrayArg);
|
||||
ParseNode* valueArg = NextNode(indexArg);
|
||||
|
||||
size_t needsBoundsCheckAt;
|
||||
size_t viewTypeAt;
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsExchange, &needsBoundsCheckAt, &viewTypeAt))
|
||||
if (!WriteAtomicOperator(f, Expr::I32AtomicsExchange, &viewTypeAt))
|
||||
return false;
|
||||
|
||||
Scalar::Type viewType;
|
||||
NeedsBoundsCheck needsBoundsCheck;
|
||||
int32_t mask;
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &needsBoundsCheck, &mask))
|
||||
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &mask))
|
||||
return false;
|
||||
|
||||
Type valueArgType;
|
||||
@@ -4231,7 +4175,6 @@ CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
|
||||
if (!valueArgType.isIntish())
|
||||
return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars());
|
||||
|
||||
f.encoder().patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
|
||||
f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
|
||||
|
||||
*type = Type::Int;
|
||||
@@ -5078,12 +5021,9 @@ CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call)
|
||||
if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
|
||||
if (!f.m().tryConstantAccess(indexLit, Simd128DataSize))
|
||||
return f.fail(indexExpr, "constant index out of range");
|
||||
return f.encoder().writeU8(NO_BOUNDS_CHECK) && f.writeInt32Lit(indexLit);
|
||||
return f.writeInt32Lit(indexLit);
|
||||
}
|
||||
|
||||
if (!f.encoder().writeU8(NEEDS_BOUNDS_CHECK))
|
||||
return false;
|
||||
|
||||
Type indexType;
|
||||
if (!CheckExpr(f, indexExpr, &indexType))
|
||||
return false;
|
||||
|
||||
+47
-111
@@ -362,13 +362,24 @@ DecodeIfElse(FunctionDecoder& f, bool hasElse, ExprType expected)
|
||||
static bool
|
||||
DecodeLoadStoreAddress(FunctionDecoder &f)
|
||||
{
|
||||
uint32_t offset, align;
|
||||
return DecodeExpr(f, ExprType::I32) &&
|
||||
f.d().readVarU32(&offset) &&
|
||||
f.d().readVarU32(&align) &&
|
||||
mozilla::IsPowerOfTwo(align) &&
|
||||
(offset == 0 || f.fail("NYI: address offsets")) &&
|
||||
f.fail("NYI: wasm loads and stores");
|
||||
uint32_t offset;
|
||||
if (!f.d().readVarU32(&offset))
|
||||
return f.fail("expected memory access offset");
|
||||
|
||||
if (offset != 0)
|
||||
return f.fail("NYI: address offsets");
|
||||
|
||||
uint32_t align;
|
||||
if (!f.d().readVarU32(&align))
|
||||
return f.fail("expected memory access alignment");
|
||||
|
||||
if (!mozilla::IsPowerOfTwo(align))
|
||||
return f.fail("memory access alignment must be a power of two");
|
||||
|
||||
if (!DecodeExpr(f, ExprType::I32))
|
||||
return false;
|
||||
|
||||
return f.fail("NYI: wasm loads and stores");
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -386,6 +397,13 @@ DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type)
|
||||
CheckType(f, type, expected);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeReturn(FunctionDecoder& f)
|
||||
{
|
||||
return f.ret() == ExprType::Void ||
|
||||
DecodeExpr(f, f.ret());
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeExpr(FunctionDecoder& f, ExprType expected)
|
||||
{
|
||||
@@ -613,6 +631,8 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
|
||||
return DecodeStore(f, expected, ExprType::F32);
|
||||
case Expr::F64StoreMem:
|
||||
return DecodeStore(f, expected, ExprType::F64);
|
||||
case Expr::Return:
|
||||
return DecodeReturn(f);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -846,6 +866,20 @@ DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckTypeForJS(JSContext* cx, Decoder& d, const Sig& sig)
|
||||
{
|
||||
for (ValType argType : sig.args()) {
|
||||
if (argType == ValType::I64)
|
||||
return Fail(cx, d, "cannot import/export i64 argument");
|
||||
}
|
||||
|
||||
if (sig.ret() == ExprType::I64)
|
||||
return Fail(cx, d, "cannot import/export i64 return type");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
|
||||
{
|
||||
@@ -856,6 +890,9 @@ DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVec
|
||||
if (!init->imports.emplaceBack(sig))
|
||||
return false;
|
||||
|
||||
if (!CheckTypeForJS(cx, d, *sig))
|
||||
return false;
|
||||
|
||||
UniqueChars moduleName = d.readCString();
|
||||
if (!moduleName)
|
||||
return Fail(cx, d, "expected import module name");
|
||||
@@ -969,6 +1006,9 @@ DecodeFunctionExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, CStringSet*
|
||||
if (funcIndex >= mg.numFuncSigs())
|
||||
return Fail(cx, d, "export function index out of range");
|
||||
|
||||
if (!CheckTypeForJS(cx, d, mg.funcSig(funcIndex)))
|
||||
return false;
|
||||
|
||||
UniqueChars fieldName = DecodeFieldName(cx, d, dupSet);
|
||||
if (!fieldName)
|
||||
return false;
|
||||
@@ -1254,14 +1294,6 @@ wasm::HasCompilerSupport(ExclusiveContext* cx)
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setBoolean(HasCompilerSupport(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckCompilerSupport(JSContext* cx)
|
||||
{
|
||||
@@ -1357,99 +1389,3 @@ wasm::Eval(JSContext* cx, Handle<ArrayBufferObject*> code,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() < 1 || args.length() > 2) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
|
||||
ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject importObj(cx);
|
||||
if (!args.get(1).isUndefined()) {
|
||||
if (!args.get(1).isObject()) {
|
||||
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
|
||||
return false;
|
||||
}
|
||||
importObj = &args[1].toObject();
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
|
||||
|
||||
RootedObject exportObj(cx);
|
||||
if (!Eval(cx, code, importObj, &exportObj))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*exportObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() != 1) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isString()) {
|
||||
ReportUsageError(cx, callee, "First argument must be a String");
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoStableStringChars twoByteChars(cx);
|
||||
if (!twoByteChars.initTwoByte(cx, args[0].toString()))
|
||||
return false;
|
||||
|
||||
UniqueChars error;
|
||||
UniqueBytecode bytes = TextToBinary(twoByteChars.twoByteChars(), &error);
|
||||
if (!bytes) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
|
||||
error.get() ? error.get() : "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx, ArrayBufferObject::create(cx, bytes->length()));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
memcpy(buffer->dataPointer(), bytes->begin(), bytes->length());
|
||||
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp WasmTestingFunctions[] = {
|
||||
JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
|
||||
"wasmIsSupported()",
|
||||
" Returns a boolean indicating whether WebAssembly is supported on the current device."),
|
||||
|
||||
JS_FN_HELP("wasmEval", WasmEval, 2, 0,
|
||||
"wasmEval(buffer, imports)",
|
||||
" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n"
|
||||
" and links the module's imports with the given 'imports' object."),
|
||||
|
||||
JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
|
||||
"wasmTextToBinary(str)",
|
||||
" Translates the given text wasm module into its binary encoding."),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
bool
|
||||
wasm::DefineTestingFunctions(JSContext* cx, HandleObject globalObj)
|
||||
{
|
||||
return JS_DefineFunctionsWithHelp(cx, globalObj, WasmTestingFunctions);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@ class ArrayBufferObject;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
// Add wasm testing JS functions to the given JS global object.
|
||||
bool
|
||||
DefineTestingFunctions(JSContext* cx, JS::HandleObject globalObj);
|
||||
|
||||
// Return whether WebAssembly can be compiled on this platform.
|
||||
bool
|
||||
HasCompilerSupport(ExclusiveContext* cx);
|
||||
|
||||
+16
-12
@@ -322,12 +322,6 @@ enum class Expr : uint16_t
|
||||
Limit
|
||||
};
|
||||
|
||||
enum NeedsBoundsCheck : uint8_t
|
||||
{
|
||||
NO_BOUNDS_CHECK,
|
||||
NEEDS_BOUNDS_CHECK
|
||||
};
|
||||
|
||||
typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytecode;
|
||||
typedef UniquePtr<Bytecode> UniqueBytecode;
|
||||
|
||||
@@ -713,19 +707,29 @@ class Decoder
|
||||
double uncheckedReadFixedF64() {
|
||||
return uncheckedRead<double>();
|
||||
}
|
||||
uint32_t uncheckedReadVarU32() {
|
||||
uint32_t decoded = 0;
|
||||
template <typename UInt>
|
||||
UInt uncheckedReadVarU() {
|
||||
static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
|
||||
static const unsigned remainderBits = numBits % 7;
|
||||
static const unsigned numBitsInSevens = numBits - remainderBits;
|
||||
UInt decoded = 0;
|
||||
uint32_t shift = 0;
|
||||
do {
|
||||
uint8_t byte = *cur_++;
|
||||
if (!(byte & 0x80))
|
||||
return decoded | (uint32_t(byte) << shift);
|
||||
decoded |= uint32_t(byte & 0x7f) << shift;
|
||||
return decoded | (UInt(byte) << shift);
|
||||
decoded |= UInt(byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (shift != 28);
|
||||
} while (shift != numBitsInSevens);
|
||||
uint8_t byte = *cur_++;
|
||||
MOZ_ASSERT(!(byte & 0xf0));
|
||||
return decoded | (uint32_t(byte) << 28);
|
||||
return decoded | (UInt(byte) << numBitsInSevens);
|
||||
}
|
||||
uint32_t uncheckedReadVarU32() {
|
||||
return uncheckedReadVarU<uint32_t>();
|
||||
}
|
||||
uint64_t uncheckedReadVarU64() {
|
||||
return uncheckedReadVarU<uint64_t>();
|
||||
}
|
||||
Expr uncheckedReadExpr() {
|
||||
return uncheckedReadEnum<Expr>();
|
||||
|
||||
@@ -357,16 +357,7 @@ wasm::GenerateFunctionEpilogue(MacroAssembler& masm, unsigned framePushed, FuncO
|
||||
masm.twoByteNop();
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
masm.nop();
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.nop();
|
||||
masm.nop();
|
||||
#endif
|
||||
@@ -740,12 +731,12 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo
|
||||
MOZ_CRASH();
|
||||
void* callee = nullptr;
|
||||
(void)callerRetAddr;
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next());
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t));
|
||||
void* callee = (void*)Assembler::ExtractLoad64Value(instr);
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
uint8_t* caller = callerRetAddr - 2 * sizeof(uint32_t);
|
||||
InstImm* callerInsn = reinterpret_cast<InstImm*>(caller);
|
||||
BOffImm16 calleeOffset;
|
||||
callerInsn->extractImm16(&calleeOffset);
|
||||
void* callee = calleeOffset.getDest(reinterpret_cast<Instruction*>(caller));
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
void* callee = nullptr;
|
||||
@@ -771,13 +762,8 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
(void)to;
|
||||
MOZ_CRASH();
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Assembler::WriteLuiOriInstructions(instr, instr->next(),
|
||||
ScratchRegister, (uint32_t)to);
|
||||
instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)to);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
callerInsn->setBOffImm16(BOffImm16(to - caller));
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
@@ -833,25 +819,12 @@ wasm::EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange,
|
||||
(void)jump;
|
||||
(void)profilingEpilogue;
|
||||
MOZ_CRASH();
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
Instruction* instr = (Instruction*)jump;
|
||||
if (enabled) {
|
||||
Assembler::WriteLuiOriInstructions(instr, instr->next(),
|
||||
ScratchRegister, (uint32_t)profilingEpilogue);
|
||||
instr[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
|
||||
} else {
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
instr[i].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
Instruction* instr = (Instruction*)jump;
|
||||
if (enabled) {
|
||||
Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)profilingEpilogue);
|
||||
instr[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
|
||||
} else {
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
instr[i].makeNop();
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
InstImm* instr = (InstImm*)jump;
|
||||
if (enabled)
|
||||
instr->setBOffImm16(BOffImm16(profilingEpilogue - jump));
|
||||
else
|
||||
instr->makeNop();
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#else
|
||||
|
||||
@@ -522,20 +522,6 @@ ModuleGenerator::finishStaticLinkData(uint8_t* code, uint32_t codeBytes, StaticL
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// On MIPS we need to update all the long jumps because they contain an
|
||||
// absolute adress. The values are correctly patched for the current address
|
||||
// space, but not after serialization or profiling-mode toggling.
|
||||
for (size_t i = 0; i < masm_.numLongJumps(); i++) {
|
||||
size_t off = masm_.longJump(i);
|
||||
StaticLinkData::InternalLink inLink(StaticLinkData::InternalLink::InstructionImmediate);
|
||||
inLink.patchAtOffset = off;
|
||||
inLink.targetOffset = Assembler::ExtractInstructionImmediate(code + off) - uintptr_t(code);
|
||||
if (!link->internalLinks.append(inLink))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
// Global data accesses on x64 use rip-relative addressing and thus do
|
||||
// not need patching after deserialization.
|
||||
@@ -651,6 +637,15 @@ ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ModuleGenerator::bumpMinHeapLength(uint32_t newMinHeapLength)
|
||||
{
|
||||
MOZ_ASSERT(isAsmJS());
|
||||
MOZ_ASSERT(newMinHeapLength >= shared_->minHeapLength);
|
||||
|
||||
shared_->minHeapLength = newMinHeapLength;
|
||||
}
|
||||
|
||||
const DeclaredSig&
|
||||
ModuleGenerator::funcSig(uint32_t funcIndex) const
|
||||
{
|
||||
@@ -741,9 +736,6 @@ ModuleGenerator::startFuncDefs()
|
||||
if (!threadView_)
|
||||
return false;
|
||||
|
||||
if (!funcIndexToExport_.init())
|
||||
return false;
|
||||
|
||||
uint32_t numTasks;
|
||||
if (ParallelCompilationEnabled(cx_) &&
|
||||
HelperThreadState().wasmCompilationInProgress.compareExchange(false, true))
|
||||
|
||||
@@ -99,6 +99,7 @@ struct ModuleGeneratorData
|
||||
CompileArgs args;
|
||||
ModuleKind kind;
|
||||
uint32_t numTableElems;
|
||||
mozilla::Atomic<uint32_t> minHeapLength;
|
||||
|
||||
DeclaredSigVector sigs;
|
||||
TableModuleGeneratorDataVector sigToTable;
|
||||
@@ -111,7 +112,7 @@ struct ModuleGeneratorData
|
||||
}
|
||||
|
||||
explicit ModuleGeneratorData(ExclusiveContext* cx, ModuleKind kind = ModuleKind::Wasm)
|
||||
: args(cx), kind(kind), numTableElems(0)
|
||||
: args(cx), kind(kind), numTableElems(0), minHeapLength(0)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -140,6 +141,9 @@ class ModuleGeneratorThreadView
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return shared_.numTableElems;
|
||||
}
|
||||
uint32_t minHeapLength() const {
|
||||
return shared_.minHeapLength;
|
||||
}
|
||||
const DeclaredSig& sig(uint32_t sigIndex) const {
|
||||
return shared_.sigs[sigIndex];
|
||||
}
|
||||
@@ -258,6 +262,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
||||
bool initImport(uint32_t importIndex, uint32_t sigIndex);
|
||||
bool initSigTableLength(uint32_t sigIndex, uint32_t numElems);
|
||||
void initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
|
||||
void bumpMinHeapLength(uint32_t newMinHeapLength);
|
||||
|
||||
// asm.js global variables:
|
||||
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index);
|
||||
|
||||
@@ -198,6 +198,15 @@ class FunctionCompiler
|
||||
return constant;
|
||||
}
|
||||
|
||||
MDefinition* constant(int64_t i)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
MConstant* constant = MConstant::NewInt64(alloc(), i);
|
||||
curBlock_->add(constant);
|
||||
return constant;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
MDefinition* unary(MDefinition* op)
|
||||
{
|
||||
@@ -470,53 +479,46 @@ class FunctionCompiler
|
||||
curBlock_->setSlot(info().localSlot(slot), def);
|
||||
}
|
||||
|
||||
MDefinition* loadHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk)
|
||||
MDefinition* loadHeap(Scalar::Type accessType, MDefinition* ptr)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD loads should use loadSimdHeap");
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck);
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr);
|
||||
curBlock_->add(load);
|
||||
return load;
|
||||
}
|
||||
|
||||
MDefinition* loadSimdHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk,
|
||||
unsigned numElems)
|
||||
MDefinition* loadSimdHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numElems)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MOZ_ASSERT(Scalar::isSimdType(accessType), "loadSimdHeap can only load from a SIMD view");
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck,
|
||||
numElems);
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, numElems);
|
||||
curBlock_->add(load);
|
||||
return load;
|
||||
}
|
||||
|
||||
void storeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, NeedsBoundsCheck chk)
|
||||
void storeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD stores should use storeSimdHeap");
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck);
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v);
|
||||
curBlock_->add(store);
|
||||
}
|
||||
|
||||
void storeSimdHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v,
|
||||
NeedsBoundsCheck chk, unsigned numElems)
|
||||
unsigned numElems)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MOZ_ASSERT(Scalar::isSimdType(accessType), "storeSimdHeap can only load from a SIMD view");
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck,
|
||||
numElems);
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, numElems);
|
||||
curBlock_->add(store);
|
||||
}
|
||||
|
||||
@@ -528,66 +530,59 @@ class FunctionCompiler
|
||||
curBlock_->add(ins);
|
||||
}
|
||||
|
||||
MDefinition* atomicLoadHeap(Scalar::Type accessType, MDefinition* ptr, NeedsBoundsCheck chk)
|
||||
MDefinition* atomicLoadHeap(Scalar::Type accessType, MDefinition* ptr)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck,
|
||||
/* numElems */ 0,
|
||||
MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, /* numElems */ 0,
|
||||
MembarBeforeLoad, MembarAfterLoad);
|
||||
curBlock_->add(load);
|
||||
return load;
|
||||
}
|
||||
|
||||
void atomicStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, NeedsBoundsCheck chk)
|
||||
void atomicStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck,
|
||||
MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v,
|
||||
/* numElems = */ 0,
|
||||
MembarBeforeStore, MembarAfterStore);
|
||||
curBlock_->add(store);
|
||||
}
|
||||
|
||||
MDefinition* atomicCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv,
|
||||
MDefinition* newv, NeedsBoundsCheck chk)
|
||||
MDefinition* newv)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MAsmJSCompareExchangeHeap* cas =
|
||||
MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv, needsBoundsCheck);
|
||||
MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv);
|
||||
curBlock_->add(cas);
|
||||
return cas;
|
||||
}
|
||||
|
||||
MDefinition* atomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value,
|
||||
NeedsBoundsCheck chk)
|
||||
MDefinition* atomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MAsmJSAtomicExchangeHeap* cas =
|
||||
MAsmJSAtomicExchangeHeap::New(alloc(), accessType, ptr, value, needsBoundsCheck);
|
||||
MAsmJSAtomicExchangeHeap::New(alloc(), accessType, ptr, value);
|
||||
curBlock_->add(cas);
|
||||
return cas;
|
||||
}
|
||||
|
||||
MDefinition* atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type accessType, MDefinition* ptr,
|
||||
MDefinition* v, NeedsBoundsCheck chk)
|
||||
MDefinition* v)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK;
|
||||
MAsmJSAtomicBinopHeap* binop =
|
||||
MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v, needsBoundsCheck);
|
||||
MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v);
|
||||
curBlock_->add(binop);
|
||||
return binop;
|
||||
}
|
||||
@@ -1234,6 +1229,7 @@ class FunctionCompiler
|
||||
|
||||
uint8_t readU8() { return decoder_.uncheckedReadFixedU8(); }
|
||||
uint32_t readVarU32() { return decoder_.uncheckedReadVarU32(); }
|
||||
uint64_t readVarU64() { return decoder_.uncheckedReadVarU64(); }
|
||||
float readF32() { return decoder_.uncheckedReadFixedF32(); }
|
||||
double readF64() { return decoder_.uncheckedReadFixedF64(); }
|
||||
Expr readExpr() { return decoder_.uncheckedReadExpr(); }
|
||||
@@ -1364,7 +1360,9 @@ EmitLiteral(FunctionCompiler& f, ExprType type, MDefinition** def)
|
||||
return true;
|
||||
}
|
||||
case ExprType::I64: {
|
||||
MOZ_CRASH("int64");
|
||||
int64_t val = f.readVarU64();
|
||||
*def = f.constant(val);
|
||||
return true;
|
||||
}
|
||||
case ExprType::F32: {
|
||||
float val = f.readF32();
|
||||
@@ -1425,19 +1423,16 @@ static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullpt
|
||||
static bool
|
||||
EmitLoadArray(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
MDefinition* ptr;
|
||||
if (!EmitExpr(f, ExprType::I32, &ptr))
|
||||
return false;
|
||||
*def = f.loadHeap(scalarType, ptr, needsBoundsCheck);
|
||||
*def = f.loadHeap(scalarType, ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
|
||||
MDefinition* ptr;
|
||||
if (!EmitExpr(f, ExprType::I32, &ptr))
|
||||
return false;
|
||||
@@ -1461,7 +1456,7 @@ EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
|
||||
default: MOZ_CRASH("unexpected scalar type");
|
||||
}
|
||||
|
||||
f.storeHeap(viewType, ptr, rhs, needsBoundsCheck);
|
||||
f.storeHeap(viewType, ptr, rhs);
|
||||
*def = rhs;
|
||||
return true;
|
||||
}
|
||||
@@ -1470,7 +1465,6 @@ static bool
|
||||
EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type viewType,
|
||||
MDefinition **def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
MDefinition* ptr;
|
||||
if (!EmitExpr(f, ExprType::I32, &ptr))
|
||||
return false;
|
||||
@@ -1489,7 +1483,7 @@ EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type vi
|
||||
MOZ_CRASH("unexpected coerced store");
|
||||
}
|
||||
|
||||
f.storeHeap(viewType, ptr, coerced, needsBoundsCheck);
|
||||
f.storeHeap(viewType, ptr, coerced);
|
||||
*def = rhs;
|
||||
return true;
|
||||
}
|
||||
@@ -1541,19 +1535,17 @@ EmitMathMinMax(FunctionCompiler& f, ExprType type, bool isMax, MDefinition** def
|
||||
static bool
|
||||
EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
Scalar::Type viewType = Scalar::Type(f.readU8());
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
return false;
|
||||
*def = f.atomicLoadHeap(viewType, index, needsBoundsCheck);
|
||||
*def = f.atomicLoadHeap(viewType, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
Scalar::Type viewType = Scalar::Type(f.readU8());
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
@@ -1561,7 +1553,7 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
|
||||
MDefinition* value;
|
||||
if (!EmitExpr(f, ExprType::I32, &value))
|
||||
return false;
|
||||
f.atomicStoreHeap(viewType, index, value, needsBoundsCheck);
|
||||
f.atomicStoreHeap(viewType, index, value);
|
||||
*def = value;
|
||||
return true;
|
||||
}
|
||||
@@ -1569,7 +1561,6 @@ EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
|
||||
static bool
|
||||
EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
Scalar::Type viewType = Scalar::Type(f.readU8());
|
||||
js::jit::AtomicOp op = js::jit::AtomicOp(f.readU8());
|
||||
MDefinition* index;
|
||||
@@ -1578,14 +1569,13 @@ EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
|
||||
MDefinition* value;
|
||||
if (!EmitExpr(f, ExprType::I32, &value))
|
||||
return false;
|
||||
*def = f.atomicBinopHeap(op, viewType, index, value, needsBoundsCheck);
|
||||
*def = f.atomicBinopHeap(op, viewType, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
Scalar::Type viewType = Scalar::Type(f.readU8());
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
@@ -1596,14 +1586,13 @@ EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
|
||||
MDefinition* newValue;
|
||||
if (!EmitExpr(f, ExprType::I32, &newValue))
|
||||
return false;
|
||||
*def = f.atomicCompareExchangeHeap(viewType, index, oldValue, newValue, needsBoundsCheck);
|
||||
*def = f.atomicCompareExchangeHeap(viewType, index, oldValue, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
|
||||
{
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
Scalar::Type viewType = Scalar::Type(f.readU8());
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
@@ -1611,7 +1600,7 @@ EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
|
||||
MDefinition* value;
|
||||
if (!EmitExpr(f, ExprType::I32, &value))
|
||||
return false;
|
||||
*def = f.atomicExchangeHeap(viewType, index, value, needsBoundsCheck);
|
||||
*def = f.atomicExchangeHeap(viewType, index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1980,13 +1969,11 @@ EmitSimdLoad(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition*
|
||||
if (!numElems)
|
||||
numElems = defaultNumElems;
|
||||
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
return false;
|
||||
|
||||
*def = f.loadSimdHeap(viewType, index, needsBoundsCheck, numElems);
|
||||
*def = f.loadSimdHeap(viewType, index, numElems);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1999,8 +1986,6 @@ EmitSimdStore(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition
|
||||
if (!numElems)
|
||||
numElems = defaultNumElems;
|
||||
|
||||
NeedsBoundsCheck needsBoundsCheck = NeedsBoundsCheck(f.readU8());
|
||||
|
||||
MDefinition* index;
|
||||
if (!EmitExpr(f, ExprType::I32, &index))
|
||||
return false;
|
||||
@@ -2009,7 +1994,7 @@ EmitSimdStore(FunctionCompiler& f, ExprType type, unsigned numElems, MDefinition
|
||||
if (!EmitExpr(f, type, &vec))
|
||||
return false;
|
||||
|
||||
f.storeSimdHeap(viewType, index, vec, needsBoundsCheck, numElems);
|
||||
f.storeSimdHeap(viewType, index, vec, numElems);
|
||||
*def = vec;
|
||||
return true;
|
||||
}
|
||||
@@ -2837,6 +2822,9 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
|
||||
return EmitAtomicsStore(f, def);
|
||||
case Expr::I32AtomicsBinOp:
|
||||
return EmitAtomicsBinOp(f, def);
|
||||
// I64
|
||||
case Expr::I64Const:
|
||||
return EmitLiteral(f, ExprType::I64, def);
|
||||
// F32
|
||||
case Expr::F32Const:
|
||||
return EmitLiteral(f, ExprType::F32, def);
|
||||
@@ -2978,7 +2966,6 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
|
||||
case Expr::F64Nearest:
|
||||
case Expr::F64Trunc:
|
||||
case Expr::I32WrapI64:
|
||||
case Expr::I64Const:
|
||||
case Expr::I64ExtendSI32:
|
||||
case Expr::I64ExtendUI32:
|
||||
case Expr::I64TruncSF32:
|
||||
@@ -3060,8 +3047,9 @@ wasm::IonCompileFunction(IonCompileTask* task)
|
||||
MIRGraph graph(&results.alloc());
|
||||
CompileInfo compileInfo(func.numLocals());
|
||||
MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo,
|
||||
IonOptimizations.get(OptimizationLevel::AsmJS),
|
||||
task->mg().args().useSignalHandlersForOOB);
|
||||
IonOptimizations.get(OptimizationLevel::AsmJS));
|
||||
mir.initUsesSignalHandlersForAsmJSOOB(task->mg().args().useSignalHandlersForOOB);
|
||||
mir.initMinAsmJSHeapLength(task->mg().minHeapLength());
|
||||
|
||||
// Build MIR graph
|
||||
{
|
||||
|
||||
+1028
-208
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,18 @@ js::IsVectorObject(HandleValue v)
|
||||
FOR_EACH_SIMD(InstantiateIsVectorObject_)
|
||||
#undef InstantiateIsVectorObject_
|
||||
|
||||
const char*
|
||||
js::SimdTypeToString(SimdType type)
|
||||
{
|
||||
switch (type) {
|
||||
#define RETSTR_(TYPE) case SimdType::TYPE: return #TYPE;
|
||||
FOR_EACH_SIMD(RETSTR_)
|
||||
#undef RETSTR_
|
||||
case SimdType::Count: break;
|
||||
}
|
||||
return "<bad SimdType>";
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ErrorBadArgs(JSContext* cx)
|
||||
{
|
||||
|
||||
@@ -1105,6 +1105,8 @@ struct Bool64x2 {
|
||||
|
||||
PropertyName* SimdTypeToName(JSContext* cx, SimdType type);
|
||||
|
||||
const char* SimdTypeToString(SimdType type);
|
||||
|
||||
template<typename V>
|
||||
JSObject* CreateSimd(JSContext* cx, const typename V::Elem* data);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "asmjs/Wasm.h"
|
||||
#include "asmjs/WasmText.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#include "js/Debug.h"
|
||||
@@ -487,6 +488,87 @@ IsProxy(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().setBoolean(wasm::HasCompilerSupport(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() < 1 || args.length() > 2) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
|
||||
ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject importObj(cx);
|
||||
if (!args.get(1).isUndefined()) {
|
||||
if (!args.get(1).isObject()) {
|
||||
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
|
||||
return false;
|
||||
}
|
||||
importObj = &args[1].toObject();
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
|
||||
|
||||
RootedObject exportObj(cx);
|
||||
if (!wasm::Eval(cx, code, importObj, &exportObj))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*exportObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() != 1) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isString()) {
|
||||
ReportUsageError(cx, callee, "First argument must be a String");
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoStableStringChars twoByteChars(cx);
|
||||
if (!twoByteChars.initTwoByte(cx, args[0].toString()))
|
||||
return false;
|
||||
|
||||
UniqueChars error;
|
||||
wasm::UniqueBytecode bytes = wasm::TextToBinary(twoByteChars.twoByteChars(), &error);
|
||||
if (!bytes) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
|
||||
error.get() ? error.get() : "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx, ArrayBufferObject::create(cx, bytes->length()));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
memcpy(buffer->dataPointer(), bytes->begin(), bytes->length());
|
||||
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsLazyFunction(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -3575,6 +3657,19 @@ gc::ZealModeHelpText),
|
||||
" Returns whether the given value is a nested function in an asm.js module that has been\n"
|
||||
" both compile- and link-time validated."),
|
||||
|
||||
JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
|
||||
"wasmIsSupported()",
|
||||
" Returns a boolean indicating whether WebAssembly is supported on the current device."),
|
||||
|
||||
JS_FN_HELP("wasmEval", WasmEval, 2, 0,
|
||||
"wasmEval(buffer, imports)",
|
||||
" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n"
|
||||
" and links the module's imports with the given 'imports' object."),
|
||||
|
||||
JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
|
||||
"wasmTextToBinary(str)",
|
||||
" Translates the given text wasm module into its binary encoding."),
|
||||
|
||||
JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
|
||||
"isLazyFunction(fun)",
|
||||
" True if fun is a lazy JSFunction."),
|
||||
@@ -3828,9 +3923,6 @@ js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
|
||||
|
||||
disableOOMFunctions = disableOOMFunctions_;
|
||||
|
||||
if (!wasm::DefineTestingFunctions(cx, obj))
|
||||
return false;
|
||||
|
||||
if (!JS_DefineProperties(cx, obj, TestingProperties))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
function assertEqVec(v, w) {
|
||||
[0].forEach(i => v, w);
|
||||
function assertEqX4(...opts) {}
|
||||
}
|
||||
gczeal(1);
|
||||
function f() {
|
||||
SIMD.Float32x4();
|
||||
var i1 = SIMD.Int32x4();
|
||||
for (j = 0; j < 100000; ++j, eval.eval)
|
||||
assertEqVec(SIMD.Int32x4.check(i1), i1);
|
||||
}
|
||||
f();
|
||||
|
||||
@@ -131,3 +131,41 @@ assertEq(counter, 0);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (if (i32.const 42) (i32.const 0))))'), TypeError, mismatchError("void", "i32"));
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (if_else (i32.const 1) (i32.const 0) (if (i32.const 1) (i32.const 1)))))'), TypeError, mismatchError("void", "i32"));
|
||||
wasmEvalText('(module (func (if_else (i32.const 1) (i32.const 0) (if (i32.const 1) (i32.const 1)))))');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// return
|
||||
|
||||
assertEq(wasmEvalText('(module (func (return)) (export "" 0))')(), undefined);
|
||||
assertEq(wasmEvalText('(module (func (result i32) (return (i32.const 1))) (export "" 0))')(), 1);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (result f32) (return (i32.const 1))) (export "" 0))'), TypeError, mismatchError("i32", "f32"));
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (func (result i32) (return)) (export "" 0))'), TypeError);
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (func (return (i32.const 1))) (export "" 0))'), TypeError);
|
||||
|
||||
// TODO: convert these to wasmEval and assert some results once they are implemented
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// br / br_if
|
||||
|
||||
wasmTextToBinary('(module (func (block (br 0))))');
|
||||
wasmTextToBinary('(module (func (block $l (br $l))))');
|
||||
wasmTextToBinary('(module (func (result i32) (block (br_if 0 (i32.const 1)))))');
|
||||
wasmTextToBinary('(module (func (result i32) (block $l (br_if $l (i32.const 1)))))');
|
||||
|
||||
wasmTextToBinary('(module (func (block $l (block $m (br $l)))))');
|
||||
wasmTextToBinary('(module (func (block $l (block $m (br $m)))))');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// loop
|
||||
|
||||
wasmTextToBinary('(module (func (loop (br 0))))');
|
||||
wasmTextToBinary('(module (func (loop (br 1))))');
|
||||
wasmTextToBinary('(module (func (loop $a (br $a))))');
|
||||
wasmTextToBinary('(module (func (loop $a $b (br $a))))');
|
||||
wasmTextToBinary('(module (func (loop $a $b (br $b))))');
|
||||
wasmTextToBinary('(module (func (loop $a $a (br $a))))');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// tableswitch
|
||||
|
||||
wasmTextToBinary('(module (func (param i32) (block $b1 (block $b2 (tableswitch (get_local 0) (table (br $b1) (br $b2)) (br $b2))))))');
|
||||
wasmTextToBinary('(module (func (param i32) (block $b1 (tableswitch (get_local 0) (table) (br $b1)))))');
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
load(libdir + "wasm.js");
|
||||
|
||||
assertEq(wasmEvalText('(module (func (result f32) (f32.const -1)) (export "" 0))')(), -1);
|
||||
assertEq(wasmEvalText('(module (func (result f32) (f32.const 1)) (export "" 0))')(), 1);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const -2)) (export "" 0))')(), -2);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const 2)) (export "" 0))')(), 2);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const 4294967296)) (export "" 0))')(), 4294967296);
|
||||
assertEq(wasmEvalText('(module (func (result f32) (f32.const 1.5)) (export "" 0))')(), 1.5);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const 2.5)) (export "" 0))')(), 2.5);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const 10e2)) (export "" 0))')(), 10e2);
|
||||
assertEq(wasmEvalText('(module (func (result f32) (f32.const 10e2)) (export "" 0))')(), 10e2);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const -0x8000000000000000)) (export "" 0))')(), -0x8000000000000000);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const -9223372036854775808)) (export "" 0))')(), -9223372036854775808);
|
||||
assertEq(wasmEvalText('(module (func (result f64) (f64.const 1797693134862315708145274e284)) (export "" 0))')(), 1797693134862315708145274e284);
|
||||
|
||||
function testUnary(type, opcode, op, expect) {
|
||||
assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
load(libdir + "wasm.js");
|
||||
|
||||
assertEq(wasmEvalText('(module (func (result i32) (i32.const -1)) (export "" 0))')(), -1);
|
||||
assertEq(wasmEvalText('(module (func (result i32) (i32.const -2147483648)) (export "" 0))')(), -2147483648);
|
||||
assertEq(wasmEvalText('(module (func (result i32) (i32.const 4294967295)) (export "" 0))')(), -1);
|
||||
|
||||
function testUnary(type, opcode, op, expect) {
|
||||
assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@ function testLoad(type, ext, base, offset, align, expect) {
|
||||
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
|
||||
' )' +
|
||||
' (func (param i32) (result ' + type + ')' +
|
||||
' (' + type + '.load' + ext + ' (get_local 0)' +
|
||||
' (' + type + '.load' + ext +
|
||||
' offset=' + offset +
|
||||
' ' + (align != 0 ? 'align=' + align : '') +
|
||||
' (get_local 0)' +
|
||||
' )' +
|
||||
' ) (export "" 0))');
|
||||
assertEq(wasmEvalText(
|
||||
@@ -29,9 +30,10 @@ function testLoad(type, ext, base, offset, align, expect) {
|
||||
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
|
||||
' )' +
|
||||
' (func (param i32) (result ' + type + ')' +
|
||||
' (' + type + '.load' + ext + ' (get_local 0)' +
|
||||
' (' + type + '.load' + ext +
|
||||
' offset=' + offset +
|
||||
' ' + (align != 0 ? 'align=' + align : '') +
|
||||
' (get_local 0)' +
|
||||
' )' +
|
||||
' ) (export "" 0))'
|
||||
)(base), expect);
|
||||
@@ -45,9 +47,10 @@ function testStore(type, ext, base, offset, align, value) {
|
||||
' (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
|
||||
' )' +
|
||||
' (func (param i32) (param ' + type + ') (result ' + type + ')' +
|
||||
' (' + type + '.store' + ext + ' (get_local 0)' +
|
||||
' (' + type + '.store' + ext +
|
||||
' offset=' + offset +
|
||||
' ' + (align != 0 ? 'align=' + align : '') +
|
||||
' (get_local 0)' +
|
||||
' (get_local 1)' +
|
||||
' )' +
|
||||
' ) (export "" 0))'
|
||||
|
||||
@@ -46,6 +46,9 @@ assertEq(desc.value(), undefined);
|
||||
|
||||
wasmEvalText('(module (func) (func) (export "a" 0))');
|
||||
wasmEvalText('(module (func) (func) (export "a" 1))');
|
||||
wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
|
||||
wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /export function index out of range/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /export function index out of range/);
|
||||
|
||||
@@ -121,6 +124,7 @@ wasmEvalText(code, {a:()=>{}, b:{c:()=>{}}, c:()=>{}});
|
||||
wasmEvalText('(module (import "a" "" (result i32)))', {a: ()=> {}});
|
||||
wasmEvalText('(module (import "a" "" (result f32)))', {a: ()=> {}});
|
||||
wasmEvalText('(module (import "a" "" (result f64)))', {a: ()=> {}});
|
||||
wasmEvalText('(module (import $foo "a" "" (result f64)))', {a: ()=> {}});
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// memory
|
||||
@@ -221,6 +225,11 @@ assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (get_
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), TypeError, /NYI/);
|
||||
|
||||
assertEq(wasmEvalText('(module (func (param $a i32) (result i32) (get_local $a)) (export "" 0))')(), 0);
|
||||
assertEq(wasmEvalText('(module (func (param $a i32) (local $b i32) (result i32) (block (set_local $b (get_local $a)) (get_local $b))) (export "" 0))')(42), 42);
|
||||
|
||||
wasmEvalText('(module (func (local i32) (local $a f32) (set_local 0 (i32.const 1)) (set_local $a (f32.const nan))))');
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// blocks
|
||||
|
||||
@@ -252,6 +261,8 @@ wasmEvalText('(module (func (param i32) (nop)) (func (call 0 (i32.const 0))))');
|
||||
assertEq(wasmEvalText('(module (func (result i32) (i32.const 42)) (func (result i32) (call 0)) (export "" 1))')(), 42);
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 0)) (export "" 0))')(), InternalError);
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 1)) (func (call 0)) (export "" 0))')(), InternalError);
|
||||
wasmEvalText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (f32.const nan))))');
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (i32.const 0))))'), TypeError, mismatchError("i32", "f32"));
|
||||
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (import "a" "") (func (call_import 0 (i32.const 0))))', {a:()=>{}}), TypeError);
|
||||
assertThrowsInstanceOf(() => wasmEvalText('(module (import "a" "" (param i32)) (func (call_import 0)))', {a:()=>{}}), TypeError);
|
||||
@@ -336,3 +347,38 @@ for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math
|
||||
assertThrowsInstanceOf(() => i2i(bad, 0), RangeError);
|
||||
assertThrowsInstanceOf(() => i2v(bad, 0), RangeError);
|
||||
}
|
||||
|
||||
// When the test below starts failing, remove it and uncomment the lines below!
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
|
||||
/*
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/);
|
||||
*/
|
||||
|
||||
|
||||
var {v2i, i2i, i2v} = wasmEvalText(`(module
|
||||
(type $a (func (result i32)))
|
||||
(type $b (func (param i32) (result i32)))
|
||||
(type $c (func (param i32)))
|
||||
(func $a (type $a) (i32.const 13))
|
||||
(func $b (type $a) (i32.const 42))
|
||||
(func $c (type $b) (i32.add (get_local 0) (i32.const 1)))
|
||||
(func $d (type $b) (i32.add (get_local 0) (i32.const 2)))
|
||||
(func $e (type $b) (i32.add (get_local 0) (i32.const 3)))
|
||||
(func $f (type $b) (i32.add (get_local 0) (i32.const 4)))
|
||||
(table $a $b $c $d $e $f)
|
||||
(func (param i32) (result i32) (call_indirect $a (get_local 0)))
|
||||
(func (param i32) (param i32) (result i32) (call_indirect $b (get_local 0) (get_local 1)))
|
||||
(func (param i32) (call_indirect $c (get_local 0) (i32.const 0)))
|
||||
(export "v2i" 6)
|
||||
(export "i2i" 7)
|
||||
(export "i2v" 8)
|
||||
)`);
|
||||
|
||||
wasmEvalText('(module (func $foo (nop)) (func (call $foo)))');
|
||||
wasmEvalText('(module (func (call $foo)) (func $foo (nop)))');
|
||||
wasmEvalText('(module (import $bar "a" "") (func (call_import $bar)) (func $foo (nop)))', {a:()=>{}});
|
||||
assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call_import $abc)))'), SyntaxError, /import not found/);
|
||||
|
||||
|
||||
@@ -8,6 +8,19 @@ assertErrorMessage(() => wasmEvalText('(m'), SyntaxError, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText('(module'), SyntaxError, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText('(moduler'), SyntaxError, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (export "a'), SyntaxError, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (local $a i32) (param $b f32)))'), SyntaxError, parsingError);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (func $a) (func) (export "a" $a) (export "b" $b))'), SyntaxError, /function not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (import $foo "a" "b") (import $foo "a" "b"))'), SyntaxError, /duplicate import/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func $foo) (func $foo))'), SyntaxError, /duplicate function/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (param $a i32) (local $a i32)))'), SyntaxError, /duplicate var/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (get_local $a)))'), SyntaxError, /local not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (type $a (func (param i32))))'), SyntaxError, /duplicate signature/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call_import $abc)))'), SyntaxError, /import not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (type $b) (i32.const 13)))'), SyntaxError, /signature not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (type $a (func)) (func (call_indirect $c (get_local 0) (i32.const 0))))'), SyntaxError, /signature not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (br $a)))'), SyntaxError, /label not found/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (block $a) (br $a)))'), SyntaxError, /label not found/);
|
||||
|
||||
// Note: the s-expression text format is temporary, this file is mostly just to
|
||||
// hold basic error smoke tests.
|
||||
|
||||
@@ -148,6 +148,14 @@ EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins)
|
||||
ins->block()->insertBefore(ins, zero);
|
||||
ins->replacePtr(zero);
|
||||
}
|
||||
|
||||
// If the index is within the minimum heap length, we can optimize
|
||||
// away the bounds check.
|
||||
if (imm >= 0) {
|
||||
int32_t end = (uint32_t)imm + ins->byteSize();
|
||||
if (end >= imm && (uint32_t)end <= mir_->minAsmJSHeapLength())
|
||||
ins->removeBoundsCheck();
|
||||
}
|
||||
} else if (ptr->isAdd()) {
|
||||
// Look for heap[a+i] where i is a constant offset, and fold the offset.
|
||||
// Alignment masks have already been moved out of the way by the
|
||||
|
||||
@@ -1269,7 +1269,9 @@ GuessPhiType(MPhi* phi, bool* hasInputsWithEmptyTypes)
|
||||
// If we only saw definitions that can be converted into Float32 before and
|
||||
// encounter a Float32 value, promote previous values to Float32
|
||||
type = MIRType_Float32;
|
||||
} else if (IsNumberType(type) && IsNumberType(in->type())) {
|
||||
} else if (IsTypeRepresentableAsDouble(type) &&
|
||||
IsTypeRepresentableAsDouble(in->type()))
|
||||
{
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
type = MIRType_Double;
|
||||
convertibleToFloat32 &= in->canProduceFloat32();
|
||||
@@ -1329,7 +1331,9 @@ TypeAnalyzer::propagateSpecialization(MPhi* phi)
|
||||
}
|
||||
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
if (IsNumberType(use->type()) && IsNumberType(phi->type())) {
|
||||
if (IsTypeRepresentableAsDouble(use->type()) &&
|
||||
IsTypeRepresentableAsDouble(phi->type()))
|
||||
{
|
||||
if (!respecialize(use, MIRType_Double))
|
||||
return false;
|
||||
continue;
|
||||
@@ -2507,6 +2511,7 @@ IsResumableMIRType(MIRType type)
|
||||
case MIRType_ObjectGroup:
|
||||
case MIRType_Doublex2: // NYI, see also RSimdBox::recover
|
||||
case MIRType_SinCosDouble:
|
||||
case MIRType_Int64:
|
||||
return false;
|
||||
}
|
||||
MOZ_CRASH("Unknown MIRType.");
|
||||
|
||||
+15
-1
@@ -399,6 +399,7 @@ enum MIRType
|
||||
MIRType_Null,
|
||||
MIRType_Boolean,
|
||||
MIRType_Int32,
|
||||
MIRType_Int64,
|
||||
MIRType_Double,
|
||||
MIRType_Float32,
|
||||
MIRType_String,
|
||||
@@ -504,6 +505,8 @@ StringFromMIRType(MIRType type)
|
||||
return "Bool";
|
||||
case MIRType_Int32:
|
||||
return "Int32";
|
||||
case MIRType_Int64:
|
||||
return "Int64";
|
||||
case MIRType_Double:
|
||||
return "Double";
|
||||
case MIRType_Float32:
|
||||
@@ -556,7 +559,18 @@ StringFromMIRType(MIRType type)
|
||||
static inline bool
|
||||
IsNumberType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32;
|
||||
return type == MIRType_Int32 ||
|
||||
type == MIRType_Double ||
|
||||
type == MIRType_Float32 ||
|
||||
type == MIRType_Int64;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsTypeRepresentableAsDouble(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 ||
|
||||
type == MIRType_Double ||
|
||||
type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
||||
@@ -1276,7 +1276,7 @@ IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outp
|
||||
|
||||
// Optimize some constant powers.
|
||||
if (rhs->isConstant()) {
|
||||
double pow = rhs->toConstant()->toNumber();
|
||||
double pow = rhs->toConstant()->numberToDouble();
|
||||
|
||||
// Math.pow(x, 0.5) is a sqrt with edge-case detection.
|
||||
if (pow == 0.5) {
|
||||
@@ -1482,7 +1482,7 @@ IonBuilder::inlineMathMinMax(CallInfo& callInfo, bool max)
|
||||
// Don't force a double MMinMax for arguments that would be a NOP
|
||||
// when doing an integer MMinMax.
|
||||
if (arg->isConstant()) {
|
||||
double cte = arg->toConstant()->toNumber();
|
||||
double cte = arg->toConstant()->numberToDouble();
|
||||
// min(int32, cte >= INT32_MAX) = int32
|
||||
if (cte >= INT32_MAX && !max)
|
||||
break;
|
||||
@@ -3296,10 +3296,12 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr)
|
||||
defVal = constant(Int32Value(0));
|
||||
} else if (laneType == MIRType_Boolean) {
|
||||
defVal = constant(BooleanValue(false));
|
||||
} else {
|
||||
MOZ_ASSERT(IsFloatingPointType(laneType));
|
||||
} else if (laneType == MIRType_Double) {
|
||||
defVal = constant(DoubleNaNValue());
|
||||
defVal->setResultType(laneType);
|
||||
} else {
|
||||
MOZ_ASSERT(laneType == MIRType_Float32);
|
||||
defVal = MConstant::NewFloat32(alloc(), GenericNaN());
|
||||
current->add(defVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+222
-75
@@ -85,7 +85,8 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt
|
||||
MDefinition* left = ins->getOperand(0);
|
||||
MDefinition* right = ins->getOperand(1);
|
||||
|
||||
MOZ_ASSERT(IsNumberType(left->type()) && IsNumberType(right->type()));
|
||||
MOZ_ASSERT(IsTypeRepresentableAsDouble(left->type()));
|
||||
MOZ_ASSERT(IsTypeRepresentableAsDouble(right->type()));
|
||||
|
||||
if (!left->isConstant() || !right->isConstant())
|
||||
return nullptr;
|
||||
@@ -114,25 +115,25 @@ EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* pt
|
||||
ret.setNumber(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F));
|
||||
break;
|
||||
case MDefinition::Op_Add:
|
||||
ret.setNumber(lhs->toNumber() + rhs->toNumber());
|
||||
ret.setNumber(lhs->numberToDouble() + rhs->numberToDouble());
|
||||
break;
|
||||
case MDefinition::Op_Sub:
|
||||
ret.setNumber(lhs->toNumber() - rhs->toNumber());
|
||||
ret.setNumber(lhs->numberToDouble() - rhs->numberToDouble());
|
||||
break;
|
||||
case MDefinition::Op_Mul:
|
||||
ret.setNumber(lhs->toNumber() * rhs->toNumber());
|
||||
ret.setNumber(lhs->numberToDouble() * rhs->numberToDouble());
|
||||
break;
|
||||
case MDefinition::Op_Div:
|
||||
if (ins->toDiv()->isUnsigned())
|
||||
ret.setInt32(rhs->isInt32(0) ? 0 : uint32_t(lhs->toInt32()) / uint32_t(rhs->toInt32()));
|
||||
else
|
||||
ret.setNumber(NumberDiv(lhs->toNumber(), rhs->toNumber()));
|
||||
ret.setNumber(NumberDiv(lhs->numberToDouble(), rhs->numberToDouble()));
|
||||
break;
|
||||
case MDefinition::Op_Mod:
|
||||
if (ins->toMod()->isUnsigned())
|
||||
ret.setInt32(rhs->isInt32(0) ? 0 : uint32_t(lhs->toInt32()) % uint32_t(rhs->toInt32()));
|
||||
else
|
||||
ret.setNumber(NumberMod(lhs->toNumber(), rhs->toNumber()));
|
||||
ret.setNumber(NumberMod(lhs->numberToDouble(), rhs->numberToDouble()));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("NYI");
|
||||
@@ -166,7 +167,7 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins)
|
||||
return nullptr;
|
||||
|
||||
int32_t num;
|
||||
if (!mozilla::NumberIsInt32(right->toConstant()->toNumber(), &num))
|
||||
if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num))
|
||||
return nullptr;
|
||||
|
||||
// check if rhs is a power of two
|
||||
@@ -175,8 +176,12 @@ EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins)
|
||||
|
||||
Value ret;
|
||||
ret.setDouble(1.0 / (double) num);
|
||||
MConstant* foldedRhs = MConstant::New(alloc, ret);
|
||||
foldedRhs->setResultType(ins->type());
|
||||
MConstant* foldedRhs;
|
||||
if (ins->type() == MIRType_Float32)
|
||||
foldedRhs = MConstant::NewFloat32(alloc, ret.toDouble());
|
||||
else
|
||||
foldedRhs = MConstant::New(alloc, ret);
|
||||
MOZ_ASSERT(foldedRhs->type() == ins->type());
|
||||
ins->block()->insertBefore(ins, foldedRhs);
|
||||
|
||||
MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
|
||||
@@ -660,23 +665,26 @@ MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* con
|
||||
}
|
||||
|
||||
MConstant*
|
||||
MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
|
||||
CompilerConstraintList* constraints)
|
||||
MConstant::NewFloat32(TempAllocator& alloc, double d)
|
||||
{
|
||||
MOZ_ASSERT(!IsSimdType(type));
|
||||
MOZ_ASSERT_IF(type == MIRType_Float32,
|
||||
IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
|
||||
MConstant* constant = new(alloc) MConstant(v, constraints);
|
||||
constant->setResultType(type);
|
||||
return constant;
|
||||
MOZ_ASSERT(IsNaN(d) || d == double(float(d)));
|
||||
return new(alloc) MConstant(float(d));
|
||||
}
|
||||
|
||||
MConstant*
|
||||
MConstant::NewInt64(TempAllocator& alloc, int64_t i)
|
||||
{
|
||||
return new(alloc) MConstant(i);
|
||||
}
|
||||
|
||||
MConstant*
|
||||
MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type)
|
||||
{
|
||||
if (type == MIRType_Float32)
|
||||
return NewTypedValue(alloc, Float32Value(v.toNumber()), type);
|
||||
return NewTypedValue(alloc, v, type);
|
||||
return NewFloat32(alloc, v.toNumber());
|
||||
MConstant* res = New(alloc, v);
|
||||
MOZ_ASSERT(res->type() == type);
|
||||
return res;
|
||||
}
|
||||
|
||||
MConstant*
|
||||
@@ -739,16 +747,44 @@ jit::IonCompilationCanUseNurseryPointers()
|
||||
#endif // DEBUG
|
||||
|
||||
MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
|
||||
: value_(vp)
|
||||
{
|
||||
setResultType(MIRTypeFromValue(vp));
|
||||
if (vp.isObject()) {
|
||||
|
||||
MOZ_ASSERT(payload_.asBits == 0);
|
||||
|
||||
switch (type()) {
|
||||
case MIRType_Undefined:
|
||||
case MIRType_Null:
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
payload_.b = vp.toBoolean();
|
||||
break;
|
||||
case MIRType_Int32:
|
||||
payload_.i32 = vp.toInt32();
|
||||
break;
|
||||
case MIRType_Double:
|
||||
payload_.d = vp.toDouble();
|
||||
break;
|
||||
case MIRType_String:
|
||||
MOZ_ASSERT(vp.toString()->isAtom());
|
||||
payload_.str = vp.toString();
|
||||
break;
|
||||
case MIRType_Symbol:
|
||||
payload_.sym = vp.toSymbol();
|
||||
break;
|
||||
case MIRType_Object:
|
||||
payload_.obj = &vp.toObject();
|
||||
// Create a singleton type set for the object. This isn't necessary for
|
||||
// other types as the result type encodes all needed information.
|
||||
MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
|
||||
setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
|
||||
}
|
||||
if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
|
||||
break;
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
case MIRType_MagicOptimizedOut:
|
||||
case MIRType_MagicHole:
|
||||
case MIRType_MagicIsConstructing:
|
||||
break;
|
||||
case MIRType_MagicUninitializedLexical:
|
||||
// JS_UNINITIALIZED_LEXICAL does not escape to script and is not
|
||||
// observed in type sets. However, it may flow around freely during
|
||||
// Ion compilation. Give it an unknown typeset to poison any type sets
|
||||
@@ -757,38 +793,92 @@ MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
|
||||
// TODO We could track uninitialized lexicals more precisely by tracking
|
||||
// them in type sets.
|
||||
setResultTypeSet(MakeUnknownTypeSet());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected type");
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom());
|
||||
|
||||
setMovable();
|
||||
}
|
||||
|
||||
MConstant::MConstant(JSObject* obj)
|
||||
: value_(ObjectValue(*obj))
|
||||
{
|
||||
MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers());
|
||||
setResultType(MIRType_Object);
|
||||
payload_.obj = obj;
|
||||
setMovable();
|
||||
}
|
||||
|
||||
MConstant::MConstant(float f)
|
||||
{
|
||||
setResultType(MIRType_Float32);
|
||||
payload_.f = f;
|
||||
setMovable();
|
||||
}
|
||||
|
||||
MConstant::MConstant(int64_t i)
|
||||
{
|
||||
setResultType(MIRType_Int64);
|
||||
payload_.i64 = i;
|
||||
setMovable();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
MConstant::assertInitializedPayload() const
|
||||
{
|
||||
// valueHash() and equals() expect the unused payload bits to be
|
||||
// initialized to zero. Assert this in debug builds.
|
||||
|
||||
switch (type()) {
|
||||
case MIRType_Int32:
|
||||
case MIRType_Float32:
|
||||
MOZ_ASSERT((payload_.asBits >> 32) == 0);
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
MOZ_ASSERT((payload_.asBits >> 1) == 0);
|
||||
break;
|
||||
case MIRType_Double:
|
||||
case MIRType_Int64:
|
||||
break;
|
||||
case MIRType_String:
|
||||
case MIRType_Object:
|
||||
case MIRType_Symbol:
|
||||
MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
|
||||
MOZ_ASSERT(payload_.asBits == 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
HashNumber
|
||||
MConstant::valueHash() const
|
||||
{
|
||||
static_assert(sizeof(Payload) == sizeof(uint64_t),
|
||||
"Code below assumes payload fits in 64 bits");
|
||||
|
||||
assertInitializedPayload();
|
||||
|
||||
// Build a 64-bit value holding both the payload and the type.
|
||||
static const size_t TypeBits = 8;
|
||||
static const size_t TypeShift = 64 - TypeBits;
|
||||
MOZ_ASSERT(uintptr_t(type()) <= (1 << TypeBits) - 1);
|
||||
uint64_t bits = (uint64_t(type()) << TypeShift) ^ payload_.asBits;
|
||||
|
||||
// Fold all 64 bits into the 32-bit result. It's tempting to just discard
|
||||
// half of the bits, as this is just a hash, however there are many common
|
||||
// patterns of values where only the low or the high bits vary, so
|
||||
// discarding either side would lead to excessive hash collisions.
|
||||
uint64_t bits = JSVAL_TO_IMPL(value_).asBits;
|
||||
return (HashNumber)bits ^ (HashNumber)(bits >> 32);
|
||||
}
|
||||
|
||||
bool
|
||||
MConstant::congruentTo(const MDefinition* ins) const
|
||||
{
|
||||
if (!ins->isConstant())
|
||||
return false;
|
||||
return ins->toConstant()->value_ == value_;
|
||||
return ins->isConstant() && equals(ins->toConstant());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -866,7 +956,7 @@ MConstant::printOpcode(GenericPrinter& out) const
|
||||
bool
|
||||
MConstant::canProduceFloat32() const
|
||||
{
|
||||
if (!IsNumberType(type()))
|
||||
if (!isTypeRepresentableAsDouble())
|
||||
return false;
|
||||
|
||||
if (type() == MIRType_Int32)
|
||||
@@ -880,7 +970,42 @@ MConstant::canProduceFloat32() const
|
||||
Value
|
||||
MConstant::toJSValue() const
|
||||
{
|
||||
return value_;
|
||||
// Wasm has types like int64 that cannot be stored as js::Value. It also
|
||||
// doesn't want the NaN canonicalization enforced by js::Value.
|
||||
MOZ_ASSERT(!IsCompilingAsmJS());
|
||||
|
||||
switch (type()) {
|
||||
case MIRType_Undefined:
|
||||
return UndefinedValue();
|
||||
case MIRType_Null:
|
||||
return NullValue();
|
||||
case MIRType_Boolean:
|
||||
return BooleanValue(toBoolean());
|
||||
case MIRType_Int32:
|
||||
return Int32Value(toInt32());
|
||||
case MIRType_Double:
|
||||
return DoubleValue(toDouble());
|
||||
case MIRType_Float32:
|
||||
return Float32Value(toFloat32());
|
||||
case MIRType_String:
|
||||
return StringValue(toString());
|
||||
case MIRType_Symbol:
|
||||
return SymbolValue(toSymbol());
|
||||
case MIRType_Object:
|
||||
return ObjectValue(toObject());
|
||||
case MIRType_MagicOptimizedArguments:
|
||||
return MagicValue(JS_OPTIMIZED_ARGUMENTS);
|
||||
case MIRType_MagicOptimizedOut:
|
||||
return MagicValue(JS_OPTIMIZED_OUT);
|
||||
case MIRType_MagicHole:
|
||||
return MagicValue(JS_ELEMENTS_HOLE);
|
||||
case MIRType_MagicIsConstructing:
|
||||
return MagicValue(JS_IS_CONSTRUCTING);
|
||||
case MIRType_MagicUninitializedLexical:
|
||||
return MagicValue(JS_UNINITIALIZED_LEXICAL);
|
||||
default:
|
||||
MOZ_CRASH("Unexpected type");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -888,14 +1013,16 @@ MConstant::valueToBoolean(bool* res) const
|
||||
{
|
||||
switch (type()) {
|
||||
case MIRType_Boolean:
|
||||
*res = value_.toBoolean();
|
||||
*res = toBoolean();
|
||||
return true;
|
||||
case MIRType_Int32:
|
||||
*res = value_.toInt32() != 0;
|
||||
*res = toInt32() != 0;
|
||||
return true;
|
||||
case MIRType_Double:
|
||||
*res = !mozilla::IsNaN(toDouble()) && toDouble() != 0.0;
|
||||
return true;
|
||||
case MIRType_Float32:
|
||||
*res = !mozilla::IsNaN(value_.toDouble()) && value_.toDouble() != 0;
|
||||
*res = !mozilla::IsNaN(toFloat32()) && toFloat32() != 0.0f;
|
||||
return true;
|
||||
case MIRType_Null:
|
||||
case MIRType_Undefined:
|
||||
@@ -905,10 +1032,10 @@ MConstant::valueToBoolean(bool* res) const
|
||||
*res = true;
|
||||
return true;
|
||||
case MIRType_String:
|
||||
*res = value_.toString()->length() != 0;
|
||||
*res = toString()->length() != 0;
|
||||
return true;
|
||||
case MIRType_Object:
|
||||
*res = !EmulatesUndefined(&value_.toObject());
|
||||
*res = !EmulatesUndefined(&toObject());
|
||||
return true;
|
||||
default:
|
||||
MOZ_ASSERT(IsMagicType(type()));
|
||||
@@ -955,7 +1082,7 @@ MSimdValueX4::foldsTo(TempAllocator& alloc)
|
||||
case MIRType_Float32x4: {
|
||||
float a[4];
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
a[i] = getOperand(i)->toConstant()->toNumber();
|
||||
a[i] = getOperand(i)->toConstant()->numberToDouble();
|
||||
cst = SimdConstant::CreateX4(a);
|
||||
break;
|
||||
}
|
||||
@@ -991,7 +1118,7 @@ MSimdSplatX4::foldsTo(TempAllocator& alloc)
|
||||
break;
|
||||
}
|
||||
case MIRType_Float32x4: {
|
||||
float v = op->toConstant()->toNumber();
|
||||
float v = op->toConstant()->numberToDouble();
|
||||
cst = SimdConstant::SplatX4(v);
|
||||
break;
|
||||
}
|
||||
@@ -1232,6 +1359,21 @@ MSimdInsertElement::printOpcode(GenericPrinter& out) const
|
||||
out.printf(" (%s)", MSimdInsertElement::LaneName(lane()));
|
||||
}
|
||||
|
||||
void
|
||||
MSimdBox::printOpcode(GenericPrinter& out) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" (%s%s)", SimdTypeToString(simdType()),
|
||||
initialHeap() == gc::TenuredHeap ? ", tenured" : "");
|
||||
}
|
||||
|
||||
void
|
||||
MSimdUnbox::printOpcode(GenericPrinter& out) const
|
||||
{
|
||||
MDefinition::printOpcode(out);
|
||||
out.printf(" (%s)", SimdTypeToString(simdType()));
|
||||
}
|
||||
|
||||
MCloneLiteral*
|
||||
MCloneLiteral::New(TempAllocator& alloc, MDefinition* obj)
|
||||
{
|
||||
@@ -1319,10 +1461,10 @@ MDefinition*
|
||||
MMathFunction::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
MDefinition* input = getOperand(0);
|
||||
if (!input->isConstant() || !input->toConstant()->isNumber())
|
||||
if (!input->isConstant() || !input->toConstant()->isTypeRepresentableAsDouble())
|
||||
return this;
|
||||
|
||||
double in = input->toConstant()->toNumber();
|
||||
double in = input->toConstant()->numberToDouble();
|
||||
double out;
|
||||
switch (function_) {
|
||||
case Log:
|
||||
@@ -1402,7 +1544,7 @@ MMathFunction::foldsTo(TempAllocator& alloc)
|
||||
}
|
||||
|
||||
if (input->type() == MIRType_Float32)
|
||||
return MConstant::NewTypedValue(alloc, DoubleValue(out), MIRType_Float32);
|
||||
return MConstant::NewFloat32(alloc, out);
|
||||
return MConstant::New(alloc, DoubleValue(out));
|
||||
}
|
||||
|
||||
@@ -1890,7 +2032,7 @@ MPhi::foldsTernary()
|
||||
// If testArg is an int32 type we can:
|
||||
// - fold testArg ? testArg : 0 to testArg
|
||||
// - fold testArg ? 0 : testArg to 0
|
||||
if (testArg->type() == MIRType_Int32 && c->toNumber() == 0) {
|
||||
if (testArg->type() == MIRType_Int32 && c->numberToDouble() == 0) {
|
||||
// When folding to the constant we need to hoist it.
|
||||
if (trueDef == c && !c->block()->dominates(block()))
|
||||
c->block()->moveBefore(pred->lastIns(), c);
|
||||
@@ -2032,7 +2174,7 @@ jit::MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
|
||||
if (newTypeSet && newTypeSet->empty())
|
||||
return true;
|
||||
if (newType != *ptype) {
|
||||
if (IsNumberType(newType) && IsNumberType(*ptype)) {
|
||||
if (IsTypeRepresentableAsDouble(newType) && IsTypeRepresentableAsDouble(*ptype)) {
|
||||
*ptype = MIRType_Double;
|
||||
} else if (*ptype != MIRType_Value) {
|
||||
if (!*ptypeSet) {
|
||||
@@ -2266,7 +2408,7 @@ IsConstant(MDefinition* def, double v)
|
||||
if (!def->isConstant())
|
||||
return false;
|
||||
|
||||
return NumbersAreIdentical(def->toConstant()->toNumber(), v);
|
||||
return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
@@ -2680,11 +2822,14 @@ MMinMax::foldsTo(TempAllocator& alloc)
|
||||
// Directly apply math utility to compare the rhs() and lhs() when
|
||||
// they are both constants.
|
||||
if (lhs()->isConstant() && rhs()->isConstant()) {
|
||||
if (!lhs()->toConstant()->isNumber() || !rhs()->toConstant()->isNumber())
|
||||
if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
|
||||
!rhs()->toConstant()->isTypeRepresentableAsDouble())
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
double lnum = lhs()->toConstant()->toNumber();
|
||||
double rnum = rhs()->toConstant()->toNumber();
|
||||
double lnum = lhs()->toConstant()->numberToDouble();
|
||||
double rnum = rhs()->toConstant()->numberToDouble();
|
||||
double result;
|
||||
if (isMax())
|
||||
result = js::math_max_impl(lnum, rnum);
|
||||
@@ -2697,12 +2842,11 @@ MMinMax::foldsTo(TempAllocator& alloc)
|
||||
int32_t cast;
|
||||
if (mozilla::NumberEqualsInt32(result, &cast))
|
||||
return MConstant::New(alloc, Int32Value(cast));
|
||||
} else if (type() == MIRType_Float32) {
|
||||
return MConstant::NewFloat32(alloc, result);
|
||||
} else {
|
||||
MOZ_ASSERT(IsFloatingPointType(type()));
|
||||
MConstant* constant = MConstant::New(alloc, DoubleValue(result));
|
||||
if (type() == MIRType_Float32)
|
||||
constant->setResultType(MIRType_Float32);
|
||||
return constant;
|
||||
MOZ_ASSERT(type() == MIRType_Double);
|
||||
return MConstant::New(alloc, DoubleValue(result));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2711,7 +2855,10 @@ MMinMax::foldsTo(TempAllocator& alloc)
|
||||
|
||||
if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) {
|
||||
// min(int32, cte >= INT32_MAX) = int32
|
||||
if (constant->isNumber() && constant->toNumber() >= INT32_MAX && !isMax()) {
|
||||
if (!isMax() &&
|
||||
constant->isTypeRepresentableAsDouble() &&
|
||||
constant->numberToDouble() >= INT32_MAX)
|
||||
{
|
||||
MLimitedTruncate* limit =
|
||||
MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
|
||||
block()->insertBefore(this, limit);
|
||||
@@ -2720,7 +2867,10 @@ MMinMax::foldsTo(TempAllocator& alloc)
|
||||
}
|
||||
|
||||
// max(int32, cte <= INT32_MIN) = int32
|
||||
if (constant->isNumber() && constant->toNumber() <= INT32_MIN && isMax()) {
|
||||
if (isMax() &&
|
||||
constant->isTypeRepresentableAsDouble() &&
|
||||
constant->numberToDouble() <= INT32_MIN)
|
||||
{
|
||||
MLimitedTruncate* limit =
|
||||
MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
|
||||
block()->insertBefore(this, limit);
|
||||
@@ -3039,7 +3189,7 @@ MustBeUInt32(MDefinition* def, MDefinition** pwrapped)
|
||||
|
||||
if (MConstant* defConst = def->maybeConstantValue()) {
|
||||
*pwrapped = defConst;
|
||||
return defConst->isInt32(0);
|
||||
return defConst->type() == MIRType_Int32 && defConst->toInt32() >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -3112,7 +3262,7 @@ MCompare::determineCompareType(JSOp op, MDefinition* left, MDefinition* right)
|
||||
}
|
||||
|
||||
// Numeric comparisons against a double coerce to double.
|
||||
if (IsNumberType(lhs) && IsNumberType(rhs))
|
||||
if (IsTypeRepresentableAsDouble(lhs) && IsTypeRepresentableAsDouble(rhs))
|
||||
return Compare_Double;
|
||||
|
||||
// Any comparison is allowed except strict eq.
|
||||
@@ -3522,7 +3672,7 @@ MToInt32::foldsTo(TempAllocator& alloc)
|
||||
case MIRType_Double:
|
||||
int32_t ival;
|
||||
// Only the value within the range of Int32 can be substituted as constant.
|
||||
if (mozilla::NumberEqualsInt32(input->toConstant()->toNumber(), &ival))
|
||||
if (mozilla::NumberEqualsInt32(input->toConstant()->numberToDouble(), &ival))
|
||||
return MConstant::New(alloc, Int32Value(ival));
|
||||
break;
|
||||
default:
|
||||
@@ -3570,8 +3720,8 @@ MToDouble::foldsTo(TempAllocator& alloc)
|
||||
if (input->type() == MIRType_Double)
|
||||
return input;
|
||||
|
||||
if (input->isConstant() && input->toConstant()->isNumber()) {
|
||||
double out = input->toConstant()->toNumber();
|
||||
if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble()) {
|
||||
double out = input->toConstant()->numberToDouble();
|
||||
return MConstant::New(alloc, DoubleValue(out));
|
||||
}
|
||||
|
||||
@@ -3592,12 +3742,9 @@ MToFloat32::foldsTo(TempAllocator& alloc)
|
||||
if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32)
|
||||
return input->toToDouble()->input();
|
||||
|
||||
if (input->isConstant() && input->toConstant()->isNumber()) {
|
||||
float out = float(input->toConstant()->toNumber());
|
||||
MConstant* c = MConstant::New(alloc, DoubleValue(out));
|
||||
c->setResultType(MIRType_Float32);
|
||||
return c;
|
||||
}
|
||||
if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble())
|
||||
return MConstant::NewFloat32(alloc, float(input->toConstant()->numberToDouble()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3617,8 +3764,8 @@ MDefinition*
|
||||
MClampToUint8::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
if (MConstant* inputConst = input()->maybeConstantValue()) {
|
||||
if (inputConst->isNumber()) {
|
||||
int32_t clamped = ClampDoubleToUint8(inputConst->toNumber());
|
||||
if (inputConst->isTypeRepresentableAsDouble()) {
|
||||
int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
|
||||
return MConstant::New(alloc, Int32Value(clamped));
|
||||
}
|
||||
}
|
||||
@@ -3960,29 +4107,29 @@ MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lhs->isNumber() || !rhs->isNumber())
|
||||
if (!lhs->isTypeRepresentableAsDouble() || !rhs->isTypeRepresentableAsDouble())
|
||||
return false;
|
||||
|
||||
switch (jsop_) {
|
||||
case JSOP_LT:
|
||||
*result = (lhs->toNumber() < rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() < rhs->numberToDouble());
|
||||
break;
|
||||
case JSOP_LE:
|
||||
*result = (lhs->toNumber() <= rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() <= rhs->numberToDouble());
|
||||
break;
|
||||
case JSOP_GT:
|
||||
*result = (lhs->toNumber() > rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() > rhs->numberToDouble());
|
||||
break;
|
||||
case JSOP_GE:
|
||||
*result = (lhs->toNumber() >= rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() >= rhs->numberToDouble());
|
||||
break;
|
||||
case JSOP_STRICTEQ: // Fall through.
|
||||
case JSOP_EQ:
|
||||
*result = (lhs->toNumber() == rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() == rhs->numberToDouble());
|
||||
break;
|
||||
case JSOP_STRICTNE: // Fall through.
|
||||
case JSOP_NE:
|
||||
*result = (lhs->toNumber() != rhs->toNumber());
|
||||
*result = (lhs->numberToDouble() != rhs->numberToDouble());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
||||
+97
-51
@@ -605,6 +605,21 @@ class MDefinition : public MNode
|
||||
Truncate = 3
|
||||
};
|
||||
|
||||
static const char * TruncateKindString(TruncateKind kind) {
|
||||
switch(kind) {
|
||||
case NoTruncate:
|
||||
return "NoTruncate";
|
||||
case TruncateAfterBailouts:
|
||||
return "TruncateAfterBailouts";
|
||||
case IndirectTruncate:
|
||||
return "IndirectTruncate";
|
||||
case Truncate:
|
||||
return "Truncate";
|
||||
default:
|
||||
MOZ_CRASH("Unknown truncate kind.");
|
||||
}
|
||||
}
|
||||
|
||||
// |needTruncation| records the truncation kind of the results, such that it
|
||||
// can be used to truncate the operands of this instruction. If
|
||||
// |needTruncation| function returns true, then the |truncate| function is
|
||||
@@ -1352,18 +1367,44 @@ class MLimitedTruncate
|
||||
// A constant js::Value.
|
||||
class MConstant : public MNullaryInstruction
|
||||
{
|
||||
Value value_;
|
||||
struct Payload {
|
||||
union {
|
||||
bool b;
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
float f;
|
||||
double d;
|
||||
JSString* str;
|
||||
JS::Symbol* sym;
|
||||
JSObject* obj;
|
||||
uint64_t asBits;
|
||||
};
|
||||
Payload() : asBits(0) {}
|
||||
};
|
||||
|
||||
Payload payload_;
|
||||
|
||||
static_assert(sizeof(Payload) == sizeof(uint64_t),
|
||||
"asBits must be big enough for all payload bits");
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertInitializedPayload() const;
|
||||
#else
|
||||
void assertInitializedPayload() const {}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
MConstant(const Value& v, CompilerConstraintList* constraints);
|
||||
explicit MConstant(JSObject* obj);
|
||||
explicit MConstant(float f);
|
||||
explicit MConstant(int64_t i);
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(Constant)
|
||||
static MConstant* New(TempAllocator& alloc, const Value& v,
|
||||
CompilerConstraintList* constraints = nullptr);
|
||||
static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
|
||||
CompilerConstraintList* constraints = nullptr);
|
||||
static MConstant* NewFloat32(TempAllocator& alloc, double d);
|
||||
static MConstant* NewInt64(TempAllocator& alloc, int64_t i);
|
||||
static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type);
|
||||
static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
|
||||
|
||||
@@ -1407,57 +1448,62 @@ class MConstant : public MNullaryInstruction
|
||||
|
||||
ALLOW_CLONE(MConstant)
|
||||
|
||||
bool equals(const MConstant* other) {
|
||||
return value_ == other->value_;
|
||||
bool equals(const MConstant* other) const {
|
||||
assertInitializedPayload();
|
||||
return type() == other->type() && payload_.asBits == other->payload_.asBits;
|
||||
}
|
||||
|
||||
bool toBoolean() const {
|
||||
MOZ_ASSERT(type() == MIRType_Boolean);
|
||||
return value_.toBoolean();
|
||||
return payload_.b;
|
||||
}
|
||||
int32_t toInt32() const {
|
||||
MOZ_ASSERT(type() == MIRType_Int32);
|
||||
return value_.toInt32();
|
||||
return payload_.i32;
|
||||
}
|
||||
int64_t toInt64() const {
|
||||
MOZ_ASSERT(type() == MIRType_Int64);
|
||||
return payload_.i64;
|
||||
}
|
||||
bool isInt32(int32_t i) const {
|
||||
return type() == MIRType_Int32 && value_.toInt32() == i;
|
||||
return type() == MIRType_Int32 && payload_.i32 == i;
|
||||
}
|
||||
double toDouble() const {
|
||||
MOZ_ASSERT(type() == MIRType_Double);
|
||||
return value_.toDouble();
|
||||
return payload_.d;
|
||||
}
|
||||
float toFloat32() const {
|
||||
MOZ_ASSERT(type() == MIRType_Float32);
|
||||
return value_.toDouble();
|
||||
return payload_.f;
|
||||
}
|
||||
JSString* toString() const {
|
||||
MOZ_ASSERT(type() == MIRType_String);
|
||||
return value_.toString();
|
||||
return payload_.str;
|
||||
}
|
||||
JS::Symbol* toSymbol() const {
|
||||
MOZ_ASSERT(type() == MIRType_Symbol);
|
||||
return value_.toSymbol();
|
||||
return payload_.sym;
|
||||
}
|
||||
JSObject& toObject() const {
|
||||
MOZ_ASSERT(type() == MIRType_Object);
|
||||
return value_.toObject();
|
||||
return *payload_.obj;
|
||||
}
|
||||
JSObject* toObjectOrNull() const {
|
||||
if (type() == MIRType_Object)
|
||||
return &value_.toObject();
|
||||
return payload_.obj;
|
||||
MOZ_ASSERT(type() == MIRType_Null);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isNumber() const {
|
||||
return IsNumberType(type());
|
||||
bool isTypeRepresentableAsDouble() const {
|
||||
return IsTypeRepresentableAsDouble(type());
|
||||
}
|
||||
double toNumber() const {
|
||||
double numberToDouble() const {
|
||||
MOZ_ASSERT(isTypeRepresentableAsDouble());
|
||||
if (type() == MIRType_Int32)
|
||||
return toInt32();
|
||||
if (type() == MIRType_Double)
|
||||
return toDouble();
|
||||
MOZ_ASSERT(type() == MIRType_Float32);
|
||||
return toFloat32();
|
||||
}
|
||||
|
||||
@@ -3354,7 +3400,9 @@ class MSimdBox
|
||||
const MSimdBox* box = ins->toSimdBox();
|
||||
if (box->simdType() != simdType())
|
||||
return false;
|
||||
MOZ_ASSERT(box->initialHeap() == initialHeap());
|
||||
MOZ_ASSERT(box->templateObject() == templateObject());
|
||||
if (box->initialHeap() != initialHeap())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3362,6 +3410,7 @@ class MSimdBox
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
bool canRecoverOnBailout() const override {
|
||||
return true;
|
||||
@@ -3407,6 +3456,8 @@ class MSimdUnbox
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void printOpcode(GenericPrinter& out) const override;
|
||||
};
|
||||
|
||||
// Creates a new derived type object. At runtime, this is just a call
|
||||
@@ -13657,12 +13708,12 @@ class MAsmJSHeapAccess
|
||||
MemoryBarrierBits barrierAfter_;
|
||||
|
||||
public:
|
||||
MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, unsigned numSimdElems = 0,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
explicit MAsmJSHeapAccess(Scalar::Type accessType, unsigned numSimdElems = 0,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
: offset_(0),
|
||||
accessType_(accessType),
|
||||
needsBoundsCheck_(needsBoundsCheck),
|
||||
needsBoundsCheck_(true),
|
||||
numSimdElems_(numSimdElems),
|
||||
barrierBefore_(barrierBefore),
|
||||
barrierAfter_(barrierAfter)
|
||||
@@ -13695,10 +13746,10 @@ class MAsmJSLoadHeap
|
||||
public MAsmJSHeapAccess,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, bool needsBoundsCheck,
|
||||
unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
MAsmJSLoadHeap(Scalar::Type accessType, MDefinition* ptr, unsigned numSimdElems,
|
||||
MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MUnaryInstruction(ptr),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after)
|
||||
MAsmJSHeapAccess(accessType, numSimdElems, before, after)
|
||||
{
|
||||
if (before|after)
|
||||
setGuard(); // Not removable
|
||||
@@ -13736,13 +13787,12 @@ class MAsmJSLoadHeap
|
||||
INSTRUCTION_HEADER(AsmJSLoadHeap)
|
||||
|
||||
static MAsmJSLoadHeap* New(TempAllocator& alloc, Scalar::Type accessType,
|
||||
MDefinition* ptr, bool needsBoundsCheck,
|
||||
unsigned numSimdElems = 0,
|
||||
MDefinition* ptr, unsigned numSimdElems = 0,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
{
|
||||
return new(alloc) MAsmJSLoadHeap(accessType, ptr, needsBoundsCheck,
|
||||
numSimdElems, barrierBefore, barrierAfter);
|
||||
return new(alloc) MAsmJSLoadHeap(accessType, ptr, numSimdElems,
|
||||
barrierBefore, barrierAfter);
|
||||
}
|
||||
|
||||
MDefinition* ptr() const { return getOperand(0); }
|
||||
@@ -13764,10 +13814,10 @@ class MAsmJSStoreHeap
|
||||
public MAsmJSHeapAccess,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v, bool needsBoundsCheck,
|
||||
MAsmJSStoreHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* v,
|
||||
unsigned numSimdElems, MemoryBarrierBits before, MemoryBarrierBits after)
|
||||
: MBinaryInstruction(ptr, v),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck, numSimdElems, before, after)
|
||||
MAsmJSHeapAccess(accessType, numSimdElems, before, after)
|
||||
{
|
||||
if (before|after)
|
||||
setGuard(); // Not removable
|
||||
@@ -13777,13 +13827,12 @@ class MAsmJSStoreHeap
|
||||
INSTRUCTION_HEADER(AsmJSStoreHeap)
|
||||
|
||||
static MAsmJSStoreHeap* New(TempAllocator& alloc, Scalar::Type accessType,
|
||||
MDefinition* ptr, MDefinition* v, bool needsBoundsCheck,
|
||||
unsigned numSimdElems = 0,
|
||||
MDefinition* ptr, MDefinition* v, unsigned numSimdElems = 0,
|
||||
MemoryBarrierBits barrierBefore = MembarNobits,
|
||||
MemoryBarrierBits barrierAfter = MembarNobits)
|
||||
{
|
||||
return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, needsBoundsCheck,
|
||||
numSimdElems, barrierBefore, barrierAfter);
|
||||
return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, numSimdElems,
|
||||
barrierBefore, barrierAfter);
|
||||
}
|
||||
|
||||
MDefinition* ptr() const { return getOperand(0); }
|
||||
@@ -13801,9 +13850,9 @@ class MAsmJSCompareExchangeHeap
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MAsmJSCompareExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* oldv,
|
||||
MDefinition* newv, bool needsBoundsCheck)
|
||||
MDefinition* newv)
|
||||
: MTernaryInstruction(ptr, oldv, newv),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck)
|
||||
MAsmJSHeapAccess(accessType)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
setResultType(MIRType_Int32);
|
||||
@@ -13814,9 +13863,9 @@ class MAsmJSCompareExchangeHeap
|
||||
|
||||
static MAsmJSCompareExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType,
|
||||
MDefinition* ptr, MDefinition* oldv,
|
||||
MDefinition* newv, bool needsBoundsCheck)
|
||||
MDefinition* newv)
|
||||
{
|
||||
return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv, needsBoundsCheck);
|
||||
return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv);
|
||||
}
|
||||
|
||||
MDefinition* ptr() const { return getOperand(0); }
|
||||
@@ -13833,10 +13882,9 @@ class MAsmJSAtomicExchangeHeap
|
||||
public MAsmJSHeapAccess,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
MAsmJSAtomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value,
|
||||
bool needsBoundsCheck)
|
||||
MAsmJSAtomicExchangeHeap(Scalar::Type accessType, MDefinition* ptr, MDefinition* value)
|
||||
: MBinaryInstruction(ptr, value),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck)
|
||||
MAsmJSHeapAccess(accessType)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
setResultType(MIRType_Int32);
|
||||
@@ -13846,10 +13894,9 @@ class MAsmJSAtomicExchangeHeap
|
||||
INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
|
||||
|
||||
static MAsmJSAtomicExchangeHeap* New(TempAllocator& alloc, Scalar::Type accessType,
|
||||
MDefinition* ptr, MDefinition* value,
|
||||
bool needsBoundsCheck)
|
||||
MDefinition* ptr, MDefinition* value)
|
||||
{
|
||||
return new(alloc) MAsmJSAtomicExchangeHeap(accessType, ptr, value, needsBoundsCheck);
|
||||
return new(alloc) MAsmJSAtomicExchangeHeap(accessType, ptr, value);
|
||||
}
|
||||
|
||||
MDefinition* ptr() const { return getOperand(0); }
|
||||
@@ -13867,10 +13914,9 @@ class MAsmJSAtomicBinopHeap
|
||||
{
|
||||
AtomicOp op_;
|
||||
|
||||
MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition* ptr, MDefinition* v,
|
||||
bool needsBoundsCheck)
|
||||
MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition* ptr, MDefinition* v)
|
||||
: MBinaryInstruction(ptr, v),
|
||||
MAsmJSHeapAccess(accessType, needsBoundsCheck),
|
||||
MAsmJSHeapAccess(accessType),
|
||||
op_(op)
|
||||
{
|
||||
setGuard(); // Not removable
|
||||
@@ -13881,9 +13927,9 @@ class MAsmJSAtomicBinopHeap
|
||||
INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
|
||||
|
||||
static MAsmJSAtomicBinopHeap* New(TempAllocator& alloc, AtomicOp op, Scalar::Type accessType,
|
||||
MDefinition* ptr, MDefinition* v, bool needsBoundsCheck)
|
||||
MDefinition* ptr, MDefinition* v)
|
||||
{
|
||||
return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v, needsBoundsCheck);
|
||||
return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v);
|
||||
}
|
||||
|
||||
AtomicOp operation() const { return op_; }
|
||||
|
||||
@@ -37,8 +37,16 @@ class MIRGenerator
|
||||
public:
|
||||
MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
|
||||
TempAllocator* alloc, MIRGraph* graph,
|
||||
const CompileInfo* info, const OptimizationInfo* optimizationInfo,
|
||||
bool usesSignalHandlersForAsmJSOOB = false);
|
||||
const CompileInfo* info, const OptimizationInfo* optimizationInfo);
|
||||
|
||||
void initUsesSignalHandlersForAsmJSOOB(bool init) {
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
usesSignalHandlersForAsmJSOOB_ = init;
|
||||
#endif
|
||||
}
|
||||
void initMinAsmJSHeapLength(uint32_t init) {
|
||||
minAsmJSHeapLength_ = init;
|
||||
}
|
||||
|
||||
TempAllocator& alloc() {
|
||||
return *alloc_;
|
||||
@@ -141,6 +149,9 @@ class MIRGenerator
|
||||
MOZ_ASSERT(compilingAsmJS());
|
||||
maxAsmJSStackArgBytes_ = n;
|
||||
}
|
||||
uint32_t minAsmJSHeapLength() const {
|
||||
return minAsmJSHeapLength_;
|
||||
}
|
||||
void setPerformsCall() {
|
||||
performsCall_ = true;
|
||||
}
|
||||
@@ -199,6 +210,7 @@ class MIRGenerator
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
bool usesSignalHandlersForAsmJSOOB_;
|
||||
#endif
|
||||
uint32_t minAsmJSHeapLength_;
|
||||
|
||||
void setForceAbort() {
|
||||
shouldForceAbort_ = true;
|
||||
|
||||
@@ -19,8 +19,7 @@ using mozilla::Swap;
|
||||
|
||||
MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
|
||||
TempAllocator* alloc, MIRGraph* graph, const CompileInfo* info,
|
||||
const OptimizationInfo* optimizationInfo,
|
||||
bool usesSignalHandlersForAsmJSOOB)
|
||||
const OptimizationInfo* optimizationInfo)
|
||||
: compartment(compartment),
|
||||
info_(info),
|
||||
optimizationInfo_(optimizationInfo),
|
||||
@@ -41,8 +40,9 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti
|
||||
instrumentedProfilingIsCached_(false),
|
||||
safeForMinorGC_(true),
|
||||
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
|
||||
usesSignalHandlersForAsmJSOOB_(false),
|
||||
#endif
|
||||
minAsmJSHeapLength_(0),
|
||||
options(options),
|
||||
gs_(alloc)
|
||||
{ }
|
||||
|
||||
@@ -124,6 +124,20 @@ SpewRange(MDefinition* def)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
SpewTruncate(MDefinition* def, MDefinition::TruncateKind kind, bool shouldClone)
|
||||
{
|
||||
#ifdef JS_JITSPEW
|
||||
if (JitSpewEnabled(JitSpew_Range)) {
|
||||
JitSpewHeader(JitSpew_Range);
|
||||
Fprinter& out = JitSpewPrinter();
|
||||
out.printf("truncating ");
|
||||
def->printName(out);
|
||||
out.printf(" (kind: %s, clone: %d)\n", MDefinition::TruncateKindString(kind), shouldClone);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TempAllocator&
|
||||
RangeAnalysis::alloc() const
|
||||
{
|
||||
@@ -182,12 +196,12 @@ RangeAnalysis::addBetaNodes()
|
||||
|
||||
MConstant* leftConst = left->maybeConstantValue();
|
||||
MConstant* rightConst = right->maybeConstantValue();
|
||||
if (leftConst && leftConst->isNumber()) {
|
||||
bound = leftConst->toNumber();
|
||||
if (leftConst && leftConst->isTypeRepresentableAsDouble()) {
|
||||
bound = leftConst->numberToDouble();
|
||||
val = right;
|
||||
jsop = ReverseCompareOp(jsop);
|
||||
} else if (rightConst && rightConst->isNumber()) {
|
||||
bound = rightConst->toNumber();
|
||||
} else if (rightConst && rightConst->isTypeRepresentableAsDouble()) {
|
||||
bound = rightConst->numberToDouble();
|
||||
val = left;
|
||||
} else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) {
|
||||
MDefinition* smaller = nullptr;
|
||||
@@ -1275,8 +1289,8 @@ MBeta::computeRange(TempAllocator& alloc)
|
||||
void
|
||||
MConstant::computeRange(TempAllocator& alloc)
|
||||
{
|
||||
if (isNumber()) {
|
||||
double d = toNumber();
|
||||
if (isTypeRepresentableAsDouble()) {
|
||||
double d = numberToDouble();
|
||||
setRange(Range::NewDoubleSingletonRange(alloc, d));
|
||||
} else if (type() == MIRType_Boolean) {
|
||||
bool b = toBoolean();
|
||||
@@ -2383,7 +2397,7 @@ MDefinition::truncate()
|
||||
bool
|
||||
MConstant::needTruncation(TruncateKind kind)
|
||||
{
|
||||
return value_.isDouble();
|
||||
return IsFloatingPointType(type());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2392,8 +2406,9 @@ MConstant::truncate()
|
||||
MOZ_ASSERT(needTruncation(Truncate));
|
||||
|
||||
// Truncate the double to int, since all uses truncates it.
|
||||
int32_t res = ToInt32(value_.toDouble());
|
||||
value_.setInt32(res);
|
||||
int32_t res = ToInt32(numberToDouble());
|
||||
payload_.asBits = 0;
|
||||
payload_.i32 = res;
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->setInt32(res, res);
|
||||
@@ -3033,6 +3048,8 @@ RangeAnalysis::truncate()
|
||||
if (!iter->needTruncation(kind))
|
||||
continue;
|
||||
|
||||
SpewTruncate(*iter, kind, shouldClone);
|
||||
|
||||
// If needed, clone the current instruction for keeping it for the
|
||||
// bailout path. This give us the ability to truncate instructions
|
||||
// even after the removal of branches.
|
||||
@@ -3056,6 +3073,9 @@ RangeAnalysis::truncate()
|
||||
// Truncate this phi if possible.
|
||||
if (shouldClone || !iter->needTruncation(kind))
|
||||
continue;
|
||||
|
||||
SpewTruncate(*iter, kind, shouldClone);
|
||||
|
||||
iter->truncate();
|
||||
|
||||
// Delay updates of inputs/outputs to avoid creating node which
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// How far forward/back can a jump go? Provide a generous buffer for thunks.
|
||||
static const uint32_t JumpImmediateRange = 100 * 1024;
|
||||
|
||||
class Registers
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -221,6 +221,12 @@ BOffImm16::BOffImm16(InstImm inst)
|
||||
{
|
||||
}
|
||||
|
||||
Instruction*
|
||||
BOffImm16::getDest(Instruction* src) const
|
||||
{
|
||||
return &src[(((int32_t)data << 16) >> 16) + 1];
|
||||
}
|
||||
|
||||
bool
|
||||
AssemblerMIPSShared::oom() const
|
||||
{
|
||||
@@ -1329,7 +1335,21 @@ AssemblerMIPSShared::bind(Label* label, BufferOffset boff)
|
||||
void
|
||||
AssemblerMIPSShared::bindLater(Label* label, wasm::JumpTarget target)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
if (label->used()) {
|
||||
int32_t next;
|
||||
|
||||
BufferOffset b(label);
|
||||
do {
|
||||
Instruction* inst = editSrc(b);
|
||||
|
||||
append(target, b.getOffset());
|
||||
next = inst[1].encode();
|
||||
inst[1].makeNop();
|
||||
|
||||
b = BufferOffset(next);
|
||||
} while (next != LabelBase::INVALID_OFFSET);
|
||||
}
|
||||
label->reset();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -486,7 +486,7 @@ class BOffImm16
|
||||
bool isInvalid() {
|
||||
return data == INVALID;
|
||||
}
|
||||
Instruction* getDest(Instruction* src);
|
||||
Instruction* getDest(Instruction* src) const;
|
||||
|
||||
BOffImm16(InstImm inst);
|
||||
};
|
||||
|
||||
@@ -1662,7 +1662,7 @@ CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
||||
ToFloatRegister(out));
|
||||
} else {
|
||||
if (mir->isAtomicAccess())
|
||||
masm.ma_b(masm.asmOnOutOfBoundsLabel());
|
||||
masm.ma_b(wasm::JumpTarget::OutOfBounds);
|
||||
else
|
||||
masm.move32(Imm32(0), ToRegister(out));
|
||||
}
|
||||
@@ -1750,7 +1750,7 @@ CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
|
||||
masm.bind(&outOfRange);
|
||||
// Offset is out of range.
|
||||
if (mir->isAtomicAccess())
|
||||
masm.ma_b(masm.asmOnOutOfBoundsLabel());
|
||||
masm.ma_b(wasm::JumpTarget::OutOfBounds);
|
||||
masm.bind(&done);
|
||||
|
||||
memoryBarrier(mir->barrierAfter());
|
||||
@@ -1777,7 +1777,7 @@ CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap
|
||||
if (mir->needsBoundsCheck()) {
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
|
||||
}
|
||||
masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
srcAddr, oldval, newval, InvalidReg,
|
||||
@@ -1805,7 +1805,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap*
|
||||
if (mir->needsBoundsCheck()) {
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
|
||||
}
|
||||
masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
srcAddr, value, InvalidReg, valueTemp,
|
||||
@@ -1836,7 +1836,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
|
||||
if (mir->needsBoundsCheck()) {
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
|
||||
}
|
||||
if (value->isConstant())
|
||||
atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
|
||||
@@ -1874,7 +1874,7 @@ CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHea
|
||||
if (mir->needsBoundsCheck()) {
|
||||
BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
|
||||
maybeCmpOffset = bo.getOffset();
|
||||
masm.ma_b(ptrReg, ScratchRegister, masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
|
||||
masm.ma_b(ptrReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
|
||||
}
|
||||
|
||||
if (value->isConstant())
|
||||
|
||||
@@ -441,12 +441,37 @@ MacroAssemblerMIPSShared::ma_b(Register lhs, ImmPtr imm, Label* l, Condition c,
|
||||
asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Register lhs, T rhs, wasm::JumpTarget target, Condition c,
|
||||
JumpKind jumpKind)
|
||||
{
|
||||
Label label;
|
||||
ma_b(lhs, rhs, &label, c, jumpKind);
|
||||
bindLater(&label, target);
|
||||
}
|
||||
|
||||
template void MacroAssemblerMIPSShared::ma_b<Register>(Register lhs, Register rhs,
|
||||
wasm::JumpTarget target, Condition c,
|
||||
JumpKind jumpKind);
|
||||
template void MacroAssemblerMIPSShared::ma_b<ImmTag>(Register lhs, ImmTag rhs,
|
||||
wasm::JumpTarget target, Condition c,
|
||||
JumpKind jumpKind);
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Label* label, JumpKind jumpKind)
|
||||
{
|
||||
asMasm().branchWithCode(getBranchCode(BranchIsJump), label, jumpKind);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(wasm::JumpTarget target, JumpKind jumpKind)
|
||||
{
|
||||
Label label;
|
||||
asMasm().branchWithCode(getBranchCode(BranchIsJump), &label, jumpKind);
|
||||
bindLater(&label, target);
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerMIPSShared::ma_cmp(Register scratch, Register lhs, Register rhs, Condition c)
|
||||
{
|
||||
@@ -1143,35 +1168,41 @@ MacroAssembler::call(Label* label)
|
||||
CodeOffset
|
||||
MacroAssembler::callWithPatch()
|
||||
{
|
||||
addLongJump(nextOffset());
|
||||
ma_liPatchable(ScratchRegister, ImmWord(0));
|
||||
return call(ScratchRegister);
|
||||
as_bal(BOffImm16(0));
|
||||
as_nop();
|
||||
return CodeOffset(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
BufferOffset call(callerOffset - 2 * sizeof(uint32_t));
|
||||
InstImm* bal = (InstImm*)editSrc(call);
|
||||
bal->setBOffImm16(BufferOffset(calleeOffset).diffB<BOffImm16>(call));
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::thunkWithPatch()
|
||||
{
|
||||
addLongJump(nextOffset());
|
||||
ma_liPatchable(ScratchRegister, ImmWord(0));
|
||||
as_jr(ScratchRegister);
|
||||
as_nop();
|
||||
return CodeOffset(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
BufferOffset li(callerOffset - Assembler::PatchWrite_NearCallSize());
|
||||
Assembler::PatchInstructionImmediate((uint8_t*)editSrc(li),
|
||||
PatchedImmPtr((const void*)calleeOffset));
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::thunkWithPatch()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::repatchThunk(uint8_t* code, uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
Assembler::PatchInstructionImmediate(code, PatchedImmPtr((const void*)calleeOffset));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -140,8 +140,12 @@ class MacroAssemblerMIPSShared : public Assembler
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_b(lhs, ScratchRegister, l, c, jumpKind);
|
||||
}
|
||||
template <typename T>
|
||||
void ma_b(Register lhs, T rhs, wasm::JumpTarget target, Condition c,
|
||||
JumpKind jumpKind = LongJump);
|
||||
|
||||
void ma_b(Label* l, JumpKind jumpKind = LongJump);
|
||||
void ma_b(wasm::JumpTarget target, JumpKind jumpKind = LongJump);
|
||||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
|
||||
@@ -1392,6 +1392,14 @@ MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand& va
|
||||
branchTestMagic(cond, value.typeReg(), label);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand& value,
|
||||
wasm::JumpTarget target)
|
||||
{
|
||||
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
||||
ma_b(value.typeReg(), ImmTag(JSVAL_TAG_MAGIC), target, cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, Register tag, Label* label)
|
||||
{
|
||||
|
||||
@@ -296,6 +296,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
branch(code);
|
||||
}
|
||||
|
||||
void jump(wasm::JumpTarget target) {
|
||||
ma_b(target);
|
||||
}
|
||||
|
||||
void negl(Register reg) {
|
||||
ma_negu(reg, reg);
|
||||
}
|
||||
@@ -417,6 +421,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void branchTestNumber(Condition cond, Register tag, Label* label);
|
||||
|
||||
void branchTestMagic(Condition cond, const ValueOperand& value, Label* label);
|
||||
void branchTestMagic(Condition cond, const ValueOperand& value, wasm::JumpTarget target);
|
||||
void branchTestMagic(Condition cond, Register tag, Label* label);
|
||||
void branchTestMagic(Condition cond, const Address& address, Label* label);
|
||||
void branchTestMagic(Condition cond, const BaseIndex& src, Label* label);
|
||||
|
||||
@@ -1531,6 +1531,15 @@ MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, const ValueOperand&
|
||||
branchTestMagic(cond, SecondScratchReg, label);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, const ValueOperand& value,
|
||||
wasm::JumpTarget target)
|
||||
{
|
||||
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
||||
splitTag(value, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), target, cond);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::branchTestMagic(Condition cond, Register tag, Label* label)
|
||||
{
|
||||
|
||||
@@ -319,6 +319,10 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
|
||||
branch(code);
|
||||
}
|
||||
|
||||
void jump(wasm::JumpTarget target) {
|
||||
ma_b(target);
|
||||
}
|
||||
|
||||
void splitTag(Register src, Register dest) {
|
||||
ma_dsrl(dest, src, Imm32(JSVAL_TAG_SHIFT));
|
||||
}
|
||||
@@ -458,6 +462,7 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
|
||||
void branchTestNumber(Condition cond, Register tag, Label* label);
|
||||
|
||||
void branchTestMagic(Condition cond, const ValueOperand& value, Label* label);
|
||||
void branchTestMagic(Condition cond, const ValueOperand& value, wasm::JumpTarget target);
|
||||
void branchTestMagic(Condition cond, Register tag, Label* label);
|
||||
void branchTestMagic(Condition cond, const Address& address, Label* label);
|
||||
void branchTestMagic(Condition cond, const BaseIndex& src, Label* label);
|
||||
|
||||
@@ -28,7 +28,7 @@ ToInt32(const LAllocation* a)
|
||||
static inline double
|
||||
ToDouble(const LAllocation* a)
|
||||
{
|
||||
return a->toConstant()->toNumber();
|
||||
return a->toConstant()->numberToDouble();
|
||||
}
|
||||
|
||||
static inline Register
|
||||
|
||||
@@ -136,7 +136,7 @@ BEGIN_TEST(testJitDCEinGVN_phi)
|
||||
|
||||
// c1 should be deleted.
|
||||
for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
|
||||
CHECK(!ins->isConstant() || (ins->toConstant()->toNumber() != 1.0));
|
||||
CHECK(!ins->isConstant() || (ins->toConstant()->numberToDouble() != 1.0));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ BEGIN_TEST(testJitFoldsTo_DivReciprocal)
|
||||
CHECK(op->isMul());
|
||||
CHECK(op->getOperand(0) == left);
|
||||
CHECK(op->getOperand(1)->isConstant());
|
||||
CHECK(op->getOperand(1)->toConstant()->toNumber() == 0.25);
|
||||
CHECK(op->getOperand(1)->toConstant()->numberToDouble() == 0.25);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitFoldsTo_DivReciprocal)
|
||||
@@ -229,7 +229,7 @@ BEGIN_TEST(testJitFoldsTo_UnsignedDiv)
|
||||
|
||||
// Test that the div got folded to 0.
|
||||
MConstant* op = ret->getOperand(0)->toConstant();
|
||||
CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 0.0));
|
||||
CHECK(mozilla::NumbersAreIdentical(op->numberToDouble(), 0.0));
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitFoldsTo_UnsignedDiv)
|
||||
@@ -254,7 +254,7 @@ BEGIN_TEST(testJitFoldsTo_UnsignedMod)
|
||||
|
||||
// Test that the mod got folded to 1.
|
||||
MConstant* op = ret->getOperand(0)->toConstant();
|
||||
CHECK(mozilla::NumbersAreIdentical(op->toNumber(), 1.0));
|
||||
CHECK(mozilla::NumbersAreIdentical(op->numberToDouble(), 1.0));
|
||||
return true;
|
||||
}
|
||||
END_TEST(testJitFoldsTo_UnsignedMod)
|
||||
|
||||
Reference in New Issue
Block a user