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:
FranklinDM 2022-05-20 15:38:43 +08:00 committed by roytam1
commit 5812721809
12 changed files with 123 additions and 16 deletions

View file

@ -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)
{

View file

@ -45,6 +45,7 @@ namespace jit {
_(JSOP_GOTO) \
_(JSOP_IFEQ) \
_(JSOP_IFNE) \
_(JSOP_COALESCE) \
_(JSOP_AND) \
_(JSOP_OR) \
_(JSOP_NOT) \

View file

@ -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)
{

View file

@ -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);

View file

@ -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))

View file

@ -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);

View file

@ -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)
{

View file

@ -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);

View file

@ -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,

View file

@ -271,6 +271,7 @@ namespace jit {
_(IsConstructor) \
_(IsCallable) \
_(IsObject) \
_(IsNullOrUndefined) \
_(HasClass) \
_(GuardToClass) \
_(CopySign) \

View file

@ -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:

View file

@ -385,6 +385,7 @@
_(IsConstructor) \
_(IsObject) \
_(IsObjectAndBranch) \
_(IsNullOrUndefined) \
_(HasClass) \
_(GuardToClass) \
_(RecompileCheck) \