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:
2023-09-21 17:24:18 +08:00
parent ac47e909a0
commit e5aa6ca2dc
41 changed files with 1954 additions and 725 deletions
+28 -88
View File
@@ -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
View File
@@ -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);
}
-4
View File
@@ -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
View File
@@ -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>();
+15 -42
View File
@@ -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
+9 -17
View File
@@ -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))
+6 -1
View File
@@ -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);
+47 -59
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+12
View File
@@ -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)
{
+2
View File
@@ -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);
+95 -3
View File
@@ -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;
+13
View File
@@ -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)))))');
+13
View File
@@ -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);
}
+6 -3
View File
@@ -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
View File
@@ -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/);
+13
View File
@@ -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.
+8
View File
@@ -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
+7 -2
View File
@@ -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
View File
@@ -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
+7 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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_; }
+14 -2
View File
@@ -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;
+3 -3
View File
@@ -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)
{ }
+29 -9
View File
@@ -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);
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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)