#include "X86MCTargetDesc.h"
#include "X86TargetStreamer.h"
#include "llvm/DebugInfo/CodeView/CodeView.h"
#include "llvm/MC/MCCodeView.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/FormattedStream.h"
using namespace llvm;
using namespace llvm::codeview;
namespace {
class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
formatted_raw_ostream &OS;
MCInstPrinter &InstPrinter;
public:
X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
MCInstPrinter &InstPrinter)
: X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}
bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
SMLoc L) override;
bool emitFPOEndPrologue(SMLoc L) override;
bool emitFPOEndProc(SMLoc L) override;
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
};
struct FPOInstruction {
MCSymbol *Label;
enum Operation {
PushReg,
StackAlloc,
StackAlign,
SetFrame,
} Op;
unsigned RegOrOffset;
};
struct FPOData {
const MCSymbol *Function = nullptr;
MCSymbol *Begin = nullptr;
MCSymbol *PrologueEnd = nullptr;
MCSymbol *End = nullptr;
unsigned ParamsSize = 0;
SmallVector<FPOInstruction, 5> Instructions;
};
class X86WinCOFFTargetStreamer : public X86TargetStreamer {
DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;
std::unique_ptr<FPOData> CurFPOData;
bool haveOpenFPOData() { return !!CurFPOData; }
bool checkInFPOPrologue(SMLoc L);
MCSymbol *emitFPOLabel();
MCContext &getContext() { return getStreamer().getContext(); }
public:
X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
SMLoc L) override;
bool emitFPOEndPrologue(SMLoc L) override;
bool emitFPOEndProc(SMLoc L) override;
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
};
}
bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
unsigned ParamsSize, SMLoc L) {
OS << "\t.cv_fpo_proc\t";
ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
OS << ' ' << ParamsSize << '\n';
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
OS << "\t.cv_fpo_endprologue\n";
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
OS << "\t.cv_fpo_endproc\n";
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
SMLoc L) {
OS << "\t.cv_fpo_data\t";
ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
OS << '\n';
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
OS << "\t.cv_fpo_pushreg\t";
InstPrinter.printRegName(OS, Reg);
OS << '\n';
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
SMLoc L) {
OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
return false;
}
bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
OS << "\t.cv_fpo_setframe\t";
InstPrinter.printRegName(OS, Reg);
OS << '\n';
return false;
}
bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
getContext().reportError(
L,
"directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
return true;
}
return false;
}
MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
MCSymbol *Label = getContext().createTempSymbol("cfi", true);
getStreamer().emitLabel(Label);
return Label;
}
bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
unsigned ParamsSize, SMLoc L) {
if (haveOpenFPOData()) {
getContext().reportError(
L, "opening new .cv_fpo_proc before closing previous frame");
return true;
}
CurFPOData = std::make_unique<FPOData>();
CurFPOData->Function = ProcSym;
CurFPOData->Begin = emitFPOLabel();
CurFPOData->ParamsSize = ParamsSize;
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
if (!haveOpenFPOData()) {
getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
return true;
}
if (!CurFPOData->PrologueEnd) {
if (!CurFPOData->Instructions.empty()) {
getContext().reportError(L, "missing .cv_fpo_endprologue");
CurFPOData->Instructions.clear();
}
CurFPOData->PrologueEnd = CurFPOData->Begin;
}
CurFPOData->End = emitFPOLabel();
const MCSymbol *Fn = CurFPOData->Function;
AllFPOData.insert({Fn, std::move(CurFPOData)});
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
if (checkInFPOPrologue(L))
return true;
FPOInstruction Inst;
Inst.Label = emitFPOLabel();
Inst.Op = FPOInstruction::SetFrame;
Inst.RegOrOffset = Reg;
CurFPOData->Instructions.push_back(Inst);
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
if (checkInFPOPrologue(L))
return true;
FPOInstruction Inst;
Inst.Label = emitFPOLabel();
Inst.Op = FPOInstruction::PushReg;
Inst.RegOrOffset = Reg;
CurFPOData->Instructions.push_back(Inst);
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
if (checkInFPOPrologue(L))
return true;
FPOInstruction Inst;
Inst.Label = emitFPOLabel();
Inst.Op = FPOInstruction::StackAlloc;
Inst.RegOrOffset = StackAlloc;
CurFPOData->Instructions.push_back(Inst);
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
if (checkInFPOPrologue(L))
return true;
if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
return Inst.Op == FPOInstruction::SetFrame;
})) {
getContext().reportError(
L, "a frame register must be established before aligning the stack");
return true;
}
FPOInstruction Inst;
Inst.Label = emitFPOLabel();
Inst.Op = FPOInstruction::StackAlign;
Inst.RegOrOffset = Align;
CurFPOData->Instructions.push_back(Inst);
return false;
}
bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
if (checkInFPOPrologue(L))
return true;
CurFPOData->PrologueEnd = emitFPOLabel();
return false;
}
namespace {
struct RegSaveOffset {
RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
unsigned Reg = 0;
unsigned Offset = 0;
};
struct FPOStateMachine {
explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
const FPOData *FPO = nullptr;
unsigned FrameReg = 0;
unsigned FrameRegOff = 0;
unsigned CurOffset = 0;
unsigned LocalSize = 0;
unsigned SavedRegSize = 0;
unsigned StackOffsetBeforeAlign = 0;
unsigned StackAlign = 0;
unsigned Flags = 0;
SmallString<128> FrameFunc;
SmallVector<RegSaveOffset, 4> RegSaveOffsets;
void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
};
}
static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
return Printable([MRI, LLVMReg](raw_ostream &OS) {
switch (LLVMReg) {
case X86::EAX: OS << "$eax"; break;
case X86::EBX: OS << "$ebx"; break;
case X86::ECX: OS << "$ecx"; break;
case X86::EDX: OS << "$edx"; break;
case X86::EDI: OS << "$edi"; break;
case X86::ESI: OS << "$esi"; break;
case X86::ESP: OS << "$esp"; break;
case X86::EBP: OS << "$ebp"; break;
case X86::EIP: OS << "$eip"; break;
default:
OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
break;
}
});
}
void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
unsigned CurFlags = Flags;
if (Label == FPO->Begin)
CurFlags |= FrameData::IsFunctionStart;
FrameFunc.clear();
raw_svector_ostream FuncOS(FrameFunc);
const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
assert((StackAlign == 0 || FrameReg != 0) &&
"cannot align stack without frame reg");
StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
if (FrameReg) {
FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
<< " + = ";
if (StackAlign) {
FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
<< StackAlign << " @ = ";
}
} else {
FuncOS << CFAVar << " .raSearch = ";
}
FuncOS << "$eip " << CFAVar << " ^ = ";
FuncOS << "$esp " << CFAVar << " 4 + = ";
for (RegSaveOffset RO : RegSaveOffsets)
FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
<< " - ^ = ";
CodeViewContext &CVCtx = OS.getContext().getCVContext();
unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
unsigned MaxStackSize = 0;
OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); OS.emitInt32(LocalSize);
OS.emitInt32(FPO->ParamsSize);
OS.emitInt32(MaxStackSize);
OS.emitInt32(FrameFuncStrTabOff); OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
OS.emitInt16(SavedRegSize);
OS.emitInt32(CurFlags);
}
bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
MCStreamer &OS = getStreamer();
MCContext &Ctx = OS.getContext();
auto I = AllFPOData.find(ProcSym);
if (I == AllFPOData.end()) {
Ctx.reportError(L, Twine("no FPO data found for symbol ") +
ProcSym->getName());
return true;
}
const FPOData *FPO = I->second.get();
assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
MCSymbol *FrameBegin = Ctx.createTempSymbol(),
*FrameEnd = Ctx.createTempSymbol();
OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));
OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
OS.emitLabel(FrameBegin);
OS.emitValue(MCSymbolRefExpr::create(FPO->Function,
MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),
4);
FPOStateMachine FSM(FPO);
FSM.emitFrameDataRecord(OS, FPO->Begin);
for (const FPOInstruction &Inst : FPO->Instructions) {
switch (Inst.Op) {
case FPOInstruction::PushReg:
FSM.CurOffset += 4;
FSM.SavedRegSize += 4;
FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
break;
case FPOInstruction::SetFrame:
FSM.FrameReg = Inst.RegOrOffset;
FSM.FrameRegOff = FSM.CurOffset;
break;
case FPOInstruction::StackAlign:
FSM.StackOffsetBeforeAlign = FSM.CurOffset;
FSM.StackAlign = Inst.RegOrOffset;
break;
case FPOInstruction::StackAlloc:
FSM.CurOffset += Inst.RegOrOffset;
FSM.LocalSize += Inst.RegOrOffset;
if (FSM.FrameReg)
continue;
break;
}
FSM.emitFrameDataRecord(OS, Inst.Label);
}
OS.emitValueToAlignment(4, 0);
OS.emitLabel(FrameEnd);
return false;
}
MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
formatted_raw_ostream &OS,
MCInstPrinter *InstPrinter,
bool IsVerboseAsm) {
return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
}
MCTargetStreamer *
llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
if (!STI.getTargetTriple().isOSBinFormatCOFF())
return nullptr;
return new X86WinCOFFTargetStreamer(S);
}