Commit cecded1c authored by ssanfilippo's avatar ssanfilippo Committed by Commit bot

Emit unwinding information for TurboFan code.

This commit introduces support for writing unwinding tables in the
.eh_frame format, to be inserted in the jitdump read by Linux perf and
emitted with FLAG_perf_prof and FLAG_perf_prof_unwinding_info enabled.

x64 is fully implemented and tested, arm and arm64 are untested and the
unwinding information needs to be expanded, but the mechanism is ready.

BUG=v8:4899
LOG=N

Review-Url: https://codereview.chromium.org/2026313002
Cr-Commit-Position: refs/heads/master@{#37799}
parent c6d6cd2f
......@@ -1066,6 +1066,7 @@ v8_source_set("v8_base") {
"src/compiler/type-hints.h",
"src/compiler/typer.cc",
"src/compiler/typer.h",
"src/compiler/unwinding-info-writer.h",
"src/compiler/value-numbering-reducer.cc",
"src/compiler/value-numbering-reducer.h",
"src/compiler/verifier.cc",
......@@ -1631,6 +1632,8 @@ v8_source_set("v8_base") {
"src/compiler/x64/instruction-codes-x64.h",
"src/compiler/x64/instruction-scheduler-x64.cc",
"src/compiler/x64/instruction-selector-x64.cc",
"src/compiler/x64/unwinding-info-writer-x64.cc",
"src/compiler/x64/unwinding-info-writer-x64.h",
"src/crankshaft/x64/lithium-codegen-x64.cc",
"src/crankshaft/x64/lithium-codegen-x64.h",
"src/crankshaft/x64/lithium-gap-resolver-x64.cc",
......@@ -1691,6 +1694,8 @@ v8_source_set("v8_base") {
"src/compiler/arm/instruction-codes-arm.h",
"src/compiler/arm/instruction-scheduler-arm.cc",
"src/compiler/arm/instruction-selector-arm.cc",
"src/compiler/arm/unwinding-info-writer-arm.cc",
"src/compiler/arm/unwinding-info-writer-arm.h",
"src/crankshaft/arm/lithium-arm.cc",
"src/crankshaft/arm/lithium-arm.h",
"src/crankshaft/arm/lithium-codegen-arm.cc",
......@@ -1745,6 +1750,8 @@ v8_source_set("v8_base") {
"src/compiler/arm64/instruction-codes-arm64.h",
"src/compiler/arm64/instruction-scheduler-arm64.cc",
"src/compiler/arm64/instruction-selector-arm64.cc",
"src/compiler/arm64/unwinding-info-writer-arm64.cc",
"src/compiler/arm64/unwinding-info-writer-arm64.h",
"src/crankshaft/arm64/delayed-masm-arm64-inl.h",
"src/crankshaft/arm64/delayed-masm-arm64.cc",
"src/crankshaft/arm64/delayed-masm-arm64.h",
......
......@@ -188,7 +188,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Register index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
RecordWriteMode mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(index),
......@@ -197,11 +198,13 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
must_save_lr_(!gen->frame_access_state()->has_frame()) {}
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer) {}
OutOfLineRecordWrite(CodeGenerator* gen, Register object, int32_t index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
RecordWriteMode mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(no_reg),
......@@ -210,7 +213,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
must_save_lr_(!gen->frame_access_state()->has_frame()) {}
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
......@@ -227,6 +231,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (must_save_lr_) {
// We need to save and restore lr if the frame was elided.
__ Push(lr);
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset());
}
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
remembered_set_action, save_fp_mode);
......@@ -239,6 +244,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
__ CallStub(&stub);
if (must_save_lr_) {
__ Pop(lr);
unwinding_info_writer_->MarkPopLinkRegisterFromTopOfStack(__ pc_offset());
}
}
......@@ -251,6 +257,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
Register const scratch1_;
RecordWriteMode const mode_;
bool must_save_lr_;
UnwindingInfoWriter* const unwinding_info_writer_;
};
......@@ -413,6 +420,7 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
void CodeGenerator::AssembleDeconstructFrame() {
__ LeaveFrame(StackFrame::MANUAL);
unwinding_info_writer_.MarkFrameDeconstructed(__ pc_offset());
}
void CodeGenerator::AssemblePrepareTailCall() {
......@@ -600,6 +608,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Jump(ip);
}
DCHECK_EQ(LeaveCC, i.OutputSBit());
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -607,6 +616,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kArchTailCallAddress: {
CHECK(!instr->InputAt(0)->IsImmediate());
__ Jump(i.InputRegister(0));
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -742,14 +752,16 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
AddressingModeField::decode(instr->opcode());
if (addressing_mode == kMode_Offset_RI) {
int32_t index = i.InputInt32(1);
ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
ool = new (zone())
OutOfLineRecordWrite(this, object, index, value, scratch0, scratch1,
mode, &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
} else {
DCHECK_EQ(kMode_Offset_RR, addressing_mode);
Register index(i.InputRegister(1));
ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
ool = new (zone())
OutOfLineRecordWrite(this, object, index, value, scratch0, scratch1,
mode, &unwinding_info_writer_);
__ str(value, MemOperand(object, index));
}
__ CheckPageFlag(object, scratch0,
......@@ -1602,6 +1614,10 @@ void CodeGenerator::AssembleConstructFrame() {
} else {
__ StubPrologue(info()->GetOutputStackFrameType());
}
if (!info()->GeneratePreagedPrologue()) {
unwinding_info_writer_.MarkFrameConstructed(__ pc_offset());
}
}
int shrink_slots = frame()->GetSpillSlotCount();
......@@ -1665,6 +1681,8 @@ void CodeGenerator::AssembleReturn() {
DwVfpRegister::from_code(last));
}
unwinding_info_writer_.MarkBlockWillExit();
if (descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame();
} else if (frame_access_state()->has_frame()) {
......
// Copyright 2016 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.
#include "src/compiler/arm/unwinding-info-writer-arm.h"
#include "src/compiler/instruction.h"
namespace v8 {
namespace internal {
namespace compiler {
void UnwindingInfoWriter::BeginInstructionBlock(int pc_offset,
const InstructionBlock* block) {
if (!enabled()) return;
block_will_exit_ = false;
DCHECK_LT(block->rpo_number().ToInt(), block_initial_states_.size());
const BlockInitialState* initial_state =
block_initial_states_[block->rpo_number().ToInt()];
if (initial_state) {
if (initial_state->saved_lr_ != saved_lr_) {
eh_frame_writer_.AdvanceLocation(pc_offset);
if (initial_state->saved_lr_) {
eh_frame_writer_.RecordRegisterSavedToStack(lr, kPointerSize);
} else {
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
}
saved_lr_ = initial_state->saved_lr_;
}
} else {
// The entry block always lacks an explicit initial state.
// The exit block may lack an explicit state, if it is only reached by
// the block ending in a bx lr.
// All the other blocks must have an explicit initial state.
DCHECK(block->predecessors().empty() || block->successors().empty());
}
}
void UnwindingInfoWriter::EndInstructionBlock(const InstructionBlock* block) {
if (!enabled() || block_will_exit_) return;
for (const RpoNumber& successor : block->successors()) {
int successor_index = successor.ToInt();
DCHECK_LT(successor_index, block_initial_states_.size());
const BlockInitialState* existing_state =
block_initial_states_[successor_index];
// If we already had an entry for this BB, check that the values are the
// same we are trying to insert.
if (existing_state) {
DCHECK_EQ(existing_state->saved_lr_, saved_lr_);
} else {
block_initial_states_[successor_index] =
new (zone_) BlockInitialState(saved_lr_);
}
}
}
void UnwindingInfoWriter::MarkFrameConstructed(int at_pc) {
if (!enabled()) return;
// Regardless of the type of frame constructed, the relevant part of the
// layout is always the one in the diagram:
//
// | .... | higher addresses
// +----------+ ^
// | LR | | |
// +----------+ | |
// | saved FP | | |
// +----------+ <-- FP v
// | .... | stack growth
//
// The LR is pushed on the stack, and we can record this fact at the end of
// the construction, since the LR itself is not modified in the process.
eh_frame_writer_.AdvanceLocation(at_pc);
eh_frame_writer_.RecordRegisterSavedToStack(lr, kPointerSize);
saved_lr_ = true;
}
void UnwindingInfoWriter::MarkFrameDeconstructed(int at_pc) {
if (!enabled()) return;
// The lr is restored by the last operation in LeaveFrame().
eh_frame_writer_.AdvanceLocation(at_pc);
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
saved_lr_ = false;
}
void UnwindingInfoWriter::MarkLinkRegisterOnTopOfStack(int pc_offset) {
if (!enabled()) return;
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegisterAndOffset(sp, 0);
eh_frame_writer_.RecordRegisterSavedToStack(lr, 0);
}
void UnwindingInfoWriter::MarkPopLinkRegisterFromTopOfStack(int pc_offset) {
if (!enabled()) return;
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegisterAndOffset(fp, 0);
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_ARM_UNWINDING_INFO_WRITER_H_
#define V8_COMPILER_ARM_UNWINDING_INFO_WRITER_H_
#include "src/eh-frame.h"
namespace v8 {
namespace internal {
namespace compiler {
class InstructionBlock;
class UnwindingInfoWriter {
public:
explicit UnwindingInfoWriter(Zone* zone)
: zone_(zone),
eh_frame_writer_(zone),
saved_lr_(false),
block_will_exit_(false),
block_initial_states_(zone) {
if (enabled()) eh_frame_writer_.Initialize();
}
void SetNumberOfInstructionBlocks(int number) {
if (enabled()) block_initial_states_.resize(number);
}
void BeginInstructionBlock(int pc_offset, const InstructionBlock* block);
void EndInstructionBlock(const InstructionBlock* block);
void MarkLinkRegisterOnTopOfStack(int pc_offset);
void MarkPopLinkRegisterFromTopOfStack(int pc_offset);
void MarkFrameConstructed(int at_pc);
void MarkFrameDeconstructed(int at_pc);
void MarkBlockWillExit() { block_will_exit_ = true; }
void Finish(int code_size) {
if (enabled()) eh_frame_writer_.Finish(code_size);
}
EhFrameWriter* eh_frame_writer() {
return enabled() ? &eh_frame_writer_ : nullptr;
}
private:
bool enabled() const { return FLAG_perf_prof_unwinding_info; }
class BlockInitialState : public ZoneObject {
public:
explicit BlockInitialState(bool saved_lr) : saved_lr_(saved_lr) {}
bool saved_lr_;
};
Zone* zone_;
EhFrameWriter eh_frame_writer_;
bool saved_lr_;
bool block_will_exit_;
ZoneVector<const BlockInitialState*> block_initial_states_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
......@@ -308,7 +308,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand index,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode)
RecordWriteMode mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
index_(index),
......@@ -316,7 +317,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
must_save_lr_(!gen->frame_access_state()->has_frame()) {}
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer) {}
void Generate() final {
if (mode_ > RecordWriteMode::kValueIsPointer) {
......@@ -333,6 +335,8 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
if (must_save_lr_) {
// We need to save and restore lr if the frame was elided.
__ Push(lr);
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset(),
__ StackPointer());
}
RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_,
remembered_set_action, save_fp_mode);
......@@ -340,6 +344,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
__ CallStub(&stub);
if (must_save_lr_) {
__ Pop(lr);
unwinding_info_writer_->MarkPopLinkRegisterFromTopOfStack(__ pc_offset());
}
}
......@@ -351,6 +356,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
Register const scratch1_;
RecordWriteMode const mode_;
bool must_save_lr_;
UnwindingInfoWriter* const unwinding_info_writer_;
};
......@@ -544,6 +550,8 @@ void CodeGenerator::AssembleDeconstructFrame() {
__ Mov(jssp, fp);
}
__ Pop(fp, lr);
unwinding_info_writer_.MarkFrameDeconstructed(__ pc_offset());
}
void CodeGenerator::AssemblePrepareTailCall() {
......@@ -659,6 +667,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Add(target, target, Code::kHeaderSize - kHeapObjectTag);
__ Jump(target);
}
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -666,6 +675,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kArchTailCallAddress: {
CHECK(!instr->InputAt(0)->IsImmediate());
__ Jump(i.InputRegister(0));
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -810,8 +820,9 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = new (zone()) OutOfLineRecordWrite(this, object, index, value,
scratch0, scratch1, mode);
auto ool = new (zone())
OutOfLineRecordWrite(this, object, index, value, scratch0, scratch1,
mode, &unwinding_info_writer_);
__ Str(value, MemOperand(object, index));
__ CheckPageFlagSet(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask,
......@@ -1802,6 +1813,10 @@ void CodeGenerator::AssembleConstructFrame() {
frame()->GetTotalFrameSlotCount());
}
}
if (!info()->GeneratePreagedPrologue()) {
unwinding_info_writer_.MarkFrameConstructed(__ pc_offset());
}
}
int shrink_slots = frame()->GetSpillSlotCount();
......@@ -1861,6 +1876,8 @@ void CodeGenerator::AssembleReturn() {
__ PopCPURegList(saves_fp);
}
unwinding_info_writer_.MarkBlockWillExit();
int pop_count = static_cast<int>(descriptor->StackParameterCount());
if (descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame();
......
// Copyright 2016 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.
#include "src/compiler/arm64/unwinding-info-writer-arm64.h"
#include "src/compiler/instruction.h"
namespace v8 {
namespace internal {
namespace compiler {
void UnwindingInfoWriter::BeginInstructionBlock(int pc_offset,
const InstructionBlock* block) {
if (!enabled()) return;
block_will_exit_ = false;
DCHECK_LT(block->rpo_number().ToInt(), block_initial_states_.size());
const BlockInitialState* initial_state =
block_initial_states_[block->rpo_number().ToInt()];
if (initial_state) {
if (initial_state->saved_lr_ != saved_lr_) {
eh_frame_writer_.AdvanceLocation(pc_offset);
if (initial_state->saved_lr_) {
eh_frame_writer_.RecordRegisterSavedToStack(lr, kPointerSize);
} else {
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
}
saved_lr_ = initial_state->saved_lr_;
}
} else {
// The entry block always lacks an explicit initial state.
// The exit block may lack an explicit state, if it is only reached by
// the block ending in a ret.
// All the other blocks must have an explicit initial state.
DCHECK(block->predecessors().empty() || block->successors().empty());
}
}
void UnwindingInfoWriter::EndInstructionBlock(const InstructionBlock* block) {
if (!enabled() || block_will_exit_) return;
for (const RpoNumber& successor : block->successors()) {
int successor_index = successor.ToInt();
DCHECK_LT(successor_index, block_initial_states_.size());
const BlockInitialState* existing_state =
block_initial_states_[successor_index];
// If we already had an entry for this BB, check that the values are the
// same we are trying to insert.
if (existing_state) {
DCHECK_EQ(existing_state->saved_lr_, saved_lr_);
} else {
block_initial_states_[successor_index] =
new (zone_) BlockInitialState(saved_lr_);
}
}
}
void UnwindingInfoWriter::MarkFrameConstructed(int at_pc) {
if (!enabled()) return;
// Regardless of the type of frame constructed, the relevant part of the
// layout is always the one in the diagram:
//
// | .... | higher addresses
// +----------+ ^
// | LR | | |
// +----------+ | |
// | saved FP | | |
// +----------+ <-- FP v
// | .... | stack growth
//
// The LR is pushed on the stack, and we can record this fact at the end of
// the construction, since the LR itself is not modified in the process.
eh_frame_writer_.AdvanceLocation(at_pc);
eh_frame_writer_.RecordRegisterSavedToStack(lr, kPointerSize);
saved_lr_ = true;
}
void UnwindingInfoWriter::MarkFrameDeconstructed(int at_pc) {
if (!enabled()) return;
// The lr is restored by the last operation in LeaveFrame().
eh_frame_writer_.AdvanceLocation(at_pc);
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
saved_lr_ = false;
}
void UnwindingInfoWriter::MarkLinkRegisterOnTopOfStack(int pc_offset,
const Register& sp) {
if (!enabled()) return;
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegisterAndOffset(sp, 0);
eh_frame_writer_.RecordRegisterSavedToStack(lr, 0);
}
void UnwindingInfoWriter::MarkPopLinkRegisterFromTopOfStack(int pc_offset) {
if (!enabled()) return;
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegisterAndOffset(fp, 0);
eh_frame_writer_.RecordRegisterFollowsInitialRule(lr);
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_ARM64_UNWINDING_INFO_WRITER_H_
#define V8_COMPILER_ARM64_UNWINDING_INFO_WRITER_H_
#include "src/eh-frame.h"
namespace v8 {
namespace internal {
namespace compiler {
class InstructionBlock;
class UnwindingInfoWriter {
public:
explicit UnwindingInfoWriter(Zone* zone)
: zone_(zone),
eh_frame_writer_(zone),
saved_lr_(false),
block_will_exit_(false),
block_initial_states_(zone) {
if (enabled()) eh_frame_writer_.Initialize();
}
void SetNumberOfInstructionBlocks(int number) {
if (enabled()) block_initial_states_.resize(number);
}
void BeginInstructionBlock(int pc_offset, const InstructionBlock* block);
void EndInstructionBlock(const InstructionBlock* block);
void MarkLinkRegisterOnTopOfStack(int pc_offset, const Register& sp);
void MarkPopLinkRegisterFromTopOfStack(int pc_offset);
void MarkFrameConstructed(int at_pc);
void MarkFrameDeconstructed(int at_pc);
void MarkBlockWillExit() { block_will_exit_ = true; }
void Finish(int code_size) {
if (enabled()) eh_frame_writer_.Finish(code_size);
}
EhFrameWriter* eh_frame_writer() {
return enabled() ? &eh_frame_writer_ : nullptr;
}
private:
bool enabled() const { return FLAG_perf_prof_unwinding_info; }
class BlockInitialState : public ZoneObject {
public:
explicit BlockInitialState(bool saved_lr) : saved_lr_(saved_lr) {}
bool saved_lr_;
};
Zone* zone_;
EhFrameWriter eh_frame_writer_;
bool saved_lr_;
bool block_will_exit_;
ZoneVector<const BlockInitialState*> block_initial_states_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
......@@ -37,6 +37,7 @@ CodeGenerator::CodeGenerator(Frame* frame, Linkage* linkage,
: frame_access_state_(nullptr),
linkage_(linkage),
code_(code),
unwinding_info_writer_(zone()),
info_(info),
labels_(zone()->NewArray<Label>(code->InstructionBlockCount())),
current_block_(RpoNumber::Invalid()),
......@@ -101,6 +102,9 @@ Handle<Code> CodeGenerator::GenerateCode() {
}
}
unwinding_info_writer_.SetNumberOfInstructionBlocks(
code()->InstructionBlockCount());
// Assemble all non-deferred blocks, followed by deferred ones.
for (int deferred = 0; deferred < 2; ++deferred) {
for (const InstructionBlock* block : code()->instruction_blocks()) {
......@@ -113,6 +117,7 @@ Handle<Code> CodeGenerator::GenerateCode() {
if (block->IsHandler()) EnsureSpaceForLazyDeopt();
// Bind a label for a block.
current_block_ = block->rpo_number();
unwinding_info_writer_.BeginInstructionBlock(masm()->pc_offset(), block);
if (FLAG_code_comments) {
// TODO(titzer): these code comments are a giant memory leak.
Vector<char> buffer = Vector<char>::New(200);
......@@ -163,6 +168,7 @@ Handle<Code> CodeGenerator::GenerateCode() {
result = AssembleBlock(block);
}
if (result != kSuccess) return Handle<Code>();
unwinding_info_writer_.EndInstructionBlock(block);
}
}
......@@ -203,8 +209,10 @@ Handle<Code> CodeGenerator::GenerateCode() {
safepoints()->Emit(masm(), frame()->GetTotalFrameSlotCount());
unwinding_info_writer_.Finish(masm()->pc_offset());
Handle<Code> result = v8::internal::CodeGenerator::MakeCodeEpilogue(
masm(), nullptr, info, Handle<Object>());
masm(), unwinding_info_writer_.eh_frame_writer(), info, Handle<Object>());
result->set_is_turbofanned(true);
result->set_stack_slots(frame()->GetTotalFrameSlotCount());
result->set_safepoint_table_offset(safepoints()->GetCodeOffset());
......
......@@ -7,6 +7,7 @@
#include "src/compiler/gap-resolver.h"
#include "src/compiler/instruction.h"
#include "src/compiler/unwinding-info-writer.h"
#include "src/deoptimizer.h"
#include "src/macro-assembler.h"
#include "src/safepoint-table.h"
......@@ -254,6 +255,7 @@ class CodeGenerator final : public GapResolver::Assembler {
FrameAccessState* frame_access_state_;
Linkage* const linkage_;
InstructionSequence* const code_;
UnwindingInfoWriter unwinding_info_writer_;
CompilationInfo* const info_;
Label* const labels_;
Label return_label_;
......
// Copyright 2014 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.
#ifndef V8_COMPILER_UNWINDING_INFO_WRITER_H_
#define V8_COMPILER_UNWINDING_INFO_WRITER_H_
#if V8_TARGET_ARCH_ARM
#include "src/compiler/arm/unwinding-info-writer-arm.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/compiler/arm64/unwinding-info-writer-arm64.h"
#elif V8_TARGET_ARCH_X64
#include "src/compiler/x64/unwinding-info-writer-x64.h"
#else
// Placeholder for unsupported architectures.
#include "src/base/logging.h"
namespace v8 {
namespace internal {
class EhFrameWriter;
namespace compiler {
class InstructionBlock;
class UnwindingInfoWriter {
public:
explicit UnwindingInfoWriter(Zone* zone) {}
void SetNumberOfInstructionBlocks(int number) {
if (FLAG_perf_prof_unwinding_info) UNIMPLEMENTED();
}
void BeginInstructionBlock(int pc_offset, const InstructionBlock* block) {
if (FLAG_perf_prof_unwinding_info) UNIMPLEMENTED();
}
void EndInstructionBlock(const InstructionBlock* block) {
if (FLAG_perf_prof_unwinding_info) UNIMPLEMENTED();
}
void Finish(int code_size) {}
EhFrameWriter* eh_frame_writer() { return nullptr; }
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
#endif
......@@ -180,19 +180,28 @@ class OutOfLineLoadNaN final : public OutOfLineCode {
class OutOfLineTruncateDoubleToI final : public OutOfLineCode {
public:
OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result,
XMMRegister input)
: OutOfLineCode(gen), result_(result), input_(input) {}
XMMRegister input,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
result_(result),
input_(input),
unwinding_info_writer_(unwinding_info_writer) {}
void Generate() final {
__ subp(rsp, Immediate(kDoubleSize));
unwinding_info_writer_->MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kDoubleSize);
__ Movsd(MemOperand(rsp, 0), input_);
__ SlowTruncateToI(result_, rsp, 0);
__ addp(rsp, Immediate(kDoubleSize));
unwinding_info_writer_->MaybeIncreaseBaseOffsetAt(__ pc_offset(),
-kDoubleSize);
}
private:
Register const result_;
XMMRegister const input_;
UnwindingInfoWriter* const unwinding_info_writer_;
};
......@@ -622,6 +631,7 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} while (false)
void CodeGenerator::AssembleDeconstructFrame() {
unwinding_info_writer_.MarkFrameDeconstructed(__ pc_offset());
__ movq(rsp, rbp);
__ popq(rbp);
}
......@@ -756,6 +766,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ addp(reg, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ jmp(reg);
}
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -764,6 +775,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
CHECK(!HasImmediateInput(instr, 0));
Register reg = i.InputRegister(0);
__ jmp(reg);
unwinding_info_writer_.MarkBlockWillExit();
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
......@@ -872,7 +884,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kArchTruncateDoubleToI: {
auto result = i.OutputRegister();
auto input = i.InputDoubleRegister(0);
auto ool = new (zone()) OutOfLineTruncateDoubleToI(this, result, input);
auto ool = new (zone()) OutOfLineTruncateDoubleToI(
this, result, input, &unwinding_info_writer_);
// We use Cvttsd2siq instead of Cvttsd2si due to performance reasons. The
// use of Cvttsd2siq requires the movl below to avoid sign extension.
__ Cvttsd2siq(result, input);
......@@ -1234,6 +1247,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
break;
case kSSEFloat64Mod: {
__ subq(rsp, Immediate(kDoubleSize));
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kDoubleSize);
// Move values to st(0) and st(1).
__ Movsd(Operand(rsp, 0), i.InputDoubleRegister(1));
__ fld_d(Operand(rsp, 0));
......@@ -1254,7 +1269,11 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ shrl(rax, Immediate(8));
__ andl(rax, Immediate(0xFF));
__ pushq(rax);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
__ popfq();
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
-kPointerSize);
}
__ j(parity_even, &mod_loop);
// Move output to stack and clean up.
......@@ -1262,6 +1281,8 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ fstp_d(Operand(rsp, 0));
__ Movsd(i.OutputDoubleRegister(), Operand(rsp, 0));
__ addq(rsp, Immediate(kDoubleSize));
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
-kDoubleSize);
break;
}
case kSSEFloat64Max:
......@@ -1843,18 +1864,26 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
if (HasImmediateInput(instr, 0)) {
__ pushq(i.InputImmediate(0));
frame_access_state()->IncreaseSPDelta(1);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
} else {
if (instr->InputAt(0)->IsRegister()) {
__ pushq(i.InputRegister(0));
frame_access_state()->IncreaseSPDelta(1);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
} else if (instr->InputAt(0)->IsFPRegister()) {
// TODO(titzer): use another machine instruction?
__ subq(rsp, Immediate(kDoubleSize));
frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kDoubleSize);
__ Movsd(Operand(rsp, 0), i.InputDoubleRegister(0));
} else {
__ pushq(i.InputOperand(0));
frame_access_state()->IncreaseSPDelta(1);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
}
}
break;
......@@ -2147,6 +2176,8 @@ void CodeGenerator::FinishFrame(Frame* frame) {
void CodeGenerator::AssembleConstructFrame() {
CallDescriptor* descriptor = linkage()->GetIncomingDescriptor();
if (frame_access_state()->has_frame()) {
int pc_base = __ pc_offset();
if (descriptor->IsCFunctionCall()) {
__ pushq(rbp);
__ movq(rbp, rsp);
......@@ -2155,6 +2186,10 @@ void CodeGenerator::AssembleConstructFrame() {
} else {
__ StubPrologue(info()->GetOutputStackFrameType());
}
if (!descriptor->IsJSFunctionCall() || !info()->GeneratePreagedPrologue()) {
unwinding_info_writer_.MarkFrameConstructed(pc_base);
}
}
int shrink_slots = frame()->GetSpillSlotCount();
......@@ -2228,6 +2263,8 @@ void CodeGenerator::AssembleReturn() {
__ addp(rsp, Immediate(stack_size));
}
unwinding_info_writer_.MarkBlockWillExit();
if (descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame();
} else if (frame_access_state()->has_frame()) {
......@@ -2416,11 +2453,15 @@ void CodeGenerator::AssembleSwap(InstructionOperand* source,
Register src = g.ToRegister(source);
__ pushq(src);
frame_access_state()->IncreaseSPDelta(1);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
Operand dst = g.ToOperand(destination);
__ movq(src, dst);
frame_access_state()->IncreaseSPDelta(-1);
dst = g.ToOperand(destination);
__ popq(dst);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
-kPointerSize);
} else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
(source->IsFPStackSlot() && destination->IsFPStackSlot())) {
// Memory-memory.
......@@ -2431,12 +2472,16 @@ void CodeGenerator::AssembleSwap(InstructionOperand* source,
Register tmp = kScratchRegister;
__ movq(tmp, dst);
__ pushq(src);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
kPointerSize);
frame_access_state()->IncreaseSPDelta(1);
src = g.ToOperand(source);
__ movq(src, tmp);
frame_access_state()->IncreaseSPDelta(-1);
dst = g.ToOperand(destination);
__ popq(dst);
unwinding_info_writer_.MaybeIncreaseBaseOffsetAt(__ pc_offset(),
-kPointerSize);
} else {
// Use the XOR trick to swap without a temporary.
__ Movups(kScratchDoubleReg, src);
......@@ -2490,7 +2535,7 @@ void CodeGenerator::EnsureSpaceForLazyDeopt() {
int space_needed = Deoptimizer::patch_size();
// Ensure that we have enough space after the previous lazy-bailout
// instruction for patching the code here.
int current_pc = masm()->pc_offset();
int current_pc = __ pc_offset();
if (current_pc < last_lazy_deopt_pc_ + space_needed) {
int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc;
__ Nop(padding_size);
......
// Copyright 2016 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.
#include "src/compiler/x64/unwinding-info-writer-x64.h"
#include "src/compiler/instruction.h"
namespace v8 {
namespace internal {
namespace compiler {
void UnwindingInfoWriter::BeginInstructionBlock(int pc_offset,
const InstructionBlock* block) {
if (!enabled()) return;
block_will_exit_ = false;
DCHECK_LT(block->rpo_number().ToInt(), block_initial_states_.size());
const BlockInitialState* initial_state =
block_initial_states_[block->rpo_number().ToInt()];
if (initial_state) {
if (!initial_state->register_.is(eh_frame_writer_.base_register()) &&
initial_state->offset_ != eh_frame_writer_.base_offset()) {
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegisterAndOffset(initial_state->register_,
initial_state->offset_);
} else if (!initial_state->register_.is(eh_frame_writer_.base_register())) {
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressRegister(initial_state->register_);
} else if (initial_state->offset_ != eh_frame_writer_.base_offset()) {
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.SetBaseAddressOffset(initial_state->offset_);
}
tracking_fp_ = initial_state->tracking_fp_;
} else {
// The entry block always lacks an explicit initial state.
// The exit block may lack an explicit state, if it is only reached by
// the block ending in a ret.
// All the other blocks must have an explicit initial state.
DCHECK(block->predecessors().empty() || block->successors().empty());
}
}
void UnwindingInfoWriter::EndInstructionBlock(const InstructionBlock* block) {
if (!enabled() || block_will_exit_) return;
for (const RpoNumber& successor : block->successors()) {
int successor_index = successor.ToInt();
DCHECK_LT(successor_index, block_initial_states_.size());
const BlockInitialState* existing_state =
block_initial_states_[successor_index];
// If we already had an entry for this BB, check that the values are the
// same we are trying to insert.
if (existing_state) {
DCHECK(existing_state->register_.is(eh_frame_writer_.base_register()));
DCHECK_EQ(existing_state->offset_, eh_frame_writer_.base_offset());
DCHECK_EQ(existing_state->tracking_fp_, tracking_fp_);
} else {
block_initial_states_[successor_index] = new (zone_)
BlockInitialState(eh_frame_writer_.base_register(),
eh_frame_writer_.base_offset(), tracking_fp_);
}
}
}
void UnwindingInfoWriter::MarkFrameConstructed(int pc_base) {
if (!enabled()) return;
// push rbp
eh_frame_writer_.AdvanceLocation(pc_base + 1);
eh_frame_writer_.IncreaseBaseAddressOffset(kInt64Size);
// <base address> points at the bottom of the current frame on x64 and
// <base register> is rsp, which points to the top of the frame by definition.
// Thus, the distance between <base address> and the top is -<base offset>.
int top_of_stack = -eh_frame_writer_.base_offset();
eh_frame_writer_.RecordRegisterSavedToStack(rbp, top_of_stack);
// mov rbp, rsp
eh_frame_writer_.AdvanceLocation(pc_base + 4);
eh_frame_writer_.SetBaseAddressRegister(rbp);
tracking_fp_ = true;
}
void UnwindingInfoWriter::MarkFrameDeconstructed(int pc_base) {
if (!enabled()) return;
// mov rsp, rbp
eh_frame_writer_.AdvanceLocation(pc_base + 3);
eh_frame_writer_.SetBaseAddressRegister(rsp);
// pop rbp
eh_frame_writer_.AdvanceLocation(pc_base + 4);
eh_frame_writer_.IncreaseBaseAddressOffset(-kInt64Size);
tracking_fp_ = false;
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2016 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.
#ifndef V8_COMPILER_X64_UNWINDING_INFO_WRITER_H_
#define V8_COMPILER_X64_UNWINDING_INFO_WRITER_H_
#include "src/eh-frame.h"
namespace v8 {
namespace internal {
namespace compiler {
class InstructionBlock;
class UnwindingInfoWriter {
public:
explicit UnwindingInfoWriter(Zone* zone)
: zone_(zone),
eh_frame_writer_(zone),
tracking_fp_(false),
block_will_exit_(false),
block_initial_states_(zone) {
if (enabled()) eh_frame_writer_.Initialize();
}
void MaybeIncreaseBaseOffsetAt(int pc_offset, int base_delta) {
if (enabled() && !tracking_fp_) {
eh_frame_writer_.AdvanceLocation(pc_offset);
eh_frame_writer_.IncreaseBaseAddressOffset(base_delta);
}
}
void SetNumberOfInstructionBlocks(int number) {
if (enabled()) block_initial_states_.resize(number);
}
void BeginInstructionBlock(int pc_offset, const InstructionBlock* block);
void EndInstructionBlock(const InstructionBlock* block);
void MarkFrameConstructed(int pc_base);
void MarkFrameDeconstructed(int pc_base);
void MarkBlockWillExit() { block_will_exit_ = true; }
void Finish(int code_size) {
if (enabled()) eh_frame_writer_.Finish(code_size);
}
EhFrameWriter* eh_frame_writer() {
return enabled() ? &eh_frame_writer_ : nullptr;
}
private:
bool enabled() const { return FLAG_perf_prof_unwinding_info; }
class BlockInitialState : public ZoneObject {
public:
BlockInitialState(Register reg, int offset, bool tracking_fp)
: register_(reg), offset_(offset), tracking_fp_(tracking_fp) {}
Register register_;
int offset_;
bool tracking_fp_;
};
Zone* zone_;
EhFrameWriter eh_frame_writer_;
bool tracking_fp_;
bool block_will_exit_;
ZoneVector<const BlockInitialState*> block_initial_states_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
......@@ -680,6 +680,7 @@
'compiler/type-hints.h',
'compiler/typer.cc',
'compiler/typer.h',
'compiler/unwinding-info-writer.h',
'compiler/value-numbering-reducer.cc',
'compiler/value-numbering-reducer.h',
'compiler/verifier.cc',
......@@ -1240,6 +1241,8 @@
'compiler/arm/instruction-codes-arm.h',
'compiler/arm/instruction-scheduler-arm.cc',
'compiler/arm/instruction-selector-arm.cc',
'compiler/arm/unwinding-info-writer-arm.h',
'compiler/arm/unwinding-info-writer-arm.cc',
'crankshaft/arm/lithium-arm.cc',
'crankshaft/arm/lithium-arm.h',
'crankshaft/arm/lithium-codegen-arm.cc',
......@@ -1295,6 +1298,8 @@
'compiler/arm64/instruction-codes-arm64.h',
'compiler/arm64/instruction-scheduler-arm64.cc',
'compiler/arm64/instruction-selector-arm64.cc',
'compiler/arm64/unwinding-info-writer-arm64.h',
'compiler/arm64/unwinding-info-writer-arm64.cc',
'crankshaft/arm64/delayed-masm-arm64.cc',
'crankshaft/arm64/delayed-masm-arm64.h',
'crankshaft/arm64/delayed-masm-arm64-inl.h',
......@@ -1520,6 +1525,8 @@
'compiler/x64/instruction-codes-x64.h',
'compiler/x64/instruction-scheduler-x64.cc',
'compiler/x64/instruction-selector-x64.cc',
'compiler/x64/unwinding-info-writer-x64.h',
'compiler/x64/unwinding-info-writer-x64.cc',
'x64/eh-frame-x64.cc',
],
}],
......
......@@ -41,6 +41,7 @@
'compiler/graph-builder-tester.h',
'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc',
'compiler/test-run-unwinding-info.cc',
'compiler/test-gap-resolver.cc',
'compiler/test-graph-visualizer.cc',
'compiler/test-code-assembler.cc',
......
// Copyright 2016 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.
// Test enabled only on supported architectures.
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM) || \
defined(V8_TARGET_ARCH_ARM64)
#include "test/cctest/compiler/function-tester.h"
namespace v8 {
namespace internal {
namespace compiler {
TEST(RunUnwindingInfo) {
FLAG_turbo = true;
FLAG_perf_prof_unwinding_info = true;
FunctionTester tester(
"(function (x) {\n"
" function f(x) { return x*x; }\n"
" return x > 0 ? x+1 : f(x);\n"
"})");
tester.Call(tester.Val(-1));
CHECK(tester.function->code()->has_unwinding_info());
}
// TODO(ssanfilippo) Build low-level graph and check that state is correctly
// restored in the following situation:
//
// +-----------------+
// | no frame |---+
// check that a +-----------------+ |
// a noframe state | construct frame |<--+
// is restored here --> +-----------------+ |
// | construct frame |<--+
// +-----------------+
//
// Same for <construct>/<destruct>/<destruct> (a <construct> status is restored)
// TODO(ssanfilippo) Intentionally reach a BB with different initial states
// and check that the UnwindingInforWriter fails in debug mode:
//
// +----------------+
// +---| State A |
// | +----------------+
// | | State B != A |---+
// | +----------------+ |
// +-->| Failure here |<--+
// +----------------+
} // namespace compiler
} // namespace internal
} // namespace v8
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment