mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 17:31:47 +09:00
Issue #1894 - Part 4: Implement IonMonkey support for nullish coalescing
Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1566141
This commit is contained in:
parent
f282a4c05f
commit
5812721809
12 changed files with 123 additions and 16 deletions
|
|
@ -1252,6 +1252,25 @@ BaselineCompiler::emit_JSOP_IFNE()
|
|||
return emitTest(true);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_COALESCE() {
|
||||
// COALESCE leaves the original value on the stack.
|
||||
frame.syncStack(0);
|
||||
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
|
||||
|
||||
Label undefinedOrNull;
|
||||
|
||||
masm.branchTestUndefined(Assembler::Equal, R0, &undefinedOrNull);
|
||||
masm.branchTestNull(Assembler::Equal, R0, &undefinedOrNull);
|
||||
|
||||
jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
|
||||
masm.jump(labelOf(target));
|
||||
|
||||
masm.bind(&undefinedOrNull);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emitAndOr(bool branchIfTrue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ namespace jit {
|
|||
_(JSOP_GOTO) \
|
||||
_(JSOP_IFEQ) \
|
||||
_(JSOP_IFNE) \
|
||||
_(JSOP_COALESCE) \
|
||||
_(JSOP_AND) \
|
||||
_(JSOP_OR) \
|
||||
_(JSOP_NOT) \
|
||||
|
|
|
|||
|
|
@ -11517,6 +11517,24 @@ CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins)
|
|||
testObjectEmitBranch(Assembler::Equal, value, ins->ifTrue(), ins->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins)
|
||||
{
|
||||
Register output = ToRegister(ins->output());
|
||||
ValueOperand value = ToValue(ins, LIsNullOrUndefined::Input);
|
||||
|
||||
Label isNotNull, done;
|
||||
masm.branchTestNull(Assembler::NotEqual, value, &isNotNull);
|
||||
|
||||
masm.move32(Imm32(1), output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isNotNull);
|
||||
masm.testUndefinedSet(Assembler::Equal, value, output);
|
||||
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::loadOutermostJSScript(Register reg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
|
||||
void visitIsObject(LIsObject* lir);
|
||||
void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
|
||||
void visitIsNullOrUndefined(LIsNullOrUndefined* ins);
|
||||
void visitHasClass(LHasClass* lir);
|
||||
void visitGuardToClass(LGuardToClass* lir);
|
||||
void visitWasmParameter(LWasmParameter* lir);
|
||||
|
|
|
|||
|
|
@ -276,10 +276,10 @@ IonBuilder::CFGState::IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* t
|
|||
}
|
||||
|
||||
IonBuilder::CFGState
|
||||
IonBuilder::CFGState::AndOr(jsbytecode* join, MBasicBlock* lhs)
|
||||
IonBuilder::CFGState::Logical(jsbytecode* join, MBasicBlock* lhs)
|
||||
{
|
||||
CFGState state;
|
||||
state.state = AND_OR;
|
||||
state.state = LOGICAL;
|
||||
state.stopAt = join;
|
||||
state.branch.ifFalse = lhs;
|
||||
state.branch.test = nullptr;
|
||||
|
|
@ -1737,9 +1737,10 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_TOSTRING:
|
||||
return jsop_tostring();
|
||||
|
||||
case JSOP_COALESCE:
|
||||
case JSOP_AND:
|
||||
case JSOP_OR:
|
||||
return jsop_andor(op);
|
||||
return jsop_logical(op);
|
||||
|
||||
case JSOP_DEFVAR:
|
||||
return jsop_defvar(GET_UINT32_INDEX(pc));
|
||||
|
|
@ -2345,8 +2346,8 @@ IonBuilder::processCfgEntry(CFGState& state)
|
|||
case CFGState::COND_SWITCH_BODY:
|
||||
return processCondSwitchBody(state);
|
||||
|
||||
case CFGState::AND_OR:
|
||||
return processAndOrEnd(state);
|
||||
case CFGState::LOGICAL:
|
||||
return processLogicalEnd(state);
|
||||
|
||||
case CFGState::LABEL:
|
||||
return processLabelEnd(state);
|
||||
|
|
@ -2948,7 +2949,7 @@ IonBuilder::processNextTableSwitchCase(CFGState& state)
|
|||
}
|
||||
|
||||
IonBuilder::ControlStatus
|
||||
IonBuilder::processAndOrEnd(CFGState& state)
|
||||
IonBuilder::processLogicalEnd(CFGState& state)
|
||||
{
|
||||
MOZ_ASSERT(current);
|
||||
MBasicBlock* lhs = state.branch.ifFalse;
|
||||
|
|
@ -4413,9 +4414,9 @@ IonBuilder::processCondSwitchBody(CFGState& state)
|
|||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_andor(JSOp op)
|
||||
IonBuilder::jsop_logical(JSOp op)
|
||||
{
|
||||
MOZ_ASSERT(op == JSOP_AND || op == JSOP_OR);
|
||||
MOZ_ASSERT(op == JSOP_AND || op == JSOP_OR || op == JSOP_COALESCE);
|
||||
|
||||
jsbytecode* rhsStart = pc + CodeSpec[op].length;
|
||||
jsbytecode* joinStart = pc + GetJumpOffset(pc);
|
||||
|
|
@ -4429,9 +4430,24 @@ IonBuilder::jsop_andor(JSOp op)
|
|||
if (!evalLhs || !evalRhs)
|
||||
return false;
|
||||
|
||||
MTest* test = (op == JSOP_AND)
|
||||
? newTest(lhs, evalRhs, evalLhs)
|
||||
: newTest(lhs, evalLhs, evalRhs);
|
||||
MTest* test;
|
||||
switch (op) {
|
||||
case JSOP_COALESCE: {
|
||||
MIsNullOrUndefined* isNullOrUndefined =
|
||||
MIsNullOrUndefined::New(alloc(), lhs);
|
||||
current->add(isNullOrUndefined);
|
||||
test = newTest(isNullOrUndefined, evalLhs, evalRhs);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_AND:
|
||||
test = newTest(lhs, evalRhs, evalLhs);
|
||||
break;
|
||||
|
||||
case JSOP_OR:
|
||||
test = newTest(lhs, evalLhs, evalRhs);
|
||||
break;
|
||||
}
|
||||
current->end(test);
|
||||
|
||||
// Create the lhs block and specialize.
|
||||
|
|
@ -4442,7 +4458,7 @@ IonBuilder::jsop_andor(JSOp op)
|
|||
return false;
|
||||
|
||||
// Create the rhs block.
|
||||
if (!cfgStack_.append(CFGState::AndOr(joinStart, evalLhs)))
|
||||
if (!cfgStack_.append(CFGState::Logical(joinStart, evalLhs)))
|
||||
return false;
|
||||
|
||||
if (!setCurrentAndSpecializePhis(evalRhs))
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class IonBuilder
|
|||
TABLE_SWITCH, // switch() { x }
|
||||
COND_SWITCH_CASE, // switch() { case X: ... }
|
||||
COND_SWITCH_BODY, // switch() { case ...: X }
|
||||
AND_OR, // && x, || x
|
||||
LOGICAL, // && x, || x, ?? x
|
||||
LABEL, // label: x
|
||||
TRY // try { x } catch(e) { }
|
||||
};
|
||||
|
|
@ -197,7 +197,7 @@ class IonBuilder
|
|||
|
||||
static CFGState If(jsbytecode* join, MTest* test);
|
||||
static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, MTest* test);
|
||||
static CFGState AndOr(jsbytecode* join, MBasicBlock* lhs);
|
||||
static CFGState Logical(jsbytecode* join, MBasicBlock* lhs);
|
||||
static CFGState TableSwitch(jsbytecode* exitpc, MTableSwitch* ins);
|
||||
static CFGState CondSwitch(IonBuilder* builder, jsbytecode* exitpc, jsbytecode* defaultTarget);
|
||||
static CFGState Label(jsbytecode* exitpc);
|
||||
|
|
@ -257,7 +257,7 @@ class IonBuilder
|
|||
ControlStatus processCondSwitchBody(CFGState& state);
|
||||
ControlStatus processSwitchBreak(JSOp op);
|
||||
ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
|
||||
ControlStatus processAndOrEnd(CFGState& state);
|
||||
ControlStatus processLogicalEnd(CFGState& state);
|
||||
ControlStatus processLabelEnd(CFGState& state);
|
||||
ControlStatus processTryEnd(CFGState& state);
|
||||
ControlStatus processReturn(JSOp op);
|
||||
|
|
@ -712,7 +712,7 @@ class IonBuilder
|
|||
MOZ_MUST_USE bool jsop_try();
|
||||
MOZ_MUST_USE bool jsop_label();
|
||||
MOZ_MUST_USE bool jsop_condswitch();
|
||||
MOZ_MUST_USE bool jsop_andor(JSOp op);
|
||||
MOZ_MUST_USE bool jsop_logical(JSOp op);
|
||||
MOZ_MUST_USE bool jsop_dup2();
|
||||
MOZ_MUST_USE bool jsop_loophead(jsbytecode* pc);
|
||||
MOZ_MUST_USE bool jsop_compare(JSOp op);
|
||||
|
|
|
|||
|
|
@ -4162,6 +4162,16 @@ LIRGenerator::visitIsObject(MIsObject* ins)
|
|||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitIsNullOrUndefined(MIsNullOrUndefined* ins)
|
||||
{
|
||||
MDefinition* opd = ins->input();
|
||||
MOZ_ASSERT(opd->type() == MIRType::Value);
|
||||
LIsNullOrUndefined* lir =
|
||||
new(alloc()) LIsNullOrUndefined(useBoxAtStart(opd));
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitHasClass(MHasClass* ins)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitIsCallable(MIsCallable* ins);
|
||||
void visitIsConstructor(MIsConstructor* ins);
|
||||
void visitIsObject(MIsObject* ins);
|
||||
void visitIsNullOrUndefined(MIsNullOrUndefined* ins);
|
||||
void visitHasClass(MHasClass* ins);
|
||||
void visitGuardToClass(MGuardToClass* ins);
|
||||
void visitWasmAddOffset(MWasmAddOffset* ins);
|
||||
|
|
|
|||
|
|
@ -13156,6 +13156,30 @@ class MIsObject
|
|||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MIsNullOrUndefined
|
||||
: public MUnaryInstruction,
|
||||
public BoxInputsPolicy::Data
|
||||
{
|
||||
explicit MIsNullOrUndefined(MDefinition* object)
|
||||
: MUnaryInstruction(object)
|
||||
{
|
||||
setResultType(MIRType::Boolean);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IsNullOrUndefined)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MHasClass
|
||||
: public MUnaryInstruction,
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ namespace jit {
|
|||
_(IsConstructor) \
|
||||
_(IsCallable) \
|
||||
_(IsObject) \
|
||||
_(IsNullOrUndefined) \
|
||||
_(HasClass) \
|
||||
_(GuardToClass) \
|
||||
_(CopySign) \
|
||||
|
|
|
|||
|
|
@ -7851,6 +7851,21 @@ class LIsObjectAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LIsNullOrUndefined : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IsNullOrUndefined);
|
||||
static const size_t Input = 0;
|
||||
|
||||
explicit LIsNullOrUndefined(const LBoxAllocation& input) {
|
||||
setBoxOperand(Input, input);
|
||||
}
|
||||
|
||||
MIsNullOrUndefined* mir() const {
|
||||
return mir_->toIsNullOrUndefined();
|
||||
}
|
||||
};
|
||||
|
||||
class LHasClass : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -385,6 +385,7 @@
|
|||
_(IsConstructor) \
|
||||
_(IsObject) \
|
||||
_(IsObjectAndBranch) \
|
||||
_(IsNullOrUndefined) \
|
||||
_(HasClass) \
|
||||
_(GuardToClass) \
|
||||
_(RecompileCheck) \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue