Regenerate exception handling information after optimizations.

Summary:
Regenerate exception handling information after optimizations.
Use '-print-eh-ranges' to see CFG with updated ranges.

(cherry picked from FBD2660982)
This commit is contained in:
Maksim Panchenko
2015-11-13 14:18:45 -08:00
parent 56cca2fb5b
commit bc9d6e3b6c
4 changed files with 197 additions and 34 deletions

View File

@@ -100,14 +100,26 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
uint64_t Offset{0};
auto printInstruction = [&](const MCInst &Instruction) {
if (BC.MIA->isEHLabel(Instruction)) {
OS << " EH_LABEL: "
<< cast<MCSymbolRefExpr>(Instruction.getOperand(0).getExpr())->
getSymbol()
<< '\n';
return;
}
OS << format(" %08" PRIx64 ": ", Offset);
BC.InstPrinter->printInst(&Instruction, OS, "", *BC.STI);
if (BC.MIA->isCall(Instruction)) {
if (BC.MIA->isTailCall(Instruction))
OS << " # TAILCALL ";
if (Instruction.getNumOperands() > 1) {
OS << " # handler: " << Instruction.getOperand(1);
OS << "; action: " << Instruction.getOperand(2);
OS << " # handler: ";
if (Instruction.getOperand(1).isExpr())
OS << cast<MCSymbolRefExpr>(Instruction.getOperand(1).getExpr())->
getSymbol();
else
OS << '0';
OS << "; action: " << Instruction.getOperand(2).getImm();
}
}
OS << "\n";
@@ -190,34 +202,46 @@ void BinaryFunction::print(raw_ostream &OS, std::string Annotation,
OS << '\n';
}
if (FrameInstructions.empty()) {
OS << "End of Function \"" << getName() << "\"\n";
return;
}
OS << "DWARF CFI Instructions:\n";
for (auto &CFIInstr : FrameInstructions) {
OS << format(" %08x: ", CFIInstr.first);
switch(CFIInstr.second.getOperation()) {
case MCCFIInstruction::OpSameValue: OS << "OpSameValue"; break;
case MCCFIInstruction::OpRememberState: OS << "OpRememberState"; break;
case MCCFIInstruction::OpRestoreState: OS << "OpRestoreState"; break;
case MCCFIInstruction::OpOffset: OS << "OpOffset"; break;
case MCCFIInstruction::OpDefCfaRegister: OS << "OpDefCfaRegister"; break;
case MCCFIInstruction::OpDefCfaOffset: OS << "OpDefCfaOffset"; break;
case MCCFIInstruction::OpDefCfa: OS << "OpDefCfa"; break;
case MCCFIInstruction::OpRelOffset: OS << "OpRelOffset"; break;
case MCCFIInstruction::OpAdjustCfaOffset: OS << "OfAdjustCfaOffset"; break;
case MCCFIInstruction::OpEscape: OS << "OpEscape"; break;
case MCCFIInstruction::OpRestore: OS << "OpRestore"; break;
case MCCFIInstruction::OpUndefined: OS << "OpUndefined"; break;
case MCCFIInstruction::OpRegister: OS << "OpRegister"; break;
case MCCFIInstruction::OpWindowSave: OS << "OpWindowSave"; break;
// Dump new exception ranges for the function.
if (!CallSites.empty()) {
OS << "EH table:\n";
for (auto &CSI : CallSites) {
OS << " [" << *CSI.Start << ", " << *CSI.End << ") landing pad : ";
if (CSI.LP)
OS << *CSI.LP;
else
OS << "0";
OS << ", action : " << CSI.Action << '\n';
}
OS << "\n";
OS << '\n';
}
OS << "End of Function \"" << getName() << "\"\n";
if (!FrameInstructions.empty()) {
OS << "DWARF CFI Instructions:\n";
for (auto &CFIInstr : FrameInstructions) {
OS << format(" %08x: ", CFIInstr.first);
switch(CFIInstr.second.getOperation()) {
case MCCFIInstruction::OpSameValue: OS << "OpSameValue"; break;
case MCCFIInstruction::OpRememberState: OS << "OpRememberState"; break;
case MCCFIInstruction::OpRestoreState: OS << "OpRestoreState"; break;
case MCCFIInstruction::OpOffset: OS << "OpOffset"; break;
case MCCFIInstruction::OpDefCfaRegister: OS << "OpDefCfaRegister"; break;
case MCCFIInstruction::OpDefCfaOffset: OS << "OpDefCfaOffset"; break;
case MCCFIInstruction::OpDefCfa: OS << "OpDefCfa"; break;
case MCCFIInstruction::OpRelOffset: OS << "OpRelOffset"; break;
case MCCFIInstruction::OpAdjustCfaOffset:OS << "OfAdjustCfaOffset"; break;
case MCCFIInstruction::OpEscape: OS << "OpEscape"; break;
case MCCFIInstruction::OpRestore: OS << "OpRestore"; break;
case MCCFIInstruction::OpUndefined: OS << "OpUndefined"; break;
case MCCFIInstruction::OpRegister: OS << "OpRegister"; break;
case MCCFIInstruction::OpWindowSave: OS << "OpWindowSave"; break;
}
OS << '\n';
}
OS << '\n';
}
OS << "End of Function \"" << getName() << "\"\n\n";
}
bool BinaryFunction::disassemble(ArrayRef<uint8_t> FunctionData) {

View File

@@ -182,6 +182,15 @@ private:
using CFIInstrMapType = std::multimap<uint32_t, MCCFIInstruction>;
CFIInstrMapType FrameInstructions;
/// Exception handling ranges.
struct CallSite {
const MCSymbol *Start;
const MCSymbol *End;
const MCSymbol *LP;
uint64_t Action;
};
std::vector<CallSite> CallSites;
// Blocks are kept sorted in the layout order. If we need to change the
// layout (if BasicBlocksLayout stores a different order than BasicBlocks),
// the terminating instructions need to be modified.
@@ -469,6 +478,9 @@ public:
/// Process LSDA information for the function.
void parseLSDA(ArrayRef<uint8_t> LSDAData, uint64_t LSDAAddress);
/// Update exception handling ranges for the function.
void updateEHRanges();
virtual ~BinaryFunction() {}
};

View File

@@ -332,7 +332,7 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
if (Label != Labels.end()) {
LPSymbol = Label->second;
} else {
LPSymbol = BC.Ctx->createTempSymbol("LP");
LPSymbol = BC.Ctx->createTempSymbol("LP", true);
Labels[LandingPad] = LPSymbol;
}
LandingPads.insert(LPSymbol);
@@ -429,6 +429,108 @@ void BinaryFunction::parseLSDA(ArrayRef<uint8_t> LSDASectionData,
errs() << '\n';
}
void BinaryFunction::updateEHRanges() {
assert(CurrentState == State::CFG && "unexpected state");
// Build call sites table.
struct EHInfo {
const MCSymbol *LP; // landing pad
uint64_t Action;
};
// Markers for begining and the end of exceptions range.
const MCSymbol *StartRange{nullptr};
const MCSymbol *EndRange{nullptr};
// If previous call can throw, this is its exception handler.
EHInfo PreviousEH = {nullptr, 0};
for(auto &BB : BasicBlocksLayout) {
for (auto II = BB->begin(); II != BB->end(); ++II) {
auto Instr = *II;
if (!BC.MIA->isCall(Instr))
continue;
// Instruction can throw an exception that should be handled.
bool Throws = Instr.getNumOperands() > 1;
// Ignore the call if it's a continuation of a no-throw gap.
if (!Throws && !StartRange)
continue;
// Extract exception handling information from the instruction.
const MCSymbol *LP =
Throws ? (Instr.getOperand(1).isExpr()
? &(cast<MCSymbolRefExpr>(
Instr.getOperand(1).getExpr())->getSymbol())
: nullptr)
: nullptr;
uint64_t Action = Throws ? Instr.getOperand(2).getImm() : 0;
// No action if the exception handler has not changed.
if (Throws &&
StartRange &&
PreviousEH.LP == LP &&
PreviousEH.Action == Action)
continue;
// Same symbol is used for the beginning and the end of the range.
const MCSymbol *EHSymbol = BC.Ctx->createTempSymbol("EH", true);
MCInst EHLabel;
BC.MIA->createEHLabel(EHLabel, EHSymbol, BC.Ctx.get());
II = BB->Instructions.insert(II, EHLabel);
++II;
// At this point we could be in the one of the following states:
//
// I. Exception handler has changed and we need to close the prev range
// and start the new one.
//
// II. Start the new exception range after the gap.
//
// III. Close exception range and start the new gap.
if (StartRange) {
// I, III:
EndRange = EHSymbol;
} else {
// II:
StartRange = EHSymbol;
EndRange = nullptr;
}
// Close the previous range.
if (EndRange) {
assert(StartRange && "beginning of the range expected");
CallSites.emplace_back(CallSite{StartRange, EndRange,
PreviousEH.LP, PreviousEH.Action});
EndRange = nullptr;
}
if (Throws) {
// I, II:
StartRange = EHSymbol;
PreviousEH = EHInfo{LP, Action};
} else {
StartRange = nullptr;
}
}
}
// Check if we need to close the range.
if (StartRange) {
assert(!EndRange && "unexpected end of range");
EndRange = BC.Ctx->createTempSymbol("EH", true);
MCInst EHLabel;
BC.MIA->createEHLabel(EHLabel, EndRange, BC.Ctx.get());
BasicBlocksLayout.back()->Instructions.emplace_back(EHLabel);
CallSites.emplace_back(CallSite{StartRange, EndRange,
PreviousEH.LP, PreviousEH.Action});
}
}
const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0;
const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f;

View File

@@ -127,6 +127,11 @@ static cl::opt<bool>
PrintDisasm("print-disasm", cl::desc("print function after disassembly"),
cl::Hidden);
static cl::opt<bool>
PrintEHRanges("print-eh-ranges",
cl::desc("print function with updated exception ranges"),
cl::Hidden);
static cl::opt<bool>
PrintReordered("print-reordered",
cl::desc("print functions after layout optimization"),
@@ -544,7 +549,13 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) {
(SectionContents.data()) + FunctionOffset,
Function.getSize());
if (!Function.disassemble(FunctionData) || !Function.isSimple())
if (!Function.disassemble(FunctionData))
continue;
if (opts::PrintAll || opts::PrintDisasm)
Function.print(errs(), "after disassembly");
if (!Function.isSimple())
continue;
// Fill in CFI information for this function
@@ -552,12 +563,8 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) {
DwCFIReader.fillCFIInfoFor(Function);
// Parse LSDA.
if (Function.getLSDAAddress() != 0) {
if (Function.getLSDAAddress() != 0)
Function.parseLSDA(LSDAData, LSDAAddress);
}
if (opts::PrintAll || opts::PrintDisasm)
Function.print(errs(), "after disassembly");
if (!Function.buildCFG())
continue;
@@ -586,6 +593,9 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) {
if (!opts::shouldProcess(Function.getName()))
continue;
if (!Function.isSimple())
continue;
// Detect and eliminate unreachable basic blocks. We could have those
// filled with nops and they are used for alignment.
//
@@ -639,6 +649,12 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) {
if (opts::PrintAll || opts::PrintReordered)
Function.print(errs(), "after reordering blocks");
}
// Post-processing passes.
Function.updateEHRanges();
if (opts::PrintAll || opts::PrintEHRanges) {
Function.print(errs(), "after updating EH ranges");
}
}
std::error_code EC;
@@ -719,6 +735,15 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) {
Streamer->EmitCodeAlignment(BB->getAlignment());
Streamer->EmitLabel(BB->getLabel());
for (const auto &Instr : *BB) {
// Handle pseudo instructions.
if (BC->MIA->isEHLabel(Instr)) {
assert(Instr.getNumOperands() == 1 && Instr.getOperand(0).isExpr() &&
"bad EH_LABEL instruction");
auto Label = &(cast<MCSymbolRefExpr>(
Instr.getOperand(0).getExpr())->getSymbol());
Streamer->EmitLabel(const_cast<MCSymbol *>(Label));
continue;
}
Streamer->EmitInstruction(Instr, *BC->STI);
}
}