From 74f9d8c901ba68c2d664b6e1e3ba80cf0efe8e02 Mon Sep 17 00:00:00 2001 From: danno Date: Fri, 5 Jun 2015 05:28:51 -0700 Subject: [PATCH] Add %GetCallerJSFunction intrinsic Only optimized for TF R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1146963002 Cr-Commit-Position: refs/heads/master@{#28812} --- src/compiler/access-builder.cc | 14 ++++++++++ src/compiler/access-builder.h | 6 +++++ src/compiler/arm/code-generator-arm.cc | 4 +++ src/compiler/arm64/code-generator-arm64.cc | 3 +++ src/compiler/ia32/code-generator-ia32.cc | 3 +++ src/compiler/instruction-codes.h | 1 + src/compiler/instruction-selector.cc | 8 ++++++ src/compiler/js-intrinsic-lowering.cc | 27 +++++++++++++++++++ src/compiler/js-intrinsic-lowering.h | 1 + src/compiler/linkage.cc | 1 + src/compiler/machine-operator.cc | 3 ++- src/compiler/machine-operator.h | 1 + src/compiler/mips/code-generator-mips.cc | 3 +++ src/compiler/mips64/code-generator-mips64.cc | 3 +++ src/compiler/opcodes.h | 1 + src/compiler/ppc/code-generator-ppc.cc | 4 +++ src/compiler/raw-machine-assembler.h | 1 + src/compiler/simplified-lowering.cc | 1 + src/compiler/typer.cc | 5 ++++ src/compiler/verifier.cc | 1 + src/compiler/x64/code-generator-x64.cc | 3 +++ src/runtime/runtime-internal.cc | 10 +++++++ src/runtime/runtime.h | 3 ++- test/mjsunit/get-caller-js-function-throws.js | 14 ++++++++++ test/mjsunit/get-caller-js-function.js | 21 +++++++++++++++ 25 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 test/mjsunit/get-caller-js-function-throws.js create mode 100644 test/mjsunit/get-caller-js-function.js diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc index b9986f28f..c03b787aa 100644 --- a/src/compiler/access-builder.cc +++ b/src/compiler/access-builder.cc @@ -197,6 +197,20 @@ FieldAccess AccessBuilder::ForSharedFunctionInfoTypeFeedbackVector() { Handle(), Type::Any(), kMachAnyTagged}; } + +// static +FieldAccess AccessBuilder::ForFrameCallerFramePtr() { + return {kUntaggedBase, StandardFrameConstants::kCallerFPOffset, + MaybeHandle(), Type::Internal(), kMachPtr}; +} + + +// static +FieldAccess AccessBuilder::ForFrameMarker() { + return {kUntaggedBase, StandardFrameConstants::kMarkerOffset, + MaybeHandle(), Type::Tagged(), kMachAnyTagged}; +} + } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h index f0cd5e096..7c6029d9c 100644 --- a/src/compiler/access-builder.h +++ b/src/compiler/access-builder.h @@ -83,6 +83,12 @@ class AccessBuilder final : public AllStatic { // Provides access to the TypeFeedbackVector in SharedFunctionInfo. static FieldAccess ForSharedFunctionInfoTypeFeedbackVector(); + // Provides access to the next frame pointer in a stack frame. + static FieldAccess ForFrameCallerFramePtr(); + + // Provides access to the marker in a stack frame. + static FieldAccess ForFrameMarker(); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(AccessBuilder); }; diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index 192a92024..a26430849 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -403,6 +403,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { __ mov(i.OutputRegister(), sp); DCHECK_EQ(LeaveCC, i.OutputSBit()); break; + case kArchFramePointer: + __ mov(i.OutputRegister(), fp); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; case kArchTruncateDoubleToI: __ TruncateDoubleToI(i.OutputRegister(), i.InputFloat64Register(0)); DCHECK_EQ(LeaveCC, i.OutputSBit()); diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index 91b100b06..3f2f1c9fe 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -443,6 +443,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchStackPointer: __ mov(i.OutputRegister(), masm()->StackPointer()); break; + case kArchFramePointer: + __ mov(i.OutputRegister(), fp); + break; case kArchTruncateDoubleToI: __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); break; diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index de9ac2e81..53b4ebe7c 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -376,6 +376,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchStackPointer: __ mov(i.OutputRegister(), esp); break; + case kArchFramePointer: + __ mov(i.OutputRegister(), ebp); + break; case kArchTruncateDoubleToI: { auto result = i.OutputRegister(); auto input = i.InputDoubleRegister(0); diff --git a/src/compiler/instruction-codes.h b/src/compiler/instruction-codes.h index 7b42eb778..dba4c102e 100644 --- a/src/compiler/instruction-codes.h +++ b/src/compiler/instruction-codes.h @@ -45,6 +45,7 @@ namespace compiler { V(ArchDeoptimize) \ V(ArchRet) \ V(ArchStackPointer) \ + V(ArchFramePointer) \ V(ArchTruncateDoubleToI) \ V(CheckedLoadInt8) \ V(CheckedLoadUint8) \ diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index ae9d4c652..1c1f3fafd 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -761,6 +761,8 @@ void InstructionSelector::VisitNode(Node* node) { return MarkAsFloat64(node), VisitFloat64InsertHighWord32(node); case IrOpcode::kLoadStackPointer: return VisitLoadStackPointer(node); + case IrOpcode::kLoadFramePointer: + return VisitLoadFramePointer(node); case IrOpcode::kCheckedLoad: { MachineType rep = OpParameter(node); MarkAsRepresentation(rep, node); @@ -791,6 +793,12 @@ void InstructionSelector::VisitLoadStackPointer(Node* node) { } +void InstructionSelector::VisitLoadFramePointer(Node* node) { + OperandGenerator g(this); + Emit(kArchFramePointer, g.DefineAsRegister(node)); +} + + void InstructionSelector::EmitTableSwitch(const SwitchInfo& sw, InstructionOperand& index_operand) { OperandGenerator g(this); diff --git a/src/compiler/js-intrinsic-lowering.cc b/src/compiler/js-intrinsic-lowering.cc index 2a3bdf8fa..b679a75fa 100644 --- a/src/compiler/js-intrinsic-lowering.cc +++ b/src/compiler/js-intrinsic-lowering.cc @@ -84,6 +84,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) { return ReduceFixedArraySet(node); case Runtime::kInlineGetTypeFeedbackVector: return ReduceGetTypeFeedbackVector(node); + case Runtime::kInlineGetCallerJSFunction: + return ReduceGetCallerJSFunction(node); default: break; } @@ -455,6 +457,31 @@ Reduction JSIntrinsicLowering::ReduceGetTypeFeedbackVector(Node* node) { } +Reduction JSIntrinsicLowering::ReduceGetCallerJSFunction(Node* node) { + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + Node* const frame_state = NodeProperties::GetFrameStateInput(node, 0); + Node* outer_frame = frame_state->InputAt(kFrameStateOuterStateInput); + if (outer_frame->opcode() == IrOpcode::kFrameState) { + // Use the runtime implementation to throw the appropriate error if the + // containing function is inlined. + return NoChange(); + } + + // TODO(danno): This implementation forces intrinsic lowering to happen after + // inlining, which is fine for now, but eventually the frame-querying logic + // probably should go later, e.g. in instruction selection, so that there is + // no phase-ordering dependency. + FieldAccess access = AccessBuilder::ForFrameCallerFramePtr(); + Node* fp = graph()->NewNode(machine()->LoadFramePointer()); + Node* next_fp = + graph()->NewNode(simplified()->LoadField(access), fp, effect, control); + return Change(node, simplified()->LoadField(AccessBuilder::ForFrameMarker()), + next_fp, effect, control); +} + + Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, Node* b) { node->set_op(op); diff --git a/src/compiler/js-intrinsic-lowering.h b/src/compiler/js-intrinsic-lowering.h index 75a889246..3ceabd69b 100644 --- a/src/compiler/js-intrinsic-lowering.h +++ b/src/compiler/js-intrinsic-lowering.h @@ -53,6 +53,7 @@ class JSIntrinsicLowering final : public AdvancedReducer { Reduction ReduceValueOf(Node* node); Reduction ReduceFixedArraySet(Node* node); Reduction ReduceGetTypeFeedbackVector(Node* node); + Reduction ReduceGetCallerJSFunction(Node* node); Reduction Change(Node* node, const Operator* op); Reduction Change(Node* node, const Operator* op, Node* a, Node* b); diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index 3349c52cc..afc999b4b 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -134,6 +134,7 @@ bool Linkage::NeedsFrameState(Runtime::FunctionId function) { case Runtime::kInlineCallFunction: case Runtime::kInlineDateField: // TODO(bmeurer): Remove this. case Runtime::kInlineDeoptimizeNow: + case Runtime::kInlineGetCallerJSFunction: case Runtime::kInlineGetPrototype: case Runtime::kInlineRegExpExec: case Runtime::kInlineThrowIfNotADate: diff --git a/src/compiler/machine-operator.cc b/src/compiler/machine-operator.cc index 07bcf3f77..dc00ddc58 100644 --- a/src/compiler/machine-operator.cc +++ b/src/compiler/machine-operator.cc @@ -148,7 +148,8 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) { V(Float32Min, Operator::kNoProperties, 2, 0, 1) \ V(Float64Max, Operator::kNoProperties, 2, 0, 1) \ V(Float64Min, Operator::kNoProperties, 2, 0, 1) \ - V(LoadStackPointer, Operator::kNoProperties, 0, 0, 1) + V(LoadStackPointer, Operator::kNoProperties, 0, 0, 1) \ + V(LoadFramePointer, Operator::kNoProperties, 0, 0, 1) #define MACHINE_TYPE_LIST(V) \ diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index 141586965..40a68b3df 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -225,6 +225,7 @@ class MachineOperatorBuilder final : public ZoneObject { // Access to the machine stack. const Operator* LoadStackPointer(); + const Operator* LoadFramePointer(); // checked-load heap, index, length const Operator* CheckedLoad(CheckedLoadRepresentation); diff --git a/src/compiler/mips/code-generator-mips.cc b/src/compiler/mips/code-generator-mips.cc index 6e2c4e244..872eeb454 100644 --- a/src/compiler/mips/code-generator-mips.cc +++ b/src/compiler/mips/code-generator-mips.cc @@ -487,6 +487,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchStackPointer: __ mov(i.OutputRegister(), sp); break; + case kArchFramePointer: + __ mov(i.OutputRegister(), fp); + break; case kArchTruncateDoubleToI: __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); break; diff --git a/src/compiler/mips64/code-generator-mips64.cc b/src/compiler/mips64/code-generator-mips64.cc index caf678446..94e96d429 100644 --- a/src/compiler/mips64/code-generator-mips64.cc +++ b/src/compiler/mips64/code-generator-mips64.cc @@ -487,6 +487,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchStackPointer: __ mov(i.OutputRegister(), sp); break; + case kArchFramePointer: + __ mov(i.OutputRegister(), fp); + break; case kArchTruncateDoubleToI: __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); break; diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index d38f24d5d..fc67e740f 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -280,6 +280,7 @@ V(Float64InsertLowWord32) \ V(Float64InsertHighWord32) \ V(LoadStackPointer) \ + V(LoadFramePointer) \ V(CheckedLoad) \ V(CheckedStore) diff --git a/src/compiler/ppc/code-generator-ppc.cc b/src/compiler/ppc/code-generator-ppc.cc index f4fac6daf..936b1e038 100644 --- a/src/compiler/ppc/code-generator-ppc.cc +++ b/src/compiler/ppc/code-generator-ppc.cc @@ -687,6 +687,10 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { __ mr(i.OutputRegister(), sp); DCHECK_EQ(LeaveRC, i.OutputRCBit()); break; + case kArchFramePointer: + __ mr(i.OutputRegister(), fp); + DCHECK_EQ(LeaveRC, i.OutputRCBit()); + break; case kArchTruncateDoubleToI: // TODO(mbrandy): move slow call to stub out of line. __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); diff --git a/src/compiler/raw-machine-assembler.h b/src/compiler/raw-machine-assembler.h index 8e3871f67..0cf4637ca 100644 --- a/src/compiler/raw-machine-assembler.h +++ b/src/compiler/raw-machine-assembler.h @@ -441,6 +441,7 @@ class RawMachineAssembler : public GraphBuilder { // Stack operations. Node* LoadStackPointer() { return NewNode(machine()->LoadStackPointer()); } + Node* LoadFramePointer() { return NewNode(machine()->LoadFramePointer()); } // Parameters. Node* Parameter(size_t index); diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 64f7e2249..38b81e27e 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -1038,6 +1038,7 @@ class RepresentationSelector { case IrOpcode::kFloat64InsertHighWord32: return VisitBinop(node, kMachFloat64, kMachInt32, kMachFloat64); case IrOpcode::kLoadStackPointer: + case IrOpcode::kLoadFramePointer: return VisitLeaf(node, kMachPtr); case IrOpcode::kStateValues: VisitStateValues(node); diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 3937a487b..9cc7cfcfd 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -2312,6 +2312,11 @@ Bounds Typer::Visitor::TypeLoadStackPointer(Node* node) { } +Bounds Typer::Visitor::TypeLoadFramePointer(Node* node) { + return Bounds(Type::Internal()); +} + + Bounds Typer::Visitor::TypeCheckedLoad(Node* node) { return Bounds::Unbounded(zone()); } diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 59224539e..e678f4e6a 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -867,6 +867,7 @@ void Verifier::Visitor::Check(Node* node) { case IrOpcode::kFloat64InsertLowWord32: case IrOpcode::kFloat64InsertHighWord32: case IrOpcode::kLoadStackPointer: + case IrOpcode::kLoadFramePointer: case IrOpcode::kCheckedLoad: case IrOpcode::kCheckedStore: // TODO(rossberg): Check. diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index b4d200b39..13e482d3c 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -626,6 +626,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchStackPointer: __ movq(i.OutputRegister(), rsp); break; + case kArchFramePointer: + __ movq(i.OutputRegister(), rbp); + break; case kArchTruncateDoubleToI: { auto result = i.OutputRegister(); auto input = i.InputDoubleRegister(0); diff --git a/src/runtime/runtime-internal.cc b/src/runtime/runtime-internal.cc index e0e583e40..294e4fb13 100644 --- a/src/runtime/runtime-internal.cc +++ b/src/runtime/runtime-internal.cc @@ -416,5 +416,15 @@ RUNTIME_FUNCTION(Runtime_GetTypeFeedbackVector) { CONVERT_ARG_CHECKED(JSFunction, function, 0); return function->shared()->feedback_vector(); } + + +RUNTIME_FUNCTION(Runtime_GetCallerJSFunction) { + SealHandleScope shs(isolate); + StackFrameIterator it(isolate); + RUNTIME_ASSERT(it.frame()->type() == StackFrame::STUB); + it.Advance(); + RUNTIME_ASSERT(it.frame()->type() == StackFrame::JAVA_SCRIPT); + return JavaScriptFrame::cast(it.frame())->function(); +} } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 76c52889a..1ef6cbae0 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -325,7 +325,8 @@ namespace internal { F(Likely, 1, 1) \ F(Unlikely, 1, 1) \ F(HarmonyToString, 0, 1) \ - F(GetTypeFeedbackVector, 1, 1) + F(GetTypeFeedbackVector, 1, 1) \ + F(GetCallerJSFunction, 0, 1) #define FOR_EACH_INTRINSIC_JSON(F) \ diff --git a/test/mjsunit/get-caller-js-function-throws.js b/test/mjsunit/get-caller-js-function-throws.js new file mode 100644 index 000000000..42b098aee --- /dev/null +++ b/test/mjsunit/get-caller-js-function-throws.js @@ -0,0 +1,14 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --noalways-opt --nostress-opt + +// Ensure that "real" js functions that call GetCallerJSFunction get an +// exception, since they are not stubs. +(function() { + var a = function() { + return %_GetCallerJSFunction(); + } + assertThrows(a); +}()); diff --git a/test/mjsunit/get-caller-js-function.js b/test/mjsunit/get-caller-js-function.js new file mode 100644 index 000000000..5c7af6481 --- /dev/null +++ b/test/mjsunit/get-caller-js-function.js @@ -0,0 +1,21 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --turbo-filter=* --nostress-opt + +// Test that for fully optimized but non inlined code, GetCallerJSFunction walks +// up a single stack frame to get the calling function. Full optimization elides +// the check in the runtime version of the intrinsic that would throw since the +// caller isn't a stub. It's a bit of a hack, but allows minimal testing of the +// intrinsic without writing a full-blown cctest. +(function() { + var a = function() { + return %_GetCallerJSFunction(); + }; + var b = function() { + return a(); + }; + %OptimizeFunctionOnNextCall(a); + assertEquals(b, b()); +}()); -- 2.34.1