diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -438,6 +438,57 @@ SMLoc &Loc); }; +/// \brief A sequence of MCDwarfOperations corresponds to a DWARF expression, +/// used as operand in some MCCFIInstructions. +struct MCDwarfOperation { + uint8_t Operation{0}; + uint64_t Operand0{0}; + uint64_t Operand1{0}; + + MCDwarfOperation(uint8_t O, uint64_t O0, uint64_t O1) + : Operation(O), Operand0(O0), Operand1(O1) {} + + bool operator==(const MCDwarfOperation &Other) const { + return (Other.Operation == Operation && Other.Operand0 == Operand0 && + Other.Operand1 == Operand1); + } +}; +typedef std::vector MCDwarfExpression; + +/// \brief This builder should be used to create MCDwarfExpression objects +/// before feeding them to a CFIInstruction factory method. +class MCDwarfExprBuilder { +public: + MCDwarfExprBuilder() {} + +private: + MCDwarfExpression Expr; + +public: + MCDwarfExprBuilder &appendOperation(uint8_t Operation) { + Expr.push_back(MCDwarfOperation(Operation, 0, 0)); + return *this; + } + + MCDwarfExprBuilder &appendOperation(uint8_t Operation, uint64_t Op0) { + Expr.push_back(MCDwarfOperation(Operation, Op0, 0)); + return *this; + } + + MCDwarfExprBuilder &appendOperation(uint8_t Operation, uint64_t Op0, + uint64_t Op1) { + Expr.push_back(MCDwarfOperation(Operation, Op0, Op1)); + return *this; + } + + /// \brief Return the resulting expression and reset internal state + MCDwarfExpression take() { + MCDwarfExpression Res; + std::swap(Res, Expr); + return Res; + } +}; + class MCCFIInstruction { public: enum OpType { @@ -456,6 +507,9 @@ OpRegister, OpWindowSave, OpNegateRAState, + OpExpression, + OpDefCfaExpression, + OpValExpression, OpGnuArgsSize }; @@ -468,13 +522,15 @@ unsigned Register2; }; std::vector Values; + MCDwarfExpression Expression; std::string Comment; MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, int O, StringRef V, StringRef Comment = "") : Operation(Op), Label(L), Register(R), Offset(O), Values(V.begin(), V.end()), Comment(Comment) { - assert(Op != OpRegister); + assert(Op != OpRegister && Op != OpDefCfaExpression && + Op != OpValExpression && Op != OpExpression); } MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R1, unsigned R2) @@ -482,6 +538,20 @@ assert(Op == OpRegister); } + MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, + const MCDwarfExpression &E) + : Operation(Op), Label(L), Register(R), Offset(0), Expression(E) { + assert(Op == OpDefCfaExpression || Op == OpValExpression || + Op == OpExpression); + } + + MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, MCDwarfExpression &&E) + : Operation(Op), Label(L), Register(R), Offset(0), + Expression(std::move(E)) { + assert(Op == OpDefCfaExpression || Op == OpValExpression || + Op == OpExpression); + } + public: /// .cfi_def_cfa defines a rule for computing CFA as: take address from /// Register and add Offset to it. @@ -583,14 +653,56 @@ return MCCFIInstruction(OpGnuArgsSize, L, 0, Size, ""); } + /// \brief MCCFIInstructions that refer to an expression, expression object is + /// created by copying + static MCCFIInstruction createDefCfaExpression(MCSymbol *L, + const MCDwarfExpression &E) { + return MCCFIInstruction(OpDefCfaExpression, L, 0, E); + } + + static MCCFIInstruction createValExpression(MCSymbol *L, unsigned R, + const MCDwarfExpression &E) { + return MCCFIInstruction(OpValExpression, L, R, E); + } + + static MCCFIInstruction createExpression(MCSymbol *L, unsigned R, + const MCDwarfExpression &E) { + return MCCFIInstruction(OpExpression, L, R, E); + } + + /// \brief MCCFIInstructions that refer to an expression, expression object is + /// moved from an r-value + static MCCFIInstruction createDefCfaExpression(MCSymbol *L, + MCDwarfExpression &&E) { + return MCCFIInstruction(OpDefCfaExpression, L, 0, E); + } + + static MCCFIInstruction createValExpression(MCSymbol *L, unsigned R, + MCDwarfExpression &&E) { + return MCCFIInstruction(OpValExpression, L, R, E); + } + + static MCCFIInstruction createExpression(MCSymbol *L, unsigned R, + MCDwarfExpression &&E) { + return MCCFIInstruction(OpExpression, L, R, E); + } + + bool operator==(const MCCFIInstruction &Other) const { + return (Other.Operation == Operation && Other.Label == Label && + Other.Offset == Offset && Other.Register == Register && + Other.Expression == Expression); + } + OpType getOperation() const { return Operation; } MCSymbol *getLabel() const { return Label; } + void setLabel(MCSymbol *L) { Label = L; } unsigned getRegister() const { assert(Operation == OpDefCfa || Operation == OpOffset || Operation == OpRestore || Operation == OpUndefined || Operation == OpSameValue || Operation == OpDefCfaRegister || - Operation == OpRelOffset || Operation == OpRegister); + Operation == OpRelOffset || Operation == OpRegister || + Operation == OpExpression || Operation == OpValExpression); return Register; } @@ -606,6 +718,33 @@ return Offset; } + void setOffset(int NewOffset) { + assert(Operation == OpDefCfa || Operation == OpOffset || + Operation == OpRelOffset || Operation == OpDefCfaOffset || + Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize); + Offset = NewOffset; + } + + void setRegister(unsigned NewReg) { + assert(Operation == OpDefCfa || Operation == OpOffset || + Operation == OpRestore || Operation == OpUndefined || + Operation == OpSameValue || Operation == OpDefCfaRegister || + Operation == OpRelOffset || Operation == OpRegister || + Operation == OpExpression || Operation == OpValExpression); + Register = NewReg; + } + + void setRegister2(unsigned NewReg) { + assert(Operation == OpRegister); + Register2 = NewReg; + } + + const MCDwarfExpression &getExpression() const { + assert(Operation == OpDefCfaExpression || Operation == OpExpression || + Operation == OpValExpression); + return Expression; + } + StringRef getValues() const { assert(Operation == OpEscape); return StringRef(&Values[0], Values.size()); diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -40,6 +40,7 @@ class AssemblerConstantPools; class MCAsmBackend; +class MCCFIInstruction; class MCCodeEmitter; class MCContext; struct MCDwarfFrameInfo; @@ -996,6 +997,7 @@ virtual void emitCFIRegister(int64_t Register1, int64_t Register2); virtual void emitCFIWindowSave(); virtual void emitCFINegateRAState(); + virtual void emitCFIInstruction(const MCCFIInstruction &Inst); virtual void EmitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc = SMLoc()); virtual void EmitWinCFIEndProc(SMLoc Loc = SMLoc()); diff --git a/llvm/lib/CodeGen/CFIInstrInserter.cpp b/llvm/lib/CodeGen/CFIInstrInserter.cpp --- a/llvm/lib/CodeGen/CFIInstrInserter.cpp +++ b/llvm/lib/CodeGen/CFIInstrInserter.cpp @@ -233,6 +233,12 @@ report_fatal_error( "Support for cfi_restore_state not implemented! Value of CFA may " "be incorrect!\n"); +#endif + break; + case MCCFIInstruction::OpDefCfaExpression: +#ifndef NDEBUG + report_fatal_error("Support for cfi_def_cfa_expression not " + "implemented! Value of CFA may be incorrect!\n"); #endif break; // Other CFI directives do not affect CFA value. @@ -241,6 +247,8 @@ case MCCFIInstruction::OpEscape: case MCCFIInstruction::OpWindowSave: case MCCFIInstruction::OpNegateRAState: + case MCCFIInstruction::OpExpression: + case MCCFIInstruction::OpValExpression: case MCCFIInstruction::OpGnuArgsSize: break; } diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -1333,12 +1333,217 @@ void emitCFIInstruction(const MCCFIInstruction &Instr); }; +// A stripped-down version of MCObjectStreamer that only calculates how many +// bytes were written to it. We use it to know in advance how many bytes +// DWARF expressions will use. +class SizeCalcMCStreamer { + uint64_t TotalSize = {0}; + +public: + SizeCalcMCStreamer() {} + + uint64_t getSize() { return TotalSize; } + + void emitIntValue(uint64_t Value, unsigned Size) { TotalSize += Size; } + + void emitULEB128IntValue(uint64_t Value, unsigned Padding = 0) { + TotalSize += Padding + getULEB128Size(Value); + } + + void emitSLEB128IntValue(int64_t Value) { + TotalSize += getSLEB128Size(Value); + } +}; + } // end anonymous namespace static void emitEncodingByte(MCObjectStreamer &Streamer, unsigned Encoding) { Streamer.emitInt8(Encoding); } +template +static void EmitDwarfExpression(T &Streamer, + const MCDwarfExpression &Expr) { + for (const auto &Elem : Expr) { + Streamer.emitIntValue(Elem.Operation, 1); + switch (Elem.Operation) { + default: + llvm_unreachable("Unrecognized DWARF expression opcode"); + case dwarf::DW_OP_addr: + case dwarf::DW_OP_call_ref: + llvm_unreachable("DW_OP_addr & DW_OP_call_ref are unimplemented"); + break; + case dwarf::DW_OP_const1u: + case dwarf::DW_OP_const1s: + case dwarf::DW_OP_pick: + case dwarf::DW_OP_deref_size: + case dwarf::DW_OP_xderef_size: + Streamer.emitIntValue(Elem.Operand0, 1); + break; + case dwarf::DW_OP_const2u: + case dwarf::DW_OP_const2s: + case dwarf::DW_OP_skip: + case dwarf::DW_OP_bra: + case dwarf::DW_OP_call2: + Streamer.emitIntValue(Elem.Operand0, 2); + break; + case dwarf::DW_OP_const4u: + case dwarf::DW_OP_const4s: + case dwarf::DW_OP_call4: + Streamer.emitIntValue(Elem.Operand0, 4); + break; + case dwarf::DW_OP_const8u: + case dwarf::DW_OP_const8s: + Streamer.emitIntValue(Elem.Operand0, 8); + break; + case dwarf::DW_OP_constu: + case dwarf::DW_OP_plus_uconst: + case dwarf::DW_OP_regx: + case dwarf::DW_OP_piece: + case dwarf::DW_OP_GNU_addr_index: + case dwarf::DW_OP_GNU_const_index: + Streamer.emitULEB128IntValue(Elem.Operand0); + break; + case dwarf::DW_OP_consts: + case dwarf::DW_OP_breg0: + case dwarf::DW_OP_breg1: + case dwarf::DW_OP_breg2: + case dwarf::DW_OP_breg3: + case dwarf::DW_OP_breg4: + case dwarf::DW_OP_breg5: + case dwarf::DW_OP_breg6: + case dwarf::DW_OP_breg7: + case dwarf::DW_OP_breg8: + case dwarf::DW_OP_breg9: + case dwarf::DW_OP_breg10: + case dwarf::DW_OP_breg11: + case dwarf::DW_OP_breg12: + case dwarf::DW_OP_breg13: + case dwarf::DW_OP_breg14: + case dwarf::DW_OP_breg15: + case dwarf::DW_OP_breg16: + case dwarf::DW_OP_breg17: + case dwarf::DW_OP_breg18: + case dwarf::DW_OP_breg19: + case dwarf::DW_OP_breg20: + case dwarf::DW_OP_breg21: + case dwarf::DW_OP_breg22: + case dwarf::DW_OP_breg23: + case dwarf::DW_OP_breg24: + case dwarf::DW_OP_breg25: + case dwarf::DW_OP_breg26: + case dwarf::DW_OP_breg27: + case dwarf::DW_OP_breg28: + case dwarf::DW_OP_breg29: + case dwarf::DW_OP_breg30: + case dwarf::DW_OP_breg31: + case dwarf::DW_OP_fbreg: + Streamer.emitSLEB128IntValue(Elem.Operand0); + break; + case dwarf::DW_OP_deref: + case dwarf::DW_OP_dup: + case dwarf::DW_OP_drop: + case dwarf::DW_OP_over: + case dwarf::DW_OP_swap: + case dwarf::DW_OP_rot: + case dwarf::DW_OP_xderef: + case dwarf::DW_OP_abs: + case dwarf::DW_OP_and: + case dwarf::DW_OP_div: + case dwarf::DW_OP_minus: + case dwarf::DW_OP_mod: + case dwarf::DW_OP_mul: + case dwarf::DW_OP_neg: + case dwarf::DW_OP_not: + case dwarf::DW_OP_or: + case dwarf::DW_OP_plus: + case dwarf::DW_OP_shl: + case dwarf::DW_OP_shr: + case dwarf::DW_OP_shra: + case dwarf::DW_OP_xor: + case dwarf::DW_OP_eq: + case dwarf::DW_OP_ge: + case dwarf::DW_OP_gt: + case dwarf::DW_OP_le: + case dwarf::DW_OP_lt: + case dwarf::DW_OP_ne: + case dwarf::DW_OP_lit0: + case dwarf::DW_OP_lit1: + case dwarf::DW_OP_lit2: + case dwarf::DW_OP_lit3: + case dwarf::DW_OP_lit4: + case dwarf::DW_OP_lit5: + case dwarf::DW_OP_lit6: + case dwarf::DW_OP_lit7: + case dwarf::DW_OP_lit8: + case dwarf::DW_OP_lit9: + case dwarf::DW_OP_lit10: + case dwarf::DW_OP_lit11: + case dwarf::DW_OP_lit12: + case dwarf::DW_OP_lit13: + case dwarf::DW_OP_lit14: + case dwarf::DW_OP_lit15: + case dwarf::DW_OP_lit16: + case dwarf::DW_OP_lit17: + case dwarf::DW_OP_lit18: + case dwarf::DW_OP_lit19: + case dwarf::DW_OP_lit20: + case dwarf::DW_OP_lit21: + case dwarf::DW_OP_lit22: + case dwarf::DW_OP_lit23: + case dwarf::DW_OP_lit24: + case dwarf::DW_OP_lit25: + case dwarf::DW_OP_lit26: + case dwarf::DW_OP_lit27: + case dwarf::DW_OP_lit28: + case dwarf::DW_OP_lit29: + case dwarf::DW_OP_lit30: + case dwarf::DW_OP_lit31: + case dwarf::DW_OP_reg0: + case dwarf::DW_OP_reg1: + case dwarf::DW_OP_reg2: + case dwarf::DW_OP_reg3: + case dwarf::DW_OP_reg4: + case dwarf::DW_OP_reg5: + case dwarf::DW_OP_reg6: + case dwarf::DW_OP_reg7: + case dwarf::DW_OP_reg8: + case dwarf::DW_OP_reg9: + case dwarf::DW_OP_reg10: + case dwarf::DW_OP_reg11: + case dwarf::DW_OP_reg12: + case dwarf::DW_OP_reg13: + case dwarf::DW_OP_reg14: + case dwarf::DW_OP_reg15: + case dwarf::DW_OP_reg16: + case dwarf::DW_OP_reg17: + case dwarf::DW_OP_reg18: + case dwarf::DW_OP_reg19: + case dwarf::DW_OP_reg20: + case dwarf::DW_OP_reg21: + case dwarf::DW_OP_reg22: + case dwarf::DW_OP_reg23: + case dwarf::DW_OP_reg24: + case dwarf::DW_OP_reg25: + case dwarf::DW_OP_reg26: + case dwarf::DW_OP_reg27: + case dwarf::DW_OP_reg28: + case dwarf::DW_OP_reg29: + case dwarf::DW_OP_reg30: + case dwarf::DW_OP_reg31: + case dwarf::DW_OP_nop: + case dwarf::DW_OP_push_object_address: + case dwarf::DW_OP_form_tls_address: + case dwarf::DW_OP_GNU_push_tls_address: + break; + case dwarf::DW_OP_bregx: + Streamer.emitULEB128IntValue(Elem.Operand0); + Streamer.emitSLEB128IntValue(Elem.Operand1); + break; + } + } +} + void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) { int dataAlignmentFactor = getDataAlignmentFactor(Streamer); auto *MRI = Streamer.getContext().getRegisterInfo(); @@ -1463,6 +1668,28 @@ Streamer.emitULEB128IntValue(Instr.getOffset()); return; + case MCCFIInstruction::OpDefCfaExpression: { + Streamer.emitIntValue(dwarf::DW_CFA_def_cfa_expression, 1); + SizeCalcMCStreamer FakeStreamer; + EmitDwarfExpression<>(FakeStreamer, Instr.getExpression()); + Streamer.emitULEB128IntValue(FakeStreamer.getSize()); + EmitDwarfExpression<>(Streamer, Instr.getExpression()); + return; + } + case MCCFIInstruction::OpExpression: + case MCCFIInstruction::OpValExpression: { + unsigned Reg = Instr.getRegister(); + Streamer.emitIntValue(Instr.getOperation() == MCCFIInstruction::OpExpression + ? dwarf::DW_CFA_expression + : dwarf::DW_CFA_val_expression, + 1); + Streamer.emitULEB128IntValue(Reg); + SizeCalcMCStreamer FakeStreamer; + EmitDwarfExpression<>(FakeStreamer, Instr.getExpression()); + Streamer.emitULEB128IntValue(FakeStreamer.getSize()); + EmitDwarfExpression<>(Streamer, Instr.getExpression()); + return; + } case MCCFIInstruction::OpEscape: Streamer.emitBytes(Instr.getValues()); return; diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -666,6 +666,14 @@ CurFrame->RAReg = Register; } +void MCStreamer::emitCFIInstruction(const MCCFIInstruction &Inst) { + MCSymbol *Label = emitCFILabel(); + MCCFIInstruction Instruction = Inst; + Instruction.setLabel(Label); + MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo(); + CurFrame->Instructions.push_back(Instruction); +} + WinEH::FrameInfo *MCStreamer::EnsureValidWinFrameInfo(SMLoc Loc) { const MCAsmInfo *MAI = Context.getAsmInfo(); if (!MAI->usesWindowsCFI()) {