Chromium Code Reviews| 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, ®ister_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( |