Skip to content

Commit

Permalink
Special handling for GNU_args_size call frame instruction.
Browse files Browse the repository at this point in the history
Summary:
GNU_args_size is a special kind of CFI that tells runtime to adjust
%rsp when control is passed to a landing pad. It is used for annotating
call instructions that pass (extra) parameters on the stack and there's
a corresponding landing pad.

It is also special in a way that its value is not handled by
DW_CFA_remember_state/DW_CFA_restore_state instruction sequence
that we utilize to restore the state after block re-ordering.

This diff adds association of call instructions with GNU_args_size value
when it's used. If the function does not use GNU_args_size, there is
no overhead. Otherwise, we regenerate GNU_args_size instruction during
code emission, i.e. after all optimizations and block-reordering.

(cherry picked from FBD3201322)
  • Loading branch information
maksfb committed Apr 20, 2016
1 parent ad344c4 commit 4f44d60
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 50 deletions.
62 changes: 55 additions & 7 deletions bolt/BinaryFunction.cpp
Expand Up @@ -213,6 +213,9 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
else
OS << '0';
OS << "; action: " << Action;
auto GnuArgsSize = BC.MIA->getGnuArgsSize(Instruction);
if (GnuArgsSize >= 0)
OS << "; GNU_args_size = " << GnuArgsSize;
}
}
if (opts::PrintDebugInfo && LineTable) {
Expand Down Expand Up @@ -825,6 +828,9 @@ bool BinaryFunction::buildCFG() {
// Update the state.
CurrentState = State::CFG;

// Annotate invoke instructions with GNU_args_size data.
propagateGnuArgsSizeInfo();

return true;
}

Expand Down Expand Up @@ -939,15 +945,13 @@ void BinaryFunction::annotateCFIState() {
++HighestState;
if (CFI->getOperation() == MCCFIInstruction::OpRememberState) {
StateStack.push(State);
continue;
}
if (CFI->getOperation() == MCCFIInstruction::OpRestoreState) {
} else if (CFI->getOperation() == MCCFIInstruction::OpRestoreState) {
assert(!StateStack.empty() && "Corrupt CFI stack");
State = StateStack.top();
StateStack.pop();
continue;
} else if (CFI->getOperation() != MCCFIInstruction::OpGnuArgsSize) {
State = HighestState;
}
State = HighestState;
}
}

Expand Down Expand Up @@ -995,8 +999,12 @@ bool BinaryFunction::fixCFIState() {
}

for (auto CFI : NewCFIs) {
InsertIt = addCFIPseudo(InBB, InsertIt, CFI);
++InsertIt;
// Ignore GNU_args_size instructions.
if (FrameInstructions[CFI].getOperation() !=
MCCFIInstruction::OpGnuArgsSize) {
InsertIt = addCFIPseudo(InBB, InsertIt, CFI);
++InsertIt;
}
}

return true;
Expand Down Expand Up @@ -1658,5 +1666,45 @@ void BinaryFunction::splitFunction() {
}
}

void BinaryFunction::propagateGnuArgsSizeInfo() {
assert(CurrentState == State::CFG && "unexpected function state");

if (!hasEHRanges() || !usesGnuArgsSize())
return;

// The current value of DW_CFA_GNU_args_size affects all following
// invoke instructions untill the next CFI overrides it.
// It is important to iterate basic blocks in the original order when
// assigning the value.
uint64_t CurrentGnuArgsSize = 0;
for (auto &BB : BasicBlocks) {
for (auto II = BB.begin(); II != BB.end(); ) {
auto &Instr = *II;
if (BC.MIA->isCFI(Instr)) {
auto CFI = getCFIFor(Instr);
if (CFI->getOperation() == MCCFIInstruction::OpGnuArgsSize) {
CurrentGnuArgsSize = CFI->getOffset();
// Delete DW_CFA_GNU_args_size instructions and only regenerate
// during the final code emission. The information is embedded
// inside call instructions.
II = BB.Instructions.erase(II);
} else {
++II;
}
continue;
}

if (BC.MIA->isInvoke(Instr)) {
// Add the value of GNU_args_size as an extra operand if landing pad
// is non-emptry.
if (BC.MIA->getEHInfo(Instr).first) {
Instr.addOperand(MCOperand::createImm(CurrentGnuArgsSize));
}
}
++II;
}
}
}

} // namespace bolt
} // namespace llvm
43 changes: 32 additions & 11 deletions bolt/BinaryFunction.h
Expand Up @@ -122,13 +122,6 @@ class BinaryFunction : public AddressRangesOwner {
/// Alignment requirements for the function.
uint64_t Alignment{1};

/// True if this function needs to be emitted in two separate parts, one for
/// the hot basic blocks and another for the cold basic blocks.
bool IsSplit{false};

/// Indicate if this function has associated exception handling metadata.
bool HasEHRanges{false};

MCSymbol *PersonalityFunction{nullptr};
uint8_t PersonalityEncoding{dwarf::DW_EH_PE_sdata4 | dwarf::DW_EH_PE_pcrel};

Expand All @@ -138,6 +131,16 @@ class BinaryFunction : public AddressRangesOwner {
/// flow graph and re-assemble.
bool IsSimple{true};

/// True if this function needs to be emitted in two separate parts, one for
/// the hot basic blocks and another for the cold basic blocks.
bool IsSplit{false};

/// Indicate if this function has associated exception handling metadata.
bool HasEHRanges{false};

/// True if the function uses DW_CFA_GNU_args_size CFIs.
bool UsesGnuArgsSize{false};

/// The address for the code for this function in codegen memory.
uint64_t ImageAddress{0};

Expand Down Expand Up @@ -440,10 +443,21 @@ class BinaryFunction : public AddressRangesOwner {
return IsSimple;
}

/// Return true if the function body is non-contiguous.
bool isSplit() const {
return IsSplit;
}

/// Return true if the function has exception handling tables.
bool hasEHRanges() const {
return HasEHRanges;
}

/// Return true if the function uses DW_CFA_GNU_args_size CFIs.
bool usesGnuArgsSize() const {
return UsesGnuArgsSize;
}

MCSymbol *getPersonalityFunction() const {
return PersonalityFunction;
}
Expand Down Expand Up @@ -531,7 +545,8 @@ class BinaryFunction : public AddressRangesOwner {
// with NOPs and then reorder it away.
// We fix this by moving the CFI instruction just before any NOPs.
auto I = Instructions.lower_bound(Offset);
if (I == Instructions.end() && Offset == getSize()) {
if (Offset == getSize()) {
assert(I == Instructions.end() && "unexpected iterator value");
// Sometimes compiler issues restore_state after all instructions
// in the function (even after nop).
--I;
Expand Down Expand Up @@ -593,6 +608,11 @@ class BinaryFunction : public AddressRangesOwner {
return *this;
}

BinaryFunction &setUsesGnuArgsSize(bool Uses = true) {
UsesGnuArgsSize = Uses;
return *this;
}

BinaryFunction &setPersonalityFunction(uint64_t Addr) {
PersonalityFunction = BC.getOrCreateGlobalSymbol(Addr, "FUNCat");
return *this;
Expand Down Expand Up @@ -737,6 +757,10 @@ class BinaryFunction : public AddressRangesOwner {
/// is corrupted. If it is unable to fix it, it returns false.
bool fixCFIState();

/// Associate DW_CFA_GNU_args_size info with invoke instructions
/// (call instructions with non-empty landing pad).
void propagateGnuArgsSizeInfo();

/// Traverse the CFG checking branches, inverting their condition, removing or
/// adding jumps based on a new layout order.
void fixBranches();
Expand All @@ -751,9 +775,6 @@ class BinaryFunction : public AddressRangesOwner {
/// Update exception handling ranges for the function.
void updateEHRanges();

/// Return true if the function has exception handling tables.
bool hasEHRanges() const { return HasEHRanges; }

/// Emit exception handling ranges for the function.
void emitLSDA(MCStreamer *Streamer);

Expand Down
1 change: 1 addition & 0 deletions bolt/Exceptions.cpp
Expand Up @@ -611,6 +611,7 @@ bool CFIReaderWriter::fillCFIInfoFor(BinaryFunction &Function) const {
Function.addCFIInstruction(
Offset,
MCCFIInstruction::createGnuArgsSize(nullptr, Instr.Ops[0]));
Function.setUsesGnuArgsSize();
break;
case DW_CFA_val_offset_sf:
case DW_CFA_val_offset:
Expand Down
74 changes: 42 additions & 32 deletions bolt/RewriteInstance.cpp
Expand Up @@ -1168,6 +1168,7 @@ void emitFunction(MCStreamer &Streamer, BinaryFunction &Function,
"first basic block should never be cold");

// Emit code.
int64_t CurrentGnuArgsSize = 0;
for (auto BB : Function.layout()) {
if (EmitColdPart != BB->isCold())
continue;
Expand All @@ -1189,41 +1190,50 @@ void emitFunction(MCStreamer &Streamer, BinaryFunction &Function,
Streamer.EmitLabel(const_cast<MCSymbol *>(Label));
continue;
}
if (!BC.MIA->isCFI(Instr)) {
if (opts::UpdateDebugSections) {
auto RowReference = DebugLineTableRowRef::fromSMLoc(Instr.getLoc());
if (RowReference != DebugLineTableRowRef::NULL_ROW &&
Instr.getLoc().getPointer() != LastLocSeen.getPointer()) {
auto CompileUnit =
BC.OffsetToDwarfCU[RowReference.DwCompileUnitIndex];
assert(CompileUnit &&
"Invalid CU offset set in instruction debug info.");

auto OriginalLineTable =
BC.DwCtx->getLineTableForUnit(
CompileUnit);
const auto &OriginalRow =
OriginalLineTable->Rows[RowReference.RowIndex - 1];

BC.Ctx->setCurrentDwarfLoc(
OriginalRow.File,
OriginalRow.Line,
OriginalRow.Column,
(DWARF2_FLAG_IS_STMT * OriginalRow.IsStmt) |
(DWARF2_FLAG_BASIC_BLOCK * OriginalRow.BasicBlock) |
(DWARF2_FLAG_PROLOGUE_END * OriginalRow.PrologueEnd) |
(DWARF2_FLAG_EPILOGUE_BEGIN * OriginalRow.EpilogueBegin),
OriginalRow.Isa,
OriginalRow.Discriminator);
BC.Ctx->setDwarfCompileUnitID(CompileUnit->getOffset());
LastLocSeen = Instr.getLoc();
}
if (BC.MIA->isCFI(Instr)) {
emitCFIInstr(*Function.getCFIFor(Instr));
continue;
}
if (opts::UpdateDebugSections) {
auto RowReference = DebugLineTableRowRef::fromSMLoc(Instr.getLoc());
if (RowReference != DebugLineTableRowRef::NULL_ROW &&
Instr.getLoc().getPointer() != LastLocSeen.getPointer()) {
auto CompileUnit =
BC.OffsetToDwarfCU[RowReference.DwCompileUnitIndex];
assert(CompileUnit &&
"Invalid CU offset set in instruction debug info.");

auto OriginalLineTable =
BC.DwCtx->getLineTableForUnit(
CompileUnit);
const auto &OriginalRow =
OriginalLineTable->Rows[RowReference.RowIndex - 1];

BC.Ctx->setCurrentDwarfLoc(
OriginalRow.File,
OriginalRow.Line,
OriginalRow.Column,
(DWARF2_FLAG_IS_STMT * OriginalRow.IsStmt) |
(DWARF2_FLAG_BASIC_BLOCK * OriginalRow.BasicBlock) |
(DWARF2_FLAG_PROLOGUE_END * OriginalRow.PrologueEnd) |
(DWARF2_FLAG_EPILOGUE_BEGIN * OriginalRow.EpilogueBegin),
OriginalRow.Isa,
OriginalRow.Discriminator);
BC.Ctx->setDwarfCompileUnitID(CompileUnit->getOffset());
LastLocSeen = Instr.getLoc();
}
}

Streamer.EmitInstruction(Instr, *BC.STI);
continue;
// Emit GNU_args_size CFIs as necessary.
if (Function.usesGnuArgsSize() && BC.MIA->isInvoke(Instr)) {
auto NewGnuArgsSize = BC.MIA->getGnuArgsSize(Instr);
if (NewGnuArgsSize >= 0 && NewGnuArgsSize != CurrentGnuArgsSize) {
CurrentGnuArgsSize = NewGnuArgsSize;
Streamer.EmitCFIGnuArgsSize(CurrentGnuArgsSize);
}
}
emitCFIInstr(*Function.getCFIFor(Instr));

Streamer.EmitInstruction(Instr, *BC.STI);
}

MCSymbol *BBEndLabel = BC.Ctx->createTempSymbol();
Expand Down

0 comments on commit 4f44d60

Please sign in to comment.