Special handling for GNU_args_size call frame instruction.

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)
This commit is contained in:
Maksim Panchenko
2016-04-19 22:00:29 -07:00
parent ad344c4387
commit 4f44d60947
4 changed files with 131 additions and 51 deletions

View File

@@ -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) {
@@ -825,6 +828,9 @@ bool BinaryFunction::buildCFG() {
// Update the state.
CurrentState = State::CFG;
// Annotate invoke instructions with GNU_args_size data.
propagateGnuArgsSizeInfo();
return true;
}
@@ -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;
}
}
@@ -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;
@@ -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