Compare commits
100
Commits
master
...
RC_20230928
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6fae24296 | ||
|
|
13c6b7422d | ||
|
|
24f2e663cd | ||
|
|
f96b155988 | ||
|
|
d5cc24e916 | ||
|
|
8eba271c5f | ||
|
|
19955c82b3 | ||
|
|
80ae8aec7c | ||
|
|
d8441d7c81 | ||
|
|
c2446f2478 | ||
|
|
6efb7a1904 | ||
|
|
fa0b03a10f | ||
|
|
3ae2972950 | ||
|
|
2d48de9998 | ||
|
|
3b029cdfe4 | ||
|
|
ea9077a8a5 | ||
|
|
b4af6b6265 | ||
|
|
ae1d53ceb1 | ||
|
|
c3f4709d5e | ||
|
|
a7bb3fc175 | ||
|
|
6a0203f25f | ||
|
|
b0dfcb147f | ||
|
|
06fd4c9357 | ||
|
|
b9ea7fd9ee | ||
|
|
58308a5c7a | ||
|
|
88c7d54975 | ||
|
|
eadb5678b3 | ||
|
|
54e3e60796 | ||
|
|
2a05494abe | ||
|
|
bc89e58def | ||
|
|
3a009b97f6 | ||
|
|
a105f6d5e9 | ||
|
|
5db241c0c0 | ||
|
|
d38dca10e5 | ||
|
|
54fc723126 | ||
|
|
9b8ca17ee4 | ||
|
|
cddc95b39b | ||
|
|
5676529793 | ||
|
|
ce68156cf4 | ||
|
|
5032ad9593 | ||
|
|
b55f765f2e | ||
|
|
856078d435 | ||
|
|
c74c262d2f | ||
|
|
787e29e374 | ||
|
|
e439896c9b | ||
|
|
15b508f1fb | ||
|
|
5651ebfc76 | ||
|
|
593bfc1ce8 | ||
|
|
28953cda7a | ||
|
|
516f3f545d | ||
|
|
83aff65a42 | ||
|
|
5bd97df957 | ||
|
|
8772711181 | ||
|
|
dc9526f4c6 | ||
|
|
9ab36e7ae0 | ||
|
|
e8d30841e7 | ||
|
|
9faf29374e | ||
|
|
75e4818d36 | ||
|
|
a65144e9e0 | ||
|
|
4f29548526 | ||
|
|
528db2cbd8 | ||
|
|
84e6bcc1e6 | ||
|
|
0dc9ff518d | ||
|
|
a2e3bd44aa | ||
|
|
163a0e7caf | ||
|
|
49a86db5f3 | ||
|
|
2640953d8b | ||
|
|
7eb7d67f7a | ||
|
|
e28860ba7f | ||
|
|
49a96b1d91 | ||
|
|
3130812693 | ||
|
|
71ae69aea8 | ||
|
|
9e34d697c7 | ||
|
|
8fa9125d02 | ||
|
|
4b4488c3e4 | ||
|
|
248db7d301 | ||
|
|
e199f2949b | ||
|
|
456b5450a4 | ||
|
|
d8e629c4a3 | ||
|
|
4898c4f297 | ||
|
|
7cd578bbc6 | ||
|
|
1e91df628a | ||
|
|
b110549cbd | ||
|
|
971047835a | ||
|
|
dd4d9eb032 | ||
|
|
8b94f1e480 | ||
|
|
c89610a5c7 | ||
|
|
217963cbed | ||
|
|
c4f4e18340 | ||
|
|
8a7c0e7345 | ||
|
|
52e304d450 | ||
|
|
cfc0eeca53 | ||
|
|
ebd3dd9228 | ||
|
|
cb40e4e7ef | ||
|
|
662dba55fd | ||
|
|
8760ce023c | ||
|
|
b86e148bb7 | ||
|
|
9e83721e5e | ||
|
|
dc2d6c2e60 | ||
|
|
4ada6f6762 |
No files matched your search
@@ -676,7 +676,8 @@ public:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(mPerformance);
|
||||
mPerformance->NotifyObservers();
|
||||
RefPtr<Performance> performance(mPerformance);
|
||||
performance->NotifyObservers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -700,7 +701,12 @@ Performance::RunNotificationObserversTask()
|
||||
{
|
||||
mPendingNotificationObserversTask = true;
|
||||
nsCOMPtr<nsIRunnable> task = new NotifyObserversTask(this);
|
||||
nsresult rv = NS_DispatchToCurrentThread(task);
|
||||
nsresult rv;
|
||||
if (NS_IsMainThread()) {
|
||||
rv = NS_DispatchToCurrentThread(task);
|
||||
} else {
|
||||
rv = NS_DispatchToMainThread(task);
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mPendingNotificationObserversTask = false;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ namespace JS {
|
||||
_(BinaryArith_SharedCache) \
|
||||
_(BinaryArith_Call) \
|
||||
\
|
||||
_(UnaryArith_SpecializedTypes) \
|
||||
_(UnaryArith_SpecializedOnBaselineTypes) \
|
||||
_(UnaryArith_InlineCache) \
|
||||
\
|
||||
_(InlineCache_OptimizedStub) \
|
||||
\
|
||||
_(Call_Inline)
|
||||
@@ -112,6 +116,7 @@ namespace JS {
|
||||
_(GetElemStringNotCached) \
|
||||
_(NonNativeReceiver) \
|
||||
_(IndexType) \
|
||||
_(SpeculationOnInputTypesFailed) \
|
||||
_(SetElemNonDenseNonTANotCached) \
|
||||
_(NoSimdJitSupport) \
|
||||
_(SimdTypeNotOptimized) \
|
||||
|
||||
@@ -537,8 +537,12 @@ JSObject*
|
||||
PromiseObject::resolutionSite()
|
||||
{
|
||||
auto debugInfo = PromiseDebugInfo::FromPromise(this);
|
||||
if (debugInfo)
|
||||
return debugInfo->resolutionSite();
|
||||
if (debugInfo) {
|
||||
JSObject* site = debugInfo->resolutionSite();
|
||||
if (site && !JS_IsDeadWrapper(site)) {
|
||||
return site;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -2057,7 +2057,7 @@ BytecodeEmitter::emitCallIncDec(UnaryNode* incDec)
|
||||
MOZ_ASSERT(call->isKind(PNK_CALL));
|
||||
if (!emitTree(call)) // CALLRESULT
|
||||
return false;
|
||||
if (!emit1(JSOP_POS)) // N
|
||||
if (!emit1(JSOP_TONUMERIC)) // N
|
||||
return false;
|
||||
|
||||
// The increment/decrement has no side effects, so proceed to throw for
|
||||
|
||||
@@ -233,8 +233,8 @@ ElemOpEmitter::emitIncDec()
|
||||
|
||||
MOZ_ASSERT(state_ == State::Get);
|
||||
|
||||
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
|
||||
if (!bce_->emit1(JSOP_POS)) { // ... N
|
||||
JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
|
||||
if (!bce_->emit1(JSOP_TONUMERIC)) { // ... N
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec()) {
|
||||
@@ -242,10 +242,7 @@ ElemOpEmitter::emitIncDec()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(binOp)) { // ... N? N+1
|
||||
if (!bce_->emit1(incOp)) { // ... N? N+1
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec()) {
|
||||
|
||||
@@ -339,11 +339,11 @@ NameOpEmitter::emitIncDec()
|
||||
{
|
||||
MOZ_ASSERT(state_ == State::Start);
|
||||
|
||||
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
|
||||
JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
|
||||
if (!prepareForRhs()) { // ENV? V
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(JSOP_POS)) { // ENV? N
|
||||
if (!bce_->emit1(JSOP_TONUMERIC)) { // ENV? N
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec()) {
|
||||
@@ -351,10 +351,7 @@ NameOpEmitter::emitIncDec()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!bce_->emit1(JSOP_ONE)) { // ENV? N? N 1
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(binOp)) { // ENV? N? N+1
|
||||
if (!bce_->emit1(incOp)) { // ENV? N? N+1
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec() && emittedBindOp()) {
|
||||
|
||||
@@ -217,9 +217,9 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
|
||||
|
||||
MOZ_ASSERT(state_ == State::Get);
|
||||
|
||||
JSOp binOp = isInc() ? JSOP_ADD : JSOP_SUB;
|
||||
JSOp incOp = isInc() ? JSOP_INC : JSOP_DEC;
|
||||
|
||||
if (!bce_->emit1(JSOP_POS)) { // ... N
|
||||
if (!bce_->emit1(JSOP_TONUMERIC)) { // ... N
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec()) {
|
||||
@@ -227,10 +227,7 @@ PropOpEmitter::emitIncDec(JSAtom* prop)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!bce_->emit1(JSOP_ONE)) { // ... N? N 1
|
||||
return false;
|
||||
}
|
||||
if (!bce_->emit1(binOp)) { // ... N? N+1
|
||||
if (!bce_->emit1(incOp)) { // ... N? N+1
|
||||
return false;
|
||||
}
|
||||
if (isPostIncDec()) {
|
||||
|
||||
@@ -84,6 +84,8 @@ const I64TruncUF32Code = 0xaf;
|
||||
const I64TruncSF64Code = 0xb0;
|
||||
const I64TruncUF64Code = 0xb1;
|
||||
|
||||
const FirstInvalidOpcode = 0xc5;
|
||||
|
||||
// DefinitionKind
|
||||
const FunctionCode = 0x00;
|
||||
const TableCode = 0x01;
|
||||
|
||||
@@ -1322,14 +1322,37 @@ BaselineCompiler::emit_JSOP_POS()
|
||||
// Keep top stack value in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
// Inline path for int32 and double.
|
||||
// Inline path for int32 and double; otherwise call VM.
|
||||
Label done;
|
||||
masm.branchTestNumber(Assembler::Equal, R0, &done);
|
||||
|
||||
// Call IC.
|
||||
ICToNumber_Fallback::Compiler stubCompiler(cx);
|
||||
if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
|
||||
prepareVMCall();
|
||||
pushArg(R0);
|
||||
if (!callVM(ToNumberInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
masm.bind(&done);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_TONUMERIC()
|
||||
{
|
||||
// Keep top stack value in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
// Inline path for int32 and double; otherwise call VM.
|
||||
Label done;
|
||||
masm.branchTestNumber(Assembler::Equal, R0, &done);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0);
|
||||
if (!callVM(ToNumericInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
masm.bind(&done);
|
||||
frame.push(R0);
|
||||
@@ -1939,6 +1962,18 @@ BaselineCompiler::emit_JSOP_NEG()
|
||||
return emitUnaryArith();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INC()
|
||||
{
|
||||
return emitUnaryArith();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_DEC()
|
||||
{
|
||||
return emitUnaryArith();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LT()
|
||||
{
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace jit {
|
||||
_(JSOP_OR) \
|
||||
_(JSOP_NOT) \
|
||||
_(JSOP_POS) \
|
||||
_(JSOP_TONUMERIC) \
|
||||
_(JSOP_LOOPHEAD) \
|
||||
_(JSOP_LOOPENTRY) \
|
||||
_(JSOP_VOID) \
|
||||
@@ -242,8 +243,10 @@ namespace jit {
|
||||
_(JSOP_JUMPTARGET) \
|
||||
_(JSOP_IS_CONSTRUCTING) \
|
||||
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
|
||||
_(JSOP_IMPORTMETA) \
|
||||
_(JSOP_DYNAMIC_IMPORT)
|
||||
_(JSOP_IMPORTMETA) \
|
||||
_(JSOP_DYNAMIC_IMPORT) \
|
||||
_(JSOP_INC) \
|
||||
_(JSOP_DEC)
|
||||
|
||||
class BaselineCompiler : public BaselineCompilerSpecific
|
||||
{
|
||||
@@ -327,7 +330,7 @@ class BaselineCompiler : public BaselineCompilerSpecific
|
||||
OPCODE_LIST(EMIT_OP)
|
||||
#undef EMIT_OP
|
||||
|
||||
// JSOP_NEG, JSOP_BITNOT
|
||||
// JSOP_NEG, JSOP_BITNOT, JSOP_INC, JSOP_DEC
|
||||
MOZ_MUST_USE bool emitUnaryArith();
|
||||
|
||||
// JSOP_BITXOR, JSOP_LSH, JSOP_ADD etc.
|
||||
|
||||
@@ -694,42 +694,6 @@ ICToBool_Object::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ToNumber_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoToNumberFallback(JSContext* cx, ICToNumber_Fallback* stub, HandleValue arg, MutableHandleValue ret)
|
||||
{
|
||||
FallbackICSpew(cx, stub, "ToNumber");
|
||||
ret.set(arg);
|
||||
return ToNumber(cx, ret);
|
||||
}
|
||||
|
||||
typedef bool (*DoToNumberFallbackFn)(JSContext*, ICToNumber_Fallback*, HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoToNumberFallbackInfo =
|
||||
FunctionInfo<DoToNumberFallbackFn>(DoToNumberFallback, "DoToNumberFallback", TailCall,
|
||||
PopValues(1));
|
||||
|
||||
bool
|
||||
ICToNumber_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
MOZ_ASSERT(R0 == JSReturnOperand);
|
||||
|
||||
// Restore the tail call register.
|
||||
EmitRestoreTailCallReg(masm);
|
||||
|
||||
// Ensure stack is fully synced for the expression decompiler.
|
||||
masm.pushValue(R0);
|
||||
|
||||
// Push arguments.
|
||||
masm.pushValue(R0);
|
||||
masm.push(ICStubReg);
|
||||
|
||||
return tailCallVM(DoToNumberFallbackInfo, masm);
|
||||
}
|
||||
|
||||
//
|
||||
// GetElem_Fallback
|
||||
//
|
||||
|
||||
@@ -333,32 +333,6 @@ class ICToBool_Object : public ICStub
|
||||
};
|
||||
};
|
||||
|
||||
// ToNumber
|
||||
// JSOP_POS
|
||||
|
||||
class ICToNumber_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICToNumber_Fallback(JitCode* stubCode)
|
||||
: ICFallbackStub(ICStub::ToNumber_Fallback, stubCode) {}
|
||||
|
||||
public:
|
||||
// Compiler for this stub kind.
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
public:
|
||||
explicit Compiler(JSContext* cx)
|
||||
: ICStubCompiler(cx, ICStub::ToNumber_Fallback, Engine::Baseline) {}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICToNumber_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// GetElem
|
||||
// JSOP_GETELEM
|
||||
|
||||
|
||||
@@ -34,8 +34,6 @@ namespace jit {
|
||||
_(ToBool_Double) \
|
||||
_(ToBool_Object) \
|
||||
\
|
||||
_(ToNumber_Fallback) \
|
||||
\
|
||||
_(Call_Fallback) \
|
||||
_(Call_Scripted) \
|
||||
_(Call_AnyScripted) \
|
||||
|
||||
@@ -408,6 +408,11 @@ BaselineInspector::expectedBinaryArithSpecialization(jsbytecode* pc)
|
||||
MIRType result;
|
||||
ICStub* stubs[2];
|
||||
|
||||
if (JSOp(*pc) == JSOP_POS) {
|
||||
// +x expanding to x*1, but no corresponding IC.
|
||||
return MIRType::None;
|
||||
}
|
||||
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
ICStub* stub = entry.fallbackStub();
|
||||
if (stub->isBinaryArith_Fallback() &&
|
||||
|
||||
@@ -2378,6 +2378,8 @@ CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
|
||||
switch (jsop) {
|
||||
case JSOP_BITNOT:
|
||||
case JSOP_NEG:
|
||||
case JSOP_INC:
|
||||
case JSOP_DEC:
|
||||
emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir);
|
||||
break;
|
||||
case JSOP_CALLPROP:
|
||||
@@ -3413,6 +3415,48 @@ CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir)
|
||||
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitToNumeric(LToNumeric* lir)
|
||||
{
|
||||
ValueOperand operand = ToValue(lir, LToNumeric::Input);
|
||||
ValueOperand output = ToOutValue(lir);
|
||||
bool maybeInt32 = lir->mir()->mightBeType(MIRType::Int32);
|
||||
bool maybeDouble = lir->mir()->mightBeType(MIRType::Double);
|
||||
bool maybeNumber = maybeInt32 || maybeDouble;
|
||||
bool maybeBigInt = lir->mir()->mightBeType(MIRType::BigInt);
|
||||
int checks = int(maybeNumber) + int(maybeBigInt);
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(ToNumericInfo, lir, ArgList(operand), StoreValueTo(output));
|
||||
|
||||
if (checks == 0) {
|
||||
masm.jump(ool->entry());
|
||||
} else {
|
||||
Label done;
|
||||
using Condition = Assembler::Condition;
|
||||
constexpr Condition Equal = Assembler::Equal;
|
||||
constexpr Condition NotEqual = Assembler::NotEqual;
|
||||
|
||||
if (maybeNumber) {
|
||||
checks--;
|
||||
Condition cond = checks ? Equal : NotEqual;
|
||||
Label* target = checks ? &done : ool->entry();
|
||||
masm.branchTestNumber(cond, operand, target);
|
||||
}
|
||||
if (maybeBigInt) {
|
||||
checks--;
|
||||
Condition cond = checks ? Equal : NotEqual;
|
||||
Label* target = checks ? &done : ool->entry();
|
||||
masm.branchTestBigInt(cond, operand, target);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(checks == 0);
|
||||
masm.bind(&done);
|
||||
masm.moveValue(operand, output);
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir)
|
||||
{
|
||||
@@ -12205,16 +12249,16 @@ CodeGenerator::visitRandom(LRandom* ins)
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSignExtend(LSignExtend* ins)
|
||||
CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins)
|
||||
{
|
||||
Register input = ToRegister(ins->input());
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
switch (ins->mode()) {
|
||||
case MSignExtend::Byte:
|
||||
case MSignExtendInt32::Byte:
|
||||
masm.move8SignExtend(input, output);
|
||||
break;
|
||||
case MSignExtend::Half:
|
||||
case MSignExtendInt32::Half:
|
||||
masm.move16SignExtend(input, output);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir);
|
||||
void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir);
|
||||
void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir);
|
||||
void visitToNumeric(LToNumeric* lir);
|
||||
void visitTypeBarrierV(LTypeBarrierV* lir);
|
||||
void visitTypeBarrierO(LTypeBarrierO* lir);
|
||||
void visitMonitorTypes(LMonitorTypes* lir);
|
||||
@@ -441,7 +442,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||
void visitRotate(LRotate* ins);
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitSignExtend(LSignExtend* ins);
|
||||
void visitSignExtendInt32(LSignExtendInt32* ins);
|
||||
void visitModuleMetadata(LModuleMetadata* lir);
|
||||
void visitDynamicImport(LDynamicImport* lir);
|
||||
|
||||
|
||||
+214
-24
@@ -636,7 +636,7 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
|
||||
|
||||
MPhi* phi = entry->getSlot(slot)->toPhi();
|
||||
|
||||
if (*last == JSOP_POS)
|
||||
if (*last == JSOP_POS || *last == JSOP_TONUMERIC)
|
||||
last = earlier;
|
||||
|
||||
if (CodeSpec[*last].format & JOF_TYPESET) {
|
||||
@@ -719,6 +719,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock* entry, jsbytecode* start, jsbytecod
|
||||
case JSOP_DIV:
|
||||
case JSOP_MOD:
|
||||
case JSOP_NEG:
|
||||
case JSOP_INC:
|
||||
case JSOP_DEC:
|
||||
type = inspector->expectedResultType(last);
|
||||
break;
|
||||
case JSOP_BIGINT:
|
||||
@@ -1584,6 +1586,7 @@ IonBuilder::traverseBytecode()
|
||||
break;
|
||||
|
||||
case JSOP_POS:
|
||||
case JSOP_TONUMERIC:
|
||||
case JSOP_TOID:
|
||||
case JSOP_TOSTRING:
|
||||
// These ops may leave their input on the stack without setting
|
||||
@@ -1737,9 +1740,16 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
case JSOP_POS:
|
||||
return jsop_pos();
|
||||
|
||||
case JSOP_TONUMERIC:
|
||||
return jsop_tonumeric();
|
||||
|
||||
case JSOP_NEG:
|
||||
return jsop_neg();
|
||||
|
||||
case JSOP_INC:
|
||||
case JSOP_DEC:
|
||||
return jsop_inc_or_dec(op);
|
||||
|
||||
case JSOP_TOSTRING:
|
||||
return jsop_tostring();
|
||||
|
||||
@@ -4852,7 +4862,7 @@ IonBuilder::jsop_bitop(JSOp op)
|
||||
}
|
||||
|
||||
MDefinition::Opcode
|
||||
JSOpToMDefinition(JSOp op)
|
||||
BinaryJSOpToMDefinition(JSOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case JSOP_ADD:
|
||||
@@ -4953,6 +4963,40 @@ IonBuilder::powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* pow
|
||||
return true;
|
||||
}
|
||||
|
||||
MIRType
|
||||
IonBuilder::binaryArithNumberSpecialization(MDefinition* left, MDefinition* right)
|
||||
{
|
||||
// Try to specialize as int32.
|
||||
if (left->type() == MIRType::Int32 && right->type() == MIRType::Int32 &&
|
||||
!inspector->hasSeenDoubleResult(pc)) {
|
||||
return MIRType::Int32;
|
||||
}
|
||||
return MIRType::Double;
|
||||
}
|
||||
|
||||
MBinaryArithInstruction*
|
||||
IonBuilder::binaryArithEmitSpecialized(MDefinition::Opcode op, MIRType specialization,
|
||||
MDefinition* left, MDefinition* right)
|
||||
{
|
||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), op, left, right);
|
||||
ins->setSpecialization(specialization);
|
||||
|
||||
if (op == MDefinition::Op_Add || op == MDefinition::Op_Mul) {
|
||||
ins->setCommutative();
|
||||
}
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
MOZ_ASSERT(!ins->isEffectful());
|
||||
|
||||
if(!maybeInsertResume()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ins;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
SimpleArithOperand(MDefinition* op)
|
||||
{
|
||||
@@ -4986,19 +5030,20 @@ IonBuilder::binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
|
||||
return true;
|
||||
}
|
||||
|
||||
MDefinition::Opcode defOp = JSOpToMDefinition(op);
|
||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
|
||||
ins->setNumberSpecialization(alloc(), inspector, pc);
|
||||
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||
MIRType specialization = binaryArithNumberSpecialization(left, right);
|
||||
MBinaryArithInstruction* ins = binaryArithEmitSpecialized(defOp, specialization, left, right);
|
||||
|
||||
if (op == JSOP_ADD || op == JSOP_MUL)
|
||||
ins->setCommutative();
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
MOZ_ASSERT(!ins->isEffectful());
|
||||
if (!maybeInsertResume())
|
||||
if(!ins) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Relax int32 to double if, despite the fact that we have int32 operands and
|
||||
// we've never seen a double result, we know the result may overflow or be a
|
||||
// double.
|
||||
if (specialization == MIRType::Int32 && ins->constantDoubleResult(alloc())) {
|
||||
ins->setSpecialization(MIRType::Double);
|
||||
}
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
@@ -5022,16 +5067,10 @@ IonBuilder::binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||
return true;
|
||||
}
|
||||
|
||||
MDefinition::Opcode def_op = JSOpToMDefinition(op);
|
||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
|
||||
ins->setSpecialization(specialization);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
MOZ_ASSERT(!ins->isEffectful());
|
||||
if (!maybeInsertResume())
|
||||
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||
if(!binaryArithEmitSpecialized(defOp, specialization, left, right)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
@@ -5124,8 +5163,8 @@ IonBuilder::jsop_binary_arith(JSOp op, MDefinition* left, MDefinition* right)
|
||||
trackOptimizationAttempt(TrackedStrategy::BinaryArith_Call);
|
||||
trackOptimizationSuccess();
|
||||
|
||||
MDefinition::Opcode def_op = JSOpToMDefinition(op);
|
||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), def_op, left, right);
|
||||
MDefinition::Opcode defOp = BinaryJSOpToMDefinition(op);
|
||||
MBinaryArithInstruction* ins = MBinaryArithInstruction::New(alloc(), defOp, left, right);
|
||||
|
||||
// Decrease type from 'any type' to 'empty type' when one of the operands
|
||||
// is 'empty typed'.
|
||||
@@ -5202,6 +5241,157 @@ IonBuilder::jsop_neg()
|
||||
return jsop_binary_arith(JSOP_MUL, negator, right);
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
IonBuilder::unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp)
|
||||
{
|
||||
switch (op) {
|
||||
case JSOP_INC: {
|
||||
*defOp = MDefinition::Op_Add;
|
||||
MConstant* right = MConstant::New(alloc(), Int32Value(1));
|
||||
current->add(right);
|
||||
return right;
|
||||
}
|
||||
case JSOP_DEC: {
|
||||
*defOp = MDefinition::Op_Sub;
|
||||
MConstant* right = MConstant::New(alloc(), Int32Value(1));
|
||||
current->add(right);
|
||||
return right;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("unexpected unary opcode");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
// Try to convert Inc(x) or Dec(x) to Add(x,1) or Sub(x,1) if the operand is a
|
||||
// number.
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedTypes);
|
||||
|
||||
if (!IsNumberType(value->type())) {
|
||||
trackOptimizationOutcome(TrackedOutcome::OperandNotNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
MDefinition::Opcode defOp;
|
||||
MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
|
||||
MIRType specialization = binaryArithNumberSpecialization(value, rhs);
|
||||
if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* value)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
// Try to emit a specialized binary instruction speculating the
|
||||
// type using the baseline caches.
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::UnaryArith_SpecializedOnBaselineTypes);
|
||||
|
||||
MIRType specialization = inspector->expectedBinaryArithSpecialization(pc);
|
||||
if (specialization == MIRType::None) {
|
||||
trackOptimizationOutcome(TrackedOutcome::SpeculationOnInputTypesFailed);
|
||||
return true;
|
||||
}
|
||||
|
||||
MDefinition::Opcode defOp;
|
||||
MDefinition* rhs = unaryArithConvertToBinary(op, &defOp);
|
||||
if (!binaryArithEmitSpecialized(defOp, specialization, value, rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackOptimizationSuccess();
|
||||
*emitted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_tonumeric()
|
||||
{
|
||||
MDefinition* peeked = current->peek(-1);
|
||||
|
||||
if (IsNumericType(peeked->type())) {
|
||||
// Elide the ToNumeric as we already unboxed the value.
|
||||
peeked->setImplicitlyUsedUnchecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
LifoAlloc* lifoAlloc = alloc().lifoAlloc();
|
||||
TemporaryTypeSet* types = lifoAlloc->new_<TemporaryTypeSet>();
|
||||
if (!types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
types->addType(TypeSet::Int32Type(), lifoAlloc);
|
||||
types->addType(TypeSet::DoubleType(), lifoAlloc);
|
||||
types->addType(TypeSet::BigIntType(), lifoAlloc);
|
||||
|
||||
if (peeked->type() == MIRType::Value && peeked->resultTypeSet() &&
|
||||
peeked->resultTypeSet()->isSubset(types)) {
|
||||
// Elide the ToNumeric because the arg is already a boxed numeric.
|
||||
peeked->setImplicitlyUsedUnchecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, pop the value and add an MToNumeric.
|
||||
MDefinition* popped = current->pop();
|
||||
MToNumeric* ins = MToNumeric::New(alloc(), popped, types);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
// toValue() is effectful, so add a resume point.
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_inc_or_dec(JSOp op)
|
||||
{
|
||||
bool emitted = false;
|
||||
MDefinition* value = current->pop();
|
||||
|
||||
startTrackingOptimizations();
|
||||
|
||||
trackTypeInfo(TrackedTypeSite::Operand, value->type(), value->resultTypeSet());
|
||||
|
||||
if (!unaryArithTrySpecialized(&emitted, op, value)) {
|
||||
return false;
|
||||
}
|
||||
if (emitted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!unaryArithTrySpecializedOnBaselineInspector(&emitted, op, value)) {
|
||||
return false;
|
||||
}
|
||||
if (emitted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::UnaryArith_InlineCache);
|
||||
trackOptimizationSuccess();
|
||||
|
||||
MInstruction* stub = MUnarySharedStub::New(alloc(), value);
|
||||
current->add(stub);
|
||||
current->push(stub);
|
||||
|
||||
// Decrease type from 'any type' to 'empty type' when one of the operands
|
||||
// is 'empty typed'.
|
||||
maybeMarkEmpty(stub);
|
||||
|
||||
return resumeAfter(stub);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_tostring()
|
||||
{
|
||||
|
||||
@@ -497,8 +497,13 @@ class IonBuilder
|
||||
|
||||
// jsop_binary_arith helpers.
|
||||
MBinaryArithInstruction* binaryArithInstruction(JSOp op, MDefinition* left, MDefinition* right);
|
||||
MIRType binaryArithNumberSpecialization(MDefinition* left, MDefinition* right);
|
||||
MOZ_MUST_USE bool binaryArithTryConcat(bool* emitted, JSOp op, MDefinition* left,
|
||||
MDefinition* right);
|
||||
MOZ_MUST_USE MBinaryArithInstruction* binaryArithEmitSpecialized(MDefinition::Opcode op,
|
||||
MIRType specialization,
|
||||
MDefinition* left,
|
||||
MDefinition* right);
|
||||
MOZ_MUST_USE bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left,
|
||||
MDefinition* right);
|
||||
MOZ_MUST_USE bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||
@@ -510,6 +515,12 @@ class IonBuilder
|
||||
// jsop_bitnot helpers.
|
||||
MOZ_MUST_USE bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
|
||||
|
||||
// jsop_inc_or_dec helpers.
|
||||
MDefinition* unaryArithConvertToBinary(JSOp op, MDefinition::Opcode* defOp);
|
||||
MOZ_MUST_USE bool unaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* value);
|
||||
MOZ_MUST_USE bool unaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op,
|
||||
MDefinition* value);
|
||||
|
||||
// jsop_pow helpers.
|
||||
MOZ_MUST_USE bool powTrySpecialized(bool* emitted, MDefinition* base, MDefinition* power,
|
||||
MIRType outputType);
|
||||
@@ -692,6 +703,8 @@ class IonBuilder
|
||||
MOZ_MUST_USE bool jsop_pow();
|
||||
MOZ_MUST_USE bool jsop_pos();
|
||||
MOZ_MUST_USE bool jsop_neg();
|
||||
MOZ_MUST_USE bool jsop_tonumeric();
|
||||
MOZ_MUST_USE bool jsop_inc_or_dec(JSOp op);
|
||||
MOZ_MUST_USE bool jsop_tostring();
|
||||
MOZ_MUST_USE bool jsop_setarg(uint32_t arg);
|
||||
MOZ_MUST_USE bool jsop_defvar(uint32_t index);
|
||||
|
||||
@@ -644,6 +644,10 @@ IsNumberType(MIRType type)
|
||||
type == MIRType::Int64;
|
||||
}
|
||||
|
||||
static inline bool IsNumericType(MIRType type) {
|
||||
return IsNumberType(type) || type == MIRType::BigInt;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsTypeRepresentableAsDouble(MIRType type)
|
||||
{
|
||||
|
||||
+13
-4
@@ -1328,14 +1328,14 @@ LIRGenerator::visitUrsh(MUrsh* ins)
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSignExtend(MSignExtend* ins)
|
||||
LIRGenerator::visitSignExtendInt32(MSignExtendInt32* ins)
|
||||
{
|
||||
LInstructionHelper<1, 1, 0>* lir;
|
||||
|
||||
if (ins->mode() == MSignExtend::Byte)
|
||||
lir = new(alloc()) LSignExtend(useByteOpRegisterAtStart(ins->input()), ins->mode());
|
||||
if (ins->mode() == MSignExtendInt32::Byte)
|
||||
lir = new(alloc()) LSignExtendInt32(useByteOpRegisterAtStart(ins->input()), ins->mode());
|
||||
else
|
||||
lir = new(alloc()) LSignExtend(useRegisterAtStart(ins->input()), ins->mode());
|
||||
lir = new(alloc()) LSignExtendInt32(useRegisterAtStart(ins->input()), ins->mode());
|
||||
|
||||
define(lir, ins);
|
||||
}
|
||||
@@ -2165,6 +2165,15 @@ LIRGenerator::visitToInt32(MToInt32* convert)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitToNumeric(MToNumeric* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->input()->type() == MIRType::Value);
|
||||
LToNumeric* lir = new (alloc()) LToNumeric(useBoxAtStart(ins->input()));
|
||||
defineBox(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitLsh(MLsh* ins);
|
||||
void visitRsh(MRsh* ins);
|
||||
void visitUrsh(MUrsh* ins);
|
||||
void visitSignExtend(MSignExtend* ins);
|
||||
void visitSignExtendInt32(MSignExtendInt32* ins);
|
||||
void visitRotate(MRotate* ins);
|
||||
void visitFloor(MFloor* ins);
|
||||
void visitCeil(MCeil* ins);
|
||||
@@ -336,6 +336,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
|
||||
void visitModuleMetadata(MModuleMetadata* ins);
|
||||
void visitDynamicImport(MDynamicImport* ins);
|
||||
void visitToNumeric(MToNumeric* ins);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
||||
+54
-19
@@ -3110,23 +3110,6 @@ MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
|
||||
jsbytecode* pc)
|
||||
{
|
||||
setSpecialization(MIRType::Double);
|
||||
|
||||
// Try to specialize as int32.
|
||||
if (getOperand(0)->type() == MIRType::Int32 && getOperand(1)->type() == MIRType::Int32) {
|
||||
bool seenDouble = inspector->hasSeenDoubleResult(pc);
|
||||
|
||||
// Use int32 specialization if the operation doesn't overflow on its
|
||||
// constant operands and if the operation has never overflowed.
|
||||
if (!seenDouble && !constantDoubleResult(alloc))
|
||||
setInt32Specialization();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
|
||||
{
|
||||
@@ -3159,9 +3142,9 @@ MRsh::foldsTo(TempAllocator& alloc)
|
||||
|
||||
switch (shift) {
|
||||
case 16:
|
||||
return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Half);
|
||||
return MSignExtendInt32::New(alloc, lhs->getOperand(0), MSignExtendInt32::Half);
|
||||
case 24:
|
||||
return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Byte);
|
||||
return MSignExtendInt32::New(alloc, lhs->getOperand(0), MSignExtendInt32::Byte);
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -4189,6 +4172,23 @@ MResumePoint::isRecoverableOperand(MUse* u) const
|
||||
return block()->info().isRecoverableOperand(indexOf(u));
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MToNumeric::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
MDefinition* input = getOperand(0);
|
||||
|
||||
if (input->isBox()) {
|
||||
MDefinition* unboxed = input->getOperand(0);
|
||||
if (IsNumericType(unboxed->type())) {
|
||||
// If the argument is an MBox and we can see that it boxes a numeric
|
||||
// value, ToNumeric can be elided.
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MToInt32::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
@@ -4326,6 +4326,41 @@ MExtendInt32ToInt64::foldsTo(TempAllocator& alloc)
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MSignExtendInt32::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
MDefinition* input = this->input();
|
||||
if (input->isConstant()) {
|
||||
int32_t c = input->toConstant()->toInt32();
|
||||
int32_t res;
|
||||
switch (mode_) {
|
||||
case Byte: res = int32_t(int8_t(c & 0xFF)); break;
|
||||
case Half: res = int32_t(int16_t(c & 0xFFFF)); break;
|
||||
}
|
||||
return MConstant::New(alloc, Int32Value(res));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MSignExtendInt64::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
MDefinition* input = this->input();
|
||||
if (input->isConstant()) {
|
||||
int64_t c = input->toConstant()->toInt64();
|
||||
int64_t res;
|
||||
switch (mode_) {
|
||||
case Byte: res = int64_t(int8_t(c & 0xFF)); break;
|
||||
case Half: res = int64_t(int16_t(c & 0xFFFF)); break;
|
||||
case Word: res = int64_t(int32_t(c & 0xFFFFFFFFU)); break;
|
||||
}
|
||||
return MConstant::NewInt64(alloc, res);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
MToDouble::foldsTo(TempAllocator& alloc)
|
||||
{
|
||||
|
||||
+92
-6
@@ -5534,6 +5534,43 @@ class MInt64ToFloatingPoint
|
||||
}
|
||||
};
|
||||
|
||||
// Takes a boxed Value and returns a Value containing either a Number or a
|
||||
// BigInt. Usually this will be the value itself, but it may be an object that
|
||||
// has a @@toPrimitive, valueOf, or toString method.
|
||||
class MToNumeric : public MUnaryInstruction, public BoxInputsPolicy::Data
|
||||
{
|
||||
MToNumeric(MDefinition* arg, TemporaryTypeSet* types)
|
||||
: MUnaryInstruction(arg)
|
||||
{
|
||||
MOZ_ASSERT(!IsNumericType(arg->type()),
|
||||
"Unboxable definitions don't need ToNumeric");
|
||||
setResultType(MIRType::Value);
|
||||
// Although `types' is always Int32|Double|BigInt, we have to compute it in
|
||||
// IonBuilder to know whether emitting an MToNumeric is needed, so we just
|
||||
// pass it through as an argument instead of recomputing it here.
|
||||
setResultTypeSet(types);
|
||||
setGuard();
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ToNumeric)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
static MToNumeric* New(TempAllocator& alloc, MDefinition* arg,
|
||||
TemporaryTypeSet* types) {
|
||||
return new (alloc) MToNumeric(arg, types);
|
||||
}
|
||||
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
|
||||
ALLOW_CLONE(MToNumeric)
|
||||
};
|
||||
|
||||
// Converts a primitive (either typed or untyped) to an int32. If the input is
|
||||
// not primitive at runtime, a bailout occurs. If the input cannot be converted
|
||||
// to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
|
||||
@@ -6135,7 +6172,7 @@ class MUrsh : public MShiftInstruction
|
||||
ALLOW_CLONE(MUrsh)
|
||||
};
|
||||
|
||||
class MSignExtend
|
||||
class MSignExtendInt32
|
||||
: public MUnaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
@@ -6148,7 +6185,7 @@ class MSignExtend
|
||||
private:
|
||||
Mode mode_;
|
||||
|
||||
MSignExtend(MDefinition* op, Mode mode)
|
||||
MSignExtendInt32(MDefinition* op, Mode mode)
|
||||
: MUnaryInstruction(op), mode_(mode)
|
||||
{
|
||||
setResultType(MIRType::Int32);
|
||||
@@ -6156,17 +6193,67 @@ class MSignExtend
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SignExtend)
|
||||
INSTRUCTION_HEADER(SignExtendInt32)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
Mode mode() { return mode_; }
|
||||
Mode mode() const { return mode_; }
|
||||
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!congruentIfOperandsEqual(ins))
|
||||
return false;
|
||||
return ins->isSignExtendInt32() && ins->toSignExtendInt32()->mode_ == mode_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
bool canRecoverOnBailout() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSignExtend)
|
||||
ALLOW_CLONE(MSignExtendInt32)
|
||||
};
|
||||
|
||||
class MSignExtendInt64
|
||||
: public MUnaryInstruction,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
public:
|
||||
enum Mode {
|
||||
Byte,
|
||||
Half,
|
||||
Word
|
||||
};
|
||||
|
||||
private:
|
||||
Mode mode_;
|
||||
|
||||
MSignExtendInt64(MDefinition* op, Mode mode)
|
||||
: MUnaryInstruction(op), mode_(mode)
|
||||
{
|
||||
setResultType(MIRType::Int64);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SignExtendInt64)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
Mode mode() const { return mode_; }
|
||||
|
||||
MDefinition* foldsTo(TempAllocator& alloc) override;
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!congruentIfOperandsEqual(ins))
|
||||
return false;
|
||||
return ins->isSignExtendInt64() && ins->toSignExtendInt64()->mode_ == mode_;
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSignExtendInt64)
|
||||
};
|
||||
|
||||
class MBinaryArithInstruction
|
||||
@@ -6219,7 +6306,6 @@ class MBinaryArithInstruction
|
||||
specialization_ = MIRType::Int32;
|
||||
setResultType(MIRType::Int32);
|
||||
}
|
||||
void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
|
||||
|
||||
virtual void trySpecializeFloat32(TempAllocator& alloc) override;
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ namespace jit {
|
||||
_(Lsh) \
|
||||
_(Rsh) \
|
||||
_(Ursh) \
|
||||
_(SignExtend) \
|
||||
_(SignExtendInt32) \
|
||||
_(SignExtendInt64) \
|
||||
_(MinMax) \
|
||||
_(Abs) \
|
||||
_(Clz) \
|
||||
@@ -123,6 +124,7 @@ namespace jit {
|
||||
_(ToDouble) \
|
||||
_(ToFloat32) \
|
||||
_(ToInt32) \
|
||||
_(ToNumeric) \
|
||||
_(TruncateToInt32) \
|
||||
_(WrapInt64ToInt32) \
|
||||
_(ExtendInt32ToInt64) \
|
||||
|
||||
@@ -1741,6 +1741,12 @@ MTruncateToInt32::computeRange(TempAllocator& alloc)
|
||||
setRange(output);
|
||||
}
|
||||
|
||||
void
|
||||
MToNumeric::computeRange(TempAllocator& alloc)
|
||||
{
|
||||
setRange(new (alloc) Range(getOperand(0)));
|
||||
}
|
||||
|
||||
void
|
||||
MToInt32::computeRange(TempAllocator& alloc)
|
||||
{
|
||||
|
||||
@@ -336,32 +336,32 @@ RUrsh::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
}
|
||||
|
||||
bool
|
||||
MSignExtend::writeRecoverData(CompactBufferWriter& writer) const
|
||||
MSignExtendInt32::writeRecoverData(CompactBufferWriter& writer) const
|
||||
{
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtend));
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtendInt32));
|
||||
MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
|
||||
writer.writeByte(uint8_t(mode_));
|
||||
return true;
|
||||
}
|
||||
|
||||
RSignExtend::RSignExtend(CompactBufferReader& reader)
|
||||
RSignExtendInt32::RSignExtendInt32(CompactBufferReader& reader)
|
||||
{
|
||||
mode_ = reader.readByte();
|
||||
}
|
||||
|
||||
bool
|
||||
RSignExtend::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
RSignExtendInt32::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
{
|
||||
RootedValue operand(cx, iter.read());
|
||||
|
||||
int32_t result;
|
||||
switch (MSignExtend::Mode(mode_)) {
|
||||
case MSignExtend::Byte:
|
||||
switch (MSignExtendInt32::Mode(mode_)) {
|
||||
case MSignExtendInt32::Byte:
|
||||
if (!js::SignExtendOperation<int8_t>(cx, operand, &result))
|
||||
return false;
|
||||
break;
|
||||
case MSignExtend::Half:
|
||||
case MSignExtendInt32::Half:
|
||||
if (!js::SignExtendOperation<int16_t>(cx, operand, &result))
|
||||
return false;
|
||||
break;
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace jit {
|
||||
_(Lsh) \
|
||||
_(Rsh) \
|
||||
_(Ursh) \
|
||||
_(SignExtend) \
|
||||
_(SignExtendInt32) \
|
||||
_(Add) \
|
||||
_(Sub) \
|
||||
_(Mul) \
|
||||
@@ -255,13 +255,13 @@ class RUrsh final : public RInstruction
|
||||
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
|
||||
};
|
||||
|
||||
class RSignExtend final : public RInstruction
|
||||
class RSignExtendInt32 final : public RInstruction
|
||||
{
|
||||
private:
|
||||
uint8_t mode_;
|
||||
|
||||
public:
|
||||
RINSTRUCTION_HEADER_NUM_OP_(SignExtend, 1)
|
||||
RINSTRUCTION_HEADER_NUM_OP_(SignExtendInt32, 1)
|
||||
|
||||
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
|
||||
};
|
||||
|
||||
+36
-10
@@ -1469,21 +1469,33 @@ DoUnaryArithFallback(JSContext* cx, void* payload, ICUnaryArith_Fallback* stub_,
|
||||
JSOp op = JSOp(*pc);
|
||||
FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
|
||||
|
||||
// The unary operations take a copied val because the original value is needed
|
||||
// below.
|
||||
RootedValue valCopy(cx, val);
|
||||
switch (op) {
|
||||
case JSOP_BITNOT: {
|
||||
RootedValue valCopy(cx, val);
|
||||
if (!BitNot(cx, &valCopy, res)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSOP_NEG: {
|
||||
// We copy val here because the original value is needed below.
|
||||
RootedValue valCopy(cx, val);
|
||||
if (!NegOperation(cx, script, pc, &valCopy, res))
|
||||
if (!NegOperation(cx, script, pc, &valCopy, res))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case JSOP_INC: {
|
||||
if (!IncOperation(cx, &valCopy, res)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSOP_DEC: {
|
||||
if (!DecOperation(cx, &valCopy, res)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Unexpected op");
|
||||
}
|
||||
@@ -1558,12 +1570,8 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
Label failure;
|
||||
masm.ensureDouble(R0, FloatReg0, &failure);
|
||||
|
||||
MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT);
|
||||
|
||||
if (op == JSOP_NEG) {
|
||||
masm.negateDouble(FloatReg0);
|
||||
masm.boxDouble(FloatReg0, R0);
|
||||
} else {
|
||||
switch (op) {
|
||||
case JSOP_BITNOT: {
|
||||
// Truncate the double to an int32.
|
||||
Register scratchReg = R1.scratchReg();
|
||||
|
||||
@@ -1581,6 +1589,24 @@ ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.bind(&doneTruncate);
|
||||
masm.not32(scratchReg);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
break;
|
||||
}
|
||||
case JSOP_NEG:
|
||||
masm.negateDouble(FloatReg0);
|
||||
masm.boxDouble(FloatReg0, R0);
|
||||
break;
|
||||
case JSOP_INC:
|
||||
case JSOP_DEC:
|
||||
masm.loadConstantDouble(1.0, ScratchDoubleReg);
|
||||
if (op == JSOP_INC) {
|
||||
masm.addDouble(ScratchDoubleReg, FloatReg0);
|
||||
} else {
|
||||
masm.subDouble(ScratchDoubleReg, FloatReg0);
|
||||
}
|
||||
masm.boxDouble(FloatReg0, R0);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected op");
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
@@ -1855,6 +1855,8 @@ class ICBinaryArith_DoubleWithInt32 : public ICStub
|
||||
// UnaryArith
|
||||
// JSOP_BITNOT
|
||||
// JSOP_NEG
|
||||
// JSOP_INC
|
||||
// JSOP_DEC
|
||||
|
||||
class ICUnaryArith_Fallback : public ICFallbackStub
|
||||
{
|
||||
|
||||
@@ -1186,6 +1186,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
|
||||
// Lists of all TypePolicy specializations which are used by MIR Instructions.
|
||||
#define TYPE_POLICY_LIST(_) \
|
||||
_(AllDoublePolicy) \
|
||||
_(ArithPolicy) \
|
||||
_(BitwisePolicy) \
|
||||
_(BoxInputsPolicy) \
|
||||
@@ -1204,7 +1205,6 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
_(StoreUnboxedScalarPolicy) \
|
||||
_(StoreUnboxedObjectOrNullPolicy) \
|
||||
_(TestPolicy) \
|
||||
_(AllDoublePolicy) \
|
||||
_(ToDoublePolicy) \
|
||||
_(ToInt32Policy) \
|
||||
_(ToStringPolicy) \
|
||||
|
||||
@@ -1357,5 +1357,19 @@ CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <bool allowBigInt = false>
|
||||
static bool DoToNumeric(JSContext* cx, HandleValue arg, MutableHandleValue ret)
|
||||
{
|
||||
ret.set(arg);
|
||||
if (allowBigInt) {
|
||||
return ToNumeric(cx, ret);
|
||||
}
|
||||
return ToNumber(cx, ret);
|
||||
}
|
||||
|
||||
typedef bool (*ToNumericFn)(JSContext*, HandleValue, MutableHandleValue);
|
||||
const VMFunction ToNumberInfo = FunctionInfo<ToNumericFn>(DoToNumeric, "ToNumber");
|
||||
const VMFunction ToNumericInfo = FunctionInfo<ToNumericFn>(DoToNumeric<true>, "ToNumeric");
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
@@ -803,6 +803,9 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue
|
||||
MOZ_MUST_USE bool
|
||||
CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
|
||||
|
||||
extern const VMFunction ToNumberInfo;
|
||||
extern const VMFunction ToNumericInfo;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -3364,6 +3364,24 @@ CodeGeneratorARM::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||
masm.ma_asr(Imm32(31), output.low, output.high);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitSignExtendInt64(LSignExtendInt64* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
Register64 output = ToOutRegister64(lir);
|
||||
switch (lir->mode()) {
|
||||
case MSignExtendInt64::Byte:
|
||||
masm.move8SignExtend(input.low, output.low);
|
||||
break;
|
||||
case MSignExtendInt64::Half:
|
||||
masm.move16SignExtend(input.low, output.low);
|
||||
break;
|
||||
case MSignExtendInt64::Word:
|
||||
break;
|
||||
}
|
||||
masm.ma_asr(Imm32(31), output.low, output.high);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitDivOrModI64(LDivOrModI64* lir)
|
||||
{
|
||||
|
||||
@@ -168,6 +168,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
|
||||
virtual void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||
virtual void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||
virtual void visitSignExtendInt64(LSignExtendInt64* ins);
|
||||
virtual void visitAddI64(LAddI64* lir);
|
||||
virtual void visitSubI64(LSubI64* lir);
|
||||
virtual void visitMulI64(LMulI64* lir);
|
||||
|
||||
@@ -1028,3 +1028,9 @@ LIRGeneratorARM::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
||||
|
||||
lir->setDef(0, def);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorARM::visitSignExtendInt64(MSignExtendInt64* ins)
|
||||
{
|
||||
defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
|
||||
}
|
||||
@@ -121,6 +121,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
|
||||
void visitCopySign(MCopySign* ins);
|
||||
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
|
||||
void visitSignExtendInt64(MSignExtendInt64* ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorARM LIRGeneratorSpecific;
|
||||
|
||||
@@ -366,3 +366,9 @@ LIRGeneratorARM64::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorARM64::visitSignExtendInt64(MSignExtendInt64* ins)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
@@ -121,6 +121,7 @@ class LIRGeneratorARM64 : public LIRGeneratorShared
|
||||
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
|
||||
void visitCopySign(MCopySign* ins);
|
||||
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
|
||||
void visitSignExtendInt64(MSignExtendInt64* ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorARM64 LIRGeneratorSpecific;
|
||||
|
||||
@@ -107,6 +107,7 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
||||
void visitWasmTruncateToInt64(MWasmTruncateToInt64*) { MOZ_CRASH(); }
|
||||
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint*) { MOZ_CRASH(); }
|
||||
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins) { MOZ_CRASH(); }
|
||||
void visitSignExtendInt64(MSignExtendInt64* ins) { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
typedef LIRGeneratorNone LIRGeneratorSpecific;
|
||||
|
||||
@@ -3351,19 +3351,34 @@ class LShiftI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>
|
||||
};
|
||||
|
||||
// Sign extension
|
||||
class LSignExtend : public LInstructionHelper<1, 1, 0>
|
||||
class LSignExtendInt32 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
MSignExtend::Mode mode_;
|
||||
MSignExtendInt32::Mode mode_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(SignExtend);
|
||||
explicit LSignExtend(const LAllocation& num, MSignExtend::Mode mode)
|
||||
LIR_HEADER(SignExtendInt32);
|
||||
explicit LSignExtendInt32(const LAllocation& num, MSignExtendInt32::Mode mode)
|
||||
: mode_(mode)
|
||||
{
|
||||
setOperand(0, num);
|
||||
}
|
||||
|
||||
MSignExtend::Mode mode() { return mode_; }
|
||||
MSignExtendInt32::Mode mode() { return mode_; }
|
||||
};
|
||||
|
||||
class LSignExtendInt64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SignExtendInt64)
|
||||
explicit LSignExtendInt64(const LInt64Allocation& input) {
|
||||
setInt64Operand(0, input);
|
||||
}
|
||||
|
||||
const MSignExtendInt64* mir() const {
|
||||
return mir_->toSignExtendInt64();
|
||||
}
|
||||
|
||||
MSignExtendInt64::Mode mode() const { return mir()->mode(); }
|
||||
};
|
||||
|
||||
class LUrshD : public LBinaryMath<1>
|
||||
@@ -7494,6 +7509,21 @@ class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure that a value is numeric, possibly via a VM call-out that invokes
|
||||
// valueOf().
|
||||
class LToNumeric : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0> {
|
||||
public:
|
||||
LIR_HEADER(ToNumeric)
|
||||
|
||||
explicit LToNumeric(const LBoxAllocation& input) {
|
||||
setBoxOperand(Input, input);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
const MToNumeric* mir() const { return mir_->toToNumeric(); }
|
||||
};
|
||||
|
||||
// Guard that a value is in a TypeSet.
|
||||
class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
|
||||
{
|
||||
|
||||
@@ -111,7 +111,8 @@
|
||||
_(BitOpV) \
|
||||
_(ShiftI) \
|
||||
_(ShiftI64) \
|
||||
_(SignExtend) \
|
||||
_(SignExtendInt32) \
|
||||
_(SignExtendInt64) \
|
||||
_(UrshD) \
|
||||
_(Return) \
|
||||
_(Throw) \
|
||||
@@ -258,6 +259,7 @@
|
||||
_(GuardClass) \
|
||||
_(GuardUnboxedExpando) \
|
||||
_(LoadUnboxedExpando) \
|
||||
_(ToNumeric) \
|
||||
_(TypeBarrierV) \
|
||||
_(TypeBarrierO) \
|
||||
_(MonitorTypes) \
|
||||
|
||||
@@ -470,6 +470,9 @@ class Assembler : public AssemblerX86Shared
|
||||
|
||||
void movsbq(const Operand& src, Register dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::REG:
|
||||
masm.movsbq_rr(src.reg(), dest.encoding());
|
||||
break;
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.movsbq_mr(src.disp(), src.base(), dest.encoding());
|
||||
break;
|
||||
@@ -489,6 +492,9 @@ class Assembler : public AssemblerX86Shared
|
||||
|
||||
void movswq(const Operand& src, Register dest) {
|
||||
switch (src.kind()) {
|
||||
case Operand::REG:
|
||||
masm.movswq_rr(src.reg(), dest.encoding());
|
||||
break;
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.movswq_mr(src.disp(), src.base(), dest.encoding());
|
||||
break;
|
||||
|
||||
@@ -652,6 +652,11 @@ class BaseAssemblerX64 : public BaseAssembler
|
||||
m_formatter.immediate64(imm);
|
||||
}
|
||||
|
||||
void movsbq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("movsbq %s, %s", GPReg32Name(src), GPReg64Name(dst));
|
||||
m_formatter.twoByteOp64(OP2_MOVSX_GvEb, src, dst);
|
||||
}
|
||||
void movsbq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("movsbq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
@@ -663,6 +668,11 @@ class BaseAssemblerX64 : public BaseAssembler
|
||||
m_formatter.twoByteOp64(OP2_MOVSX_GvEb, offset, base, index, scale, dst);
|
||||
}
|
||||
|
||||
void movswq_rr(RegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("movswq %s, %s", GPReg32Name(src), GPReg64Name(dst));
|
||||
m_formatter.twoByteOp64(OP2_MOVSX_GvEw, src, dst);
|
||||
}
|
||||
void movswq_mr(int32_t offset, RegisterID base, RegisterID dst)
|
||||
{
|
||||
spew("movswq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
|
||||
|
||||
@@ -802,6 +802,24 @@ CodeGeneratorX64::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||
masm.movslq(ToOperand(input), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitSignExtendInt64(LSignExtendInt64* ins)
|
||||
{
|
||||
Register64 input = ToRegister64(ins->getInt64Operand(0));
|
||||
Register64 output = ToOutRegister64(ins);
|
||||
switch (ins->mode()) {
|
||||
case MSignExtendInt64::Byte:
|
||||
masm.movsbq(Operand(input.reg), output.reg);
|
||||
break;
|
||||
case MSignExtendInt64::Half:
|
||||
masm.movswq(Operand(input.reg), output.reg);
|
||||
break;
|
||||
case MSignExtendInt64::Word:
|
||||
masm.movslq(Operand(input.reg), output.reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
void visitTruncateFToInt32(LTruncateFToInt32* ins);
|
||||
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||
void visitSignExtendInt64(LSignExtendInt64* ins);
|
||||
void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir);
|
||||
void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
|
||||
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
|
||||
|
||||
@@ -494,3 +494,10 @@ LIRGeneratorX64::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
||||
{
|
||||
defineInt64(new(alloc()) LExtendInt32ToInt64(useAtStart(ins->input())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorX64::visitSignExtendInt64(MSignExtendInt64* ins)
|
||||
{
|
||||
defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
||||
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
|
||||
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
|
||||
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
|
||||
void visitSignExtendInt64(MSignExtendInt64* ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorX64 LIRGeneratorSpecific;
|
||||
|
||||
@@ -216,6 +216,16 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
|
||||
masm.negl(R0.valueReg());
|
||||
break;
|
||||
case JSOP_INC: {
|
||||
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.valueReg());
|
||||
masm.inc32(&rval);
|
||||
break;
|
||||
}
|
||||
case JSOP_DEC: {
|
||||
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.valueReg());
|
||||
masm.dec32(&rval);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Unexpected op");
|
||||
}
|
||||
|
||||
@@ -4937,7 +4937,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
|
||||
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
|
||||
{
|
||||
m_buffer.ensureSpace(MaxInstructionSize);
|
||||
emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
|
||||
emitRexIf(byteRegRequiresRex(reg)||byteRegRequiresRex(rm), reg, 0, rm);
|
||||
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
|
||||
m_buffer.putByteUnchecked(opcode);
|
||||
registerModRM(rm, reg);
|
||||
@@ -4946,7 +4946,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
|
||||
void twoByteOp8(TwoByteOpcodeID opcode, int32_t offset, RegisterID base, RegisterID reg)
|
||||
{
|
||||
m_buffer.ensureSpace(MaxInstructionSize);
|
||||
emitRexIf(byteRegRequiresRex(reg)|regRequiresRex(base), reg, 0, base);
|
||||
emitRexIf(byteRegRequiresRex(reg)||regRequiresRex(base), reg, 0, base);
|
||||
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
|
||||
m_buffer.putByteUnchecked(opcode);
|
||||
memoryModRM(offset, base, reg);
|
||||
@@ -4956,7 +4956,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
|
||||
int scale, RegisterID reg)
|
||||
{
|
||||
m_buffer.ensureSpace(MaxInstructionSize);
|
||||
emitRexIf(byteRegRequiresRex(reg)|regRequiresRex(base)|regRequiresRex(index),
|
||||
emitRexIf(byteRegRequiresRex(reg)||regRequiresRex(base)||regRequiresRex(index),
|
||||
reg, index, base);
|
||||
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
|
||||
m_buffer.putByteUnchecked(opcode);
|
||||
@@ -4970,7 +4970,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off
|
||||
void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID rm, RegisterID reg)
|
||||
{
|
||||
m_buffer.ensureSpace(MaxInstructionSize);
|
||||
emitRexIf(regRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
|
||||
emitRexIf(regRequiresRex(reg)||byteRegRequiresRex(rm), reg, 0, rm);
|
||||
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
|
||||
m_buffer.putByteUnchecked(opcode);
|
||||
registerModRM(rm, reg);
|
||||
|
||||
@@ -1182,6 +1182,28 @@ CodeGeneratorX86::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX86::visitSignExtendInt64(LSignExtendInt64* lir)
|
||||
{
|
||||
Register64 input = ToRegister64(lir->getInt64Operand(0));
|
||||
Register64 output = ToOutRegister64(lir);
|
||||
MOZ_ASSERT(input.low == eax);
|
||||
MOZ_ASSERT(output.low == eax);
|
||||
MOZ_ASSERT(input.high == edx);
|
||||
MOZ_ASSERT(output.high == edx);
|
||||
switch (lir->mode()) {
|
||||
case MSignExtendInt64::Byte:
|
||||
masm.move8SignExtend(eax, eax);
|
||||
break;
|
||||
case MSignExtendInt64::Half:
|
||||
masm.move16SignExtend(eax, eax);
|
||||
break;
|
||||
case MSignExtendInt64::Word:
|
||||
break;
|
||||
}
|
||||
masm.cdq();
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX86::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
|
||||
{
|
||||
|
||||
@@ -77,6 +77,7 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
|
||||
void visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir);
|
||||
void visitWasmReinterpretToI64(LWasmReinterpretToI64* lir);
|
||||
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
|
||||
void visitSignExtendInt64(LSignExtendInt64* ins);
|
||||
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
|
||||
void visitClzI64(LClzI64* lir);
|
||||
void visitCtzI64(LCtzI64* lir);
|
||||
|
||||
@@ -657,3 +657,14 @@ LIRGeneratorX86::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
||||
LAllocation(AnyRegister(eax))));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorX86::visitSignExtendInt64(MSignExtendInt64* ins)
|
||||
{
|
||||
// Here we'll end up using cdq which requires input and output in (edx,eax).
|
||||
LSignExtendInt64* lir =
|
||||
new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input()));
|
||||
defineInt64Fixed(lir, ins, LInt64Allocation(LAllocation(AnyRegister(edx)),
|
||||
LAllocation(AnyRegister(eax))));
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
||||
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
|
||||
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
|
||||
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
|
||||
void visitSignExtendInt64(MSignExtendInt64* ins);
|
||||
void lowerPhi(MPhi* phi);
|
||||
|
||||
static bool allowTypedElementHoleCheck() {
|
||||
|
||||
@@ -226,6 +226,17 @@ ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
|
||||
masm.negl(R0.payloadReg());
|
||||
break;
|
||||
case JSOP_INC: {
|
||||
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.payloadReg());
|
||||
masm.inc32(&rval);
|
||||
break;
|
||||
}
|
||||
case JSOP_DEC: {
|
||||
RegisterOrInt32Constant rval = RegisterOrInt32Constant(R0.payloadReg());
|
||||
masm.dec32(&rval);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unexpected op");
|
||||
}
|
||||
|
||||
+8
-3
@@ -277,10 +277,15 @@ PreprocessValue(JSContext* cx, HandleObject holder, KeyType key, MutableHandleVa
|
||||
|
||||
RootedString keyStr(cx);
|
||||
|
||||
/* Step 2. */
|
||||
if (vp.isObject()) {
|
||||
// Step 2. Modified by BigInt spec 6.1 to check for a toJSON method on the
|
||||
// BigInt prototype when the value is a BigInt.
|
||||
if (vp.isObject() || vp.isBigInt()) {
|
||||
RootedValue toJSON(cx);
|
||||
RootedObject obj(cx, &vp.toObject());
|
||||
RootedObject obj(cx, JS::ToObject(cx, vp));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetProperty(cx, obj, obj, cx->names().toJSON, &toJSON))
|
||||
return false;
|
||||
|
||||
|
||||
+88
-55
@@ -185,16 +185,21 @@ BigInt* BigInt::zero(ExclusiveContext* cx) {
|
||||
return createUninitialized(cx, 0, false);
|
||||
}
|
||||
|
||||
BigInt* BigInt::one(ExclusiveContext* cx) {
|
||||
BigInt* ret = createUninitialized(cx, 1, false);
|
||||
|
||||
if (!ret) {
|
||||
BigInt* BigInt::createFromDigit(ExclusiveContext* cx, Digit d, bool isNegative) {
|
||||
MOZ_ASSERT(d != 0);
|
||||
BigInt* res = createUninitialized(cx, 1, isNegative);
|
||||
if (!res) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret->setDigit(0, 1);
|
||||
res->setDigit(0, d);
|
||||
return res;
|
||||
}
|
||||
|
||||
return ret;
|
||||
BigInt* BigInt::one(ExclusiveContext* cx) { return createFromDigit(cx, 1, false); }
|
||||
|
||||
BigInt* BigInt::negativeOne(ExclusiveContext* cx) {
|
||||
return createFromDigit(cx, 1, true);
|
||||
}
|
||||
|
||||
BigInt* BigInt::neg(ExclusiveContext* cx, HandleBigInt x) {
|
||||
@@ -977,21 +982,26 @@ BigInt* BigInt::absoluteAddOne(ExclusiveContext* cx, HandleBigInt x,
|
||||
return destructivelyTrimHighZeroDigits(cx, result);
|
||||
}
|
||||
|
||||
// Like the above, but you can specify that the allocated result should have
|
||||
// length `resultLength`, which must be at least as large as `x->digitLength()`.
|
||||
// The result will be unsigned.
|
||||
BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
|
||||
unsigned resultLength) {
|
||||
bool resultNegative) {
|
||||
MOZ_ASSERT(!x->isZero());
|
||||
MOZ_ASSERT(resultLength >= x->digitLength());
|
||||
bool resultNegative = false;
|
||||
RootedBigInt result(cx,
|
||||
createUninitialized(cx, resultLength, resultNegative));
|
||||
|
||||
unsigned length = x->digitLength();
|
||||
|
||||
if (length == 1) {
|
||||
Digit d = x->digit(0);
|
||||
if (d == 1) {
|
||||
// Ignore resultNegative.
|
||||
return zero(cx);
|
||||
}
|
||||
return createFromDigit(cx, d - 1, resultNegative);
|
||||
}
|
||||
|
||||
RootedBigInt result(cx, createUninitialized(cx, length, resultNegative));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned length = x->digitLength();
|
||||
Digit borrow = 1;
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
Digit newBorrow = 0;
|
||||
@@ -999,13 +1009,36 @@ BigInt* BigInt::absoluteSubOne(ExclusiveContext* cx, HandleBigInt x,
|
||||
borrow = newBorrow;
|
||||
}
|
||||
MOZ_ASSERT(!borrow);
|
||||
for (unsigned i = length; i < resultLength; i++) {
|
||||
result->setDigit(i, 0);
|
||||
}
|
||||
|
||||
return destructivelyTrimHighZeroDigits(cx, result);
|
||||
}
|
||||
|
||||
BigInt* BigInt::inc(ExclusiveContext* cx, HandleBigInt x) {
|
||||
if (x->isZero()) {
|
||||
return one(cx);
|
||||
}
|
||||
|
||||
bool isNegative = x->isNegative();
|
||||
if (isNegative) {
|
||||
return absoluteSubOne(cx, x, isNegative);
|
||||
}
|
||||
|
||||
return absoluteAddOne(cx, x, isNegative);
|
||||
}
|
||||
|
||||
BigInt* BigInt::dec(ExclusiveContext* cx, HandleBigInt x) {
|
||||
if (x->isZero()) {
|
||||
return negativeOne(cx);
|
||||
}
|
||||
|
||||
bool isNegative = x->isNegative();
|
||||
if (isNegative) {
|
||||
return absoluteAddOne(cx, x, isNegative);
|
||||
}
|
||||
|
||||
return absoluteSubOne(cx, x, isNegative);
|
||||
}
|
||||
|
||||
// Lookup table for the maximum number of bits required per character of a
|
||||
// base-N string representation of a number. To increase accuracy, the array
|
||||
// value is the actual value multiplied by 32. To generate this table:
|
||||
@@ -1569,13 +1602,7 @@ BigInt* BigInt::createFromUint64(ExclusiveContext* cx, uint64_t n) {
|
||||
return res;
|
||||
}
|
||||
|
||||
BigInt* res = createUninitialized(cx, 1, isNegative);
|
||||
if (!res) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res->setDigit(0, n);
|
||||
return res;
|
||||
return createFromDigit(cx, n, isNegative);
|
||||
}
|
||||
|
||||
BigInt* BigInt::createFromInt64(ExclusiveContext* cx, int64_t n) {
|
||||
@@ -1770,12 +1797,7 @@ BigInt* BigInt::mod(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
return zero(cx);
|
||||
}
|
||||
|
||||
BigInt* remainder = createUninitialized(cx, 1, x->isNegative());
|
||||
if (!remainder) {
|
||||
return nullptr;
|
||||
}
|
||||
remainder->setDigit(0, remainderDigit);
|
||||
return remainder;
|
||||
return createFromDigit(cx, remainderDigit, x->isNegative());
|
||||
} else {
|
||||
RootedBigInt remainder(cx);
|
||||
if (!absoluteDivWithBigIntDivisor(cx, x, y, Nothing(), Some(&remainder),
|
||||
@@ -1930,15 +1952,7 @@ BigInt* BigInt::lshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt
|
||||
}
|
||||
|
||||
BigInt* BigInt::rshByMaximum(ExclusiveContext* cx, bool isNegative) {
|
||||
if (isNegative) {
|
||||
RootedBigInt negativeOne(cx, createUninitialized(cx, 1, isNegative));
|
||||
if (!negativeOne) {
|
||||
return nullptr;
|
||||
}
|
||||
negativeOne->setDigit(0, 1);
|
||||
return negativeOne;
|
||||
}
|
||||
return zero(cx);
|
||||
return isNegative ? negativeOne(cx) : zero(cx);
|
||||
}
|
||||
|
||||
BigInt* BigInt::rshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
@@ -2049,14 +2063,13 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
}
|
||||
|
||||
if (x->isNegative() && y->isNegative()) {
|
||||
int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
|
||||
// (-x) & (-y) == ~(x-1) & ~(y-1) == ~((x-1) | (y-1))
|
||||
// == -(((x-1) | (y-1)) + 1)
|
||||
RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
|
||||
RootedBigInt x1(cx, absoluteSubOne(cx, x));
|
||||
if (!x1) {
|
||||
return nullptr;
|
||||
}
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||
if (!y1) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2072,7 +2085,7 @@ BigInt* BigInt::bitAnd(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
HandleBigInt& pos = x->isNegative() ? y : x;
|
||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||
|
||||
RootedBigInt neg1(cx, absoluteSubOne(cx, neg, neg->digitLength()));
|
||||
RootedBigInt neg1(cx, absoluteSubOne(cx, neg));
|
||||
if (!neg1) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2096,27 +2109,24 @@ BigInt* BigInt::bitXor(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
}
|
||||
|
||||
if (x->isNegative() && y->isNegative()) {
|
||||
int resultLength = std::max(x->digitLength(), y->digitLength());
|
||||
|
||||
// (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1)
|
||||
RootedBigInt x1(cx, absoluteSubOne(cx, x, resultLength));
|
||||
RootedBigInt x1(cx, absoluteSubOne(cx, x));
|
||||
if (!x1) {
|
||||
return nullptr;
|
||||
}
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||
if (!y1) {
|
||||
return nullptr;
|
||||
}
|
||||
return absoluteXor(cx, x1, y1);
|
||||
}
|
||||
MOZ_ASSERT(x->isNegative() != y->isNegative());
|
||||
int resultLength = std::max(x->digitLength(), y->digitLength()) + 1;
|
||||
|
||||
HandleBigInt& pos = x->isNegative() ? y : x;
|
||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||
|
||||
// x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1)
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, neg));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2138,7 +2148,6 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
return x;
|
||||
}
|
||||
|
||||
unsigned resultLength = std::max(x->digitLength(), y->digitLength());
|
||||
bool resultNegative = x->isNegative() || y->isNegative();
|
||||
|
||||
if (!resultNegative) {
|
||||
@@ -2148,11 +2157,11 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
if (x->isNegative() && y->isNegative()) {
|
||||
// (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1))
|
||||
// == -(((x-1) & (y-1)) + 1)
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, x, resultLength));
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, x));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y, y->digitLength()));
|
||||
RootedBigInt y1(cx, absoluteSubOne(cx, y));
|
||||
if (!y1) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2168,7 +2177,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
HandleBigInt& neg = x->isNegative() ? x : y;
|
||||
|
||||
// x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(((y-1) &~ x) + 1)
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, neg, resultLength));
|
||||
RootedBigInt result(cx, absoluteSubOne(cx, neg));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -2183,7 +2192,7 @@ BigInt* BigInt::bitOr(ExclusiveContext* cx, HandleBigInt x, HandleBigInt y) {
|
||||
BigInt* BigInt::bitNot(ExclusiveContext* cx, HandleBigInt x) {
|
||||
if (x->isNegative()) {
|
||||
// ~(-x) == ~(~(x-1)) == x-1
|
||||
return absoluteSubOne(cx, x, x->digitLength());
|
||||
return absoluteSubOne(cx, x);
|
||||
} else {
|
||||
// ~x == -x-1 == -(x+1)
|
||||
bool resultNegative = true;
|
||||
@@ -2535,6 +2544,30 @@ bool BigInt::neg(ExclusiveContext* cx, HandleValue operand, MutableHandleValue r
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigInt::inc(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
|
||||
MOZ_ASSERT(operand.isBigInt());
|
||||
|
||||
RootedBigInt operandBigInt(cx, operand.toBigInt());
|
||||
BigInt* resBigInt = BigInt::inc(cx, operandBigInt);
|
||||
if (!resBigInt) {
|
||||
return false;
|
||||
}
|
||||
res.setBigInt(resBigInt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigInt::dec(ExclusiveContext* cx, HandleValue operand, MutableHandleValue res) {
|
||||
MOZ_ASSERT(operand.isBigInt());
|
||||
|
||||
RootedBigInt operandBigInt(cx, operand.toBigInt());
|
||||
BigInt* resBigInt = BigInt::dec(cx, operandBigInt);
|
||||
if (!resBigInt) {
|
||||
return false;
|
||||
}
|
||||
res.setBigInt(resBigInt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigInt::lsh(ExclusiveContext* cx, HandleValue lhs, HandleValue rhs,
|
||||
MutableHandleValue res) {
|
||||
if (!ValidBigIntOperands(cx, lhs, rhs)) {
|
||||
|
||||
@@ -97,9 +97,11 @@ class BigInt final : public js::gc::TenuredCell {
|
||||
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
|
||||
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
|
||||
static BigInt* createFromInt64(js::ExclusiveContext* cx, int64_t n);
|
||||
static BigInt* createFromDigit(js::ExclusiveContext* cx, Digit d, bool isNegative);
|
||||
// FIXME: Cache these values.
|
||||
static BigInt* zero(js::ExclusiveContext* cx);
|
||||
static BigInt* one(js::ExclusiveContext* cx);
|
||||
static BigInt* negativeOne(js::ExclusiveContext* cx);
|
||||
|
||||
static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
|
||||
static BigInt* add(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
@@ -109,6 +111,8 @@ class BigInt final : public js::gc::TenuredCell {
|
||||
static BigInt* mod(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
static BigInt* pow(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
static BigInt* neg(js::ExclusiveContext* cx, Handle<BigInt*> x);
|
||||
static BigInt* inc(js::ExclusiveContext* cx, Handle<BigInt*> x);
|
||||
static BigInt* dec(js::ExclusiveContext* cx, Handle<BigInt*> x);
|
||||
static BigInt* lsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
static BigInt* rsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
static BigInt* bitAnd(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
|
||||
@@ -145,6 +149,10 @@ class BigInt final : public js::gc::TenuredCell {
|
||||
MutableHandle<Value> res);
|
||||
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
|
||||
MutableHandle<Value> res);
|
||||
static bool inc(js::ExclusiveContext* cx, Handle<Value> operand,
|
||||
MutableHandle<Value> res);
|
||||
static bool dec(js::ExclusiveContext* cx, Handle<Value> operand,
|
||||
MutableHandle<Value> res);
|
||||
static bool lsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
|
||||
MutableHandle<Value> res);
|
||||
static bool rsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
|
||||
@@ -288,7 +296,7 @@ class BigInt final : public js::gc::TenuredCell {
|
||||
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
|
||||
// |x| != 0.
|
||||
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
|
||||
unsigned resultLength);
|
||||
bool resultNegative = false);
|
||||
|
||||
// Return `a + b`, incrementing `*carry` if the addition overflows.
|
||||
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
|
||||
|
||||
@@ -416,6 +416,46 @@ NegOperation(JSContext* cx, HandleScript script, jsbytecode* pc, MutableHandleVa
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IncOperation(JSContext* cx,
|
||||
MutableHandleValue val,
|
||||
MutableHandleValue res)
|
||||
{
|
||||
int32_t i;
|
||||
if (val.isInt32() && (i = val.toInt32()) != INT32_MAX) {
|
||||
res.setInt32(i + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (val.isNumber()) {
|
||||
res.setNumber(val.toNumber() + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(val.isBigInt(), "+1 only callable on result of JSOP_TONUMERIC");
|
||||
return BigInt::inc(cx, val, res);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
DecOperation(JSContext* cx,
|
||||
MutableHandleValue val,
|
||||
MutableHandleValue res)
|
||||
{
|
||||
int32_t i;
|
||||
if (val.isInt32() && (i = val.toInt32()) != INT32_MIN) {
|
||||
res.setInt32(i - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (val.isNumber()) {
|
||||
res.setNumber(val.toNumber() - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(val.isBigInt(), "-1 only callable on result of JSOP_TONUMERIC");
|
||||
return BigInt::dec(cx, val, res);
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval,
|
||||
MutableHandleValue res)
|
||||
|
||||
@@ -4176,6 +4176,34 @@ CASE(JSOP_IS_CONSTRUCTING)
|
||||
PUSH_MAGIC(JS_IS_CONSTRUCTING);
|
||||
END_CASE(JSOP_IS_CONSTRUCTING)
|
||||
|
||||
CASE(JSOP_INC)
|
||||
{
|
||||
ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
|
||||
MutableHandleValue res = REGS.stackHandleAt(-1);
|
||||
if (!IncOperation(cx, &val, res)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_INC)
|
||||
|
||||
CASE(JSOP_DEC)
|
||||
{
|
||||
ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
|
||||
MutableHandleValue res = REGS.stackHandleAt(-1);
|
||||
if (!DecOperation(cx, &val, res)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_DEC)
|
||||
|
||||
CASE(JSOP_TONUMERIC)
|
||||
{
|
||||
if (!ToNumeric(cx, REGS.stackHandleAt(-1))) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
END_CASE(JSOP_TONUMERIC)
|
||||
|
||||
CASE(JSOP_BIGINT)
|
||||
{
|
||||
PUSH_COPY(script->getConst(GET_UINT32_INDEX(REGS.pc)));
|
||||
|
||||
+28
-5
@@ -2357,7 +2357,33 @@
|
||||
* Operands:
|
||||
* Stack: arg => rval
|
||||
*/ \
|
||||
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pops the numeric value 'val' from the stack, then pushes 'val + 1'.
|
||||
*
|
||||
* Category: Operators
|
||||
* Type: Arithmetic Operators
|
||||
* Operands:
|
||||
* Stack: val => (val + 1)
|
||||
*/ \
|
||||
macro(JSOP_INC, 235, "inc", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pops the numeric value 'val' from the stack, then pushes 'val - 1'.
|
||||
*
|
||||
* Category: Operators
|
||||
* Type: Arithmetic Operators
|
||||
* Operands:
|
||||
* Stack: val => (val - 1)
|
||||
*/ \
|
||||
macro(JSOP_DEC, 236, "dec", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pop 'val' from the stack, then push the result of 'ToNumeric(val)'.
|
||||
* Category: Operators
|
||||
* Type: Arithmetic Operators
|
||||
* Operands:
|
||||
* Stack: val => ToNumeric(val)
|
||||
*/ \
|
||||
macro(JSOP_TONUMERIC, 237, "tonumeric", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes a BigInt constant onto the stack.
|
||||
* Category: Literals
|
||||
@@ -2365,15 +2391,12 @@
|
||||
* Operands: uint32_t constIndex
|
||||
* Stack: => val
|
||||
*/ \
|
||||
macro(JSOP_BIGINT, 235, "bigint", NULL, 5, 0, 1, JOF_BIGINT)
|
||||
macro(JSOP_BIGINT, 238, "bigint", NULL, 5, 0, 1, JOF_BIGINT)
|
||||
/*
|
||||
* In certain circumstances it may be useful to "pad out" the opcode space to
|
||||
* a power of two. Use this macro to do so.
|
||||
*/
|
||||
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
|
||||
macro(236) \
|
||||
macro(237) \
|
||||
macro(238) \
|
||||
macro(239) \
|
||||
macro(240) \
|
||||
macro(241) \
|
||||
|
||||
@@ -3245,6 +3245,10 @@ js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
|
||||
return IsNativeFunction(v, Int32Array::class_constructor);
|
||||
case Scalar::Uint32:
|
||||
return IsNativeFunction(v, Uint32Array::class_constructor);
|
||||
case Scalar::BigInt64:
|
||||
return IsNativeFunction(v, BigInt64Array::class_constructor);
|
||||
case Scalar::BigUint64:
|
||||
return IsNativeFunction(v, BigUint64Array::class_constructor);
|
||||
case Scalar::Float32:
|
||||
return IsNativeFunction(v, Float32Array::class_constructor);
|
||||
case Scalar::Float64:
|
||||
|
||||
@@ -2719,6 +2719,38 @@ class BaseCompiler
|
||||
return x0;
|
||||
}
|
||||
|
||||
RegI64 popI64ForSignExtendI64() {
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
need2xI32(specific_edx, specific_eax);
|
||||
// Low on top, high underneath
|
||||
return popI64ToSpecific(RegI64(Register64(specific_edx.reg, specific_eax.reg)));
|
||||
#else
|
||||
return popI64();
|
||||
#endif
|
||||
}
|
||||
|
||||
void signExtendI64_8(RegI64 r) {
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
masm.movsbq(Operand(r.reg.reg), r.reg.reg);
|
||||
#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
|
||||
masm.move8SignExtend(r.reg.low, r.reg.low);
|
||||
signExtendI32ToI64(RegI32(r.reg.low), r);
|
||||
#else
|
||||
MOZ_CRASH("Basecompiler platform hook: signExtendI64_8");
|
||||
#endif
|
||||
}
|
||||
|
||||
void signExtendI64_16(RegI64 r) {
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
masm.movswq(Operand(r.reg.reg), r.reg.reg);
|
||||
#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
|
||||
masm.move16SignExtend(r.reg.low, r.reg.low);
|
||||
signExtendI32ToI64(RegI32(r.reg.low), r);
|
||||
#else
|
||||
MOZ_CRASH("Basecompiler platform hook: signExtendI64_16");
|
||||
#endif
|
||||
}
|
||||
|
||||
void signExtendI32ToI64(RegI32 src, RegI64 dest) {
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
masm.movslq(src.reg, dest.reg.reg);
|
||||
@@ -3707,6 +3739,11 @@ class BaseCompiler
|
||||
template<bool isUnsigned> MOZ_MUST_USE bool emitTruncateF64ToI64();
|
||||
#endif
|
||||
void emitWrapI64ToI32();
|
||||
void emitExtendI32_8();
|
||||
void emitExtendI32_16();
|
||||
void emitExtendI64_8();
|
||||
void emitExtendI64_16();
|
||||
void emitExtendI64_32();
|
||||
void emitExtendI32ToI64();
|
||||
void emitExtendU32ToI64();
|
||||
void emitReinterpretF32AsI32();
|
||||
@@ -4597,6 +4634,48 @@ BaseCompiler::emitWrapI64ToI32()
|
||||
pushI32(i0);
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI32_8()
|
||||
{
|
||||
RegI32 r = popI32();
|
||||
masm.move8SignExtend(r.reg, r.reg);
|
||||
pushI32(r);
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI32_16()
|
||||
{
|
||||
RegI32 r = popI32();
|
||||
masm.move16SignExtend(r.reg, r.reg);
|
||||
pushI32(r);
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI64_8()
|
||||
{
|
||||
RegI64 r = popI64ForSignExtendI64();
|
||||
signExtendI64_8(r);
|
||||
pushI64(r);
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI64_16()
|
||||
{
|
||||
RegI64 r = popI64ForSignExtendI64();
|
||||
signExtendI64_16(r);
|
||||
pushI64(r);
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI64_32()
|
||||
{
|
||||
RegI64 x0 = popI64ForSignExtendI64();
|
||||
RegI32 r0 = RegI32(lowPart(x0));
|
||||
signExtendI32ToI64(r0, x0);
|
||||
pushI64(x0);
|
||||
// Note: no need to free r0, since it is part of x0
|
||||
}
|
||||
|
||||
void
|
||||
BaseCompiler::emitExtendI32ToI64()
|
||||
{
|
||||
@@ -7166,6 +7245,18 @@ BaseCompiler::emitBody()
|
||||
case uint16_t(Op::I32AtomicsExchange):
|
||||
MOZ_CRASH("Unimplemented Atomics");
|
||||
|
||||
// Sign extensions
|
||||
case uint16_t(Op::I32Extend8S):
|
||||
CHECK_NEXT(emitConversion(emitExtendI32_8, ValType::I32, ValType::I32));
|
||||
case uint16_t(Op::I32Extend16S):
|
||||
CHECK_NEXT(emitConversion(emitExtendI32_16, ValType::I32, ValType::I32));
|
||||
case uint16_t(Op::I64Extend8S):
|
||||
CHECK_NEXT(emitConversion(emitExtendI64_8, ValType::I64, ValType::I64));
|
||||
case uint16_t(Op::I64Extend16S):
|
||||
CHECK_NEXT(emitConversion(emitExtendI64_16, ValType::I64, ValType::I64));
|
||||
case uint16_t(Op::I64Extend32S):
|
||||
CHECK_NEXT(emitConversion(emitExtendI64_32, ValType::I64, ValType::I64));
|
||||
|
||||
// Memory Related
|
||||
case uint16_t(Op::GrowMemory):
|
||||
CHECK_NEXT(emitGrowMemory());
|
||||
|
||||
@@ -310,6 +310,13 @@ enum class Op
|
||||
F32ReinterpretI32 = 0xbe,
|
||||
F64ReinterpretI64 = 0xbf,
|
||||
|
||||
// Sign extension
|
||||
I32Extend8S = 0xc0,
|
||||
I32Extend16S = 0xc1,
|
||||
I64Extend8S = 0xc2,
|
||||
I64Extend16S = 0xc3,
|
||||
I64Extend32S = 0xc4,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// The rest of these operators are currently only emitted internally when
|
||||
// compiling asm.js and are rejected by wasm validation.
|
||||
|
||||
@@ -279,6 +279,11 @@ wasm::Classify(Op op)
|
||||
case Op::F32x4fromInt8x16Bits:
|
||||
case Op::F32x4fromInt16x8Bits:
|
||||
case Op::F32x4fromInt32x4Bits:
|
||||
case Op::I32Extend8S:
|
||||
case Op::I32Extend16S:
|
||||
case Op::I64Extend8S:
|
||||
case Op::I64Extend16S:
|
||||
case Op::I64Extend32S:
|
||||
return OpKind::Conversion;
|
||||
case Op::I32Load8S:
|
||||
case Op::I32Load8U:
|
||||
|
||||
@@ -1300,6 +1300,17 @@ AstDecodeExpr(AstDecodeContext& c)
|
||||
if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(Op::I32Extend8S):
|
||||
case uint16_t(Op::I32Extend16S):
|
||||
if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(Op::I64Extend8S):
|
||||
case uint16_t(Op::I64Extend16S):
|
||||
case uint16_t(Op::I64Extend32S):
|
||||
if (!AstDecodeConversion(c, ValType::I64, ValType::I64, Op(op)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(Op::I32Load8S):
|
||||
case uint16_t(Op::I32Load8U):
|
||||
if (!AstDecodeLoad(c, ValType::I32, 1, Op(op)))
|
||||
|
||||
@@ -713,6 +713,11 @@ RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv)
|
||||
case Op::F64ConvertUI64: opStr = "f64.convert_u/i64"; break;
|
||||
case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
|
||||
case Op::F64PromoteF32: opStr = "f64.promote/f32"; break;
|
||||
case Op::I32Extend8S: opStr = "i32.extend8_s"; break;
|
||||
case Op::I32Extend16S: opStr = "i32.extend16_s"; break;
|
||||
case Op::I64Extend8S: opStr = "i64.extend8_s"; break;
|
||||
case Op::I64Extend16S: opStr = "i64.extend16_s"; break;
|
||||
case Op::I64Extend32S: opStr = "i64.extend32_s"; break;
|
||||
case Op::I32Eqz: opStr = "i32.eqz"; break;
|
||||
case Op::I64Eqz: opStr = "i64.eqz"; break;
|
||||
default: return Fail(c, "unexpected conversion operator");
|
||||
|
||||
@@ -347,6 +347,13 @@ DecodeFunctionBodyExprs(FunctionDecoder& f)
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
|
||||
case uint16_t(Op::F64PromoteF32):
|
||||
CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
|
||||
case uint16_t(Op::I32Extend8S):
|
||||
case uint16_t(Op::I32Extend16S):
|
||||
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
|
||||
case uint16_t(Op::I64Extend8S):
|
||||
case uint16_t(Op::I64Extend16S):
|
||||
case uint16_t(Op::I64Extend32S):
|
||||
CHECK(f.iter().readConversion(ValType::I64, ValType::I64, nullptr));
|
||||
case uint16_t(Op::I32Load8S):
|
||||
case uint16_t(Op::I32Load8U):
|
||||
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
|
||||
|
||||
@@ -666,6 +666,41 @@ class FunctionCompiler
|
||||
return ins;
|
||||
}
|
||||
|
||||
MDefinition* signExtend(MDefinition* op, uint32_t srcSize, uint32_t targetSize)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
MInstruction* ins;
|
||||
switch (targetSize) {
|
||||
case 4: {
|
||||
MSignExtendInt32::Mode mode;
|
||||
switch (srcSize) {
|
||||
case 1: mode = MSignExtendInt32::Byte; break;
|
||||
case 2: mode = MSignExtendInt32::Half; break;
|
||||
default: MOZ_CRASH("Bad sign extension");
|
||||
}
|
||||
ins = MSignExtendInt32::New(alloc(), op, mode);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
MSignExtendInt64::Mode mode;
|
||||
switch (srcSize) {
|
||||
case 1: mode = MSignExtendInt64::Byte; break;
|
||||
case 2: mode = MSignExtendInt64::Half; break;
|
||||
case 4: mode = MSignExtendInt64::Word; break;
|
||||
default: MOZ_CRASH("Bad sign extension");
|
||||
}
|
||||
ins = MSignExtendInt64::New(alloc(), op, mode);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_CRASH("Bad sign extension");
|
||||
}
|
||||
}
|
||||
curBlock_->add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
||||
MDefinition* convertI64ToFloatingPoint(MDefinition* op, MIRType type, bool isUnsigned)
|
||||
{
|
||||
if (inDeadCode())
|
||||
@@ -2181,6 +2216,18 @@ EmitTruncate(FunctionCompiler& f, ValType operandType, ValType resultType,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitSignExtend(FunctionCompiler& f, uint32_t srcSize, uint32_t targetSize)
|
||||
{
|
||||
MDefinition* input;
|
||||
ValType type = targetSize == 4 ? ValType::I32 : ValType::I64;
|
||||
if (!f.iter().readConversion(type, type, &input))
|
||||
return false;
|
||||
|
||||
f.iter().setResult(f.signExtend(input, srcSize, targetSize));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitExtendI32(FunctionCompiler& f, bool isUnsigned)
|
||||
{
|
||||
@@ -3480,6 +3527,18 @@ EmitExpr(FunctionCompiler& f)
|
||||
case Op::F64ReinterpretI64:
|
||||
return EmitReinterpret(f, ValType::F64, ValType::I64, MIRType::Double);
|
||||
|
||||
// Sign extensions
|
||||
case Op::I32Extend8S:
|
||||
return EmitSignExtend(f, 1, 4);
|
||||
case Op::I32Extend16S:
|
||||
return EmitSignExtend(f, 2, 4);
|
||||
case Op::I64Extend8S:
|
||||
return EmitSignExtend(f, 1, 8);
|
||||
case Op::I64Extend16S:
|
||||
return EmitSignExtend(f, 2, 8);
|
||||
case Op::I64Extend32S:
|
||||
return EmitSignExtend(f, 4, 8);
|
||||
|
||||
// Comparisons
|
||||
case Op::I32Eq:
|
||||
return EmitComparison(f, ValType::I32, JSOP_EQ, MCompare::Compare_Int32);
|
||||
|
||||
@@ -925,6 +925,16 @@ WasmTokenStream::next()
|
||||
case 'e':
|
||||
if (consume(u"eq"))
|
||||
return WasmToken(WasmToken::ComparisonOpcode, Op::F32Eq, begin, cur_);
|
||||
if (consume(u"extend8_s"))
|
||||
return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend8S, begin, cur_);
|
||||
if (consume(u"extend16_s"))
|
||||
return WasmToken(WasmToken::ConversionOpcode, Op::I32Extend16S, begin, cur_);
|
||||
if (consume(u"extend8_s"))
|
||||
return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend8S, begin, cur_);
|
||||
if (consume(u"extend16_s"))
|
||||
return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend16S, begin, cur_);
|
||||
if (consume(u"extend32_s"))
|
||||
return WasmToken(WasmToken::ConversionOpcode, Op::I64Extend32S, begin, cur_);
|
||||
break;
|
||||
case 'f':
|
||||
if (consume(u"floor"))
|
||||
|
||||
@@ -253,11 +253,11 @@ static int ReadHuffmanCodeLengths(
|
||||
int symbol;
|
||||
int max_symbol;
|
||||
int prev_code_len = DEFAULT_CODE_LENGTH;
|
||||
HuffmanCode table[1 << LENGTHS_TABLE_BITS];
|
||||
HuffmanTables tables;
|
||||
|
||||
if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
|
||||
code_length_code_lengths,
|
||||
NUM_CODE_LENGTH_CODES)) {
|
||||
if (!VP8LHuffmanTablesAllocate(1 << LENGTHS_TABLE_BITS, &tables) ||
|
||||
!VP8LBuildHuffmanTable(&tables, LENGTHS_TABLE_BITS,
|
||||
code_length_code_lengths, NUM_CODE_LENGTH_CODES)) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ static int ReadHuffmanCodeLengths(
|
||||
int code_len;
|
||||
if (max_symbol-- == 0) break;
|
||||
VP8LFillBitWindow(br);
|
||||
p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
|
||||
p = &tables.curr_segment->start[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
|
||||
VP8LSetBitPos(br, br->bit_pos_ + p->bits);
|
||||
code_len = p->value;
|
||||
if (code_len < kCodeLengthLiterals) {
|
||||
@@ -300,6 +300,7 @@ static int ReadHuffmanCodeLengths(
|
||||
ok = 1;
|
||||
|
||||
End:
|
||||
VP8LHuffmanTablesDeallocate(&tables);
|
||||
if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
|
||||
return ok;
|
||||
}
|
||||
@@ -307,7 +308,8 @@ static int ReadHuffmanCodeLengths(
|
||||
// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
|
||||
// tree.
|
||||
static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
|
||||
int* const code_lengths, HuffmanCode* const table) {
|
||||
int* const code_lengths,
|
||||
HuffmanTables* const table) {
|
||||
int ok = 0;
|
||||
int size = 0;
|
||||
VP8LBitReader* const br = &dec->br_;
|
||||
@@ -362,8 +364,7 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
VP8LMetadata* const hdr = &dec->hdr_;
|
||||
uint32_t* huffman_image = NULL;
|
||||
HTreeGroup* htree_groups = NULL;
|
||||
HuffmanCode* huffman_tables = NULL;
|
||||
HuffmanCode* huffman_table = NULL;
|
||||
HuffmanTables* huffman_tables = &hdr->huffman_tables_;
|
||||
int num_htree_groups = 1;
|
||||
int num_htree_groups_max = 1;
|
||||
int max_alphabet_size = 0;
|
||||
@@ -372,6 +373,10 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
int* mapping = NULL;
|
||||
int ok = 0;
|
||||
|
||||
// Check the table has been 0 initialized (through InitMetadata).
|
||||
assert(huffman_tables->root.start == NULL);
|
||||
assert(huffman_tables->curr_segment == NULL);
|
||||
|
||||
if (allow_recursion && VP8LReadBits(br, 1)) {
|
||||
// use meta Huffman codes.
|
||||
const int huffman_precision = VP8LReadBits(br, 3) + 2;
|
||||
@@ -434,16 +439,15 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
|
||||
code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
|
||||
sizeof(*code_lengths));
|
||||
huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
|
||||
sizeof(*huffman_tables));
|
||||
htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
|
||||
|
||||
if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
|
||||
if (htree_groups == NULL || code_lengths == NULL ||
|
||||
!VP8LHuffmanTablesAllocate(num_htree_groups * table_size,
|
||||
huffman_tables)) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
huffman_table = huffman_tables;
|
||||
for (i = 0; i < num_htree_groups_max; ++i) {
|
||||
// If the index "i" is unused in the Huffman image, just make sure the
|
||||
// coefficients are valid but do not store them.
|
||||
@@ -468,19 +472,20 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
int max_bits = 0;
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
htrees[j] = huffman_table;
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += (1 << color_cache_bits);
|
||||
}
|
||||
size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
|
||||
size =
|
||||
ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables);
|
||||
htrees[j] = huffman_tables->curr_segment->curr_table;
|
||||
if (size == 0) {
|
||||
goto Error;
|
||||
}
|
||||
if (is_trivial_literal && kLiteralMap[j] == 1) {
|
||||
is_trivial_literal = (huffman_table->bits == 0);
|
||||
is_trivial_literal = (htrees[j]->bits == 0);
|
||||
}
|
||||
total_size += huffman_table->bits;
|
||||
huffman_table += size;
|
||||
total_size += htrees[j]->bits;
|
||||
huffman_tables->curr_segment->curr_table += size;
|
||||
if (j <= ALPHA) {
|
||||
int local_max_bits = code_lengths[0];
|
||||
int k;
|
||||
@@ -515,14 +520,13 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
hdr->huffman_image_ = huffman_image;
|
||||
hdr->num_htree_groups_ = num_htree_groups;
|
||||
hdr->htree_groups_ = htree_groups;
|
||||
hdr->huffman_tables_ = huffman_tables;
|
||||
|
||||
Error:
|
||||
WebPSafeFree(code_lengths);
|
||||
WebPSafeFree(mapping);
|
||||
if (!ok) {
|
||||
WebPSafeFree(huffman_image);
|
||||
WebPSafeFree(huffman_tables);
|
||||
VP8LHuffmanTablesDeallocate(huffman_tables);
|
||||
VP8LHtreeGroupsFree(htree_groups);
|
||||
}
|
||||
return ok;
|
||||
@@ -1358,7 +1362,7 @@ static void ClearMetadata(VP8LMetadata* const hdr) {
|
||||
assert(hdr != NULL);
|
||||
|
||||
WebPSafeFree(hdr->huffman_image_);
|
||||
WebPSafeFree(hdr->huffman_tables_);
|
||||
VP8LHuffmanTablesDeallocate(&hdr->huffman_tables_);
|
||||
VP8LHtreeGroupsFree(hdr->htree_groups_);
|
||||
VP8LColorCacheClear(&hdr->color_cache_);
|
||||
VP8LColorCacheClear(&hdr->saved_color_cache_);
|
||||
@@ -1673,7 +1677,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) {
|
||||
|
||||
if (dec == NULL) return 0;
|
||||
|
||||
assert(dec->hdr_.huffman_tables_ != NULL);
|
||||
assert(dec->hdr_.huffman_tables_.root.start != NULL);
|
||||
assert(dec->hdr_.htree_groups_ != NULL);
|
||||
assert(dec->hdr_.num_htree_groups_ > 0);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ typedef struct {
|
||||
uint32_t* huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup* htree_groups_;
|
||||
HuffmanCode* huffman_tables_;
|
||||
HuffmanTables huffman_tables_;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct VP8LDecoder VP8LDecoder;
|
||||
|
||||
@@ -177,21 +177,24 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
if (num_open < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (root_table == NULL) continue;
|
||||
for (; count[len] > 0; --count[len]) {
|
||||
HuffmanCode code;
|
||||
if ((key & mask) != low) {
|
||||
table += table_size;
|
||||
if (root_table != NULL) table += table_size;
|
||||
table_bits = NextTableBitSize(count, len, root_bits);
|
||||
table_size = 1 << table_bits;
|
||||
total_size += table_size;
|
||||
low = key & mask;
|
||||
root_table[low].bits = (uint8_t)(table_bits + root_bits);
|
||||
root_table[low].value = (uint16_t)((table - root_table) - low);
|
||||
if (root_table != NULL) {
|
||||
root_table[low].bits = (uint8_t)(table_bits + root_bits);
|
||||
root_table[low].value = (uint16_t)((table - root_table) - low);
|
||||
}
|
||||
}
|
||||
if (root_table != NULL) {
|
||||
code.bits = (uint8_t)(len - root_bits);
|
||||
code.value = (uint16_t)sorted[symbol++];
|
||||
ReplicateValue(&table[key >> root_bits], step, table_size, code);
|
||||
}
|
||||
code.bits = (uint8_t)(len - root_bits);
|
||||
code.value = (uint16_t)sorted[symbol++];
|
||||
ReplicateValue(&table[key >> root_bits], step, table_size, code);
|
||||
key = GetNextKey(key, len);
|
||||
}
|
||||
}
|
||||
@@ -211,25 +214,83 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
|
||||
// Cut-off value for switching between heap and stack allocation.
|
||||
#define SORTED_SIZE_CUTOFF 512
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size) {
|
||||
int total_size;
|
||||
const int total_size =
|
||||
BuildHuffmanTable(NULL, root_bits, code_lengths, code_lengths_size, NULL);
|
||||
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
|
||||
if (root_table == NULL) {
|
||||
total_size = BuildHuffmanTable(NULL, root_bits,
|
||||
code_lengths, code_lengths_size, NULL);
|
||||
} else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
|
||||
if (total_size == 0 || root_table == NULL) return total_size;
|
||||
|
||||
if (root_table->curr_segment->curr_table + total_size >=
|
||||
root_table->curr_segment->start + root_table->curr_segment->size) {
|
||||
// If 'root_table' does not have enough memory, allocate a new segment.
|
||||
// The available part of root_table->curr_segment is left unused because we
|
||||
// need a contiguous buffer.
|
||||
const int segment_size = root_table->curr_segment->size;
|
||||
struct HuffmanTablesSegment* next =
|
||||
(HuffmanTablesSegment*)WebPSafeMalloc(1, sizeof(*next));
|
||||
if (next == NULL) return 0;
|
||||
// Fill the new segment.
|
||||
// We need at least 'total_size' but if that value is small, it is better to
|
||||
// allocate a big chunk to prevent more allocations later. 'segment_size' is
|
||||
// therefore chosen (any other arbitrary value could be chosen).
|
||||
next->size = total_size > segment_size ? total_size : segment_size;
|
||||
next->start =
|
||||
(HuffmanCode*)WebPSafeMalloc(next->size, sizeof(*next->start));
|
||||
if (next->start == NULL) {
|
||||
WebPSafeFree(next);
|
||||
return 0;
|
||||
}
|
||||
next->curr_table = next->start;
|
||||
next->next = NULL;
|
||||
// Point to the new segment.
|
||||
root_table->curr_segment->next = next;
|
||||
root_table->curr_segment = next;
|
||||
}
|
||||
if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
|
||||
// use local stack-allocated array.
|
||||
uint16_t sorted[SORTED_SIZE_CUTOFF];
|
||||
total_size = BuildHuffmanTable(root_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
} else { // rare case. Use heap allocation.
|
||||
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
} else { // rare case. Use heap allocation.
|
||||
uint16_t* const sorted =
|
||||
(uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
|
||||
if (sorted == NULL) return 0;
|
||||
total_size = BuildHuffmanTable(root_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
BuildHuffmanTable(root_table->curr_segment->curr_table, root_bits,
|
||||
code_lengths, code_lengths_size, sorted);
|
||||
WebPSafeFree(sorted);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables) {
|
||||
// Have 'segment' point to the first segment for now, 'root'.
|
||||
HuffmanTablesSegment* const root = &huffman_tables->root;
|
||||
huffman_tables->curr_segment = root;
|
||||
// Allocate root.
|
||||
root->start = (HuffmanCode*)WebPSafeMalloc(size, sizeof(*root->start));
|
||||
if (root->start == NULL) return 0;
|
||||
root->curr_table = root->start;
|
||||
root->next = NULL;
|
||||
root->size = size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables) {
|
||||
HuffmanTablesSegment *current, *next;
|
||||
if (huffman_tables == NULL) return;
|
||||
// Free the root node.
|
||||
current = &huffman_tables->root;
|
||||
next = current->next;
|
||||
WebPSafeFree(current->start);
|
||||
current->start = NULL;
|
||||
current->next = NULL;
|
||||
current = next;
|
||||
// Free the following nodes.
|
||||
while (current != NULL) {
|
||||
next = current->next;
|
||||
WebPSafeFree(current->start);
|
||||
WebPSafeFree(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,29 @@ typedef struct {
|
||||
// or non-literal symbol otherwise
|
||||
} HuffmanCode32;
|
||||
|
||||
// Contiguous memory segment of HuffmanCodes.
|
||||
typedef struct HuffmanTablesSegment {
|
||||
HuffmanCode* start;
|
||||
// Pointer to where we are writing into the segment. Starts at 'start' and
|
||||
// cannot go beyond 'start' + 'size'.
|
||||
HuffmanCode* curr_table;
|
||||
// Pointer to the next segment in the chain.
|
||||
struct HuffmanTablesSegment* next;
|
||||
int size;
|
||||
} HuffmanTablesSegment;
|
||||
|
||||
// Chained memory segments of HuffmanCodes.
|
||||
typedef struct HuffmanTables {
|
||||
HuffmanTablesSegment root;
|
||||
// Currently processed segment. At first, this is 'root'.
|
||||
HuffmanTablesSegment* curr_segment;
|
||||
} HuffmanTables;
|
||||
|
||||
// Allocates a HuffmanTables with 'size' contiguous HuffmanCodes. Returns 0 on
|
||||
// memory allocation error, 1 otherwise.
|
||||
int VP8LHuffmanTablesAllocate(int size, HuffmanTables* huffman_tables);
|
||||
void VP8LHuffmanTablesDeallocate(HuffmanTables* const huffman_tables);
|
||||
|
||||
#define HUFFMAN_PACKED_BITS 6
|
||||
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
|
||||
|
||||
@@ -78,9 +101,7 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
||||
// the huffman table.
|
||||
// Returns built table size or 0 in case of error (invalid tree or
|
||||
// memory error).
|
||||
// If root_table is NULL, it returns 0 if a lookup cannot be built, something
|
||||
// > 0 otherwise (but not the table size).
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
int VP8LBuildHuffmanTable(HuffmanTables* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -755,13 +755,14 @@ TransactionObserver::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext
|
||||
nsIInputStream *aStream, uint64_t aOffset, uint32_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
uint64_t newLen = aCount + mWKResponse.Length();
|
||||
uint64_t oldLen = static_cast<uint64_t>(mWKResponse.Length());
|
||||
uint64_t newLen = static_cast<uint64_t>(aCount) + oldLen;
|
||||
if (newLen < MAX_WK) {
|
||||
char *startByte = reinterpret_cast<char *>(mWKResponse.BeginWriting()) + mWKResponse.Length();
|
||||
char *startByte = reinterpret_cast<char *>(mWKResponse.BeginWriting()) + oldLen;
|
||||
uint32_t amtRead;
|
||||
if (NS_SUCCEEDED(aStream->Read(startByte, aCount, &amtRead))) {
|
||||
MOZ_ASSERT(mWKResponse.Length() + amtRead < MAX_WK);
|
||||
mWKResponse.SetLength(mWKResponse.Length() + amtRead);
|
||||
MOZ_ASSERT(oldLen + amtRead < MAX_WK);
|
||||
mWKResponse.SetLength(oldLen + amtRead);
|
||||
LOG(("TransactionObserver onDataAvailable %p read %d of .wk [%d]\n",
|
||||
this, amtRead, mWKResponse.Length()));
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user