diff options
-rw-r--r-- | js/src/jit/BaselineCompiler.cpp | 20 | ||||
-rw-r--r-- | js/src/jit/BaselineCompiler.h | 1 | ||||
-rw-r--r-- | js/src/jit/CodeGenerator.cpp | 109 | ||||
-rw-r--r-- | js/src/jit/CodeGenerator.h | 7 | ||||
-rw-r--r-- | js/src/jit/IonBuilder.cpp | 12 | ||||
-rw-r--r-- | js/src/jit/IonBuilder.h | 1 | ||||
-rw-r--r-- | js/src/jit/Lowering.cpp | 13 | ||||
-rw-r--r-- | js/src/jit/Lowering.h | 1 | ||||
-rw-r--r-- | js/src/jit/MIR.h | 32 | ||||
-rw-r--r-- | js/src/jit/MOpcodes.h | 1 | ||||
-rw-r--r-- | js/src/jit/VMFunctions.cpp | 9 | ||||
-rw-r--r-- | js/src/jit/VMFunctions.h | 4 | ||||
-rw-r--r-- | js/src/jit/shared/LIR-shared.h | 21 | ||||
-rw-r--r-- | js/src/jit/shared/LOpcodes-shared.h | 1 |
14 files changed, 189 insertions, 43 deletions
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 4524bae07..07d8e629d 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1381,6 +1381,26 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ() return true; } +typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind); +static const VMFunction CheckIsCallableInfo = + FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable"); + +bool +BaselineCompiler::emit_JSOP_CHECKISCALLABLE() +{ + frame.syncStack(0); + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); + + prepareVMCall(); + + pushArg(Imm32(GET_UINT8(pc))); + pushArg(R0); + if (!callVM(CheckIsCallableInfo)) + return false; + + return true; +} + typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame); static const VMFunction ThrowUninitializedThisInfo = FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis, diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 18e56bcd4..0bacf6f18 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -218,6 +218,7 @@ namespace jit { _(JSOP_FUNCTIONTHIS) \ _(JSOP_GLOBALTHIS) \ _(JSOP_CHECKISOBJ) \ + _(JSOP_CHECKISCALLABLE) \ _(JSOP_CHECKTHIS) \ _(JSOP_CHECKRETURN) \ _(JSOP_NEWTARGET) \ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 3b5ec6baa..ccdc5fbfa 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11326,25 +11326,35 @@ class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> } }; +template <CodeGenerator::CallableOrConstructor mode> void -CodeGenerator::visitIsCallable(LIsCallable* ins) +CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Label* failure) { - Register object = ToRegister(ins->object()); - Register output = ToRegister(ins->output()); - - OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins); - addOutOfLineCode(ool, ins->mir()); - Label notFunction, hasCOps, done; masm.loadObjClass(object, output); - // Just skim proxies off. Their notion of isCallable() is more complicated. - masm.branchTestClassIsProxy(true, output, ool->entry()); + // Just skim proxies off. Their notion of isCallable()/isConstructor() is + // more complicated. + masm.branchTestClassIsProxy(true, output, failure); // An object is callable iff: // is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call). + // An object is constructor iff: + // ((is<JSFunction>() && as<JSFunction>().isConstructor) || + // (getClass()->cOps && getClass()->cOps->construct)). masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function); - masm.move32(Imm32(1), output); + if (mode == Callable) { + masm.move32(Imm32(1), output); + } else { + Label notConstructor; + masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output); + masm.and32(Imm32(JSFunction::CONSTRUCTOR), output); + masm.branchTest32(Assembler::Zero, output, output, ¬Constructor); + masm.move32(Imm32(1), output); + masm.jump(&done); + masm.bind(¬Constructor); + masm.move32(Imm32(0), output); + } masm.jump(&done); masm.bind(¬Function); @@ -11355,10 +11365,26 @@ CodeGenerator::visitIsCallable(LIsCallable* ins) masm.bind(&hasCOps); masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output); - masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)), + size_t opsOffset = mode == Callable + ? offsetof(js::ClassOps, call) + : offsetof(js::ClassOps, construct); + masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset), ImmPtr(nullptr), output); masm.bind(&done); +} + +void +CodeGenerator::visitIsCallable(LIsCallable* ins) +{ + Register object = ToRegister(ins->object()); + Register output = ToRegister(ins->output()); + + OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins); + addOutOfLineCode(ool, ins->mir()); + + emitIsCallableOrConstructor<Callable>(object, output, ool->entry()); + masm.bind(ool->rejoin()); } @@ -11378,6 +11404,36 @@ CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) masm.jump(ool->rejoin()); } +typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind); +static const VMFunction CheckIsCallableInfo = + FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable"); + +void +CodeGenerator::visitCheckIsCallable(LCheckIsCallable* ins) +{ + ValueOperand checkValue = ToValue(ins, LCheckIsCallable::CheckValue); + Register temp = ToRegister(ins->temp()); + + // OOL code is used in the following 2 cases: + // * checkValue is not callable + // * checkValue is proxy and it's unknown whether it's callable or not + // CheckIsCallable checks if passed value is callable, regardless of the + // cases above. IsCallable operation is not observable and checking it + // again doesn't matter. + OutOfLineCode* ool = oolCallVM(CheckIsCallableInfo, ins, + ArgList(checkValue, Imm32(ins->mir()->checkKind())), + StoreNothing()); + + masm.branchTestObject(Assembler::NotEqual, checkValue, ool->entry()); + + Register object = masm.extractObject(checkValue, temp); + emitIsCallableOrConstructor<Callable>(object, temp, ool->entry()); + + masm.branchTest32(Assembler::Zero, temp, temp, ool->entry()); + + masm.bind(ool->rejoin()); +} + class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> { LIsConstructor* ins_; @@ -11404,37 +11460,8 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins) OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins); addOutOfLineCode(ool, ins->mir()); - Label notFunction, notConstructor, hasCOps, done; - masm.loadObjClass(object, output); - - // Just skim proxies off. Their notion of isConstructor() is more complicated. - masm.branchTestClassIsProxy(true, output, ool->entry()); + emitIsCallableOrConstructor<Constructor>(object, output, ool->entry()); - // An object is constructor iff - // ((is<JSFunction>() && as<JSFunction>().isConstructor) || - // (getClass()->cOps && getClass()->cOps->construct)). - masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function); - masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output); - masm.and32(Imm32(JSFunction::CONSTRUCTOR), output); - masm.branchTest32(Assembler::Zero, output, output, ¬Constructor); - masm.move32(Imm32(1), output); - masm.jump(&done); - masm.bind(¬Constructor); - masm.move32(Imm32(0), output); - masm.jump(&done); - - masm.bind(¬Function); - masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)), - ImmPtr(nullptr), &hasCOps); - masm.move32(Imm32(0), output); - masm.jump(&done); - - masm.bind(&hasCOps); - masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output); - masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)), - ImmPtr(nullptr), output); - - masm.bind(&done); masm.bind(ool->rejoin()); } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index b69e919a3..d3126651b 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -364,6 +364,12 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitCallDOMNative(LCallDOMNative* lir); void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir); void visitCallBindVar(LCallBindVar* lir); + enum CallableOrConstructor { + Callable, + Constructor + }; + template <CallableOrConstructor mode> + void emitIsCallableOrConstructor(Register object, Register output, Label* failure); void visitIsCallable(LIsCallable* lir); void visitOutOfLineIsCallable(OutOfLineIsCallable* ool); void visitIsConstructor(LIsConstructor* lir); @@ -384,6 +390,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitArrowNewTarget(LArrowNewTarget* ins); void visitCheckReturn(LCheckReturn* ins); void visitCheckIsObj(LCheckIsObj* ins); + void visitCheckIsCallable(LCheckIsCallable* ins); void visitCheckObjCoercible(LCheckObjCoercible* ins); void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins); void visitNaNToZero(LNaNToZero* ins); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 26bba0656..ed09fb504 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2183,6 +2183,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_CHECKISOBJ: return jsop_checkisobj(GET_UINT8(pc)); + case JSOP_CHECKISCALLABLE: + return jsop_checkiscallable(GET_UINT8(pc)); + case JSOP_CHECKOBJCOERCIBLE: return jsop_checkobjcoercible(); @@ -10900,6 +10903,15 @@ IonBuilder::jsop_checkisobj(uint8_t kind) } bool +IonBuilder::jsop_checkiscallable(uint8_t kind) +{ + MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind); + current->add(check); + current->push(check); + return true; +} + +bool IonBuilder::jsop_checkobjcoercible() { MDefinition* toCheck = current->peek(-1); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index c3cd9700a..35ad120f7 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -783,6 +783,7 @@ class IonBuilder MOZ_MUST_USE bool jsop_debugger(); MOZ_MUST_USE bool jsop_newtarget(); MOZ_MUST_USE bool jsop_checkisobj(uint8_t kind); + MOZ_MUST_USE bool jsop_checkiscallable(uint8_t kind); MOZ_MUST_USE bool jsop_checkobjcoercible(); MOZ_MUST_USE bool jsop_pushcallobj(); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index a21a529be..7f28a9020 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4689,6 +4689,19 @@ LIRGenerator::visitCheckIsObj(MCheckIsObj* ins) } void +LIRGenerator::visitCheckIsCallable(MCheckIsCallable* ins) +{ + MDefinition* checkVal = ins->checkValue(); + MOZ_ASSERT(checkVal->type() == MIRType::Value); + + LCheckIsCallable* lir = new(alloc()) LCheckIsCallable(useBox(checkVal), + temp()); + redefine(ins, checkVal); + add(lir, ins); + assignSafepoint(lir, ins); +} + +void LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins) { MDefinition* checkVal = ins->checkValue(); diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 4062c0960..b2805cb7a 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -329,6 +329,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins); void visitCheckReturn(MCheckReturn* ins); void visitCheckIsObj(MCheckIsObj* ins); + void visitCheckIsCallable(MCheckIsCallable* ins); void visitCheckObjCoercible(MCheckObjCoercible* ins); void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins); }; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 3caa7e357..2de91e2df 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -13455,8 +13455,9 @@ class MCheckIsObj { uint8_t checkKind_; - explicit MCheckIsObj(MDefinition* toCheck, uint8_t checkKind) - : MUnaryInstruction(toCheck), checkKind_(checkKind) + MCheckIsObj(MDefinition* toCheck, uint8_t checkKind) + : MUnaryInstruction(toCheck), + checkKind_(checkKind) { setResultType(MIRType::Value); setResultTypeSet(toCheck->resultTypeSet()); @@ -13475,6 +13476,33 @@ class MCheckIsObj } }; +class MCheckIsCallable + : public MUnaryInstruction, + public BoxInputsPolicy::Data +{ + uint8_t checkKind_; + + MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind) + : MUnaryInstruction(toCheck), + checkKind_(checkKind) + { + setResultType(MIRType::Value); + setResultTypeSet(toCheck->resultTypeSet()); + setGuard(); + } + + public: + INSTRUCTION_HEADER(CheckIsCallable) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, checkValue)) + + uint8_t checkKind() const { return checkKind_; } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + class MCheckObjCoercible : public MUnaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 1a6911247..bb2ab8190 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -285,6 +285,7 @@ namespace jit { _(ArrowNewTarget) \ _(CheckReturn) \ _(CheckIsObj) \ + _(CheckIsCallable) \ _(CheckObjCoercible) \ _(DebugCheckSelfHosted) \ _(AsmJSNeg) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 4edbc3c83..77b9e3647 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1349,5 +1349,14 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue return GetFunctionThis(cx, frame, res); } +bool +CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind) +{ + if (!IsCallable(v)) + return ThrowCheckIsCallable(cx, kind); + + return true; +} + } // namespace jit } // namespace js diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index f754d58c7..572f05373 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -13,6 +13,7 @@ #include "jit/CompileInfo.h" #include "jit/JitFrames.h" +#include "vm/Interpreter.h" namespace js { @@ -802,6 +803,9 @@ ThrowObjectCoercible(JSContext* cx, HandleValue v); MOZ_MUST_USE bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res); +MOZ_MUST_USE bool +CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind); + } // namespace jit } // namespace js diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index f8e0ce9cc..9dcb527c5 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -8893,6 +8893,27 @@ class LCheckIsObj : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0> } }; +class LCheckIsCallable : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1> +{ + public: + LIR_HEADER(CheckIsCallable) + + static const size_t CheckValue = 0; + + LCheckIsCallable(const LBoxAllocation& value, const LDefinition& temp) { + setBoxOperand(CheckValue, value); + setTemp(0, temp); + } + + const LDefinition* temp() { + return getTemp(0); + } + + MCheckIsCallable* mir() const { + return mir_->toCheckIsCallable(); + } +}; + class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0> { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index e57751437..3eea1b449 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -402,6 +402,7 @@ _(ArrowNewTarget) \ _(CheckReturn) \ _(CheckIsObj) \ + _(CheckIsCallable) \ _(CheckObjCoercible) \ _(DebugCheckSelfHosted) \ _(AsmJSLoadHeap) \ |