Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Unified Diff: src/compiler/ia32/instruction-selector-ia32.cc

Issue 1259203002: [turbofan] Implement tail calls with differing stack parameter counts (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Fix bugs in frameless tail calls Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/compiler/ia32/instruction-selector-ia32.cc
diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc
index 460384cf56f3964c17e8c2e3be83637242c53993..4c8640685258be2feb06f0d072259c4554fa0367 100644
--- a/src/compiler/ia32/instruction-selector-ia32.cc
+++ b/src/compiler/ia32/instruction-selector-ia32.cc
@@ -834,6 +834,13 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, true);
+ InstructionBlock* current_block = sequence()->InstructionBlockAt(
+ RpoNumber::FromInt(current_block_->rpo_number()));
+ current_block->mark_needs_frame();
+ // TODO(danno): For now, any instruction block that needs a frame forces
+ // the entire function to have a frame.
+ sequence()->frame()->MarkNeedsFrame();
+
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
InstructionOperand temps[] = {g.TempRegister()};
@@ -902,41 +909,139 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
-void InstructionSelector::VisitTailCall(Node* node) {
+bool InstructionSelector::EmitTailCallSetup(Node* node, CallBuffer* buffer,
Benedikt Meurer 2015/07/30 06:47:24 See my comment in code-generator-ia32.cc. Can we s
+ InstructionCode* opcode) {
IA32OperandGenerator g(this);
CallDescriptor const* descriptor = OpParameter<CallDescriptor const*>(node);
DCHECK_NE(0, descriptor->flags() & CallDescriptor::kSupportsTailCalls);
DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kPatchableCallSite);
DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
- // TODO(turbofan): Relax restriction for stack parameters.
-
- if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
- CallBuffer buffer(zone(), descriptor, nullptr);
+ size_t stack_parameters_in = 0;
+ size_t preserved_stack_parameters_in = 0;
+ size_t stack_parameters_out = 0;
+ size_t register_parameters_out = 0;
+ if (!linkage()->GetIncomingDescriptor()->CanTailCall(
+ node, &stack_parameters_in, &preserved_stack_parameters_in,
+ &stack_parameters_out, &register_parameters_out)) {
+ return false;
+ }
// Compute InstructionOperands for inputs and outputs.
- InitializeCallBuffer(node, &buffer, true, true);
+ InitializeCallBuffer(node, buffer, true, true);
- // Select the appropriate opcode based on the call type.
- InstructionCode opcode;
- switch (descriptor->kind()) {
- case CallDescriptor::kCallCodeObject:
- case CallDescriptor::kInterpreterDispatch:
- opcode = kArchTailCallCodeObject;
- break;
- case CallDescriptor::kCallJSFunction:
- opcode = kArchTailCallJSFunction;
- break;
- default:
- UNREACHABLE();
- return;
+ // Select the appropriate opcode based on the call type.
+ switch (descriptor->kind()) {
+ case CallDescriptor::kCallCodeObject:
+ case CallDescriptor::kInterpreterDispatch:
+ *opcode = kArchTailCallCodeObject;
+ break;
+ case CallDescriptor::kCallJSFunction:
+ *opcode = kArchTailCallJSFunction;
+ break;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+ *opcode |= MiscField::encode(descriptor->flags());
+
+ DCHECK(Register::kMaxNumAllocatableRegisters > register_parameters_out);
+ int remaining_registers =
+ Register::kMaxNumAllocatableRegisters - register_parameters_out;
+ if (buffer->instruction_args[0].IsImmediate()) {
+ // If the destination of the call is immediate, then there is actually an
+ // additional register free.
+ remaining_registers++;
+ }
+ int pushed_parameters = 0;
+ size_t current = 0;
+
+ bool frame_has_been_deconstructed = false;
+
+ // If we need to overwrite the return address, reserve a register.
+ if (stack_parameters_out > stack_parameters_in) {
+ remaining_registers--;
+ }
+
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kParametersReady));
+
+ for (Node* node : base::Reversed(buffer->pushed_nodes)) {
+ if (current == stack_parameters_in) {
+ DCHECK(remaining_registers > 0);
+ // Special case for the parameter which will replace the caller's
+ // return address: load into a register for swapping.
+ InstructionOperand value = g.UseRegister(node);
+ buffer->instruction_args.push_back(value);
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kReplaceReturn));
+ } else if (current >= preserved_stack_parameters_in) {
+ int location = stack_parameters_in - current;
+ if (remaining_registers > 0) {
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, location));
+ InstructionOperand value =
+ g.CanBeImmediate(node) ? g.UseImmediate(node) : g.UseRegister(node);
+ buffer->instruction_args.push_back(value);
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kStore));
+ if (!g.CanBeImmediate(node)) {
+ remaining_registers--;
+ }
+ } else {
+ if (!frame_has_been_deconstructed) {
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kDeconstructFrame));
+ frame_has_been_deconstructed = true;
+ }
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, location));
+ InstructionOperand value =
+ g.CanBeImmediate(node)
+ ? g.UseImmediate(node)
+ : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
+ Emit(kIA32Push, g.NoOutput(), value);
+ InstructionBlock* current_block = sequence()->InstructionBlockAt(
+ RpoNumber::FromInt(current_block_->rpo_number()));
+ current_block->mark_needs_frame();
+ // TODO(danno): For now, any instruction block that needs a frame
+ // forces the entire function to have a frame.
+ sequence()->frame()->MarkNeedsFrame();
+ ++pushed_parameters;
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kPopAndStore));
+ }
}
- opcode |= MiscField::encode(descriptor->flags());
+ current++;
+ }
+
+ if (!frame_has_been_deconstructed) {
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, kDeconstructFrame));
+ }
+
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, pushed_parameters));
+ int stack_delta =
+ stack_parameters_out - static_cast<int>(stack_parameters_in);
+ buffer->instruction_args.push_back(
+ ImmediateOperand(ImmediateOperand::INLINE, stack_delta));
+ sequence()->frame()->UpdateMaxTailCallStackDelta(stack_delta);
+ return true;
+}
+
+
+void InstructionSelector::VisitTailCall(Node* node) {
+ CallDescriptor const* descriptor = OpParameter<CallDescriptor const*>(node);
+ CallBuffer buffer(zone(), descriptor, nullptr);
+ InstructionCode opcode;
+ if (EmitTailCallSetup(node, &buffer, &opcode)) {
// Emit the tailcall instruction.
Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
&buffer.instruction_args.front());
} else {
+ IA32OperandGenerator g(this);
FrameStateDescriptor* frame_state_descriptor =
descriptor->NeedsFrameState()
? GetFrameStateDescriptor(

Powered by Google App Engine
This is Rietveld 408576698