Index: lib/Target/X86/CMakeLists.txt =================================================================== --- lib/Target/X86/CMakeLists.txt +++ lib/Target/X86/CMakeLists.txt @@ -15,6 +15,7 @@ set(sources X86AsmPrinter.cpp X86CallFrameOptimization.cpp + X86CFIInstrInserter.cpp X86ExpandPseudo.cpp X86FastISel.cpp X86FloatingPoint.cpp Index: lib/Target/X86/X86.h =================================================================== --- lib/Target/X86/X86.h +++ lib/Target/X86/X86.h @@ -78,6 +78,11 @@ /// in order to eliminate partial register usage, false dependences on /// the upper portions of registers, and to save code size. FunctionPass *createX86FixupBWInsts(); + +/// Return a pass that inserts CFI instructions in epilogue in order to +/// provide precise unwind info for a machine function. +FunctionPass *createX86CFIInstrInserter(); + } // End llvm namespace #endif Index: lib/Target/X86/X86CFIInstrInserter.cpp =================================================================== --- /dev/null +++ lib/Target/X86/X86CFIInstrInserter.cpp @@ -0,0 +1,392 @@ +//===------ X86CFIInstrInserter.cpp - Insert needed CFI instructions ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass inserts CFI instructions in epilogue and additional CFI +// instructions where needed in order to provide precise unwind info +// for a machine function. +// +//===----------------------------------------------------------------------===// + +#include "X86.h" +#include "X86FrameLowering.h" +#include "X86MachineFunctionInfo.h" +#include "X86Subtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include + +using namespace llvm; + +namespace { +class X86CFIInstrInserter : public MachineFunctionPass { +public: + X86CFIInstrInserter() : MachineFunctionPass(ID) {} + bool runOnMachineFunction(MachineFunction &MF) override; + static char ID; + +private: + const char *getPassName() const override { + return "X86 CFI Instruction Inserter"; + } + + // Holds information about offset and register values + // at the beginning and the end of a MBB + struct MBBInfo { + MachineBasicBlock *MBB; + int BeginOffset; + int EndOffset; + int BeginRegister; + int EndRegister; + bool IsChecked = false; + }; + + // Analyze if this MBB changes or should change offset and register values + // for calculating CFA. If this MBB is the epilogue, InsertCFIInEpilogue() is + // called. + void AnalyzeMBB(MachineFunction &MF); + + // Insert CFI instructions to the beginnings of MBBs, if that insertion is + // needed. CorrectCFA will insert additional CFI instructions if the epilogue + // sets offset or register values that do not apply to MBBs below it. + void CorrectCFA(MachineFunction &MF); + + // Inserts appropriate CFI instructions to a function epilogue + // MBBInfo - contains the epilogue MBB + // SetOffset - the def_cfa_offset set by the prologue (the offset value + // that is valid before the epilogue) + void InsertCFIInEpilogue(struct MBBInfo *MBBInfo, int SetOffset); + + // Check if the machine instruction is marked as FrameDestroy and if it + // changes the value of SP (FP). If it does, updated CFI instructions need + // to be inserted. The MBB that contains this MI is referred to as epilogue. + // This function is called from AnalyzeMBB, in order to decide + // whether InsertCFIInEpilogue should be called for that MBB or not. + bool NeedsUpdatedCFI(MachineFunction &MF, MachineInstr &MI); + + const X86Subtarget *ST; + const X86RegisterInfo *RI; + const X86FrameLowering *TFL; + const TargetRegisterInfo *TRI; + const MCRegisterInfo *MRI; + unsigned StackPtr; + unsigned FramePtr; + bool Is64Bit; + + // A vector of MBBs with additional information needed to insert CFI + // instructions + std::vector MBBInfoList; + + // Initial frame state register + int InitialRegister; + + // Initial frame state offset + int InitialOffset; + + // Were any CFI instructions inserted + bool InsertedCFIInstr = false; +}; + +char X86CFIInstrInserter::ID = 0; +} + +FunctionPass *llvm::createX86CFIInstrInserter() { + return new X86CFIInstrInserter(); +} + +bool X86CFIInstrInserter::runOnMachineFunction(MachineFunction &MF) { + + if (!MF.getMMI().hasDebugInfo()) + return false; + + ST = &MF.getSubtarget(); + RI = ST->getRegisterInfo(); + TFL = ST->getFrameLowering(); + TRI = ST->getRegisterInfo(); + StackPtr = RI->getStackRegister(); + FramePtr = RI->getPtrSizedFrameRegister(MF); + MRI = MF.getMMI().getContext().getRegisterInfo(); + Is64Bit = ST->is64Bit(); + + InitialRegister = RI->getDwarfRegNum(StackPtr, true); + InitialOffset = -RI->getSlotSize(); + + // Update ordinal numbers of MBBs + MF.RenumberBlocks(); + + // Create a list of all MBBs in the function, along with info needed for + // debug info insertion + for (auto &I : MF) { + struct MBBInfo NewMBB; + NewMBB.MBB = &I; + // Set offset and register to initial values for now + NewMBB.BeginOffset = InitialOffset; + NewMBB.EndOffset = InitialOffset; + NewMBB.BeginRegister = InitialRegister; + NewMBB.EndRegister = InitialRegister; + NewMBB.IsChecked = false; + MBBInfoList.push_back(NewMBB); + } + + // Go through all MBBs, starting with the first one, and determine whether + // they are prologue or epilogue and what register and offset values are + // correct for their beginnings and ends + AnalyzeMBB(MF); + + // Insert appropriate CFI instructions for each MBB if CFA calculation rule + // needs to be corrected for that MBB + CorrectCFA(MF); + + MBBInfoList.clear(); + return InsertedCFIInstr; +} + +void X86CFIInstrInserter::AnalyzeMBB(MachineFunction &MF) { + + // Keeps track of MBBs that need to be analyzed + std::queue BlocksToAnalyze; + + BlocksToAnalyze.push(0); + MBBInfoList[0].IsChecked = true; + + while (!BlocksToAnalyze.empty()) { + + int Index = BlocksToAnalyze.front(); + BlocksToAnalyze.pop(); + struct MBBInfo *NewMBBInfo = &MBBInfoList[Index]; + + int AdjustOffset = 0; + auto CFIInstructions = MF.getMMI().getFrameInstructions(); + unsigned CFIIndex; + MCCFIInstruction::OpType Operation; + + bool SetOffset = false; + bool SetRegister = false; + bool ShouldUpdateCFI = false; + + for (auto MBBI = NewMBBInfo->MBB->rbegin(); MBBI != NewMBBInfo->MBB->rend(); + ++MBBI) { + // Check if this MBB has CFI instructions that change rule for CFA + // calculation + if ((TFL->hasFP(MF) && (!SetOffset || !SetRegister)) || + (!TFL->hasFP(MF) && !SetOffset)) { + if (MBBI->isCFIInstruction()) { + CFIIndex = MBBI->getOperand(0).getCFIIndex(); + Operation = CFIInstructions[CFIIndex].getOperation(); + if (Operation == MCCFIInstruction::OpType::OpAdjustCfaOffset) { + AdjustOffset += -CFIInstructions[CFIIndex].getOffset(); + } + // Check the offset value + if (Operation == MCCFIInstruction::OpType::OpDefCfaOffset) { + NewMBBInfo->EndOffset = -CFIInstructions[CFIIndex].getOffset(); + NewMBBInfo->EndOffset += AdjustOffset; + SetOffset = true; + } + // Check the offset and register value + if (Operation == MCCFIInstruction::OpType::OpDefCfa) { + NewMBBInfo->EndRegister = CFIInstructions[CFIIndex].getRegister(); + NewMBBInfo->EndOffset = -CFIInstructions[CFIIndex].getOffset(); + SetOffset = true; + SetRegister = true; + } + // Check the register value + if (Operation == MCCFIInstruction::OpType::OpDefCfaRegister) { + NewMBBInfo->EndRegister = CFIInstructions[CFIIndex].getRegister(); + SetRegister = true; + } + } + } + // Check if this MBB is epilogue + if (!ShouldUpdateCFI) { + ShouldUpdateCFI = NeedsUpdatedCFI(MF, *MBBI); + } + } + // If only .cfi_adjust_cfa_offset is found in the MBB (no def_cfa_offset), + // then the resulting offset value will be value at the beginning + // of the block adjusted by the specified offset + if (!SetOffset && AdjustOffset) { + NewMBBInfo->EndOffset = NewMBBInfo->BeginOffset + AdjustOffset; + } + + if (ShouldUpdateCFI) { + // Set offset that is set by prologue + InsertCFIInEpilogue(NewMBBInfo, NewMBBInfo->EndOffset); + } + + // Set EndOffset and EndRegister values of this MBB to be + // the values at the beginnings of all its successors + for (auto Succ : NewMBBInfo->MBB->successors()) { + int SuccNumber = Succ->getNumber(); + // Check if this successor MBB needs to be analyzed + if ((Succ->getNumber() == Index) || MBBInfoList[SuccNumber].IsChecked) { + continue; + } + // Set offset and register values to the successor MBB + MBBInfoList[SuccNumber].BeginOffset = NewMBBInfo->EndOffset; + MBBInfoList[SuccNumber].EndOffset = NewMBBInfo->EndOffset; + MBBInfoList[SuccNumber].BeginRegister = NewMBBInfo->EndRegister; + MBBInfoList[SuccNumber].EndRegister = NewMBBInfo->EndRegister; + + BlocksToAnalyze.push(SuccNumber); + // Mark this MBB as checked (inserted in the BlocksToAnalyze queue) + MBBInfoList[SuccNumber].IsChecked = true; + } + } +} + +void X86CFIInstrInserter::CorrectCFA(MachineFunction &MF) { + + // Current register value + int CurrentRegister = InitialRegister; + // Current offset value + int CurrentOffset = InitialOffset; + + // Go through all MBBs and insert appropriate CFI instruction if needed + for (unsigned int I = 0; I < MF.size(); I++) { + auto MBBI = MBBInfoList[I].MBB->begin(); + DebugLoc DL = MBBInfoList[I].MBB->findDebugLoc(MBBI); + if (MBBInfoList[I].BeginRegister == CurrentRegister) { + // Insert a cfi_def_cfa_offset instruction at the beginning of this MBB + if (MBBInfoList[I].BeginOffset != CurrentOffset) { + TFL->BuildCFI(*(MBBInfoList[I].MBB), MBBI, DL, + MCCFIInstruction::createDefCfaOffset( + nullptr, MBBInfoList[I].BeginOffset)); + InsertedCFIInstr = true; + } + } else { + // Because offset and register values are different than before, create + // a cfi_def_cfa instruction and insert it at the beginning of the MBB + if (MBBInfoList[I].BeginOffset != CurrentOffset) { + TFL->BuildCFI(*(MBBInfoList[I].MBB), MBBI, DL, + MCCFIInstruction::createDefCfa( + nullptr, MBBInfoList[I].BeginRegister, + MBBInfoList[I].BeginOffset)); + InsertedCFIInstr = true; + } else { + // Because register value is different than before, create a + // cfi_def_cfa_register instruction and insert it at the beginning of + // the MBB + TFL->BuildCFI(*(MBBInfoList[I].MBB), MBBI, DL, + MCCFIInstruction::createDefCfaRegister( + nullptr, MBBInfoList[I].BeginRegister)); + InsertedCFIInstr = true; + } + } + CurrentOffset = MBBInfoList[I].EndOffset; + CurrentRegister = MBBInfoList[I].EndRegister; + } +} + +void X86CFIInstrInserter::InsertCFIInEpilogue(struct MBBInfo *EpilogueInfo, + int SetOffset) { + int CurrentOffset = SetOffset; + MachineBasicBlock::iterator MBBI = EpilogueInfo->MBB->begin(); + + if (TFL->hasFP(*EpilogueInfo->MBB->getParent())) { + while (MBBI != EpilogueInfo->MBB->end()) { + DebugLoc DL = EpilogueInfo->MBB->findDebugLoc(MBBI); + // If frame pointer is used and the instruction pops the frame pointer, + // then add new def_cfa instruction with initial offset and register + // values + if (MBBI->getOpcode() == X86::POP32r || + MBBI->getOpcode() == X86::POP64r) { + if (MBBI->getOperand(0).getReg() == FramePtr) { + MachineBasicBlock::iterator MBBITemp = MBBI; + ++MBBI; + TFL->BuildCFI( + *EpilogueInfo->MBB, MBBI, DL, + MCCFIInstruction::createDefCfa( + nullptr, MRI->getDwarfRegNum(StackPtr, true), InitialOffset)); + EpilogueInfo->EndOffset = InitialOffset; + EpilogueInfo->EndRegister = MRI->getDwarfRegNum(StackPtr, true); + MBBI = MBBITemp; + InsertedCFIInstr = true; + break; + } + } + ++MBBI; + } + } else { + while (MBBI != EpilogueInfo->MBB->end()) { + DebugLoc DL = EpilogueInfo->MBB->findDebugLoc(MBBI); + bool ShouldInsertCFI = false; + MachineBasicBlock::iterator MBBITemp = MBBI; + // If frame pointer is not used and the instruction is pop, then add new + // def_cfa_offset instruction with appropriate offset + if (MBBI->getOpcode() == X86::POP32r || + MBBI->getOpcode() == X86::POP64r) { + CurrentOffset += RI->getSlotSize(); + ShouldInsertCFI = true; + } + // If frame pointer is not used and the instruction is ADD and it changes + // stack pointer value, then add new def_cfa_offset instruction with + // appropriate offset + if (MBBI->getOpcode() == X86::ADD32ri8 || + MBBI->getOpcode() == X86::ADD64ri8 || + MBBI->getOpcode() == X86::ADD32ri) { + if (MBBI->getOperand(0).getReg() == StackPtr) { + CurrentOffset += MBBITemp->getOperand(2).getImm(); + ShouldInsertCFI = true; + } + } + // If frame pointer is not used and the instruction is LEA and it changes + // stack pointer value, then add new def_cfa_offset instruction with + // appropriate offset + if (MBBI->getOpcode() == X86::LEA32r || + MBBI->getOpcode() == X86::LEA64r) { + if (MBBI->getOperand(0).getReg() == StackPtr && + MBBI->getOperand(1).getReg() == StackPtr) { + CurrentOffset += MBBITemp->getOperand(4).getImm(); + ShouldInsertCFI = true; + } + } + if (ShouldInsertCFI) { + ++MBBI; + TFL->BuildCFI( + *EpilogueInfo->MBB, MBBI, DL, + MCCFIInstruction::createDefCfaOffset(nullptr, CurrentOffset)); + EpilogueInfo->EndOffset = CurrentOffset; + MBBI = MBBITemp; + InsertedCFIInstr = true; + } + ++MBBI; + } + } +} + +bool X86CFIInstrInserter::NeedsUpdatedCFI(MachineFunction &MF, + MachineInstr &MBBI) { + if (MBBI.getFlag(MachineInstr::FrameDestroy)) { + if (TFL->hasFP(MF) && + (MBBI.getOpcode() == X86::POP32r || MBBI.getOpcode() == X86::POP64r)) { + if (MBBI.getOperand(0).getReg() == FramePtr) { + return true; + } + } + if (!TFL->hasFP(MF) && + (MBBI.getOpcode() == X86::POP32r || MBBI.getOpcode() == X86::POP64r)) { + return true; + } + if (!TFL->hasFP(MF) && (MBBI.getOpcode() == X86::ADD32ri8 || + MBBI.getOpcode() == X86::ADD64ri8 || + MBBI.getOpcode() == X86::ADD32ri)) { + if (MBBI.getOperand(0).getReg() == StackPtr) { + return true; + } + } + if (!TFL->hasFP(MF) && + (MBBI.getOpcode() == X86::LEA32r || MBBI.getOpcode() == X86::LEA64r)) { + if (MBBI.getOperand(0).getReg() == StackPtr && + MBBI.getOperand(1).getReg() == StackPtr) { + return true; + } + } + } + return false; +} Index: lib/Target/X86/X86FrameLowering.cpp =================================================================== --- lib/Target/X86/X86FrameLowering.cpp +++ lib/Target/X86/X86FrameLowering.cpp @@ -2168,9 +2168,16 @@ assert((!MF.getRegInfo().isLiveIn(ScratchReg2) || SaveScratch2) && "Scratch register is live-in and not saved"); - if (SaveScratch2) + if (SaveScratch2) { BuildMI(checkMBB, DL, TII.get(X86::PUSH32r)) .addReg(ScratchReg2, RegState::Kill); + if (MF.getMMI().hasDebugInfo()) { + MachineBasicBlock::iterator it = checkMBB->getLastNonDebugInstr(); + ++it; + BuildCFI(*checkMBB, it, DL, + MCCFIInstruction::createAdjustCfaOffset(nullptr, SlotSize)); + } + } BuildMI(checkMBB, DL, TII.get(X86::MOV32ri), ScratchReg2) .addImm(TlsOffset); Index: lib/Target/X86/X86TargetMachine.cpp =================================================================== --- lib/Target/X86/X86TargetMachine.cpp +++ lib/Target/X86/X86TargetMachine.cpp @@ -283,4 +283,8 @@ addPass(createX86PadShortFunctions()); addPass(createX86FixupLEAs()); } + + const Triple &TT = TM->getTargetTriple(); + if (!TT.isOSDarwin() && !TT.isOSWindows()) + addPass(createX86CFIInstrInserter()); } Index: test/CodeGen/X86/epilogue-cfi-fp.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/epilogue-cfi-fp.ll @@ -0,0 +1,56 @@ +; RUN: llc < %s | FileCheck %s + +; ModuleID = 'epilogue-cfi-fp.c' +target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128" +target triple = "i686-pc-linux" + +; Function Attrs: nounwind +define i32 @foo(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m) #0 { + +; CHECK-LABEL: foo: +; CHECK: popl %ebp +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa %esp, 4 +; CHECK-NEXT: retl + + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + %5 = alloca i32, align 4 + store i32 %i, i32* %1, align 4 + store i32 %j, i32* %2, align 4 + store i32 %k, i32* %3, align 4 + store i32 %l, i32* %4, align 4 + store i32 %m, i32* %5, align 4 + ret i32 0 +} + +; Function Attrs: nounwind +define i32 @main() #0 { + +; CHECK-LABEL: main: +; CHECK: popl %ebp +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa %esp, 4 +; CHECK-NEXT: retl + + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 @foo(i32 1, i32 2, i32 3, i32 4, i32 5) + ret i32 %2 +} + +attributes #0 = {"no-frame-pointer-elim"="true"} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!11, !12, !13} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 (http://llvm.org/git/clang.git d7ffc218c4adde4846b58b3aa4b56312f9442d61) (http://llvm.org/git/llvm.git 8a96b704a3c73c081bf72f9bed1e5db5b4635d5e)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "epilogue-cfi-fp.c", directory: "epilogue-dwarf/test") +!2 = !{} +!3 = !{} +!11 = !{i32 2, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"PIC Level", i32 2} + Index: test/CodeGen/X86/epilogue-cfi-no-fp.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/epilogue-cfi-no-fp.ll @@ -0,0 +1,44 @@ +; RUN: llc < %s | FileCheck %s + +; ModuleID = 'epilogue-cfi-no-fp.c' +target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128" +target triple = "i686-pc-linux" + +; CHECK: addl $20, %esp +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa_offset 12 +; CHECK-NEXT: popl +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: popl +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa_offset 4 +; CHECK-NEXT: ret +; +; Function Attrs: nounwind +define i32 @foo(i32 %i, i32 %j, i32 %k, i32 %l, i32 %m) { +entry: + %i.addr = alloca i32, align 4 + %j.addr = alloca i32, align 4 + %k.addr = alloca i32, align 4 + %l.addr = alloca i32, align 4 + %m.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + store i32 %j, i32* %j.addr, align 4 + store i32 %k, i32* %k.addr, align 4 + store i32 %l, i32* %l.addr, align 4 + store i32 %m, i32* %m.addr, align 4 + ret i32 0 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!11, !12, !13} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 (http://llvm.org/git/clang.git 97b13649469111ebc7970314c0857d86f8f7b720) (http://llvm.org/git/llvm.git 4ced44d3d77424108ec2cad0f1baada9c4bc837e)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "epilogue-cfi-no-fp.c", directory: "epilogue-dwarf/test") +!2 = !{} +!3 = !{} +!11 = !{i32 2, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"PIC Level", i32 2} + Index: test/CodeGen/X86/throws-cfi-fp.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/throws-cfi-fp.ll @@ -0,0 +1,127 @@ +; RUN: llc %s -o - | FileCheck %s + +; ModuleID = 'throws-cfi-fp.cpp' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +$__clang_call_terminate = comdat any + +@_ZL11ShouldThrow = internal global i8 0, align 1 +@_ZTIi = external constant i8* +@.str = private unnamed_addr constant [21 x i8] c"Threw an exception!\0A\00", align 1 + +; Function Attrs: uwtable +define void @_Z6throwsv() #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { + +; CHECK-LABEL: _Z6throwsv: +; CHECK: popq %rbp +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa %rsp, 8 +; CHECK-NEXT: retq +; CHECK-NEXT: .LBB0_2: +; CHECK-NEXT: : +; CHECK-NEXT: .cfi_def_cfa %rbp, 16 + + %1 = alloca i8* + %2 = alloca i32 + %3 = load i8, i8* @_ZL11ShouldThrow, align 1 + %4 = trunc i8 %3 to i1 + br i1 %4, label %5, label %17 + +;