From d2e8f3b7b27e97a078db08f0f8be510249407103 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Wed, 9 Apr 2014 13:57:32 +0200 Subject: [PATCH] Bug 988993 - Ion-compile scripts with arrow functions. r=bhackett --- js/src/jit/BytecodeAnalysis.cpp | 1 + js/src/jit/CodeGenerator.cpp | 48 +++++++++++++++++++++++++-- js/src/jit/CodeGenerator.h | 1 + js/src/jit/IonBuilder.cpp | 26 +++++++++++++-- js/src/jit/IonBuilder.h | 1 + js/src/jit/LIR-Common.h | 22 ++++++++++++ js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 12 +++++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 38 +++++++++++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/ParallelSafetyAnalysis.cpp | 1 + js/src/vm/SelfHosting.cpp | 2 ++ 13 files changed, 149 insertions(+), 6 deletions(-) diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp index a84314d5e4d..2a08929e9f7 100644 --- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -156,6 +156,7 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn) case JSOP_CALLALIASEDVAR: case JSOP_SETALIASEDVAR: case JSOP_LAMBDA: + case JSOP_LAMBDA_ARROW: case JSOP_DEFFUN: case JSOP_DEFVAR: case JSOP_DEFCONST: diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 19e5dc4ae39..52eccfa2ae5 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -941,8 +941,7 @@ CodeGenerator::visitStringReplace(LStringReplace *lir) typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject); -static const VMFunction LambdaInfo = - FunctionInfo(js::Lambda); +static const VMFunction LambdaInfo = FunctionInfo(js::Lambda); bool CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton *lir) @@ -976,10 +975,53 @@ CodeGenerator::visitLambda(LLambda *lir) return true; } +typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue); +static const VMFunction LambdaArrowInfo = FunctionInfo(js::LambdaArrow); + +bool +CodeGenerator::visitLambdaArrow(LLambdaArrow *lir) +{ + Register scopeChain = ToRegister(lir->scopeChain()); + ValueOperand thisv = ToValue(lir, LLambdaArrow::ThisValue); + Register output = ToRegister(lir->output()); + Register tempReg = ToRegister(lir->temp()); + const LambdaFunctionInfo &info = lir->mir()->info(); + + OutOfLineCode *ool = oolCallVM(LambdaArrowInfo, lir, + (ArgList(), ImmGCPtr(info.fun), scopeChain, thisv), + StoreRegisterTo(output)); + if (!ool) + return false; + + MOZ_ASSERT(!info.useNewTypeForClone); + + if (info.singletonType) { + // If the function has a singleton type, this instruction will only be + // executed once so we don't bother inlining it. + masm.jump(ool->entry()); + masm.bind(ool->rejoin()); + return true; + } + + masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap); + masm.initGCThing(output, tempReg, info.fun); + + emitLambdaInit(output, scopeChain, info); + + // Store the lexical |this| value. + MOZ_ASSERT(info.flags & JSFunction::EXTENDED); + masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfArrowThisSlot())); + + masm.bind(ool->rejoin()); + return true; +} + void CodeGenerator::emitLambdaInit(Register output, Register scopeChain, const LambdaFunctionInfo &info) { + MOZ_ASSERT(!!(info.flags & JSFunction::ARROW) == !!(info.flags & JSFunction::EXTENDED)); + // Initialize nargs and flags. We do this with a single uint32 to avoid // 16-bit writes. union { @@ -990,7 +1032,7 @@ CodeGenerator::emitLambdaInit(Register output, Register scopeChain, uint32_t word; } u; u.s.nargs = info.fun->nargs(); - u.s.flags = info.flags & ~JSFunction::EXTENDED; + u.s.flags = info.flags; JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2); masm.store32(Imm32(u.word), Address(output, JSFunction::offsetOfNargs())); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 17d8409909d..0f93df35e7f 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -99,6 +99,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitRegExpReplace(LRegExpReplace *lir); bool visitStringReplace(LStringReplace *lir); bool visitLambda(LLambda *lir); + bool visitLambdaArrow(LLambdaArrow *lir); bool visitLambdaForSingleton(LLambdaForSingleton *lir); bool visitLambdaPar(LLambdaPar *lir); bool visitPointer(LPointer *lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 100a0711cff..b38dfaddf76 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1711,6 +1711,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_LAMBDA: return jsop_lambda(info().getFunction(pc)); + case JSOP_LAMBDA_ARROW: + return jsop_lambda_arrow(info().getFunction(pc)); + case JSOP_ITER: return jsop_iter(GET_INT8(pc)); @@ -9420,9 +9423,9 @@ IonBuilder::jsop_object(JSObject *obj) bool IonBuilder::jsop_lambda(JSFunction *fun) { - JS_ASSERT(analysis().usesScopeChain()); - if (fun->isArrow()) - return abort("bound arrow function"); + MOZ_ASSERT(analysis().usesScopeChain()); + MOZ_ASSERT(!fun->isArrow()); + if (fun->isNative() && IsAsmJSModuleNative(fun->native())) return abort("asm.js module function"); @@ -9433,6 +9436,23 @@ IonBuilder::jsop_lambda(JSFunction *fun) return resumeAfter(ins); } +bool +IonBuilder::jsop_lambda_arrow(JSFunction *fun) +{ + MOZ_ASSERT(analysis().usesScopeChain()); + MOZ_ASSERT(fun->isArrow()); + MOZ_ASSERT(!fun->isNative()); + + MDefinition *thisDef = current->pop(); + + MLambdaArrow *ins = MLambdaArrow::New(alloc(), constraints(), current->scopeChain(), + thisDef, fun); + current->add(ins); + current->push(ins); + + return resumeAfter(ins); +} + bool IonBuilder::jsop_setarg(uint32_t arg) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 9ea00c382c7..034bdf6be73 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -602,6 +602,7 @@ class IonBuilder : public MIRGenerator bool jsop_regexp(RegExpObject *reobj); bool jsop_object(JSObject *obj); bool jsop_lambda(JSFunction *fun); + bool jsop_lambda_arrow(JSFunction *fun); bool jsop_this(); bool jsop_typeof(); bool jsop_toid(); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index daf7fa952d3..ca37edd687c 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3487,6 +3487,28 @@ class LLambda : public LInstructionHelper<1, 1, 1> } }; +class LLambdaArrow : public LInstructionHelper<1, 1 + BOX_PIECES, 1> +{ + public: + LIR_HEADER(LambdaArrow) + + static const size_t ThisValue = 1; + + LLambdaArrow(const LAllocation &scopeChain, const LDefinition &temp) { + setOperand(0, scopeChain); + setTemp(0, temp); + } + const LAllocation *scopeChain() { + return getOperand(0); + } + const LDefinition *temp() { + return getTemp(0); + } + const MLambdaArrow *mir() const { + return mir_->toLambdaArrow(); + } +}; + class LLambdaPar : public LInstructionHelper<1, 2, 2> { public: diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index e353b7a679e..5b5d1709923 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -160,6 +160,7 @@ _(RegExpReplace) \ _(StringReplace) \ _(Lambda) \ + _(LambdaArrow) \ _(LambdaForSingleton) \ _(LambdaPar) \ _(ImplicitThis) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 7326c8862c5..1262efe0194 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2068,6 +2068,18 @@ LIRGenerator::visitLambda(MLambda *ins) return define(lir, ins) && assignSafepoint(lir, ins); } +bool +LIRGenerator::visitLambdaArrow(MLambdaArrow *ins) +{ + MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); + MOZ_ASSERT(ins->thisDef()->type() == MIRType_Value); + + LLambdaArrow *lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()), temp()); + if (!useBox(lir, LLambdaArrow::ThisValue, ins->thisDef())) + return false; + return define(lir, ins) && assignSafepoint(lir, ins); +} + bool LIRGenerator::visitLambdaPar(MLambdaPar *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 4c042aff5bc..528efe5048e 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -154,6 +154,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitRegExpReplace(MRegExpReplace *ins); bool visitStringReplace(MStringReplace *ins); bool visitLambda(MLambda *ins); + bool visitLambdaArrow(MLambdaArrow *ins); bool visitLambdaPar(MLambdaPar *ins); bool visitImplicitThis(MImplicitThis *ins); bool visitSlots(MSlots *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 8a13e5e228b..1722189b9fd 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5085,6 +5085,44 @@ class MLambda } }; +class MLambdaArrow + : public MBinaryInstruction, + public MixPolicy, BoxPolicy<1> > +{ + LambdaFunctionInfo info_; + + MLambdaArrow(types::CompilerConstraintList *constraints, MDefinition *scopeChain, + MDefinition *this_, JSFunction *fun) + : MBinaryInstruction(scopeChain, this_), info_(fun) + { + setResultType(MIRType_Object); + MOZ_ASSERT(!types::UseNewTypeForClone(fun)); + if (!fun->hasSingletonType()) + setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); + } + + public: + INSTRUCTION_HEADER(LambdaArrow) + + static MLambdaArrow *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, + MDefinition *scopeChain, MDefinition *this_, JSFunction *fun) + { + return new(alloc) MLambdaArrow(constraints, scopeChain, this_, fun); + } + MDefinition *scopeChain() const { + return getOperand(0); + } + MDefinition *thisDef() const { + return getOperand(1); + } + const LambdaFunctionInfo &info() const { + return info_; + } + TypePolicy *typePolicy() { + return this; + } +}; + class MLambdaPar : public MBinaryInstruction, public SingleObjectPolicy diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 51610abd635..2a3861c97fc 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -107,6 +107,7 @@ namespace jit { _(RegExpReplace) \ _(StringReplace) \ _(Lambda) \ + _(LambdaArrow) \ _(ImplicitThis) \ _(Slots) \ _(Elements) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 101360f9771..28961657894 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -196,6 +196,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor SAFE_OP(Nop) UNSAFE_OP(RegExp) CUSTOM_OP(Lambda) + UNSAFE_OP(LambdaArrow) UNSAFE_OP(ImplicitThis) SAFE_OP(Slots) SAFE_OP(Elements) diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index a3778bc9c4a..3991f447acc 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -1063,6 +1063,8 @@ CloneObject(JSContext *cx, HandleObject selfHostedObject) if (selfHostedObject->is()) { RootedFunction selfHostedFunction(cx, &selfHostedObject->as()); bool hasName = selfHostedFunction->atom() != nullptr; + // Arrow functions use the first extended slot for their lexical |this| value. + JS_ASSERT(!selfHostedFunction->isArrow()); js::gc::AllocKind kind = hasName ? JSFunction::ExtendedFinalizeKind : selfHostedFunction->getAllocKind();