summaryrefslogtreecommitdiff
path: root/js/src/jit
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit')
-rw-r--r--js/src/jit/BaselineCompiler.cpp19
-rw-r--r--js/src/jit/BaselineCompiler.h1
-rw-r--r--js/src/jit/CodeGenerator.cpp18
-rw-r--r--js/src/jit/CodeGenerator.h1
-rw-r--r--js/src/jit/IonBuilder.cpp40
-rw-r--r--js/src/jit/IonBuilder.h8
-rw-r--r--js/src/jit/Lowering.cpp10
-rw-r--r--js/src/jit/Lowering.h1
-rw-r--r--js/src/jit/MIR.h24
-rw-r--r--js/src/jit/MOpcodes.h1
-rw-r--r--js/src/jit/shared/LIR-shared.h15
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h1
12 files changed, 123 insertions, 16 deletions
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index fd85ec00ed..53254718c0 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1253,6 +1253,25 @@ BaselineCompiler::emit_JSOP_IFNE()
}
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)
{
bool knownBoolean = frame.peek(-1)->isKnownBoolean();
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index f975162a6a..d3ab58aa4e 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -45,6 +45,7 @@ namespace jit {
_(JSOP_GOTO) \
_(JSOP_IFEQ) \
_(JSOP_IFNE) \
+ _(JSOP_COALESCE) \
_(JSOP_AND) \
_(JSOP_OR) \
_(JSOP_NOT) \
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index ec3d35ff0b..66e8e25ddf 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11518,6 +11518,24 @@ CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins)
}
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)
{
// The "outermost" JSScript means the script that we are compiling
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index b8aecc8ba8..ff09222f17 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -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);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index dbecec2a7e..62470a2b81 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -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))
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 402fbbf1af..a07020a4e8 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -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);
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index 0347154675..f143ff9e04 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4163,6 +4163,16 @@ LIRGenerator::visitIsObject(MIsObject* 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)
{
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index d5d0c3af20..5340fce762 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -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);
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index a398ef3344..ea00529e09 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -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,
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index 3185d0a836..c1644ba8b3 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -271,6 +271,7 @@ namespace jit {
_(IsConstructor) \
_(IsCallable) \
_(IsObject) \
+ _(IsNullOrUndefined) \
_(HasClass) \
_(GuardToClass) \
_(CopySign) \
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index 62dc574b6c..f97dc1534f 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -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:
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index d0651af1d2..7f4cd770ac 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -385,6 +385,7 @@
_(IsConstructor) \
_(IsObject) \
_(IsObjectAndBranch) \
+ _(IsNullOrUndefined) \
_(HasClass) \
_(GuardToClass) \
_(RecompileCheck) \