Index: head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h =================================================================== --- head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h (revision 354468) +++ head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h (revision 354469) @@ -1,308 +1,308 @@ //===- DWARFDebugFrame.h - Parsing of .debug_frame --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H #define LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/Support/Error.h" #include #include namespace llvm { class raw_ostream; namespace dwarf { /// Represent a sequence of Call Frame Information instructions that, when read /// in order, construct a table mapping PC to frame state. This can also be /// referred to as "CFI rules" in DWARF literature to avoid confusion with /// computer programs in the broader sense, and in this context each instruction /// would be a rule to establish the mapping. Refer to pg. 172 in the DWARF5 /// manual, "6.4.1 Structure of Call Frame Information". class CFIProgram { public: typedef SmallVector Operands; /// An instruction consists of a DWARF CFI opcode and an optional sequence of /// operands. If it refers to an expression, then this expression has its own /// sequence of operations and operands handled separately by DWARFExpression. struct Instruction { Instruction(uint8_t Opcode) : Opcode(Opcode) {} uint8_t Opcode; Operands Ops; // Associated DWARF expression in case this instruction refers to one Optional Expression; }; using InstrList = std::vector; using iterator = InstrList::iterator; using const_iterator = InstrList::const_iterator; iterator begin() { return Instructions.begin(); } const_iterator begin() const { return Instructions.begin(); } iterator end() { return Instructions.end(); } const_iterator end() const { return Instructions.end(); } unsigned size() const { return (unsigned)Instructions.size(); } bool empty() const { return Instructions.empty(); } CFIProgram(uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor, Triple::ArchType Arch) : CodeAlignmentFactor(CodeAlignmentFactor), DataAlignmentFactor(DataAlignmentFactor), Arch(Arch) {} /// Parse and store a sequence of CFI instructions from Data, /// starting at *Offset and ending at EndOffset. *Offset is updated /// to EndOffset upon successful parsing, or indicates the offset /// where a problem occurred in case an error is returned. - Error parse(DataExtractor Data, uint32_t *Offset, uint32_t EndOffset); + Error parse(DWARFDataExtractor Data, uint32_t *Offset, uint32_t EndOffset); void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, unsigned IndentLevel = 1) const; private: std::vector Instructions; const uint64_t CodeAlignmentFactor; const int64_t DataAlignmentFactor; Triple::ArchType Arch; /// Convenience method to add a new instruction with the given opcode. void addInstruction(uint8_t Opcode) { Instructions.push_back(Instruction(Opcode)); } /// Add a new single-operand instruction. void addInstruction(uint8_t Opcode, uint64_t Operand1) { Instructions.push_back(Instruction(Opcode)); Instructions.back().Ops.push_back(Operand1); } /// Add a new instruction that has two operands. void addInstruction(uint8_t Opcode, uint64_t Operand1, uint64_t Operand2) { Instructions.push_back(Instruction(Opcode)); Instructions.back().Ops.push_back(Operand1); Instructions.back().Ops.push_back(Operand2); } /// Types of operands to CFI instructions /// In DWARF, this type is implicitly tied to a CFI instruction opcode and /// thus this type doesn't need to be explictly written to the file (this is /// not a DWARF encoding). The relationship of instrs to operand types can /// be obtained from getOperandTypes() and is only used to simplify /// instruction printing. enum OperandType { OT_Unset, OT_None, OT_Address, OT_Offset, OT_FactoredCodeOffset, OT_SignedFactDataOffset, OT_UnsignedFactDataOffset, OT_Register, OT_Expression }; /// Retrieve the array describing the types of operands according to the enum /// above. This is indexed by opcode. static ArrayRef getOperandTypes(); /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. void printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, const Instruction &Instr, unsigned OperandIdx, uint64_t Operand) const; }; /// An entry in either debug_frame or eh_frame. This entry can be a CIE or an /// FDE. class FrameEntry { public: enum FrameKind { FK_CIE, FK_FDE }; FrameEntry(FrameKind K, uint64_t Offset, uint64_t Length, uint64_t CodeAlign, int64_t DataAlign, Triple::ArchType Arch) : Kind(K), Offset(Offset), Length(Length), CFIs(CodeAlign, DataAlign, Arch) {} virtual ~FrameEntry() {} FrameKind getKind() const { return Kind; } uint64_t getOffset() const { return Offset; } uint64_t getLength() const { return Length; } const CFIProgram &cfis() const { return CFIs; } CFIProgram &cfis() { return CFIs; } /// Dump the instructions in this CFI fragment virtual void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const = 0; protected: const FrameKind Kind; /// Offset of this entry in the section. const uint64_t Offset; /// Entry length as specified in DWARF. const uint64_t Length; CFIProgram CFIs; }; /// DWARF Common Information Entry (CIE) class CIE : public FrameEntry { public: // CIEs (and FDEs) are simply container classes, so the only sensible way to // create them is by providing the full parsed contents in the constructor. CIE(uint64_t Offset, uint64_t Length, uint8_t Version, SmallString<8> Augmentation, uint8_t AddressSize, uint8_t SegmentDescriptorSize, uint64_t CodeAlignmentFactor, int64_t DataAlignmentFactor, uint64_t ReturnAddressRegister, SmallString<8> AugmentationData, uint32_t FDEPointerEncoding, uint32_t LSDAPointerEncoding, Optional Personality, Optional PersonalityEnc, Triple::ArchType Arch) : FrameEntry(FK_CIE, Offset, Length, CodeAlignmentFactor, DataAlignmentFactor, Arch), Version(Version), Augmentation(std::move(Augmentation)), AddressSize(AddressSize), SegmentDescriptorSize(SegmentDescriptorSize), CodeAlignmentFactor(CodeAlignmentFactor), DataAlignmentFactor(DataAlignmentFactor), ReturnAddressRegister(ReturnAddressRegister), AugmentationData(std::move(AugmentationData)), FDEPointerEncoding(FDEPointerEncoding), LSDAPointerEncoding(LSDAPointerEncoding), Personality(Personality), PersonalityEnc(PersonalityEnc) {} static bool classof(const FrameEntry *FE) { return FE->getKind() == FK_CIE; } StringRef getAugmentationString() const { return Augmentation; } uint64_t getCodeAlignmentFactor() const { return CodeAlignmentFactor; } int64_t getDataAlignmentFactor() const { return DataAlignmentFactor; } uint8_t getVersion() const { return Version; } uint64_t getReturnAddressRegister() const { return ReturnAddressRegister; } Optional getPersonalityAddress() const { return Personality; } Optional getPersonalityEncoding() const { return PersonalityEnc; } uint32_t getFDEPointerEncoding() const { return FDEPointerEncoding; } uint32_t getLSDAPointerEncoding() const { return LSDAPointerEncoding; } void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const override; private: /// The following fields are defined in section 6.4.1 of the DWARF standard v4 const uint8_t Version; const SmallString<8> Augmentation; const uint8_t AddressSize; const uint8_t SegmentDescriptorSize; const uint64_t CodeAlignmentFactor; const int64_t DataAlignmentFactor; const uint64_t ReturnAddressRegister; // The following are used when the CIE represents an EH frame entry. const SmallString<8> AugmentationData; const uint32_t FDEPointerEncoding; const uint32_t LSDAPointerEncoding; const Optional Personality; const Optional PersonalityEnc; }; /// DWARF Frame Description Entry (FDE) class FDE : public FrameEntry { public: // Each FDE has a CIE it's "linked to". Our FDE contains is constructed with // an offset to the CIE (provided by parsing the FDE header). The CIE itself // is obtained lazily once it's actually required. FDE(uint64_t Offset, uint64_t Length, int64_t LinkedCIEOffset, uint64_t InitialLocation, uint64_t AddressRange, CIE *Cie, Optional LSDAAddress, Triple::ArchType Arch) : FrameEntry(FK_FDE, Offset, Length, Cie ? Cie->getCodeAlignmentFactor() : 0, Cie ? Cie->getDataAlignmentFactor() : 0, Arch), LinkedCIEOffset(LinkedCIEOffset), InitialLocation(InitialLocation), AddressRange(AddressRange), LinkedCIE(Cie), LSDAAddress(LSDAAddress) {} ~FDE() override = default; const CIE *getLinkedCIE() const { return LinkedCIE; } uint64_t getInitialLocation() const { return InitialLocation; } uint64_t getAddressRange() const { return AddressRange; } Optional getLSDAAddress() const { return LSDAAddress; } void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const override; static bool classof(const FrameEntry *FE) { return FE->getKind() == FK_FDE; } private: /// The following fields are defined in section 6.4.1 of the DWARF standard v3 const uint64_t LinkedCIEOffset; const uint64_t InitialLocation; const uint64_t AddressRange; const CIE *LinkedCIE; const Optional LSDAAddress; }; } // end namespace dwarf /// A parsed .debug_frame or .eh_frame section class DWARFDebugFrame { const Triple::ArchType Arch; // True if this is parsing an eh_frame section. const bool IsEH; // Not zero for sane pointer values coming out of eh_frame const uint64_t EHFrameAddress; std::vector> Entries; using iterator = pointee_iterator; /// Return the entry at the given offset or nullptr. dwarf::FrameEntry *getEntryAtOffset(uint64_t Offset) const; public: // If IsEH is true, assume it is a .eh_frame section. Otherwise, // it is a .debug_frame section. EHFrameAddress should be different // than zero for correct parsing of .eh_frame addresses when they // use a PC-relative encoding. DWARFDebugFrame(Triple::ArchType Arch, bool IsEH = false, uint64_t EHFrameAddress = 0); ~DWARFDebugFrame(); /// Dump the section data into the given stream. void dump(raw_ostream &OS, const MCRegisterInfo *MRI, Optional Offset) const; /// Parse the section from raw data. \p Data is assumed to contain the whole /// frame section contents to be parsed. void parse(DWARFDataExtractor Data); /// Return whether the section has any entries. bool empty() const { return Entries.empty(); } /// DWARF Frame entries accessors iterator begin() const { return Entries.begin(); } iterator end() const { return Entries.end(); } iterator_range entries() const { return iterator_range(Entries.begin(), Entries.end()); } uint64_t getEHFrameAddress() const { return EHFrameAddress; } }; } // end namespace llvm #endif // LLVM_DEBUGINFO_DWARF_DWARFDEBUGFRAME_H Index: head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h =================================================================== --- head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h (revision 354468) +++ head/contrib/llvm/include/llvm/DebugInfo/DWARF/DWARFObject.h (revision 354469) @@ -1,85 +1,85 @@ //===- DWARFObject.h --------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-----------------------------------------------------------------------===/ #ifndef LLVM_DEBUGINFO_DWARF_DWARFOBJECT_H #define LLVM_DEBUGINFO_DWARF_DWARFOBJECT_H #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/Object/ObjectFile.h" namespace llvm { // This is responsible for low level access to the object file. It // knows how to find the required sections and compute relocated // values. // The default implementations of the get
methods return dummy values. // This is to allow clients that only need some of those to implement just the // ones they need. We can't use unreachable for as many cases because the parser // implementation is eager and will call some of these methods even if the // result is not used. class DWARFObject { DWARFSection Dummy; public: virtual ~DWARFObject() = default; virtual StringRef getFileName() const { llvm_unreachable("unimplemented"); } virtual const object::ObjectFile *getFile() const { return nullptr; } virtual ArrayRef getSectionNames() const { return {}; } virtual bool isLittleEndian() const = 0; virtual uint8_t getAddressSize() const { llvm_unreachable("unimplemented"); } virtual void forEachInfoSections(function_ref F) const {} virtual void forEachTypesSections(function_ref F) const {} virtual StringRef getAbbrevSection() const { return ""; } virtual const DWARFSection &getLocSection() const { return Dummy; } virtual const DWARFSection &getLoclistsSection() const { return Dummy; } virtual StringRef getARangeSection() const { return ""; } - virtual StringRef getDebugFrameSection() const { return ""; } - virtual StringRef getEHFrameSection() const { return ""; } + virtual const DWARFSection &getDebugFrameSection() const { return Dummy; } + virtual const DWARFSection &getEHFrameSection() const { return Dummy; } virtual const DWARFSection &getLineSection() const { return Dummy; } virtual StringRef getLineStringSection() const { return ""; } virtual StringRef getStringSection() const { return ""; } virtual const DWARFSection &getRangeSection() const { return Dummy; } virtual const DWARFSection &getRnglistsSection() const { return Dummy; } virtual StringRef getMacinfoSection() const { return ""; } virtual const DWARFSection &getPubNamesSection() const { return Dummy; } virtual const DWARFSection &getPubTypesSection() const { return Dummy; } virtual const DWARFSection &getGnuPubNamesSection() const { return Dummy; } virtual const DWARFSection &getGnuPubTypesSection() const { return Dummy; } virtual const DWARFSection &getStringOffsetSection() const { return Dummy; } virtual void forEachInfoDWOSections(function_ref F) const {} virtual void forEachTypesDWOSections(function_ref F) const {} virtual StringRef getAbbrevDWOSection() const { return ""; } virtual const DWARFSection &getLineDWOSection() const { return Dummy; } virtual const DWARFSection &getLocDWOSection() const { return Dummy; } virtual StringRef getStringDWOSection() const { return ""; } virtual const DWARFSection &getStringOffsetDWOSection() const { return Dummy; } virtual const DWARFSection &getRangeDWOSection() const { return Dummy; } virtual const DWARFSection &getRnglistsDWOSection() const { return Dummy; } virtual const DWARFSection &getAddrSection() const { return Dummy; } virtual const DWARFSection &getAppleNamesSection() const { return Dummy; } virtual const DWARFSection &getAppleTypesSection() const { return Dummy; } virtual const DWARFSection &getAppleNamespacesSection() const { return Dummy; } virtual const DWARFSection &getDebugNamesSection() const { return Dummy; } virtual const DWARFSection &getAppleObjCSection() const { return Dummy; } virtual StringRef getCUIndexSection() const { return ""; } virtual StringRef getGdbIndexSection() const { return ""; } virtual StringRef getTUIndexSection() const { return ""; } virtual Optional find(const DWARFSection &Sec, uint64_t Pos) const = 0; }; } // namespace llvm #endif Index: head/contrib/llvm/include/llvm/MC/MCDwarf.h =================================================================== --- head/contrib/llvm/include/llvm/MC/MCDwarf.h (revision 354468) +++ head/contrib/llvm/include/llvm/MC/MCDwarf.h (revision 354469) @@ -1,637 +1,638 @@ //===- MCDwarf.h - Machine Code Dwarf support -------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains the declaration of the MCDwarfFile to support the dwarf // .file directive and the .loc directive. // //===----------------------------------------------------------------------===// #ifndef LLVM_MC_MCDWARF_H #define LLVM_MC_MCDWARF_H #include "llvm/ADT/MapVector.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCSection.h" #include "llvm/Support/Error.h" #include "llvm/Support/MD5.h" #include #include #include #include #include namespace llvm { template class ArrayRef; class MCAsmBackend; class MCContext; class MCDwarfLineStr; class MCObjectStreamer; class MCStreamer; class MCSymbol; class raw_ostream; class SMLoc; class SourceMgr; /// Instances of this class represent the name of the dwarf .file directive and /// its associated dwarf file number in the MC file. MCDwarfFile's are created /// and uniqued by the MCContext class. In Dwarf 4 file numbers start from 1; /// i.e. the entry with file number 1 is the first element in the vector of /// DwarfFiles and there is no MCDwarfFile with file number 0. In Dwarf 5 file /// numbers start from 0, with the MCDwarfFile with file number 0 being the /// primary source file, and file numbers correspond to their index in the /// vector. struct MCDwarfFile { // The base name of the file without its directory path. std::string Name; // The index into the list of directory names for this file name. unsigned DirIndex; /// The MD5 checksum, if there is one. Non-owning pointer to data allocated /// in MCContext. Optional Checksum; /// The source code of the file. Non-owning reference to data allocated in /// MCContext. Optional Source; }; /// Instances of this class represent the information from a /// dwarf .loc directive. class MCDwarfLoc { uint32_t FileNum; uint32_t Line; uint16_t Column; // Flags (see #define's below) uint8_t Flags; uint8_t Isa; uint32_t Discriminator; // Flag that indicates the initial value of the is_stmt_start flag. #define DWARF2_LINE_DEFAULT_IS_STMT 1 #define DWARF2_FLAG_IS_STMT (1 << 0) #define DWARF2_FLAG_BASIC_BLOCK (1 << 1) #define DWARF2_FLAG_PROLOGUE_END (1 << 2) #define DWARF2_FLAG_EPILOGUE_BEGIN (1 << 3) private: // MCContext manages these friend class MCContext; friend class MCDwarfLineEntry; MCDwarfLoc(unsigned fileNum, unsigned line, unsigned column, unsigned flags, unsigned isa, unsigned discriminator) : FileNum(fileNum), Line(line), Column(column), Flags(flags), Isa(isa), Discriminator(discriminator) {} // Allow the default copy constructor and assignment operator to be used // for an MCDwarfLoc object. public: /// Get the FileNum of this MCDwarfLoc. unsigned getFileNum() const { return FileNum; } /// Get the Line of this MCDwarfLoc. unsigned getLine() const { return Line; } /// Get the Column of this MCDwarfLoc. unsigned getColumn() const { return Column; } /// Get the Flags of this MCDwarfLoc. unsigned getFlags() const { return Flags; } /// Get the Isa of this MCDwarfLoc. unsigned getIsa() const { return Isa; } /// Get the Discriminator of this MCDwarfLoc. unsigned getDiscriminator() const { return Discriminator; } /// Set the FileNum of this MCDwarfLoc. void setFileNum(unsigned fileNum) { FileNum = fileNum; } /// Set the Line of this MCDwarfLoc. void setLine(unsigned line) { Line = line; } /// Set the Column of this MCDwarfLoc. void setColumn(unsigned column) { assert(column <= UINT16_MAX); Column = column; } /// Set the Flags of this MCDwarfLoc. void setFlags(unsigned flags) { assert(flags <= UINT8_MAX); Flags = flags; } /// Set the Isa of this MCDwarfLoc. void setIsa(unsigned isa) { assert(isa <= UINT8_MAX); Isa = isa; } /// Set the Discriminator of this MCDwarfLoc. void setDiscriminator(unsigned discriminator) { Discriminator = discriminator; } }; /// Instances of this class represent the line information for /// the dwarf line table entries. Which is created after a machine /// instruction is assembled and uses an address from a temporary label /// created at the current address in the current section and the info from /// the last .loc directive seen as stored in the context. class MCDwarfLineEntry : public MCDwarfLoc { MCSymbol *Label; private: // Allow the default copy constructor and assignment operator to be used // for an MCDwarfLineEntry object. public: // Constructor to create an MCDwarfLineEntry given a symbol and the dwarf loc. MCDwarfLineEntry(MCSymbol *label, const MCDwarfLoc loc) : MCDwarfLoc(loc), Label(label) {} MCSymbol *getLabel() const { return Label; } // This is called when an instruction is assembled into the specified // section and if there is information from the last .loc directive that // has yet to have a line entry made for it is made. static void Make(MCObjectStreamer *MCOS, MCSection *Section); }; /// Instances of this class represent the line information for a compile /// unit where machine instructions have been assembled after seeing .loc /// directives. This is the information used to build the dwarf line /// table for a section. class MCLineSection { public: // Add an entry to this MCLineSection's line entries. void addLineEntry(const MCDwarfLineEntry &LineEntry, MCSection *Sec) { MCLineDivisions[Sec].push_back(LineEntry); } using MCDwarfLineEntryCollection = std::vector; using iterator = MCDwarfLineEntryCollection::iterator; using const_iterator = MCDwarfLineEntryCollection::const_iterator; using MCLineDivisionMap = MapVector; private: // A collection of MCDwarfLineEntry for each section. MCLineDivisionMap MCLineDivisions; public: // Returns the collection of MCDwarfLineEntry for a given Compile Unit ID. const MCLineDivisionMap &getMCLineEntries() const { return MCLineDivisions; } }; struct MCDwarfLineTableParams { /// First special line opcode - leave room for the standard opcodes. /// Note: If you want to change this, you'll have to update the /// "StandardOpcodeLengths" table that is emitted in /// \c Emit(). uint8_t DWARF2LineOpcodeBase = 13; /// Minimum line offset in a special line info. opcode. The value /// -5 was chosen to give a reasonable range of values. int8_t DWARF2LineBase = -5; /// Range of line offsets in a special line info. opcode. uint8_t DWARF2LineRange = 14; }; struct MCDwarfLineTableHeader { MCSymbol *Label = nullptr; SmallVector MCDwarfDirs; SmallVector MCDwarfFiles; StringMap SourceIdMap; std::string CompilationDir; MCDwarfFile RootFile; bool HasSource = false; private: bool HasAllMD5 = true; bool HasAnyMD5 = false; public: MCDwarfLineTableHeader() = default; Expected tryGetFile(StringRef &Directory, StringRef &FileName, Optional Checksum, Optional Source, uint16_t DwarfVersion, unsigned FileNumber = 0); std::pair Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, Optional &LineStr) const; std::pair Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, ArrayRef SpecialOpcodeLengths, Optional &LineStr) const; void resetMD5Usage() { HasAllMD5 = true; HasAnyMD5 = false; } void trackMD5Usage(bool MD5Used) { HasAllMD5 &= MD5Used; HasAnyMD5 |= MD5Used; } bool isMD5UsageConsistent() const { return MCDwarfFiles.empty() || (HasAllMD5 == HasAnyMD5); } void setRootFile(StringRef Directory, StringRef FileName, Optional Checksum, Optional Source) { CompilationDir = Directory; RootFile.Name = FileName; RootFile.DirIndex = 0; RootFile.Checksum = Checksum; RootFile.Source = Source; trackMD5Usage(Checksum.hasValue()); HasSource = Source.hasValue(); } void resetFileTable() { MCDwarfDirs.clear(); MCDwarfFiles.clear(); RootFile.Name.clear(); resetMD5Usage(); HasSource = false; } private: void emitV2FileDirTables(MCStreamer *MCOS) const; void emitV5FileDirTables(MCStreamer *MCOS, Optional &LineStr) const; }; class MCDwarfDwoLineTable { MCDwarfLineTableHeader Header; bool HasSplitLineTable = false; public: void maybeSetRootFile(StringRef Directory, StringRef FileName, Optional Checksum, Optional Source) { if (!Header.RootFile.Name.empty()) return; Header.setRootFile(Directory, FileName, Checksum, Source); } unsigned getFile(StringRef Directory, StringRef FileName, Optional Checksum, uint16_t DwarfVersion, Optional Source) { HasSplitLineTable = true; return cantFail(Header.tryGetFile(Directory, FileName, Checksum, Source, DwarfVersion)); } void Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params, MCSection *Section) const; }; class MCDwarfLineTable { MCDwarfLineTableHeader Header; MCLineSection MCLineSections; public: // This emits the Dwarf file and the line tables for all Compile Units. static void Emit(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params); // This emits the Dwarf file and the line tables for a given Compile Unit. void EmitCU(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params, Optional &LineStr) const; Expected tryGetFile(StringRef &Directory, StringRef &FileName, Optional Checksum, Optional Source, uint16_t DwarfVersion, unsigned FileNumber = 0); unsigned getFile(StringRef &Directory, StringRef &FileName, Optional Checksum, Optional Source, uint16_t DwarfVersion, unsigned FileNumber = 0) { return cantFail(tryGetFile(Directory, FileName, Checksum, Source, DwarfVersion, FileNumber)); } void setRootFile(StringRef Directory, StringRef FileName, Optional Checksum, Optional Source) { Header.CompilationDir = Directory; Header.RootFile.Name = FileName; Header.RootFile.DirIndex = 0; Header.RootFile.Checksum = Checksum; Header.RootFile.Source = Source; Header.trackMD5Usage(Checksum.hasValue()); Header.HasSource = Source.hasValue(); } void resetFileTable() { Header.resetFileTable(); } bool hasRootFile() const { return !Header.RootFile.Name.empty(); } const MCDwarfFile &getRootFile() const { return Header.RootFile; } // Report whether MD5 usage has been consistent (all-or-none). bool isMD5UsageConsistent() const { return Header.isMD5UsageConsistent(); } MCSymbol *getLabel() const { return Header.Label; } void setLabel(MCSymbol *Label) { Header.Label = Label; } const SmallVectorImpl &getMCDwarfDirs() const { return Header.MCDwarfDirs; } SmallVectorImpl &getMCDwarfDirs() { return Header.MCDwarfDirs; } const SmallVectorImpl &getMCDwarfFiles() const { return Header.MCDwarfFiles; } SmallVectorImpl &getMCDwarfFiles() { return Header.MCDwarfFiles; } const MCLineSection &getMCLineSections() const { return MCLineSections; } MCLineSection &getMCLineSections() { return MCLineSections; } }; class MCDwarfLineAddr { public: /// Utility function to encode a Dwarf pair of LineDelta and AddrDeltas. static void Encode(MCContext &Context, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta, raw_ostream &OS); /// Utility function to encode a Dwarf pair of LineDelta and AddrDeltas using /// fixed length operands. static bool FixedEncode(MCContext &Context, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta, raw_ostream &OS, uint32_t *Offset, uint32_t *Size); /// Utility function to emit the encoding to a streamer. static void Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta); }; class MCGenDwarfInfo { public: // // When generating dwarf for assembly source files this emits the Dwarf // sections. // static void Emit(MCStreamer *MCOS); }; // When generating dwarf for assembly source files this is the info that is // needed to be gathered for each symbol that will have a dwarf label. class MCGenDwarfLabelEntry { private: // Name of the symbol without a leading underbar, if any. StringRef Name; // The dwarf file number this symbol is in. unsigned FileNumber; // The line number this symbol is at. unsigned LineNumber; // The low_pc for the dwarf label is taken from this symbol. MCSymbol *Label; public: MCGenDwarfLabelEntry(StringRef name, unsigned fileNumber, unsigned lineNumber, MCSymbol *label) : Name(name), FileNumber(fileNumber), LineNumber(lineNumber), Label(label) {} StringRef getName() const { return Name; } unsigned getFileNumber() const { return FileNumber; } unsigned getLineNumber() const { return LineNumber; } MCSymbol *getLabel() const { return Label; } // This is called when label is created when we are generating dwarf for // assembly source files. static void Make(MCSymbol *Symbol, MCStreamer *MCOS, SourceMgr &SrcMgr, SMLoc &Loc); }; class MCCFIInstruction { public: enum OpType { OpSameValue, OpRememberState, OpRestoreState, OpOffset, OpDefCfaRegister, OpDefCfaOffset, OpDefCfa, OpRelOffset, OpAdjustCfaOffset, OpEscape, OpRestore, OpUndefined, OpRegister, OpWindowSave, OpNegateRAState, OpGnuArgsSize }; private: OpType Operation; MCSymbol *Label; unsigned Register; union { int Offset; unsigned Register2; }; std::vector Values; MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R, int O, StringRef V) : Operation(Op), Label(L), Register(R), Offset(O), Values(V.begin(), V.end()) { assert(Op != OpRegister); } MCCFIInstruction(OpType Op, MCSymbol *L, unsigned R1, unsigned R2) : Operation(Op), Label(L), Register(R1), Register2(R2) { assert(Op == OpRegister); } public: /// .cfi_def_cfa defines a rule for computing CFA as: take address from /// Register and add Offset to it. static MCCFIInstruction createDefCfa(MCSymbol *L, unsigned Register, int Offset) { return MCCFIInstruction(OpDefCfa, L, Register, -Offset, ""); } /// .cfi_def_cfa_register modifies a rule for computing CFA. From now /// on Register will be used instead of the old one. Offset remains the same. static MCCFIInstruction createDefCfaRegister(MCSymbol *L, unsigned Register) { return MCCFIInstruction(OpDefCfaRegister, L, Register, 0, ""); } /// .cfi_def_cfa_offset modifies a rule for computing CFA. Register /// remains the same, but offset is new. Note that it is the absolute offset /// that will be added to a defined register to the compute CFA address. static MCCFIInstruction createDefCfaOffset(MCSymbol *L, int Offset) { return MCCFIInstruction(OpDefCfaOffset, L, 0, -Offset, ""); } /// .cfi_adjust_cfa_offset Same as .cfi_def_cfa_offset, but /// Offset is a relative value that is added/subtracted from the previous /// offset. static MCCFIInstruction createAdjustCfaOffset(MCSymbol *L, int Adjustment) { return MCCFIInstruction(OpAdjustCfaOffset, L, 0, Adjustment, ""); } /// .cfi_offset Previous value of Register is saved at offset Offset /// from CFA. static MCCFIInstruction createOffset(MCSymbol *L, unsigned Register, int Offset) { return MCCFIInstruction(OpOffset, L, Register, Offset, ""); } /// .cfi_rel_offset Previous value of Register is saved at offset /// Offset from the current CFA register. This is transformed to .cfi_offset /// using the known displacement of the CFA register from the CFA. static MCCFIInstruction createRelOffset(MCSymbol *L, unsigned Register, int Offset) { return MCCFIInstruction(OpRelOffset, L, Register, Offset, ""); } /// .cfi_register Previous value of Register1 is saved in /// register Register2. static MCCFIInstruction createRegister(MCSymbol *L, unsigned Register1, unsigned Register2) { return MCCFIInstruction(OpRegister, L, Register1, Register2); } /// .cfi_window_save SPARC register window is saved. static MCCFIInstruction createWindowSave(MCSymbol *L) { return MCCFIInstruction(OpWindowSave, L, 0, 0, ""); } /// .cfi_negate_ra_state AArch64 negate RA state. static MCCFIInstruction createNegateRAState(MCSymbol *L) { return MCCFIInstruction(OpNegateRAState, L, 0, 0, ""); } /// .cfi_restore says that the rule for Register is now the same as it /// was at the beginning of the function, after all initial instructions added /// by .cfi_startproc were executed. static MCCFIInstruction createRestore(MCSymbol *L, unsigned Register) { return MCCFIInstruction(OpRestore, L, Register, 0, ""); } /// .cfi_undefined From now on the previous value of Register can't be /// restored anymore. static MCCFIInstruction createUndefined(MCSymbol *L, unsigned Register) { return MCCFIInstruction(OpUndefined, L, Register, 0, ""); } /// .cfi_same_value Current value of Register is the same as in the /// previous frame. I.e., no restoration is needed. static MCCFIInstruction createSameValue(MCSymbol *L, unsigned Register) { return MCCFIInstruction(OpSameValue, L, Register, 0, ""); } /// .cfi_remember_state Save all current rules for all registers. static MCCFIInstruction createRememberState(MCSymbol *L) { return MCCFIInstruction(OpRememberState, L, 0, 0, ""); } /// .cfi_restore_state Restore the previously saved state. static MCCFIInstruction createRestoreState(MCSymbol *L) { return MCCFIInstruction(OpRestoreState, L, 0, 0, ""); } /// .cfi_escape Allows the user to add arbitrary bytes to the unwind /// info. static MCCFIInstruction createEscape(MCSymbol *L, StringRef Vals) { return MCCFIInstruction(OpEscape, L, 0, 0, Vals); } /// A special wrapper for .cfi_escape that indicates GNU_ARGS_SIZE static MCCFIInstruction createGnuArgsSize(MCSymbol *L, int Size) { return MCCFIInstruction(OpGnuArgsSize, L, 0, Size, ""); } OpType getOperation() const { return Operation; } MCSymbol *getLabel() const { return Label; } unsigned getRegister() const { assert(Operation == OpDefCfa || Operation == OpOffset || Operation == OpRestore || Operation == OpUndefined || Operation == OpSameValue || Operation == OpDefCfaRegister || Operation == OpRelOffset || Operation == OpRegister); return Register; } unsigned getRegister2() const { assert(Operation == OpRegister); return Register2; } int getOffset() const { assert(Operation == OpDefCfa || Operation == OpOffset || Operation == OpRelOffset || Operation == OpDefCfaOffset || Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize); return Offset; } StringRef getValues() const { assert(Operation == OpEscape); return StringRef(&Values[0], Values.size()); } }; struct MCDwarfFrameInfo { MCDwarfFrameInfo() = default; MCSymbol *Begin = nullptr; MCSymbol *End = nullptr; const MCSymbol *Personality = nullptr; const MCSymbol *Lsda = nullptr; std::vector Instructions; unsigned CurrentCfaRegister = 0; unsigned PersonalityEncoding = 0; unsigned LsdaEncoding = 0; uint32_t CompactUnwindEncoding = 0; bool IsSignalFrame = false; bool IsSimple = false; unsigned RAReg = static_cast(INT_MAX); bool IsBKeyFrame = false; }; class MCDwarfFrameEmitter { public: // // This emits the frame info section. // static void Emit(MCObjectStreamer &streamer, MCAsmBackend *MAB, bool isEH); static void EmitAdvanceLoc(MCObjectStreamer &Streamer, uint64_t AddrDelta); static void EncodeAdvanceLoc(MCContext &Context, uint64_t AddrDelta, - raw_ostream &OS); + raw_ostream &OS, uint32_t *Offset = nullptr, + uint32_t *Size = nullptr); }; } // end namespace llvm #endif // LLVM_MC_MCDWARF_H Index: head/contrib/llvm/include/llvm/MC/MCFixup.h =================================================================== --- head/contrib/llvm/include/llvm/MC/MCFixup.h (revision 354468) +++ head/contrib/llvm/include/llvm/MC/MCFixup.h (revision 354469) @@ -1,171 +1,200 @@ //===-- llvm/MC/MCFixup.h - Instruction Relocation and Patching -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_MC_MCFIXUP_H #define LLVM_MC_MCFIXUP_H #include "llvm/MC/MCExpr.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SMLoc.h" #include namespace llvm { class MCExpr; /// Extensible enumeration to represent the type of a fixup. enum MCFixupKind { - FK_NONE = 0, ///< A no-op fixup. - FK_Data_1, ///< A one-byte fixup. - FK_Data_2, ///< A two-byte fixup. - FK_Data_4, ///< A four-byte fixup. - FK_Data_8, ///< A eight-byte fixup. - FK_PCRel_1, ///< A one-byte pc relative fixup. - FK_PCRel_2, ///< A two-byte pc relative fixup. - FK_PCRel_4, ///< A four-byte pc relative fixup. - FK_PCRel_8, ///< A eight-byte pc relative fixup. - FK_GPRel_1, ///< A one-byte gp relative fixup. - FK_GPRel_2, ///< A two-byte gp relative fixup. - FK_GPRel_4, ///< A four-byte gp relative fixup. - FK_GPRel_8, ///< A eight-byte gp relative fixup. - FK_DTPRel_4, ///< A four-byte dtp relative fixup. - FK_DTPRel_8, ///< A eight-byte dtp relative fixup. - FK_TPRel_4, ///< A four-byte tp relative fixup. - FK_TPRel_8, ///< A eight-byte tp relative fixup. - FK_SecRel_1, ///< A one-byte section relative fixup. - FK_SecRel_2, ///< A two-byte section relative fixup. - FK_SecRel_4, ///< A four-byte section relative fixup. - FK_SecRel_8, ///< A eight-byte section relative fixup. - FK_Data_Add_1, ///< A one-byte add fixup. - FK_Data_Add_2, ///< A two-byte add fixup. - FK_Data_Add_4, ///< A four-byte add fixup. - FK_Data_Add_8, ///< A eight-byte add fixup. - FK_Data_Sub_1, ///< A one-byte sub fixup. - FK_Data_Sub_2, ///< A two-byte sub fixup. - FK_Data_Sub_4, ///< A four-byte sub fixup. - FK_Data_Sub_8, ///< A eight-byte sub fixup. + FK_NONE = 0, ///< A no-op fixup. + FK_Data_1, ///< A one-byte fixup. + FK_Data_2, ///< A two-byte fixup. + FK_Data_4, ///< A four-byte fixup. + FK_Data_8, ///< A eight-byte fixup. + FK_Data_6b, ///< A six-bits fixup. + FK_PCRel_1, ///< A one-byte pc relative fixup. + FK_PCRel_2, ///< A two-byte pc relative fixup. + FK_PCRel_4, ///< A four-byte pc relative fixup. + FK_PCRel_8, ///< A eight-byte pc relative fixup. + FK_GPRel_1, ///< A one-byte gp relative fixup. + FK_GPRel_2, ///< A two-byte gp relative fixup. + FK_GPRel_4, ///< A four-byte gp relative fixup. + FK_GPRel_8, ///< A eight-byte gp relative fixup. + FK_DTPRel_4, ///< A four-byte dtp relative fixup. + FK_DTPRel_8, ///< A eight-byte dtp relative fixup. + FK_TPRel_4, ///< A four-byte tp relative fixup. + FK_TPRel_8, ///< A eight-byte tp relative fixup. + FK_SecRel_1, ///< A one-byte section relative fixup. + FK_SecRel_2, ///< A two-byte section relative fixup. + FK_SecRel_4, ///< A four-byte section relative fixup. + FK_SecRel_8, ///< A eight-byte section relative fixup. + FK_Data_Add_1, ///< A one-byte add fixup. + FK_Data_Add_2, ///< A two-byte add fixup. + FK_Data_Add_4, ///< A four-byte add fixup. + FK_Data_Add_8, ///< A eight-byte add fixup. + FK_Data_Add_6b, ///< A six-bits add fixup. + FK_Data_Sub_1, ///< A one-byte sub fixup. + FK_Data_Sub_2, ///< A two-byte sub fixup. + FK_Data_Sub_4, ///< A four-byte sub fixup. + FK_Data_Sub_8, ///< A eight-byte sub fixup. + FK_Data_Sub_6b, ///< A six-bits sub fixup. FirstTargetFixupKind = 128, // Limit range of target fixups, in case we want to pack more efficiently // later. MaxTargetFixupKind = (1 << 8) }; /// Encode information on a single operation to perform on a byte /// sequence (e.g., an encoded instruction) which requires assemble- or run- /// time patching. /// /// Fixups are used any time the target instruction encoder needs to represent /// some value in an instruction which is not yet concrete. The encoder will /// encode the instruction assuming the value is 0, and emit a fixup which /// communicates to the assembler backend how it should rewrite the encoded /// value. /// /// During the process of relaxation, the assembler will apply fixups as /// symbolic values become concrete. When relaxation is complete, any remaining /// fixups become relocations in the object file (or errors, if the fixup cannot /// be encoded on the target). class MCFixup { /// The value to put into the fixup location. The exact interpretation of the /// expression is target dependent, usually it will be one of the operands to /// an instruction or an assembler directive. const MCExpr *Value; /// The byte index of start of the relocation inside the MCFragment. uint32_t Offset; /// The target dependent kind of fixup item this is. The kind is used to /// determine how the operand value should be encoded into the instruction. unsigned Kind; /// The source location which gave rise to the fixup, if any. SMLoc Loc; public: static MCFixup create(uint32_t Offset, const MCExpr *Value, MCFixupKind Kind, SMLoc Loc = SMLoc()) { assert(unsigned(Kind) < MaxTargetFixupKind && "Kind out of range!"); MCFixup FI; FI.Value = Value; FI.Offset = Offset; FI.Kind = unsigned(Kind); FI.Loc = Loc; return FI; } /// Return a fixup corresponding to the add half of a add/sub fixup pair for /// the given Fixup. static MCFixup createAddFor(const MCFixup &Fixup) { MCFixup FI; FI.Value = Fixup.getValue(); FI.Offset = Fixup.getOffset(); FI.Kind = (unsigned)getAddKindForKind(Fixup.getKind()); FI.Loc = Fixup.getLoc(); return FI; } /// Return a fixup corresponding to the sub half of a add/sub fixup pair for /// the given Fixup. static MCFixup createSubFor(const MCFixup &Fixup) { MCFixup FI; FI.Value = Fixup.getValue(); FI.Offset = Fixup.getOffset(); FI.Kind = (unsigned)getSubKindForKind(Fixup.getKind()); FI.Loc = Fixup.getLoc(); return FI; } MCFixupKind getKind() const { return MCFixupKind(Kind); } uint32_t getOffset() const { return Offset; } void setOffset(uint32_t Value) { Offset = Value; } const MCExpr *getValue() const { return Value; } /// Return the generic fixup kind for a value with the given size. It /// is an error to pass an unsupported size. - static MCFixupKind getKindForSize(unsigned Size, bool isPCRel) { + static MCFixupKind getKindForSize(unsigned Size, bool IsPCRel) { switch (Size) { default: llvm_unreachable("Invalid generic fixup size!"); - case 1: return isPCRel ? FK_PCRel_1 : FK_Data_1; - case 2: return isPCRel ? FK_PCRel_2 : FK_Data_2; - case 4: return isPCRel ? FK_PCRel_4 : FK_Data_4; - case 8: return isPCRel ? FK_PCRel_8 : FK_Data_8; + case 1: + return IsPCRel ? FK_PCRel_1 : FK_Data_1; + case 2: + return IsPCRel ? FK_PCRel_2 : FK_Data_2; + case 4: + return IsPCRel ? FK_PCRel_4 : FK_Data_4; + case 8: + return IsPCRel ? FK_PCRel_8 : FK_Data_8; } } + /// Return the generic fixup kind for a value with the given size in bits. + /// It is an error to pass an unsupported size. + static MCFixupKind getKindForSizeInBits(unsigned Size, bool IsPCRel) { + switch (Size) { + default: + llvm_unreachable("Invalid generic fixup size!"); + case 6: + assert(!IsPCRel && "Invalid pc-relative fixup size!"); + return FK_Data_6b; + case 8: + return IsPCRel ? FK_PCRel_1 : FK_Data_1; + case 16: + return IsPCRel ? FK_PCRel_2 : FK_Data_2; + case 32: + return IsPCRel ? FK_PCRel_4 : FK_Data_4; + case 64: + return IsPCRel ? FK_PCRel_8 : FK_Data_8; + } + } + /// Return the generic fixup kind for an addition with a given size. It /// is an error to pass an unsupported size. static MCFixupKind getAddKindForKind(unsigned Kind) { switch (Kind) { default: llvm_unreachable("Unknown type to convert!"); case FK_Data_1: return FK_Data_Add_1; case FK_Data_2: return FK_Data_Add_2; case FK_Data_4: return FK_Data_Add_4; case FK_Data_8: return FK_Data_Add_8; + case FK_Data_6b: return FK_Data_Add_6b; } } /// Return the generic fixup kind for an subtraction with a given size. It /// is an error to pass an unsupported size. static MCFixupKind getSubKindForKind(unsigned Kind) { switch (Kind) { default: llvm_unreachable("Unknown type to convert!"); case FK_Data_1: return FK_Data_Sub_1; case FK_Data_2: return FK_Data_Sub_2; case FK_Data_4: return FK_Data_Sub_4; case FK_Data_8: return FK_Data_Sub_8; + case FK_Data_6b: return FK_Data_Sub_6b; } } SMLoc getLoc() const { return Loc; } }; } // End llvm namespace #endif Index: head/contrib/llvm/include/llvm/MC/MCFragment.h =================================================================== --- head/contrib/llvm/include/llvm/MC/MCFragment.h (revision 354468) +++ head/contrib/llvm/include/llvm/MC/MCFragment.h (revision 354469) @@ -1,667 +1,663 @@ //===- MCFragment.h - Fragment type hierarchy -------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_MC_MCFRAGMENT_H #define LLVM_MC_MCFRAGMENT_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/ilist_node.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/Casting.h" #include "llvm/Support/SMLoc.h" #include #include namespace llvm { class MCSection; class MCSubtargetInfo; class MCSymbol; class MCFragment : public ilist_node_with_parent { friend class MCAsmLayout; public: enum FragmentType : uint8_t { FT_Align, FT_Data, FT_CompactEncodedInst, FT_Fill, FT_Relaxable, FT_Org, FT_Dwarf, FT_DwarfFrame, FT_LEB, FT_Padding, FT_SymbolId, FT_CVInlineLines, FT_CVDefRange, FT_Dummy }; private: FragmentType Kind; protected: bool HasInstructions; private: /// LayoutOrder - The layout order of this fragment. unsigned LayoutOrder; /// The data for the section this fragment is in. MCSection *Parent; /// Atom - The atom this fragment is in, as represented by its defining /// symbol. const MCSymbol *Atom; /// \name Assembler Backend Data /// @{ // // FIXME: This could all be kept private to the assembler implementation. /// Offset - The offset of this fragment in its section. This is ~0 until /// initialized. uint64_t Offset; /// @} protected: MCFragment(FragmentType Kind, bool HasInstructions, MCSection *Parent = nullptr); ~MCFragment(); public: MCFragment() = delete; MCFragment(const MCFragment &) = delete; MCFragment &operator=(const MCFragment &) = delete; /// Destroys the current fragment. /// /// This must be used instead of delete as MCFragment is non-virtual. /// This method will dispatch to the appropriate subclass. void destroy(); FragmentType getKind() const { return Kind; } MCSection *getParent() const { return Parent; } void setParent(MCSection *Value) { Parent = Value; } const MCSymbol *getAtom() const { return Atom; } void setAtom(const MCSymbol *Value) { Atom = Value; } unsigned getLayoutOrder() const { return LayoutOrder; } void setLayoutOrder(unsigned Value) { LayoutOrder = Value; } /// Does this fragment have instructions emitted into it? By default /// this is false, but specific fragment types may set it to true. bool hasInstructions() const { return HasInstructions; } /// Return true if given frgment has FT_Dummy type. bool isDummy() const { return Kind == FT_Dummy; } void dump() const; }; class MCDummyFragment : public MCFragment { public: explicit MCDummyFragment(MCSection *Sec) : MCFragment(FT_Dummy, false, Sec) {} static bool classof(const MCFragment *F) { return F->getKind() == FT_Dummy; } }; /// Interface implemented by fragments that contain encoded instructions and/or /// data. /// class MCEncodedFragment : public MCFragment { /// Should this fragment be aligned to the end of a bundle? bool AlignToBundleEnd = false; uint8_t BundlePadding = 0; protected: MCEncodedFragment(MCFragment::FragmentType FType, bool HasInstructions, MCSection *Sec) : MCFragment(FType, HasInstructions, Sec) {} /// STI - The MCSubtargetInfo in effect when the instruction was encoded. /// must be non-null for instructions. const MCSubtargetInfo *STI = nullptr; public: static bool classof(const MCFragment *F) { MCFragment::FragmentType Kind = F->getKind(); switch (Kind) { default: return false; case MCFragment::FT_Relaxable: case MCFragment::FT_CompactEncodedInst: case MCFragment::FT_Data: case MCFragment::FT_Dwarf: + case MCFragment::FT_DwarfFrame: return true; } } /// Should this fragment be placed at the end of an aligned bundle? bool alignToBundleEnd() const { return AlignToBundleEnd; } void setAlignToBundleEnd(bool V) { AlignToBundleEnd = V; } /// Get the padding size that must be inserted before this fragment. /// Used for bundling. By default, no padding is inserted. /// Note that padding size is restricted to 8 bits. This is an optimization /// to reduce the amount of space used for each fragment. In practice, larger /// padding should never be required. uint8_t getBundlePadding() const { return BundlePadding; } /// Set the padding size for this fragment. By default it's a no-op, /// and only some fragments have a meaningful implementation. void setBundlePadding(uint8_t N) { BundlePadding = N; } /// Retrieve the MCSubTargetInfo in effect when the instruction was encoded. /// Guaranteed to be non-null if hasInstructions() == true const MCSubtargetInfo *getSubtargetInfo() const { return STI; } /// Record that the fragment contains instructions with the MCSubtargetInfo in /// effect when the instruction was encoded. void setHasInstructions(const MCSubtargetInfo &STI) { HasInstructions = true; this->STI = &STI; } }; /// Interface implemented by fragments that contain encoded instructions and/or /// data. /// template class MCEncodedFragmentWithContents : public MCEncodedFragment { SmallVector Contents; protected: MCEncodedFragmentWithContents(MCFragment::FragmentType FType, bool HasInstructions, MCSection *Sec) : MCEncodedFragment(FType, HasInstructions, Sec) {} public: SmallVectorImpl &getContents() { return Contents; } const SmallVectorImpl &getContents() const { return Contents; } }; /// Interface implemented by fragments that contain encoded instructions and/or /// data and also have fixups registered. /// template class MCEncodedFragmentWithFixups : public MCEncodedFragmentWithContents { /// Fixups - The list of fixups in this fragment. SmallVector Fixups; protected: MCEncodedFragmentWithFixups(MCFragment::FragmentType FType, bool HasInstructions, MCSection *Sec) : MCEncodedFragmentWithContents(FType, HasInstructions, Sec) {} public: using const_fixup_iterator = SmallVectorImpl::const_iterator; using fixup_iterator = SmallVectorImpl::iterator; SmallVectorImpl &getFixups() { return Fixups; } const SmallVectorImpl &getFixups() const { return Fixups; } fixup_iterator fixup_begin() { return Fixups.begin(); } const_fixup_iterator fixup_begin() const { return Fixups.begin(); } fixup_iterator fixup_end() { return Fixups.end(); } const_fixup_iterator fixup_end() const { return Fixups.end(); } static bool classof(const MCFragment *F) { MCFragment::FragmentType Kind = F->getKind(); return Kind == MCFragment::FT_Relaxable || Kind == MCFragment::FT_Data || - Kind == MCFragment::FT_CVDefRange || Kind == MCFragment::FT_Dwarf;; + Kind == MCFragment::FT_CVDefRange || Kind == MCFragment::FT_Dwarf || + Kind == MCFragment::FT_DwarfFrame; } }; /// Fragment for data and encoded instructions. /// class MCDataFragment : public MCEncodedFragmentWithFixups<32, 4> { public: MCDataFragment(MCSection *Sec = nullptr) : MCEncodedFragmentWithFixups<32, 4>(FT_Data, false, Sec) {} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Data; } }; /// This is a compact (memory-size-wise) fragment for holding an encoded /// instruction (non-relaxable) that has no fixups registered. When applicable, /// it can be used instead of MCDataFragment and lead to lower memory /// consumption. /// class MCCompactEncodedInstFragment : public MCEncodedFragmentWithContents<4> { public: MCCompactEncodedInstFragment(MCSection *Sec = nullptr) : MCEncodedFragmentWithContents(FT_CompactEncodedInst, true, Sec) { } static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_CompactEncodedInst; } }; /// A relaxable fragment holds on to its MCInst, since it may need to be /// relaxed during the assembler layout and relaxation stage. /// class MCRelaxableFragment : public MCEncodedFragmentWithFixups<8, 1> { /// Inst - The instruction this is a fragment for. MCInst Inst; public: MCRelaxableFragment(const MCInst &Inst, const MCSubtargetInfo &STI, MCSection *Sec = nullptr) : MCEncodedFragmentWithFixups(FT_Relaxable, true, Sec), Inst(Inst) { this->STI = &STI; } const MCInst &getInst() const { return Inst; } void setInst(const MCInst &Value) { Inst = Value; } static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Relaxable; } }; class MCAlignFragment : public MCFragment { /// Alignment - The alignment to ensure, in bytes. unsigned Alignment; /// EmitNops - Flag to indicate that (optimal) NOPs should be emitted instead /// of using the provided value. The exact interpretation of this flag is /// target dependent. bool EmitNops : 1; /// Value - Value to use for filling padding bytes. int64_t Value; /// ValueSize - The size of the integer (in bytes) of \p Value. unsigned ValueSize; /// MaxBytesToEmit - The maximum number of bytes to emit; if the alignment /// cannot be satisfied in this width then this fragment is ignored. unsigned MaxBytesToEmit; public: MCAlignFragment(unsigned Alignment, int64_t Value, unsigned ValueSize, unsigned MaxBytesToEmit, MCSection *Sec = nullptr) : MCFragment(FT_Align, false, Sec), Alignment(Alignment), EmitNops(false), Value(Value), ValueSize(ValueSize), MaxBytesToEmit(MaxBytesToEmit) {} /// \name Accessors /// @{ unsigned getAlignment() const { return Alignment; } int64_t getValue() const { return Value; } unsigned getValueSize() const { return ValueSize; } unsigned getMaxBytesToEmit() const { return MaxBytesToEmit; } bool hasEmitNops() const { return EmitNops; } void setEmitNops(bool Value) { EmitNops = Value; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Align; } }; /// Fragment for adding required padding. /// This fragment is always inserted before an instruction, and holds that /// instruction as context information (as well as a mask of kinds) for /// determining the padding size. /// class MCPaddingFragment : public MCFragment { /// A mask containing all the kinds relevant to this fragment. i.e. the i'th /// bit will be set iff kind i is relevant to this fragment. uint64_t PaddingPoliciesMask; /// A boolean indicating if this fragment will actually hold padding. If its /// value is false, then this fragment serves only as a placeholder, /// containing data to assist other insertion point in their decision making. bool IsInsertionPoint; uint64_t Size; struct MCInstInfo { bool IsInitialized; MCInst Inst; /// A boolean indicating whether the instruction pointed by this fragment is /// a fixed size instruction or a relaxable instruction held by a /// MCRelaxableFragment. bool IsImmutableSizedInst; union { /// If the instruction is a fixed size instruction, hold its size. size_t InstSize; /// Otherwise, hold a pointer to the MCRelaxableFragment holding it. MCRelaxableFragment *InstFragment; }; }; MCInstInfo InstInfo; public: static const uint64_t PFK_None = UINT64_C(0); enum MCPaddingFragmentKind { // values 0-7 are reserved for future target independet values. FirstTargetPerfNopFragmentKind = 8, /// Limit range of target MCPerfNopFragment kinds to fit in uint64_t MaxTargetPerfNopFragmentKind = 63 }; MCPaddingFragment(MCSection *Sec = nullptr) : MCFragment(FT_Padding, false, Sec), PaddingPoliciesMask(PFK_None), IsInsertionPoint(false), Size(UINT64_C(0)), InstInfo({false, MCInst(), false, {0}}) {} bool isInsertionPoint() const { return IsInsertionPoint; } void setAsInsertionPoint() { IsInsertionPoint = true; } uint64_t getPaddingPoliciesMask() const { return PaddingPoliciesMask; } void setPaddingPoliciesMask(uint64_t Value) { PaddingPoliciesMask = Value; } bool hasPaddingPolicy(uint64_t PolicyMask) const { assert(isPowerOf2_64(PolicyMask) && "Policy mask must contain exactly one policy"); return (getPaddingPoliciesMask() & PolicyMask) != PFK_None; } const MCInst &getInst() const { assert(isInstructionInitialized() && "Fragment has no instruction!"); return InstInfo.Inst; } size_t getInstSize() const { assert(isInstructionInitialized() && "Fragment has no instruction!"); if (InstInfo.IsImmutableSizedInst) return InstInfo.InstSize; assert(InstInfo.InstFragment != nullptr && "Must have a valid InstFragment to retrieve InstSize from"); return InstInfo.InstFragment->getContents().size(); } void setInstAndInstSize(const MCInst &Inst, size_t InstSize) { InstInfo.IsInitialized = true; InstInfo.IsImmutableSizedInst = true; InstInfo.Inst = Inst; InstInfo.InstSize = InstSize; } void setInstAndInstFragment(const MCInst &Inst, MCRelaxableFragment *InstFragment) { InstInfo.IsInitialized = true; InstInfo.IsImmutableSizedInst = false; InstInfo.Inst = Inst; InstInfo.InstFragment = InstFragment; } uint64_t getSize() const { return Size; } void setSize(uint64_t Value) { Size = Value; } bool isInstructionInitialized() const { return InstInfo.IsInitialized; } static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Padding; } }; class MCFillFragment : public MCFragment { /// Value to use for filling bytes. uint64_t Value; uint8_t ValueSize; /// The number of bytes to insert. const MCExpr &NumValues; /// Source location of the directive that this fragment was created for. SMLoc Loc; public: MCFillFragment(uint64_t Value, uint8_t VSize, const MCExpr &NumValues, SMLoc Loc, MCSection *Sec = nullptr) : MCFragment(FT_Fill, false, Sec), Value(Value), ValueSize(VSize), NumValues(NumValues), Loc(Loc) {} uint64_t getValue() const { return Value; } uint8_t getValueSize() const { return ValueSize; } const MCExpr &getNumValues() const { return NumValues; } SMLoc getLoc() const { return Loc; } static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Fill; } }; class MCOrgFragment : public MCFragment { /// The offset this fragment should start at. const MCExpr *Offset; /// Value to use for filling bytes. int8_t Value; /// Source location of the directive that this fragment was created for. SMLoc Loc; public: MCOrgFragment(const MCExpr &Offset, int8_t Value, SMLoc Loc, MCSection *Sec = nullptr) : MCFragment(FT_Org, false, Sec), Offset(&Offset), Value(Value), Loc(Loc) {} /// \name Accessors /// @{ const MCExpr &getOffset() const { return *Offset; } uint8_t getValue() const { return Value; } SMLoc getLoc() const { return Loc; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Org; } }; class MCLEBFragment : public MCFragment { /// Value - The value this fragment should contain. const MCExpr *Value; /// IsSigned - True if this is a sleb128, false if uleb128. bool IsSigned; SmallString<8> Contents; public: MCLEBFragment(const MCExpr &Value_, bool IsSigned_, MCSection *Sec = nullptr) : MCFragment(FT_LEB, false, Sec), Value(&Value_), IsSigned(IsSigned_) { Contents.push_back(0); } /// \name Accessors /// @{ const MCExpr &getValue() const { return *Value; } bool isSigned() const { return IsSigned; } SmallString<8> &getContents() { return Contents; } const SmallString<8> &getContents() const { return Contents; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_LEB; } }; class MCDwarfLineAddrFragment : public MCEncodedFragmentWithFixups<8, 1> { /// LineDelta - the value of the difference between the two line numbers /// between two .loc dwarf directives. int64_t LineDelta; /// AddrDelta - The expression for the difference of the two symbols that /// make up the address delta between two .loc dwarf directives. const MCExpr *AddrDelta; public: MCDwarfLineAddrFragment(int64_t LineDelta, const MCExpr &AddrDelta, MCSection *Sec = nullptr) : MCEncodedFragmentWithFixups<8, 1>(FT_Dwarf, false, Sec), LineDelta(LineDelta), AddrDelta(&AddrDelta) {} /// \name Accessors /// @{ int64_t getLineDelta() const { return LineDelta; } const MCExpr &getAddrDelta() const { return *AddrDelta; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Dwarf; } }; -class MCDwarfCallFrameFragment : public MCFragment { +class MCDwarfCallFrameFragment : public MCEncodedFragmentWithFixups<8, 1> { /// AddrDelta - The expression for the difference of the two symbols that /// make up the address delta between two .cfi_* dwarf directives. const MCExpr *AddrDelta; - SmallString<8> Contents; - public: MCDwarfCallFrameFragment(const MCExpr &AddrDelta, MCSection *Sec = nullptr) - : MCFragment(FT_DwarfFrame, false, Sec), AddrDelta(&AddrDelta) { - Contents.push_back(0); - } + : MCEncodedFragmentWithFixups<8, 1>(FT_DwarfFrame, false, Sec), + AddrDelta(&AddrDelta) {} /// \name Accessors /// @{ const MCExpr &getAddrDelta() const { return *AddrDelta; } - - SmallString<8> &getContents() { return Contents; } - const SmallString<8> &getContents() const { return Contents; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_DwarfFrame; } }; /// Represents a symbol table index fragment. class MCSymbolIdFragment : public MCFragment { const MCSymbol *Sym; public: MCSymbolIdFragment(const MCSymbol *Sym, MCSection *Sec = nullptr) : MCFragment(FT_SymbolId, false, Sec), Sym(Sym) {} /// \name Accessors /// @{ const MCSymbol *getSymbol() { return Sym; } const MCSymbol *getSymbol() const { return Sym; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_SymbolId; } }; /// Fragment representing the binary annotations produced by the /// .cv_inline_linetable directive. class MCCVInlineLineTableFragment : public MCFragment { unsigned SiteFuncId; unsigned StartFileId; unsigned StartLineNum; const MCSymbol *FnStartSym; const MCSymbol *FnEndSym; SmallString<8> Contents; /// CodeViewContext has the real knowledge about this format, so let it access /// our members. friend class CodeViewContext; public: MCCVInlineLineTableFragment(unsigned SiteFuncId, unsigned StartFileId, unsigned StartLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym, MCSection *Sec = nullptr) : MCFragment(FT_CVInlineLines, false, Sec), SiteFuncId(SiteFuncId), StartFileId(StartFileId), StartLineNum(StartLineNum), FnStartSym(FnStartSym), FnEndSym(FnEndSym) {} /// \name Accessors /// @{ const MCSymbol *getFnStartSym() const { return FnStartSym; } const MCSymbol *getFnEndSym() const { return FnEndSym; } SmallString<8> &getContents() { return Contents; } const SmallString<8> &getContents() const { return Contents; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_CVInlineLines; } }; /// Fragment representing the .cv_def_range directive. class MCCVDefRangeFragment : public MCEncodedFragmentWithFixups<32, 4> { SmallVector, 2> Ranges; SmallString<32> FixedSizePortion; /// CodeViewContext has the real knowledge about this format, so let it access /// our members. friend class CodeViewContext; public: MCCVDefRangeFragment( ArrayRef> Ranges, StringRef FixedSizePortion, MCSection *Sec = nullptr) : MCEncodedFragmentWithFixups<32, 4>(FT_CVDefRange, false, Sec), Ranges(Ranges.begin(), Ranges.end()), FixedSizePortion(FixedSizePortion) {} /// \name Accessors /// @{ ArrayRef> getRanges() const { return Ranges; } StringRef getFixedSizePortion() const { return FixedSizePortion; } /// @} static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_CVDefRange; } }; } // end namespace llvm #endif // LLVM_MC_MCFRAGMENT_H Index: head/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- head/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp (revision 354468) +++ head/contrib/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp (revision 354469) @@ -1,1849 +1,1853 @@ //===- DWARFContext.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Object/Decompressor.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocationResolver.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace llvm; using namespace dwarf; using namespace object; #define DEBUG_TYPE "dwarf" using DWARFLineTable = DWARFDebugLine::LineTable; using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; DWARFContext::DWARFContext(std::unique_ptr DObj, std::string DWPName) : DIContext(CK_DWARF), DWPName(std::move(DWPName)), DObj(std::move(DObj)) {} DWARFContext::~DWARFContext() = default; /// Dump the UUID load command. static void dumpUUID(raw_ostream &OS, const ObjectFile &Obj) { auto *MachO = dyn_cast(&Obj); if (!MachO) return; for (auto LC : MachO->load_commands()) { raw_ostream::uuid_t UUID; if (LC.C.cmd == MachO::LC_UUID) { if (LC.C.cmdsize < sizeof(UUID) + sizeof(LC.C)) { OS << "error: UUID load command is too short.\n"; return; } OS << "UUID: "; memcpy(&UUID, LC.Ptr+sizeof(LC.C), sizeof(UUID)); OS.write_uuid(UUID); Triple T = MachO->getArchTriple(); OS << " (" << T.getArchName() << ')'; OS << ' ' << MachO->getFileName() << '\n'; } } } using ContributionCollection = std::vector>; // Collect all the contributions to the string offsets table from all units, // sort them by their starting offsets and remove duplicates. static ContributionCollection collectContributionData(DWARFContext::unit_iterator_range Units) { ContributionCollection Contributions; for (const auto &U : Units) if (const auto &C = U->getStringOffsetsTableContribution()) Contributions.push_back(C); // Sort the contributions so that any invalid ones are placed at // the start of the contributions vector. This way they are reported // first. llvm::sort(Contributions, [](const Optional &L, const Optional &R) { if (L && R) return L->Base < R->Base; return R.hasValue(); }); // Uniquify contributions, as it is possible that units (specifically // type units in dwo or dwp files) share contributions. We don't want // to report them more than once. Contributions.erase( std::unique(Contributions.begin(), Contributions.end(), [](const Optional &L, const Optional &R) { if (L && R) return L->Base == R->Base && L->Size == R->Size; return false; }), Contributions.end()); return Contributions; } static void dumpDWARFv5StringOffsetsSection( raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, const DWARFSection &StringOffsetsSection, StringRef StringSection, DWARFContext::unit_iterator_range Units, bool LittleEndian) { auto Contributions = collectContributionData(Units); DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); DataExtractor StrData(StringSection, LittleEndian, 0); uint64_t SectionSize = StringOffsetsSection.Data.size(); uint32_t Offset = 0; for (auto &Contribution : Contributions) { // Report an ill-formed contribution. if (!Contribution) { OS << "error: invalid contribution to string offsets table in section ." << SectionName << ".\n"; return; } dwarf::DwarfFormat Format = Contribution->getFormat(); uint16_t Version = Contribution->getVersion(); uint64_t ContributionHeader = Contribution->Base; // In DWARF v5 there is a contribution header that immediately precedes // the string offsets base (the location we have previously retrieved from // the CU DIE's DW_AT_str_offsets attribute). The header is located either // 8 or 16 bytes before the base, depending on the contribution's format. if (Version >= 5) ContributionHeader -= Format == DWARF32 ? 8 : 16; // Detect overlapping contributions. if (Offset > ContributionHeader) { WithColor::error() << "overlapping contributions to string offsets table in section ." << SectionName << ".\n"; return; } // Report a gap in the table. if (Offset < ContributionHeader) { OS << format("0x%8.8x: Gap, length = ", Offset); OS << (ContributionHeader - Offset) << "\n"; } OS << format("0x%8.8x: ", (uint32_t)ContributionHeader); // In DWARF v5 the contribution size in the descriptor does not equal // the originally encoded length (it does not contain the length of the // version field and the padding, a total of 4 bytes). Add them back in // for reporting. OS << "Contribution size = " << (Contribution->Size + (Version < 5 ? 0 : 4)) << ", Format = " << (Format == DWARF32 ? "DWARF32" : "DWARF64") << ", Version = " << Version << "\n"; Offset = Contribution->Base; unsigned EntrySize = Contribution->getDwarfOffsetByteSize(); while (Offset - Contribution->Base < Contribution->Size) { OS << format("0x%8.8x: ", Offset); // FIXME: We can only extract strings if the offset fits in 32 bits. uint64_t StringOffset = StrOffsetExt.getRelocatedValue(EntrySize, &Offset); // Extract the string if we can and display it. Otherwise just report // the offset. if (StringOffset <= std::numeric_limits::max()) { uint32_t StringOffset32 = (uint32_t)StringOffset; OS << format("%8.8x ", StringOffset32); const char *S = StrData.getCStr(&StringOffset32); if (S) OS << format("\"%s\"", S); } else OS << format("%16.16" PRIx64 " ", StringOffset); OS << "\n"; } } // Report a gap at the end of the table. if (Offset < SectionSize) { OS << format("0x%8.8x: Gap, length = ", Offset); OS << (SectionSize - Offset) << "\n"; } } // Dump a DWARF string offsets section. This may be a DWARF v5 formatted // string offsets section, where each compile or type unit contributes a // number of entries (string offsets), with each contribution preceded by // a header containing size and version number. Alternatively, it may be a // monolithic series of string offsets, as generated by the pre-DWARF v5 // implementation of split DWARF. static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, const DWARFSection &StringOffsetsSection, StringRef StringSection, DWARFContext::unit_iterator_range Units, bool LittleEndian, unsigned MaxVersion) { // If we have at least one (compile or type) unit with DWARF v5 or greater, // we assume that the section is formatted like a DWARF v5 string offsets // section. if (MaxVersion >= 5) dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, StringSection, Units, LittleEndian); else { DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); uint32_t offset = 0; uint64_t size = StringOffsetsSection.Data.size(); // Ensure that size is a multiple of the size of an entry. if (size & ((uint64_t)(sizeof(uint32_t) - 1))) { OS << "error: size of ." << SectionName << " is not a multiple of " << sizeof(uint32_t) << ".\n"; size &= -(uint64_t)sizeof(uint32_t); } DataExtractor StrData(StringSection, LittleEndian, 0); while (offset < size) { OS << format("0x%8.8x: ", offset); uint32_t StringOffset = strOffsetExt.getU32(&offset); OS << format("%8.8x ", StringOffset); const char *S = StrData.getCStr(&StringOffset); if (S) OS << format("\"%s\"", S); OS << "\n"; } } } // Dump the .debug_addr section. static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, DIDumpOptions DumpOpts, uint16_t Version, uint8_t AddrSize) { uint32_t Offset = 0; while (AddrData.isValidOffset(Offset)) { DWARFDebugAddrTable AddrTable; uint32_t TableOffset = Offset; if (Error Err = AddrTable.extract(AddrData, &Offset, Version, AddrSize, DWARFContext::dumpWarning)) { WithColor::error() << toString(std::move(Err)) << '\n'; // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. if (!AddrTable.hasValidLength()) break; uint64_t Length = AddrTable.getLength(); Offset = TableOffset + Length; } else { AddrTable.dump(OS, DumpOpts); } } } // Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). static void dumpRnglistsSection( raw_ostream &OS, DWARFDataExtractor &rnglistData, llvm::function_ref(uint32_t)> LookupPooledAddress, DIDumpOptions DumpOpts) { uint32_t Offset = 0; while (rnglistData.isValidOffset(Offset)) { llvm::DWARFDebugRnglistTable Rnglists; uint32_t TableOffset = Offset; if (Error Err = Rnglists.extract(rnglistData, &Offset)) { WithColor::error() << toString(std::move(Err)) << '\n'; uint64_t Length = Rnglists.length(); // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. if (Length == 0) break; Offset = TableOffset + Length; } else { Rnglists.dump(OS, LookupPooledAddress, DumpOpts); } } } static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const MCRegisterInfo *MRI, Optional DumpOffset) { uint32_t Offset = 0; DWARFDebugLoclists Loclists; DWARFListTableHeader Header(".debug_loclists", "locations"); if (Error E = Header.extract(Data, &Offset)) { WithColor::error() << toString(std::move(E)) << '\n'; return; } Header.dump(OS, DumpOpts); DataExtractor LocData(Data.getData().drop_front(Offset), Data.isLittleEndian(), Header.getAddrSize()); Loclists.parse(LocData, Header.getVersion()); Loclists.dump(OS, 0, MRI, DumpOffset); } void DWARFContext::dump( raw_ostream &OS, DIDumpOptions DumpOpts, std::array, DIDT_ID_Count> DumpOffsets) { uint64_t DumpType = DumpOpts.DumpType; StringRef Extension = sys::path::extension(DObj->getFileName()); bool IsDWO = (Extension == ".dwo") || (Extension == ".dwp"); // Print UUID header. const auto *ObjFile = DObj->getFile(); if (DumpType & DIDT_UUID) dumpUUID(OS, *ObjFile); // Print a header for each explicitly-requested section. // Otherwise just print one for non-empty sections. // Only print empty .dwo section headers when dumping a .dwo file. bool Explicit = DumpType != DIDT_All && !IsDWO; bool ExplicitDWO = Explicit && IsDWO; auto shouldDump = [&](bool Explicit, const char *Name, unsigned ID, StringRef Section) -> Optional * { unsigned Mask = 1U << ID; bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); if (!Should) return nullptr; OS << "\n" << Name << " contents:\n"; return &DumpOffsets[ID]; }; // Dump individual sections. if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, DObj->getAbbrevSection())) getDebugAbbrev()->dump(OS); if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, DObj->getAbbrevDWOSection())) getDebugAbbrevDWO()->dump(OS); auto dumpDebugInfo = [&](const char *Name, unit_iterator_range Units) { OS << '\n' << Name << " contents:\n"; if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugInfo]) for (const auto &U : Units) U->getDIEForOffset(DumpOffset.getValue()) .dump(OS, 0, DumpOpts.noImplicitRecursion()); else for (const auto &U : Units) U->dump(OS, DumpOpts); }; if ((DumpType & DIDT_DebugInfo)) { if (Explicit || getNumCompileUnits()) dumpDebugInfo(".debug_info", info_section_units()); if (ExplicitDWO || getNumDWOCompileUnits()) dumpDebugInfo(".debug_info.dwo", dwo_info_section_units()); } auto dumpDebugType = [&](const char *Name, unit_iterator_range Units) { OS << '\n' << Name << " contents:\n"; for (const auto &U : Units) if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugTypes]) U->getDIEForOffset(*DumpOffset) .dump(OS, 0, DumpOpts.noImplicitRecursion()); else U->dump(OS, DumpOpts); }; if ((DumpType & DIDT_DebugTypes)) { if (Explicit || getNumTypeUnits()) dumpDebugType(".debug_types", types_section_units()); if (ExplicitDWO || getNumDWOTypeUnits()) dumpDebugType(".debug_types.dwo", dwo_types_section_units()); } if (const auto *Off = shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, DObj->getLocSection().Data)) { getDebugLoc()->dump(OS, getRegisterInfo(), *Off); } if (const auto *Off = shouldDump(Explicit, ".debug_loclists", DIDT_ID_DebugLoclists, DObj->getLoclistsSection().Data)) { DWARFDataExtractor Data(*DObj, DObj->getLoclistsSection(), isLittleEndian(), 0); dumpLoclistsSection(OS, DumpOpts, Data, getRegisterInfo(), *Off); } if (const auto *Off = shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, DObj->getLocDWOSection().Data)) { getDebugLocDWO()->dump(OS, 0, getRegisterInfo(), *Off); } if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, - DObj->getDebugFrameSection())) + DObj->getDebugFrameSection().Data)) getDebugFrame()->dump(OS, getRegisterInfo(), *Off); if (const auto *Off = shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, - DObj->getEHFrameSection())) + DObj->getEHFrameSection().Data)) getEHFrame()->dump(OS, getRegisterInfo(), *Off); if (DumpType & DIDT_DebugMacro) { if (Explicit || !getDebugMacro()->empty()) { OS << "\n.debug_macinfo contents:\n"; getDebugMacro()->dump(OS); } } if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, DObj->getARangeSection())) { uint32_t offset = 0; DataExtractor arangesData(DObj->getARangeSection(), isLittleEndian(), 0); DWARFDebugArangeSet set; while (set.extract(arangesData, &offset)) set.dump(OS); } auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, DIDumpOptions DumpOpts, Optional DumpOffset) { while (!Parser.done()) { if (DumpOffset && Parser.getOffset() != *DumpOffset) { Parser.skip(dumpWarning); continue; } OS << "debug_line[" << format("0x%8.8x", Parser.getOffset()) << "]\n"; if (DumpOpts.Verbose) { Parser.parseNext(dumpWarning, dumpWarning, &OS); } else { DWARFDebugLine::LineTable LineTable = Parser.parseNext(dumpWarning, dumpWarning); LineTable.dump(OS, DumpOpts); } } }; if (const auto *Off = shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, DObj->getLineSection().Data)) { DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), 0); DWARFDebugLine::SectionParser Parser(LineData, *this, compile_units(), type_units()); DumpLineSection(Parser, DumpOpts, *Off); } if (const auto *Off = shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, DObj->getLineDWOSection().Data)) { DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), isLittleEndian(), 0); DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(), dwo_type_units()); DumpLineSection(Parser, DumpOpts, *Off); } if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, DObj->getCUIndexSection())) { getCUIndex().dump(OS); } if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, DObj->getTUIndexSection())) { getTUIndex().dump(OS); } if (shouldDump(Explicit, ".debug_str", DIDT_ID_DebugStr, DObj->getStringSection())) { DataExtractor strData(DObj->getStringSection(), isLittleEndian(), 0); uint32_t offset = 0; uint32_t strOffset = 0; while (const char *s = strData.getCStr(&offset)) { OS << format("0x%8.8x: \"%s\"\n", strOffset, s); strOffset = offset; } } if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_ID_DebugStr, DObj->getStringDWOSection())) { DataExtractor strDWOData(DObj->getStringDWOSection(), isLittleEndian(), 0); uint32_t offset = 0; uint32_t strDWOOffset = 0; while (const char *s = strDWOData.getCStr(&offset)) { OS << format("0x%8.8x: \"%s\"\n", strDWOOffset, s); strDWOOffset = offset; } } if (shouldDump(Explicit, ".debug_line_str", DIDT_ID_DebugLineStr, DObj->getLineStringSection())) { DataExtractor strData(DObj->getLineStringSection(), isLittleEndian(), 0); uint32_t offset = 0; uint32_t strOffset = 0; while (const char *s = strData.getCStr(&offset)) { OS << format("0x%8.8x: \"", strOffset); OS.write_escaped(s); OS << "\"\n"; strOffset = offset; } } if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr, DObj->getAddrSection().Data)) { DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), isLittleEndian(), 0); dumpAddrSection(OS, AddrData, DumpOpts, getMaxVersion(), getCUAddrSize()); } if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, DObj->getRangeSection().Data)) { uint8_t savedAddressByteSize = getCUAddrSize(); DWARFDataExtractor rangesData(*DObj, DObj->getRangeSection(), isLittleEndian(), savedAddressByteSize); uint32_t offset = 0; DWARFDebugRangeList rangeList; while (rangesData.isValidOffset(offset)) { if (Error E = rangeList.extract(rangesData, &offset)) { WithColor::error() << toString(std::move(E)) << '\n'; break; } rangeList.dump(OS); } } auto LookupPooledAddress = [&](uint32_t Index) -> Optional { const auto &CUs = compile_units(); auto I = CUs.begin(); if (I == CUs.end()) return None; return (*I)->getAddrOffsetSectionItem(Index); }; if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, DObj->getRnglistsSection().Data)) { DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), isLittleEndian(), 0); dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); } if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, DObj->getRnglistsDWOSection().Data)) { DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), isLittleEndian(), 0); dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); } if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, DObj->getPubNamesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getPubNamesSection(), isLittleEndian(), false) .dump(OS); if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, DObj->getPubTypesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getPubTypesSection(), isLittleEndian(), false) .dump(OS); if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, DObj->getGnuPubNamesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getGnuPubNamesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, DObj->getGnuPubTypesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getGnuPubTypesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, DObj->getStringOffsetSection().Data)) dumpStringOffsetsSection(OS, "debug_str_offsets", *DObj, DObj->getStringOffsetSection(), DObj->getStringSection(), normal_units(), isLittleEndian(), getMaxVersion()); if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, DObj->getStringOffsetDWOSection().Data)) dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", *DObj, DObj->getStringOffsetDWOSection(), DObj->getStringDWOSection(), dwo_units(), isLittleEndian(), getMaxDWOVersion()); if (shouldDump(Explicit, ".gdb_index", DIDT_ID_GdbIndex, DObj->getGdbIndexSection())) { getGdbIndex().dump(OS); } if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, DObj->getAppleNamesSection().Data)) getAppleNames().dump(OS); if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, DObj->getAppleTypesSection().Data)) getAppleTypes().dump(OS); if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, DObj->getAppleNamespacesSection().Data)) getAppleNamespaces().dump(OS); if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, DObj->getAppleObjCSection().Data)) getAppleObjC().dump(OS); if (shouldDump(Explicit, ".debug_names", DIDT_ID_DebugNames, DObj->getDebugNamesSection().Data)) getDebugNames().dump(OS); } DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { parseDWOUnits(LazyParse); if (const auto &CUI = getCUIndex()) { if (const auto *R = CUI.getFromHash(Hash)) return dyn_cast_or_null( DWOUnits.getUnitForIndexEntry(*R)); return nullptr; } // If there's no index, just search through the CUs in the DWO - there's // probably only one unless this is something like LTO - though an in-process // built/cached lookup table could be used in that case to improve repeated // lookups of different CUs in the DWO. for (const auto &DWOCU : dwo_compile_units()) { // Might not have parsed DWO ID yet. if (!DWOCU->getDWOId()) { if (Optional DWOId = toUnsigned(DWOCU->getUnitDIE().find(DW_AT_GNU_dwo_id))) DWOCU->setDWOId(*DWOId); else // No DWO ID? continue; } if (DWOCU->getDWOId() == Hash) return dyn_cast(DWOCU.get()); } return nullptr; } DWARFDie DWARFContext::getDIEForOffset(uint32_t Offset) { parseNormalUnits(); if (auto *CU = NormalUnits.getUnitForOffset(Offset)) return CU->getDIEForOffset(Offset); return DWARFDie(); } bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { bool Success = true; DWARFVerifier verifier(OS, *this, DumpOpts); Success &= verifier.handleDebugAbbrev(); if (DumpOpts.DumpType & DIDT_DebugInfo) Success &= verifier.handleDebugInfo(); if (DumpOpts.DumpType & DIDT_DebugLine) Success &= verifier.handleDebugLine(); Success &= verifier.handleAccelTables(); return Success; } const DWARFUnitIndex &DWARFContext::getCUIndex() { if (CUIndex) return *CUIndex; DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); CUIndex = llvm::make_unique(DW_SECT_INFO); CUIndex->parse(CUIndexData); return *CUIndex; } const DWARFUnitIndex &DWARFContext::getTUIndex() { if (TUIndex) return *TUIndex; DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); TUIndex = llvm::make_unique(DW_SECT_TYPES); TUIndex->parse(TUIndexData); return *TUIndex; } DWARFGdbIndex &DWARFContext::getGdbIndex() { if (GdbIndex) return *GdbIndex; DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); GdbIndex = llvm::make_unique(); GdbIndex->parse(GdbIndexData); return *GdbIndex; } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { if (Abbrev) return Abbrev.get(); DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); Abbrev.reset(new DWARFDebugAbbrev()); Abbrev->extract(abbrData); return Abbrev.get(); } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { if (AbbrevDWO) return AbbrevDWO.get(); DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); AbbrevDWO.reset(new DWARFDebugAbbrev()); AbbrevDWO->extract(abbrData); return AbbrevDWO.get(); } const DWARFDebugLoc *DWARFContext::getDebugLoc() { if (Loc) return Loc.get(); Loc.reset(new DWARFDebugLoc); // Assume all units have the same address byte size. if (getNumCompileUnits()) { DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), getUnitAtIndex(0)->getAddressByteSize()); Loc->parse(LocData); } return Loc.get(); } const DWARFDebugLoclists *DWARFContext::getDebugLocDWO() { if (LocDWO) return LocDWO.get(); LocDWO.reset(new DWARFDebugLoclists()); // Assume all compile units have the same address byte size. // FIXME: We don't need AddressSize for split DWARF since relocatable // addresses cannot appear there. At the moment DWARFExpression requires it. DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 4); // Use version 4. DWO does not support the DWARF v5 .debug_loclists yet and // that means we are parsing the new style .debug_loc (pre-standatized version // of the .debug_loclists). LocDWO->parse(LocData, 4 /* Version */); return LocDWO.get(); } const DWARFDebugAranges *DWARFContext::getDebugAranges() { if (Aranges) return Aranges.get(); Aranges.reset(new DWARFDebugAranges()); Aranges->generate(this); return Aranges.get(); } const DWARFDebugFrame *DWARFContext::getDebugFrame() { if (DebugFrame) return DebugFrame.get(); // There's a "bug" in the DWARFv3 standard with respect to the target address // size within debug frame sections. While DWARF is supposed to be independent // of its container, FDEs have fields with size being "target address size", // which isn't specified in DWARF in general. It's only specified for CUs, but // .eh_frame can appear without a .debug_info section. Follow the example of // other tools (libdwarf) and extract this from the container (ObjectFile // provides this information). This problem is fixed in DWARFv4 // See this dwarf-discuss discussion for more details: // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html - DWARFDataExtractor debugFrameData(DObj->getDebugFrameSection(), + DWARFDataExtractor debugFrameData(*DObj, DObj->getDebugFrameSection(), isLittleEndian(), DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(getArch(), false /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); } const DWARFDebugFrame *DWARFContext::getEHFrame() { if (EHFrame) return EHFrame.get(); - DWARFDataExtractor debugFrameData(DObj->getEHFrameSection(), isLittleEndian(), - DObj->getAddressSize()); + DWARFDataExtractor debugFrameData(*DObj, DObj->getEHFrameSection(), + isLittleEndian(), DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(getArch(), true /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); } const DWARFDebugMacro *DWARFContext::getDebugMacro() { if (Macro) return Macro.get(); DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); Macro.reset(new DWARFDebugMacro()); Macro->parse(MacinfoData); return Macro.get(); } template static T &getAccelTable(std::unique_ptr &Cache, const DWARFObject &Obj, const DWARFSection &Section, StringRef StringSection, bool IsLittleEndian) { if (Cache) return *Cache; DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); DataExtractor StrData(StringSection, IsLittleEndian, 0); Cache.reset(new T(AccelSection, StrData)); if (Error E = Cache->extract()) llvm::consumeError(std::move(E)); return *Cache; } const DWARFDebugNames &DWARFContext::getDebugNames() { return getAccelTable(Names, *DObj, DObj->getDebugNamesSection(), DObj->getStringSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleNames() { return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), DObj->getStringSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleTypes() { return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), DObj->getStringSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { return getAccelTable(AppleNamespaces, *DObj, DObj->getAppleNamespacesSection(), DObj->getStringSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleObjC() { return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), DObj->getStringSection(), isLittleEndian()); } const DWARFDebugLine::LineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { Expected ExpectedLineTable = getLineTableForUnit(U, dumpWarning); if (!ExpectedLineTable) { dumpWarning(ExpectedLineTable.takeError()); return nullptr; } return *ExpectedLineTable; } Expected DWARFContext::getLineTableForUnit( DWARFUnit *U, std::function RecoverableErrorCallback) { if (!Line) Line.reset(new DWARFDebugLine); auto UnitDIE = U->getUnitDIE(); if (!UnitDIE) return nullptr; auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); if (!Offset) return nullptr; // No line table for this compile unit. uint32_t stmtOffset = *Offset + U->getLineTableOffset(); // See if the line table is cached. if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) return lt; // Make sure the offset is good before we try to parse. if (stmtOffset >= U->getLineSection().Data.size()) return nullptr; // We have to parse it first. DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, RecoverableErrorCallback); } void DWARFContext::parseNormalUnits() { if (!NormalUnits.empty()) return; DObj->forEachInfoSections([&](const DWARFSection &S) { NormalUnits.addUnitsForSection(*this, S, DW_SECT_INFO); }); NormalUnits.finishedInfoUnits(); DObj->forEachTypesSections([&](const DWARFSection &S) { NormalUnits.addUnitsForSection(*this, S, DW_SECT_TYPES); }); } void DWARFContext::parseDWOUnits(bool Lazy) { if (!DWOUnits.empty()) return; DObj->forEachInfoDWOSections([&](const DWARFSection &S) { DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_INFO, Lazy); }); DWOUnits.finishedInfoUnits(); DObj->forEachTypesDWOSections([&](const DWARFSection &S) { DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_TYPES, Lazy); }); } DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint32_t Offset) { parseNormalUnits(); return dyn_cast_or_null( NormalUnits.getUnitForOffset(Offset)); } DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { // First, get the offset of the compile unit. uint32_t CUOffset = getDebugAranges()->findAddress(Address); // Retrieve the compile unit. return getCompileUnitForOffset(CUOffset); } DWARFContext::DIEsForAddress DWARFContext::getDIEsForAddress(uint64_t Address) { DIEsForAddress Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address); if (!CU) return Result; Result.CompileUnit = CU; Result.FunctionDIE = CU->getSubroutineForAddress(Address); std::vector Worklist; Worklist.push_back(Result.FunctionDIE); while (!Worklist.empty()) { DWARFDie DIE = Worklist.back(); Worklist.pop_back(); if (!DIE.isValid()) continue; if (DIE.getTag() == DW_TAG_lexical_block && DIE.addressRangeContainsAddress(Address)) { Result.BlockDIE = DIE; break; } for (auto Child : DIE) Worklist.push_back(Child); } return Result; } /// TODO: change input parameter from "uint64_t Address" /// into "SectionedAddress Address" static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, uint64_t Address, FunctionNameKind Kind, std::string &FunctionName, uint32_t &StartLine) { // The address may correspond to instruction in some inlined function, // so we have to build the chain of inlined functions and take the // name of the topmost function in it. SmallVector InlinedChain; CU->getInlinedChainForAddress(Address, InlinedChain); if (InlinedChain.empty()) return false; const DWARFDie &DIE = InlinedChain[0]; bool FoundResult = false; const char *Name = nullptr; if (Kind != FunctionNameKind::None && (Name = DIE.getSubroutineName(Kind))) { FunctionName = Name; FoundResult = true; } if (auto DeclLineResult = DIE.getDeclLine()) { StartLine = DeclLineResult; FoundResult = true; } return FoundResult; } static Optional getTypeSize(DWARFDie Type, uint64_t PointerSize) { if (auto SizeAttr = Type.find(DW_AT_byte_size)) if (Optional Size = SizeAttr->getAsUnsignedConstant()) return Size; switch (Type.getTag()) { case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: return PointerSize; case DW_TAG_ptr_to_member_type: { if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) if (BaseType.getTag() == DW_TAG_subroutine_type) return 2 * PointerSize; return PointerSize; } case DW_TAG_const_type: case DW_TAG_volatile_type: case DW_TAG_restrict_type: case DW_TAG_typedef: { if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) return getTypeSize(BaseType, PointerSize); break; } case DW_TAG_array_type: { DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type); if (!BaseType) return Optional(); Optional BaseSize = getTypeSize(BaseType, PointerSize); if (!BaseSize) return Optional(); uint64_t Size = *BaseSize; for (DWARFDie Child : Type) { if (Child.getTag() != DW_TAG_subrange_type) continue; if (auto ElemCountAttr = Child.find(DW_AT_count)) if (Optional ElemCount = ElemCountAttr->getAsUnsignedConstant()) Size *= *ElemCount; if (auto UpperBoundAttr = Child.find(DW_AT_upper_bound)) if (Optional UpperBound = UpperBoundAttr->getAsSignedConstant()) { int64_t LowerBound = 0; if (auto LowerBoundAttr = Child.find(DW_AT_lower_bound)) LowerBound = LowerBoundAttr->getAsSignedConstant().getValueOr(0); Size *= *UpperBound - LowerBound + 1; } } return Size; } default: break; } return Optional(); } void DWARFContext::addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, DWARFDie Die, std::vector &Result) { if (Die.getTag() == DW_TAG_variable || Die.getTag() == DW_TAG_formal_parameter) { DILocal Local; if (auto NameAttr = Subprogram.find(DW_AT_name)) if (Optional Name = NameAttr->getAsCString()) Local.FunctionName = *Name; if (auto LocationAttr = Die.find(DW_AT_location)) if (Optional> Location = LocationAttr->getAsBlock()) if (!Location->empty() && (*Location)[0] == DW_OP_fbreg) Local.FrameOffset = decodeSLEB128(Location->data() + 1, nullptr, Location->end()); if (auto TagOffsetAttr = Die.find(DW_AT_LLVM_tag_offset)) Local.TagOffset = TagOffsetAttr->getAsUnsignedConstant(); if (auto Origin = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) Die = Origin; if (auto NameAttr = Die.find(DW_AT_name)) if (Optional Name = NameAttr->getAsCString()) Local.Name = *Name; if (auto Type = Die.getAttributeValueAsReferencedDie(DW_AT_type)) Local.Size = getTypeSize(Type, getCUAddrSize()); if (auto DeclFileAttr = Die.find(DW_AT_decl_file)) { if (const auto *LT = CU->getContext().getLineTableForUnit(CU)) LT->getFileNameByIndex( DeclFileAttr->getAsUnsignedConstant().getValue(), CU->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Local.DeclFile); } if (auto DeclLineAttr = Die.find(DW_AT_decl_line)) Local.DeclLine = DeclLineAttr->getAsUnsignedConstant().getValue(); Result.push_back(Local); return; } if (Die.getTag() == DW_TAG_inlined_subroutine) if (auto Origin = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) Subprogram = Origin; for (auto Child : Die) addLocalsForDie(CU, Subprogram, Child, Result); } std::vector DWARFContext::getLocalsForAddress(object::SectionedAddress Address) { std::vector Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Result; DWARFDie Subprogram = CU->getSubroutineForAddress(Address.Address); if (Subprogram.isValid()) addLocalsForDie(CU, Subprogram, Subprogram, Result); return Result; } DILineInfo DWARFContext::getLineInfoForAddress(object::SectionedAddress Address, DILineInfoSpecifier Spec) { DILineInfo Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Result; getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, Result.FunctionName, Result.StartLine); if (Spec.FLIKind != FileLineInfoKind::None) { if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) { LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Result); } } return Result; } DILineInfoTable DWARFContext::getLineInfoForAddressRange( object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Spec) { DILineInfoTable Lines; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Lines; std::string FunctionName = ""; uint32_t StartLine = 0; getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, FunctionName, StartLine); // If the Specifier says we don't need FileLineInfo, just // return the top-most function at the starting address. if (Spec.FLIKind == FileLineInfoKind::None) { DILineInfo Result; Result.FunctionName = FunctionName; Result.StartLine = StartLine; Lines.push_back(std::make_pair(Address.Address, Result)); return Lines; } const DWARFLineTable *LineTable = getLineTableForUnit(CU); // Get the index of row we're looking for in the line table. std::vector RowVector; if (!LineTable->lookupAddressRange({Address.Address, Address.SectionIndex}, Size, RowVector)) { return Lines; } for (uint32_t RowIndex : RowVector) { // Take file number and line/column from the row. const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; DILineInfo Result; LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(), Spec.FLIKind, Result.FileName); Result.FunctionName = FunctionName; Result.Line = Row.Line; Result.Column = Row.Column; Result.StartLine = StartLine; Lines.push_back(std::make_pair(Row.Address.Address, Result)); } return Lines; } DIInliningInfo DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address, DILineInfoSpecifier Spec) { DIInliningInfo InliningInfo; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return InliningInfo; const DWARFLineTable *LineTable = nullptr; SmallVector InlinedChain; CU->getInlinedChainForAddress(Address.Address, InlinedChain); if (InlinedChain.size() == 0) { // If there is no DIE for address (e.g. it is in unavailable .dwo file), // try to at least get file/line info from symbol table. if (Spec.FLIKind != FileLineInfoKind::None) { DILineInfo Frame; LineTable = getLineTableForUnit(CU); if (LineTable && LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Frame)) InliningInfo.addFrame(Frame); } return InliningInfo; } uint32_t CallFile = 0, CallLine = 0, CallColumn = 0, CallDiscriminator = 0; for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) { DWARFDie &FunctionDIE = InlinedChain[i]; DILineInfo Frame; // Get function name if necessary. if (const char *Name = FunctionDIE.getSubroutineName(Spec.FNKind)) Frame.FunctionName = Name; if (auto DeclLineResult = FunctionDIE.getDeclLine()) Frame.StartLine = DeclLineResult; if (Spec.FLIKind != FileLineInfoKind::None) { if (i == 0) { // For the topmost frame, initialize the line table of this // compile unit and fetch file/line info from it. LineTable = getLineTableForUnit(CU); // For the topmost routine, get file/line info from line table. if (LineTable) LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Frame); } else { // Otherwise, use call file, call line and call column from // previous DIE in inlined chain. if (LineTable) LineTable->getFileNameByIndex(CallFile, CU->getCompilationDir(), Spec.FLIKind, Frame.FileName); Frame.Line = CallLine; Frame.Column = CallColumn; Frame.Discriminator = CallDiscriminator; } // Get call file/line/column of a current DIE. if (i + 1 < n) { FunctionDIE.getCallerFrame(CallFile, CallLine, CallColumn, CallDiscriminator); } } InliningInfo.addFrame(Frame); } return InliningInfo; } std::shared_ptr DWARFContext::getDWOContext(StringRef AbsolutePath) { if (auto S = DWP.lock()) { DWARFContext *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); } std::weak_ptr *Entry = &DWOFiles[AbsolutePath]; if (auto S = Entry->lock()) { DWARFContext *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); } Expected> Obj = [&] { if (!CheckedForDWP) { SmallString<128> DWPName; auto Obj = object::ObjectFile::createObjectFile( this->DWPName.empty() ? (DObj->getFileName() + ".dwp").toStringRef(DWPName) : StringRef(this->DWPName)); if (Obj) { Entry = &DWP; return Obj; } else { CheckedForDWP = true; // TODO: Should this error be handled (maybe in a high verbosity mode) // before falling back to .dwo files? consumeError(Obj.takeError()); } } return object::ObjectFile::createObjectFile(AbsolutePath); }(); if (!Obj) { // TODO: Actually report errors helpfully. consumeError(Obj.takeError()); return nullptr; } auto S = std::make_shared(); S->File = std::move(Obj.get()); S->Context = DWARFContext::create(*S->File.getBinary()); *Entry = S; auto *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); } static Error createError(const Twine &Reason, llvm::Error E) { return make_error(Reason + toString(std::move(E)), inconvertibleErrorCode()); } /// SymInfo contains information about symbol: it's address /// and section index which is -1LL for absolute symbols. struct SymInfo { uint64_t Address; uint64_t SectionIndex; }; /// Returns the address of symbol relocation used against and a section index. /// Used for futher relocations computation. Symbol's section load address is static Expected getSymbolInfo(const object::ObjectFile &Obj, const RelocationRef &Reloc, const LoadedObjectInfo *L, std::map &Cache) { SymInfo Ret = {0, (uint64_t)-1LL}; object::section_iterator RSec = Obj.section_end(); object::symbol_iterator Sym = Reloc.getSymbol(); std::map::iterator CacheIt = Cache.end(); // First calculate the address of the symbol or section as it appears // in the object file if (Sym != Obj.symbol_end()) { bool New; std::tie(CacheIt, New) = Cache.insert({*Sym, {0, 0}}); if (!New) return CacheIt->second; Expected SymAddrOrErr = Sym->getAddress(); if (!SymAddrOrErr) return createError("failed to compute symbol address: ", SymAddrOrErr.takeError()); // Also remember what section this symbol is in for later auto SectOrErr = Sym->getSection(); if (!SectOrErr) return createError("failed to get symbol section: ", SectOrErr.takeError()); RSec = *SectOrErr; Ret.Address = *SymAddrOrErr; } else if (auto *MObj = dyn_cast(&Obj)) { RSec = MObj->getRelocationSection(Reloc.getRawDataRefImpl()); Ret.Address = RSec->getAddress(); } if (RSec != Obj.section_end()) Ret.SectionIndex = RSec->getIndex(); // If we are given load addresses for the sections, we need to adjust: // SymAddr = (Address of Symbol Or Section in File) - // (Address of Section in File) + // (Load Address of Section) // RSec is now either the section being targeted or the section // containing the symbol being targeted. In either case, // we need to perform the same computation. if (L && RSec != Obj.section_end()) if (uint64_t SectionLoadAddress = L->getSectionLoadAddress(*RSec)) Ret.Address += SectionLoadAddress - RSec->getAddress(); if (CacheIt != Cache.end()) CacheIt->second = Ret; return Ret; } static bool isRelocScattered(const object::ObjectFile &Obj, const RelocationRef &Reloc) { const MachOObjectFile *MachObj = dyn_cast(&Obj); if (!MachObj) return false; // MachO also has relocations that point to sections and // scattered relocations. auto RelocInfo = MachObj->getRelocation(Reloc.getRawDataRefImpl()); return MachObj->isRelocationScattered(RelocInfo); } ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { WithColor::error() << toString(std::move(E)) << '\n'; return ErrorPolicy::Continue; } namespace { struct DWARFSectionMap final : public DWARFSection { RelocAddrMap Relocs; }; class DWARFObjInMemory final : public DWARFObject { bool IsLittleEndian; uint8_t AddressSize; StringRef FileName; const object::ObjectFile *Obj = nullptr; std::vector SectionNames; using InfoSectionMap = MapVector>; InfoSectionMap InfoSections; InfoSectionMap TypesSections; InfoSectionMap InfoDWOSections; InfoSectionMap TypesDWOSections; DWARFSectionMap LocSection; DWARFSectionMap LocListsSection; DWARFSectionMap LineSection; DWARFSectionMap RangeSection; DWARFSectionMap RnglistsSection; DWARFSectionMap StringOffsetSection; DWARFSectionMap LineDWOSection; + DWARFSectionMap DebugFrameSection; + DWARFSectionMap EHFrameSection; DWARFSectionMap LocDWOSection; DWARFSectionMap StringOffsetDWOSection; DWARFSectionMap RangeDWOSection; DWARFSectionMap RnglistsDWOSection; DWARFSectionMap AddrSection; DWARFSectionMap AppleNamesSection; DWARFSectionMap AppleTypesSection; DWARFSectionMap AppleNamespacesSection; DWARFSectionMap AppleObjCSection; DWARFSectionMap DebugNamesSection; DWARFSectionMap PubNamesSection; DWARFSectionMap PubTypesSection; DWARFSectionMap GnuPubNamesSection; DWARFSectionMap GnuPubTypesSection; DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { return StringSwitch(Name) .Case("debug_loc", &LocSection) .Case("debug_loclists", &LocListsSection) .Case("debug_line", &LineSection) + .Case("debug_frame", &DebugFrameSection) + .Case("eh_frame", &EHFrameSection) .Case("debug_str_offsets", &StringOffsetSection) .Case("debug_ranges", &RangeSection) .Case("debug_rnglists", &RnglistsSection) .Case("debug_loc.dwo", &LocDWOSection) .Case("debug_line.dwo", &LineDWOSection) .Case("debug_names", &DebugNamesSection) .Case("debug_rnglists.dwo", &RnglistsDWOSection) .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) .Case("debug_addr", &AddrSection) .Case("apple_names", &AppleNamesSection) .Case("debug_pubnames", &PubNamesSection) .Case("debug_pubtypes", &PubTypesSection) .Case("debug_gnu_pubnames", &GnuPubNamesSection) .Case("debug_gnu_pubtypes", &GnuPubTypesSection) .Case("apple_types", &AppleTypesSection) .Case("apple_namespaces", &AppleNamespacesSection) .Case("apple_namespac", &AppleNamespacesSection) .Case("apple_objc", &AppleObjCSection) .Default(nullptr); } StringRef AbbrevSection; StringRef ARangeSection; - StringRef DebugFrameSection; - StringRef EHFrameSection; StringRef StringSection; StringRef MacinfoSection; StringRef AbbrevDWOSection; StringRef StringDWOSection; StringRef CUIndexSection; StringRef GdbIndexSection; StringRef TUIndexSection; StringRef LineStringSection; // A deque holding section data whose iterators are not invalidated when // new decompressed sections are inserted at the end. std::deque> UncompressedSections; StringRef *mapSectionToMember(StringRef Name) { if (DWARFSection *Sec = mapNameToDWARFSection(Name)) return &Sec->Data; return StringSwitch(Name) .Case("debug_abbrev", &AbbrevSection) .Case("debug_aranges", &ARangeSection) - .Case("debug_frame", &DebugFrameSection) - .Case("eh_frame", &EHFrameSection) .Case("debug_str", &StringSection) .Case("debug_macinfo", &MacinfoSection) .Case("debug_abbrev.dwo", &AbbrevDWOSection) .Case("debug_str.dwo", &StringDWOSection) .Case("debug_cu_index", &CUIndexSection) .Case("debug_tu_index", &TUIndexSection) .Case("gdb_index", &GdbIndexSection) .Case("debug_line_str", &LineStringSection) // Any more debug info sections go here. .Default(nullptr); } /// If Sec is compressed section, decompresses and updates its contents /// provided by Data. Otherwise leaves it unchanged. Error maybeDecompress(const object::SectionRef &Sec, StringRef Name, StringRef &Data) { if (!Decompressor::isCompressed(Sec)) return Error::success(); Expected Decompressor = Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); if (!Decompressor) return Decompressor.takeError(); SmallString<0> Out; if (auto Err = Decompressor->resizeAndDecompress(Out)) return Err; UncompressedSections.push_back(std::move(Out)); Data = UncompressedSections.back(); return Error::success(); } public: DWARFObjInMemory(const StringMap> &Sections, uint8_t AddrSize, bool IsLittleEndian) : IsLittleEndian(IsLittleEndian) { for (const auto &SecIt : Sections) { if (StringRef *SectionData = mapSectionToMember(SecIt.first())) *SectionData = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_info") // Find debug_info and debug_types data by section rather than name as // there are multiple, comdat grouped, of these sections. InfoSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_info.dwo") InfoDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_types") TypesSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_types.dwo") TypesDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); } } DWARFObjInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L, function_ref HandleError) : IsLittleEndian(Obj.isLittleEndian()), AddressSize(Obj.getBytesInAddress()), FileName(Obj.getFileName()), Obj(&Obj) { StringMap SectionAmountMap; for (const SectionRef &Section : Obj.sections()) { StringRef Name; Section.getName(Name); ++SectionAmountMap[Name]; SectionNames.push_back({ Name, true }); // Skip BSS and Virtual sections, they aren't interesting. if (Section.isBSS() || Section.isVirtual()) continue; // Skip sections stripped by dsymutil. if (Section.isStripped()) continue; StringRef Data; section_iterator RelocatedSection = Section.getRelocatedSection(); // Try to obtain an already relocated version of this section. // Else use the unrelocated section from the object file. We'll have to // apply relocations ourselves later. if (!L || !L->getLoadedSectionContents(*RelocatedSection, Data)) { Expected E = Section.getContents(); if (E) Data = *E; else // maybeDecompress below will error. consumeError(E.takeError()); } if (auto Err = maybeDecompress(Section, Name, Data)) { ErrorPolicy EP = HandleError(createError( "failed to decompress '" + Name + "', ", std::move(Err))); if (EP == ErrorPolicy::Halt) return; continue; } // Compressed sections names in GNU style starts from ".z", // at this point section is decompressed and we drop compression prefix. Name = Name.substr( Name.find_first_not_of("._z")); // Skip ".", "z" and "_" prefixes. // Map platform specific debug section names to DWARF standard section // names. Name = Obj.mapDebugSectionName(Name); if (StringRef *SectionData = mapSectionToMember(Name)) { *SectionData = Data; if (Name == "debug_ranges") { // FIXME: Use the other dwo range section when we emit it. RangeDWOSection.Data = Data; } } else if (Name == "debug_info") { // Find debug_info and debug_types data by section rather than name as // there are multiple, comdat grouped, of these sections. InfoSections[Section].Data = Data; } else if (Name == "debug_info.dwo") { InfoDWOSections[Section].Data = Data; } else if (Name == "debug_types") { TypesSections[Section].Data = Data; } else if (Name == "debug_types.dwo") { TypesDWOSections[Section].Data = Data; } if (RelocatedSection == Obj.section_end()) continue; StringRef RelSecName; StringRef RelSecData; RelocatedSection->getName(RelSecName); // If the section we're relocating was relocated already by the JIT, // then we used the relocated version above, so we do not need to process // relocations for it now. if (L && L->getLoadedSectionContents(*RelocatedSection, RelSecData)) continue; // In Mach-o files, the relocations do not need to be applied if // there is no load offset to apply. The value read at the // relocation point already factors in the section address // (actually applying the relocations will produce wrong results // as the section address will be added twice). if (!L && isa(&Obj)) continue; RelSecName = RelSecName.substr( RelSecName.find_first_not_of("._z")); // Skip . and _ prefixes. // TODO: Add support for relocations in other sections as needed. // Record relocations for the debug_info and debug_line sections. DWARFSectionMap *Sec = mapNameToDWARFSection(RelSecName); RelocAddrMap *Map = Sec ? &Sec->Relocs : nullptr; if (!Map) { // Find debug_info and debug_types relocs by section rather than name // as there are multiple, comdat grouped, of these sections. if (RelSecName == "debug_info") Map = &static_cast(InfoSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_info.dwo") Map = &static_cast( InfoDWOSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_types") Map = &static_cast(TypesSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_types.dwo") Map = &static_cast( TypesDWOSections[*RelocatedSection]) .Relocs; else continue; } if (Section.relocation_begin() == Section.relocation_end()) continue; // Symbol to [address, section index] cache mapping. std::map AddrCache; bool (*Supports)(uint64_t); RelocationResolver Resolver; std::tie(Supports, Resolver) = getRelocationResolver(Obj); for (const RelocationRef &Reloc : Section.relocations()) { // FIXME: it's not clear how to correctly handle scattered // relocations. if (isRelocScattered(Obj, Reloc)) continue; Expected SymInfoOrErr = getSymbolInfo(Obj, Reloc, L, AddrCache); if (!SymInfoOrErr) { if (HandleError(SymInfoOrErr.takeError()) == ErrorPolicy::Halt) return; continue; } // Check if Resolver can handle this relocation type early so as not to // handle invalid cases in DWARFDataExtractor. // // TODO Don't store Resolver in every RelocAddrEntry. if (Supports && Supports(Reloc.getType())) { auto I = Map->try_emplace( Reloc.getOffset(), RelocAddrEntry{SymInfoOrErr->SectionIndex, Reloc, SymInfoOrErr->Address, Optional(), 0, Resolver}); // If we didn't successfully insert that's because we already had a // relocation for that offset. Store it as a second relocation in the // same RelocAddrEntry instead. if (!I.second) { RelocAddrEntry &entry = I.first->getSecond(); if (entry.Reloc2) { ErrorPolicy EP = HandleError(createError( "At most two relocations per offset are supported")); if (EP == ErrorPolicy::Halt) return; } entry.Reloc2 = Reloc; entry.SymbolValue2 = SymInfoOrErr->Address; } } else { SmallString<32> Type; Reloc.getTypeName(Type); ErrorPolicy EP = HandleError( createError("failed to compute relocation: " + Type + ", ", errorCodeToError(object_error::parse_failed))); if (EP == ErrorPolicy::Halt) return; } } } for (SectionName &S : SectionNames) if (SectionAmountMap[S.Name] > 1) S.IsNameUnique = false; } Optional find(const DWARFSection &S, uint64_t Pos) const override { auto &Sec = static_cast(S); RelocAddrMap::const_iterator AI = Sec.Relocs.find(Pos); if (AI == Sec.Relocs.end()) return None; return AI->second; } const object::ObjectFile *getFile() const override { return Obj; } ArrayRef getSectionNames() const override { return SectionNames; } bool isLittleEndian() const override { return IsLittleEndian; } StringRef getAbbrevDWOSection() const override { return AbbrevDWOSection; } const DWARFSection &getLineDWOSection() const override { return LineDWOSection; } const DWARFSection &getLocDWOSection() const override { return LocDWOSection; } StringRef getStringDWOSection() const override { return StringDWOSection; } const DWARFSection &getStringOffsetDWOSection() const override { return StringOffsetDWOSection; } const DWARFSection &getRangeDWOSection() const override { return RangeDWOSection; } const DWARFSection &getRnglistsDWOSection() const override { return RnglistsDWOSection; } const DWARFSection &getAddrSection() const override { return AddrSection; } StringRef getCUIndexSection() const override { return CUIndexSection; } StringRef getGdbIndexSection() const override { return GdbIndexSection; } StringRef getTUIndexSection() const override { return TUIndexSection; } // DWARF v5 const DWARFSection &getStringOffsetSection() const override { return StringOffsetSection; } StringRef getLineStringSection() const override { return LineStringSection; } // Sections for DWARF5 split dwarf proposal. void forEachInfoDWOSections( function_ref F) const override { for (auto &P : InfoDWOSections) F(P.second); } void forEachTypesDWOSections( function_ref F) const override { for (auto &P : TypesDWOSections) F(P.second); } StringRef getAbbrevSection() const override { return AbbrevSection; } const DWARFSection &getLocSection() const override { return LocSection; } const DWARFSection &getLoclistsSection() const override { return LocListsSection; } StringRef getARangeSection() const override { return ARangeSection; } - StringRef getDebugFrameSection() const override { return DebugFrameSection; } - StringRef getEHFrameSection() const override { return EHFrameSection; } + const DWARFSection &getDebugFrameSection() const override { + return DebugFrameSection; + } + const DWARFSection &getEHFrameSection() const override { + return EHFrameSection; + } const DWARFSection &getLineSection() const override { return LineSection; } StringRef getStringSection() const override { return StringSection; } const DWARFSection &getRangeSection() const override { return RangeSection; } const DWARFSection &getRnglistsSection() const override { return RnglistsSection; } StringRef getMacinfoSection() const override { return MacinfoSection; } const DWARFSection &getPubNamesSection() const override { return PubNamesSection; } const DWARFSection &getPubTypesSection() const override { return PubTypesSection; } const DWARFSection &getGnuPubNamesSection() const override { return GnuPubNamesSection; } const DWARFSection &getGnuPubTypesSection() const override { return GnuPubTypesSection; } const DWARFSection &getAppleNamesSection() const override { return AppleNamesSection; } const DWARFSection &getAppleTypesSection() const override { return AppleTypesSection; } const DWARFSection &getAppleNamespacesSection() const override { return AppleNamespacesSection; } const DWARFSection &getAppleObjCSection() const override { return AppleObjCSection; } const DWARFSection &getDebugNamesSection() const override { return DebugNamesSection; } StringRef getFileName() const override { return FileName; } uint8_t getAddressSize() const override { return AddressSize; } void forEachInfoSections( function_ref F) const override { for (auto &P : InfoSections) F(P.second); } void forEachTypesSections( function_ref F) const override { for (auto &P : TypesSections) F(P.second); } }; } // namespace std::unique_ptr DWARFContext::create(const object::ObjectFile &Obj, const LoadedObjectInfo *L, function_ref HandleError, std::string DWPName) { auto DObj = llvm::make_unique(Obj, L, HandleError); return llvm::make_unique(std::move(DObj), std::move(DWPName)); } std::unique_ptr DWARFContext::create(const StringMap> &Sections, uint8_t AddrSize, bool isLittleEndian) { auto DObj = llvm::make_unique(Sections, AddrSize, isLittleEndian); return llvm::make_unique(std::move(DObj), ""); } Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { // Detect the architecture from the object file. We usually don't need OS // info to lookup a target and create register info. Triple TT; TT.setArch(Triple::ArchType(Obj.getArch())); TT.setVendor(Triple::UnknownVendor); TT.setOS(Triple::UnknownOS); std::string TargetLookupError; const Target *TheTarget = TargetRegistry::lookupTarget(TT.str(), TargetLookupError); if (!TargetLookupError.empty()) return createStringError(errc::invalid_argument, TargetLookupError.c_str()); RegInfo.reset(TheTarget->createMCRegInfo(TT.str())); return Error::success(); } uint8_t DWARFContext::getCUAddrSize() { // In theory, different compile units may have different address byte // sizes, but for simplicity we just use the address byte size of the // last compile unit. In practice the address size field is repeated across // various DWARF headers (at least in version 5) to make it easier to dump // them independently, not to enable varying the address size. uint8_t Addr = 0; for (const auto &CU : compile_units()) { Addr = CU->getAddressByteSize(); break; } return Addr; } void DWARFContext::dumpWarning(Error Warning) { handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { WithColor::warning() << Info.message() << '\n'; }); } Index: head/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp =================================================================== --- head/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp (revision 354468) +++ head/contrib/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp (revision 354469) @@ -1,554 +1,554 @@ //===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace dwarf; // See DWARF standard v3, section 7.23 const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; -Error CFIProgram::parse(DataExtractor Data, uint32_t *Offset, +Error CFIProgram::parse(DWARFDataExtractor Data, uint32_t *Offset, uint32_t EndOffset) { while (*Offset < EndOffset) { - uint8_t Opcode = Data.getU8(Offset); + uint8_t Opcode = Data.getRelocatedValue(1, Offset); // Some instructions have a primary opcode encoded in the top bits. uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; if (Primary) { // If it's a primary opcode, the first operand is encoded in the bottom // bits of the opcode itself. uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; switch (Primary) { default: return createStringError(errc::illegal_byte_sequence, "Invalid primary CFI opcode 0x%" PRIx8, Primary); case DW_CFA_advance_loc: case DW_CFA_restore: addInstruction(Primary, Op1); break; case DW_CFA_offset: addInstruction(Primary, Op1, Data.getULEB128(Offset)); break; } } else { // Extended opcode - its value is Opcode itself. switch (Opcode) { default: return createStringError(errc::illegal_byte_sequence, "Invalid extended CFI opcode 0x%" PRIx8, Opcode); case DW_CFA_nop: case DW_CFA_remember_state: case DW_CFA_restore_state: case DW_CFA_GNU_window_save: // No operands addInstruction(Opcode); break; case DW_CFA_set_loc: // Operands: Address - addInstruction(Opcode, Data.getAddress(Offset)); + addInstruction(Opcode, Data.getRelocatedAddress(Offset)); break; case DW_CFA_advance_loc1: // Operands: 1-byte delta - addInstruction(Opcode, Data.getU8(Offset)); + addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); break; case DW_CFA_advance_loc2: // Operands: 2-byte delta - addInstruction(Opcode, Data.getU16(Offset)); + addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); break; case DW_CFA_advance_loc4: // Operands: 4-byte delta - addInstruction(Opcode, Data.getU32(Offset)); + addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); break; case DW_CFA_restore_extended: case DW_CFA_undefined: case DW_CFA_same_value: case DW_CFA_def_cfa_register: case DW_CFA_def_cfa_offset: case DW_CFA_GNU_args_size: // Operands: ULEB128 addInstruction(Opcode, Data.getULEB128(Offset)); break; case DW_CFA_def_cfa_offset_sf: // Operands: SLEB128 addInstruction(Opcode, Data.getSLEB128(Offset)); break; case DW_CFA_offset_extended: case DW_CFA_register: case DW_CFA_def_cfa: case DW_CFA_val_offset: { // Operands: ULEB128, ULEB128 // Note: We can not embed getULEB128 directly into function // argument list. getULEB128 changes Offset and order of evaluation // for arguments is unspecified. auto op1 = Data.getULEB128(Offset); auto op2 = Data.getULEB128(Offset); addInstruction(Opcode, op1, op2); break; } case DW_CFA_offset_extended_sf: case DW_CFA_def_cfa_sf: case DW_CFA_val_offset_sf: { // Operands: ULEB128, SLEB128 // Note: see comment for the previous case auto op1 = Data.getULEB128(Offset); auto op2 = (uint64_t)Data.getSLEB128(Offset); addInstruction(Opcode, op1, op2); break; } case DW_CFA_def_cfa_expression: { uint32_t ExprLength = Data.getULEB128(Offset); addInstruction(Opcode, 0); DataExtractor Extractor( Data.getData().slice(*Offset, *Offset + ExprLength), Data.isLittleEndian(), Data.getAddressSize()); Instructions.back().Expression = DWARFExpression( Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); *Offset += ExprLength; break; } case DW_CFA_expression: case DW_CFA_val_expression: { auto RegNum = Data.getULEB128(Offset); auto BlockLength = Data.getULEB128(Offset); addInstruction(Opcode, RegNum, 0); DataExtractor Extractor( Data.getData().slice(*Offset, *Offset + BlockLength), Data.isLittleEndian(), Data.getAddressSize()); Instructions.back().Expression = DWARFExpression( Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); *Offset += BlockLength; break; } } } } return Error::success(); } namespace { } // end anonymous namespace ArrayRef CFIProgram::getOperandTypes() { static OperandType OpTypes[DW_CFA_restore+1][2]; static bool Initialized = false; if (Initialized) { return ArrayRef(&OpTypes[0], DW_CFA_restore+1); } Initialized = true; #define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ do { \ OpTypes[OP][0] = OPTYPE0; \ OpTypes[OP][1] = OPTYPE1; \ } while (false) #define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None) #define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None) DECLARE_OP1(DW_CFA_set_loc, OT_Address); DECLARE_OP1(DW_CFA_advance_loc, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset); DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP1(DW_CFA_def_cfa_register, OT_Register); DECLARE_OP1(DW_CFA_def_cfa_offset, OT_Offset); DECLARE_OP1(DW_CFA_def_cfa_offset_sf, OT_SignedFactDataOffset); DECLARE_OP1(DW_CFA_def_cfa_expression, OT_Expression); DECLARE_OP1(DW_CFA_undefined, OT_Register); DECLARE_OP1(DW_CFA_same_value, OT_Register); DECLARE_OP2(DW_CFA_offset, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_offset_extended, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_offset_extended_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP2(DW_CFA_val_offset, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_val_offset_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP2(DW_CFA_register, OT_Register, OT_Register); DECLARE_OP2(DW_CFA_expression, OT_Register, OT_Expression); DECLARE_OP2(DW_CFA_val_expression, OT_Register, OT_Expression); DECLARE_OP1(DW_CFA_restore, OT_Register); DECLARE_OP1(DW_CFA_restore_extended, OT_Register); DECLARE_OP0(DW_CFA_remember_state); DECLARE_OP0(DW_CFA_restore_state); DECLARE_OP0(DW_CFA_GNU_window_save); DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset); DECLARE_OP0(DW_CFA_nop); #undef DECLARE_OP0 #undef DECLARE_OP1 #undef DECLARE_OP2 return ArrayRef(&OpTypes[0], DW_CFA_restore+1); } /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, const Instruction &Instr, unsigned OperandIdx, uint64_t Operand) const { assert(OperandIdx < 2); uint8_t Opcode = Instr.Opcode; OperandType Type = getOperandTypes()[Opcode][OperandIdx]; switch (Type) { case OT_Unset: { OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; auto OpcodeName = CallFrameString(Opcode, Arch); if (!OpcodeName.empty()) OS << " " << OpcodeName; else OS << format(" Opcode %x", Opcode); break; } case OT_None: break; case OT_Address: OS << format(" %" PRIx64, Operand); break; case OT_Offset: // The offsets are all encoded in a unsigned form, but in practice // consumers use them signed. It's most certainly legacy due to // the lack of signed variants in the first Dwarf standards. OS << format(" %+" PRId64, int64_t(Operand)); break; case OT_FactoredCodeOffset: // Always Unsigned if (CodeAlignmentFactor) OS << format(" %" PRId64, Operand * CodeAlignmentFactor); else OS << format(" %" PRId64 "*code_alignment_factor" , Operand); break; case OT_SignedFactDataOffset: if (DataAlignmentFactor) OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); else OS << format(" %" PRId64 "*data_alignment_factor" , int64_t(Operand)); break; case OT_UnsignedFactDataOffset: if (DataAlignmentFactor) OS << format(" %" PRId64, Operand * DataAlignmentFactor); else OS << format(" %" PRId64 "*data_alignment_factor" , Operand); break; case OT_Register: OS << format(" reg%" PRId64, Operand); break; case OT_Expression: assert(Instr.Expression && "missing DWARFExpression object"); OS << " "; Instr.Expression->print(OS, MRI, nullptr, IsEH); break; } } void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, unsigned IndentLevel) const { for (const auto &Instr : Instructions) { uint8_t Opcode = Instr.Opcode; if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; OS.indent(2 * IndentLevel); OS << CallFrameString(Opcode, Arch) << ":"; for (unsigned i = 0; i < Instr.Ops.size(); ++i) printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); OS << '\n'; } } void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length, DW_CIE_ID) << "\n"; OS << format(" Version: %d\n", Version); OS << " Augmentation: \"" << Augmentation << "\"\n"; if (Version >= 4) { OS << format(" Address size: %u\n", (uint32_t)AddressSize); OS << format(" Segment desc size: %u\n", (uint32_t)SegmentDescriptorSize); } OS << format(" Code alignment factor: %u\n", (uint32_t)CodeAlignmentFactor); OS << format(" Data alignment factor: %d\n", (int32_t)DataAlignmentFactor); OS << format(" Return address column: %d\n", (int32_t)ReturnAddressRegister); if (Personality) OS << format(" Personality Address: %016" PRIx64 "\n", *Personality); if (!AugmentationData.empty()) { OS << " Augmentation data: "; for (uint8_t Byte : AugmentationData) OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); OS << "\n"; } OS << "\n"; CFIs.dump(OS, MRI, IsEH); OS << "\n"; } void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length, (int32_t)LinkedCIEOffset); OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset, (uint32_t)InitialLocation, (uint32_t)InitialLocation + (uint32_t)AddressRange); if (LSDAAddress) OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); CFIs.dump(OS, MRI, IsEH); OS << "\n"; } DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch, bool IsEH, uint64_t EHFrameAddress) : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} DWARFDebugFrame::~DWARFDebugFrame() = default; static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, uint32_t Offset, int Length) { errs() << "DUMP: "; for (int i = 0; i < Length; ++i) { uint8_t c = Data.getU8(&Offset); errs().write_hex(c); errs() << " "; } errs() << "\n"; } // This is a workaround for old compilers which do not allow // noreturn attribute usage in lambdas. Once the support for those // compilers are phased out, we can remove this and return back to // a ReportError lambda: [StartOffset](const char *ErrorMsg). static void LLVM_ATTRIBUTE_NORETURN ReportError(uint32_t StartOffset, const char *ErrorMsg) { std::string Str; raw_string_ostream OS(Str); OS << format(ErrorMsg, StartOffset); OS.flush(); report_fatal_error(Str); } void DWARFDebugFrame::parse(DWARFDataExtractor Data) { uint32_t Offset = 0; DenseMap CIEs; while (Data.isValidOffset(Offset)) { uint32_t StartOffset = Offset; bool IsDWARF64 = false; - uint64_t Length = Data.getU32(&Offset); + uint64_t Length = Data.getRelocatedValue(4, &Offset); uint64_t Id; if (Length == UINT32_MAX) { // DWARF-64 is distinguished by the first 32 bits of the initial length // field being 0xffffffff. Then, the next 64 bits are the actual entry // length. IsDWARF64 = true; - Length = Data.getU64(&Offset); + Length = Data.getRelocatedValue(8, &Offset); } // At this point, Offset points to the next field after Length. // Length is the structure size excluding itself. Compute an offset one // past the end of the structure (needed to know how many instructions to // read). // TODO: For honest DWARF64 support, DataExtractor will have to treat // offset_ptr as uint64_t* uint32_t StartStructureOffset = Offset; uint32_t EndStructureOffset = Offset + static_cast(Length); // The Id field's size depends on the DWARF format Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id)); if (IsCIE) { uint8_t Version = Data.getU8(&Offset); const char *Augmentation = Data.getCStr(&Offset); StringRef AugmentationString(Augmentation ? Augmentation : ""); uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : Data.getU8(&Offset); Data.setAddressSize(AddressSize); uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(&Offset); uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset); int64_t DataAlignmentFactor = Data.getSLEB128(&Offset); uint64_t ReturnAddressRegister = Version == 1 ? Data.getU8(&Offset) : Data.getULEB128(&Offset); // Parse the augmentation data for EH CIEs StringRef AugmentationData(""); uint32_t FDEPointerEncoding = DW_EH_PE_absptr; uint32_t LSDAPointerEncoding = DW_EH_PE_omit; Optional Personality; Optional PersonalityEncoding; if (IsEH) { Optional AugmentationLength; uint32_t StartAugmentationOffset; uint32_t EndAugmentationOffset; // Walk the augmentation string to get all the augmentation data. for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { switch (AugmentationString[i]) { default: ReportError(StartOffset, "Unknown augmentation character in entry at %lx"); case 'L': LSDAPointerEncoding = Data.getU8(&Offset); break; case 'P': { if (Personality) ReportError(StartOffset, "Duplicate personality in entry at %lx"); PersonalityEncoding = Data.getU8(&Offset); Personality = Data.getEncodedPointer( &Offset, *PersonalityEncoding, EHFrameAddress ? EHFrameAddress + Offset : 0); break; } case 'R': FDEPointerEncoding = Data.getU8(&Offset); break; case 'S': // Current frame is a signal trampoline. break; case 'z': if (i) ReportError(StartOffset, "'z' must be the first character at %lx"); // Parse the augmentation length first. We only parse it if // the string contains a 'z'. AugmentationLength = Data.getULEB128(&Offset); StartAugmentationOffset = Offset; EndAugmentationOffset = Offset + static_cast(*AugmentationLength); break; case 'B': // B-Key is used for signing functions associated with this // augmentation string break; } } if (AugmentationLength.hasValue()) { if (Offset != EndAugmentationOffset) ReportError(StartOffset, "Parsing augmentation data at %lx failed"); AugmentationData = Data.getData().slice(StartAugmentationOffset, EndAugmentationOffset); } } auto Cie = llvm::make_unique( StartOffset, Length, Version, AugmentationString, AddressSize, SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, ReturnAddressRegister, AugmentationData, FDEPointerEncoding, LSDAPointerEncoding, Personality, PersonalityEncoding, Arch); CIEs[StartOffset] = Cie.get(); Entries.emplace_back(std::move(Cie)); } else { // FDE uint64_t CIEPointer = Id; uint64_t InitialLocation = 0; uint64_t AddressRange = 0; Optional LSDAAddress; CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; if (IsEH) { // The address size is encoded in the CIE we reference. if (!Cie) ReportError(StartOffset, "Parsing FDE data at %lx failed due to missing CIE"); if (auto Val = Data.getEncodedPointer( &Offset, Cie->getFDEPointerEncoding(), EHFrameAddress ? EHFrameAddress + Offset : 0)) { InitialLocation = *Val; } if (auto Val = Data.getEncodedPointer( &Offset, Cie->getFDEPointerEncoding(), 0)) { AddressRange = *Val; } StringRef AugmentationString = Cie->getAugmentationString(); if (!AugmentationString.empty()) { // Parse the augmentation length and data for this FDE. uint64_t AugmentationLength = Data.getULEB128(&Offset); uint32_t EndAugmentationOffset = Offset + static_cast(AugmentationLength); // Decode the LSDA if the CIE augmentation string said we should. if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) { LSDAAddress = Data.getEncodedPointer( &Offset, Cie->getLSDAPointerEncoding(), EHFrameAddress ? Offset + EHFrameAddress : 0); } if (Offset != EndAugmentationOffset) ReportError(StartOffset, "Parsing augmentation data at %lx failed"); } } else { - InitialLocation = Data.getAddress(&Offset); - AddressRange = Data.getAddress(&Offset); + InitialLocation = Data.getRelocatedAddress(&Offset); + AddressRange = Data.getRelocatedAddress(&Offset); } Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, InitialLocation, AddressRange, Cie, LSDAAddress, Arch)); } if (Error E = Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) { report_fatal_error(toString(std::move(E))); } if (Offset != EndStructureOffset) ReportError(StartOffset, "Parsing entry instructions at %lx failed"); } } FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { auto It = partition_point(Entries, [=](const std::unique_ptr &E) { return E->getOffset() < Offset; }); if (It != Entries.end() && (*It)->getOffset() == Offset) return It->get(); return nullptr; } void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, Optional Offset) const { if (Offset) { if (auto *Entry = getEntryAtOffset(*Offset)) Entry->dump(OS, MRI, IsEH); return; } OS << "\n"; for (const auto &Entry : Entries) Entry->dump(OS, MRI, IsEH); } Index: head/contrib/llvm/lib/MC/MCAsmBackend.cpp =================================================================== --- head/contrib/llvm/lib/MC/MCAsmBackend.cpp (revision 354468) +++ head/contrib/llvm/lib/MC/MCAsmBackend.cpp (revision 354469) @@ -1,134 +1,137 @@ //===- MCAsmBackend.cpp - Target MC Assembly Backend ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/MC/MCAsmBackend.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCCodePadder.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCMachObjectWriter.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCWasmObjectWriter.h" #include "llvm/MC/MCWinCOFFObjectWriter.h" #include "llvm/MC/MCXCOFFObjectWriter.h" #include #include #include using namespace llvm; MCAsmBackend::MCAsmBackend(support::endianness Endian) : CodePadder(new MCCodePadder()), Endian(Endian) {} MCAsmBackend::~MCAsmBackend() = default; std::unique_ptr MCAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const { auto TW = createObjectTargetWriter(); switch (TW->getFormat()) { case Triple::ELF: return createELFObjectWriter(cast(std::move(TW)), OS, Endian == support::little); case Triple::MachO: return createMachObjectWriter(cast(std::move(TW)), OS, Endian == support::little); case Triple::COFF: return createWinCOFFObjectWriter( cast(std::move(TW)), OS); case Triple::Wasm: return createWasmObjectWriter(cast(std::move(TW)), OS); case Triple::XCOFF: return createXCOFFObjectWriter( cast(std::move(TW)), OS); default: llvm_unreachable("unexpected object format"); } } std::unique_ptr MCAsmBackend::createDwoObjectWriter(raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS) const { auto TW = createObjectTargetWriter(); if (TW->getFormat() != Triple::ELF) report_fatal_error("dwo only supported with ELF"); return createELFDwoObjectWriter(cast(std::move(TW)), OS, DwoOS, Endian == support::little); } Optional MCAsmBackend::getFixupKind(StringRef Name) const { return None; } const MCFixupKindInfo &MCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { static const MCFixupKindInfo Builtins[] = { {"FK_NONE", 0, 0, 0}, {"FK_Data_1", 0, 8, 0}, {"FK_Data_2", 0, 16, 0}, {"FK_Data_4", 0, 32, 0}, {"FK_Data_8", 0, 64, 0}, + {"FK_Data_6b", 0, 6, 0}, {"FK_PCRel_1", 0, 8, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_2", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_4", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, {"FK_PCRel_8", 0, 64, MCFixupKindInfo::FKF_IsPCRel}, {"FK_GPRel_1", 0, 8, 0}, {"FK_GPRel_2", 0, 16, 0}, {"FK_GPRel_4", 0, 32, 0}, {"FK_GPRel_8", 0, 64, 0}, {"FK_DTPRel_4", 0, 32, 0}, {"FK_DTPRel_8", 0, 64, 0}, {"FK_TPRel_4", 0, 32, 0}, {"FK_TPRel_8", 0, 64, 0}, {"FK_SecRel_1", 0, 8, 0}, {"FK_SecRel_2", 0, 16, 0}, {"FK_SecRel_4", 0, 32, 0}, {"FK_SecRel_8", 0, 64, 0}, {"FK_Data_Add_1", 0, 8, 0}, {"FK_Data_Add_2", 0, 16, 0}, {"FK_Data_Add_4", 0, 32, 0}, {"FK_Data_Add_8", 0, 64, 0}, + {"FK_Data_Add_6b", 0, 6, 0}, {"FK_Data_Sub_1", 0, 8, 0}, {"FK_Data_Sub_2", 0, 16, 0}, {"FK_Data_Sub_4", 0, 32, 0}, - {"FK_Data_Sub_8", 0, 64, 0}}; + {"FK_Data_Sub_8", 0, 64, 0}, + {"FK_Data_Sub_6b", 0, 6, 0}}; assert((size_t)Kind <= array_lengthof(Builtins) && "Unknown fixup kind"); return Builtins[Kind]; } bool MCAsmBackend::fixupNeedsRelaxationAdvanced( const MCFixup &Fixup, bool Resolved, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout, const bool WasForced) const { if (!Resolved) return true; return fixupNeedsRelaxation(Fixup, Value, DF, Layout); } void MCAsmBackend::handleCodePaddingBasicBlockStart( MCObjectStreamer *OS, const MCCodePaddingContext &Context) { CodePadder->handleBasicBlockStart(OS, Context); } void MCAsmBackend::handleCodePaddingBasicBlockEnd( const MCCodePaddingContext &Context) { CodePadder->handleBasicBlockEnd(Context); } void MCAsmBackend::handleCodePaddingInstructionBegin(const MCInst &Inst) { CodePadder->handleInstructionBegin(Inst); } void MCAsmBackend::handleCodePaddingInstructionEnd(const MCInst &Inst) { CodePadder->handleInstructionEnd(Inst); } bool MCAsmBackend::relaxFragment(MCPaddingFragment *PF, MCAsmLayout &Layout) { return CodePadder->relaxFragment(PF, Layout); } Index: head/contrib/llvm/lib/MC/MCAssembler.cpp =================================================================== --- head/contrib/llvm/lib/MC/MCAssembler.cpp (revision 354468) +++ head/contrib/llvm/lib/MC/MCAssembler.cpp (revision 354469) @@ -1,1139 +1,1154 @@ //===- lib/MC/MCAssembler.cpp - Assembler Backend Implementation ----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/MC/MCAssembler.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCFragment.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include using namespace llvm; #define DEBUG_TYPE "assembler" namespace { namespace stats { STATISTIC(EmittedFragments, "Number of emitted assembler fragments - total"); STATISTIC(EmittedRelaxableFragments, "Number of emitted assembler fragments - relaxable"); STATISTIC(EmittedDataFragments, "Number of emitted assembler fragments - data"); STATISTIC(EmittedCompactEncodedInstFragments, "Number of emitted assembler fragments - compact encoded inst"); STATISTIC(EmittedAlignFragments, "Number of emitted assembler fragments - align"); STATISTIC(EmittedFillFragments, "Number of emitted assembler fragments - fill"); STATISTIC(EmittedOrgFragments, "Number of emitted assembler fragments - org"); STATISTIC(evaluateFixup, "Number of evaluated fixups"); STATISTIC(FragmentLayouts, "Number of fragment layouts"); STATISTIC(ObjectBytes, "Number of emitted object file bytes"); STATISTIC(RelaxationSteps, "Number of assembler layout and relaxation steps"); STATISTIC(RelaxedInstructions, "Number of relaxed instructions"); STATISTIC(PaddingFragmentsRelaxations, "Number of Padding Fragments relaxations"); STATISTIC(PaddingFragmentsBytes, "Total size of all padding from adding Fragments"); } // end namespace stats } // end anonymous namespace // FIXME FIXME FIXME: There are number of places in this file where we convert // what is a 64-bit assembler value used for computation into a value in the // object file, which may truncate it. We should detect that truncation where // invalid and report errors back. /* *** */ MCAssembler::MCAssembler(MCContext &Context, std::unique_ptr Backend, std::unique_ptr Emitter, std::unique_ptr Writer) : Context(Context), Backend(std::move(Backend)), Emitter(std::move(Emitter)), Writer(std::move(Writer)), BundleAlignSize(0), RelaxAll(false), SubsectionsViaSymbols(false), IncrementalLinkerCompatible(false), ELFHeaderEFlags(0) { VersionInfo.Major = 0; // Major version == 0 for "none specified" } MCAssembler::~MCAssembler() = default; void MCAssembler::reset() { Sections.clear(); Symbols.clear(); IndirectSymbols.clear(); DataRegions.clear(); LinkerOptions.clear(); FileNames.clear(); ThumbFuncs.clear(); BundleAlignSize = 0; RelaxAll = false; SubsectionsViaSymbols = false; IncrementalLinkerCompatible = false; ELFHeaderEFlags = 0; LOHContainer.reset(); VersionInfo.Major = 0; VersionInfo.SDKVersion = VersionTuple(); // reset objects owned by us if (getBackendPtr()) getBackendPtr()->reset(); if (getEmitterPtr()) getEmitterPtr()->reset(); if (getWriterPtr()) getWriterPtr()->reset(); getLOHContainer().reset(); } bool MCAssembler::registerSection(MCSection &Section) { if (Section.isRegistered()) return false; Sections.push_back(&Section); Section.setIsRegistered(true); return true; } bool MCAssembler::isThumbFunc(const MCSymbol *Symbol) const { if (ThumbFuncs.count(Symbol)) return true; if (!Symbol->isVariable()) return false; const MCExpr *Expr = Symbol->getVariableValue(); MCValue V; if (!Expr->evaluateAsRelocatable(V, nullptr, nullptr)) return false; if (V.getSymB() || V.getRefKind() != MCSymbolRefExpr::VK_None) return false; const MCSymbolRefExpr *Ref = V.getSymA(); if (!Ref) return false; if (Ref->getKind() != MCSymbolRefExpr::VK_None) return false; const MCSymbol &Sym = Ref->getSymbol(); if (!isThumbFunc(&Sym)) return false; ThumbFuncs.insert(Symbol); // Cache it. return true; } bool MCAssembler::isSymbolLinkerVisible(const MCSymbol &Symbol) const { // Non-temporary labels should always be visible to the linker. if (!Symbol.isTemporary()) return true; // Absolute temporary labels are never visible. if (!Symbol.isInSection()) return false; if (Symbol.isUsedInReloc()) return true; return false; } const MCSymbol *MCAssembler::getAtom(const MCSymbol &S) const { // Linker visible symbols define atoms. if (isSymbolLinkerVisible(S)) return &S; // Absolute and undefined symbols have no defining atom. if (!S.isInSection()) return nullptr; // Non-linker visible symbols in sections which can't be atomized have no // defining atom. if (!getContext().getAsmInfo()->isSectionAtomizableBySymbols( *S.getFragment()->getParent())) return nullptr; // Otherwise, return the atom for the containing fragment. return S.getFragment()->getAtom(); } bool MCAssembler::evaluateFixup(const MCAsmLayout &Layout, const MCFixup &Fixup, const MCFragment *DF, MCValue &Target, uint64_t &Value, bool &WasForced) const { ++stats::evaluateFixup; // FIXME: This code has some duplication with recordRelocation. We should // probably merge the two into a single callback that tries to evaluate a // fixup and records a relocation if one is needed. // On error claim to have completely evaluated the fixup, to prevent any // further processing from being done. const MCExpr *Expr = Fixup.getValue(); MCContext &Ctx = getContext(); Value = 0; WasForced = false; if (!Expr->evaluateAsRelocatable(Target, &Layout, &Fixup)) { Ctx.reportError(Fixup.getLoc(), "expected relocatable expression"); return true; } if (const MCSymbolRefExpr *RefB = Target.getSymB()) { if (RefB->getKind() != MCSymbolRefExpr::VK_None) { Ctx.reportError(Fixup.getLoc(), "unsupported subtraction of qualified symbol"); return true; } } assert(getBackendPtr() && "Expected assembler backend"); bool IsPCRel = getBackendPtr()->getFixupKindInfo(Fixup.getKind()).Flags & MCFixupKindInfo::FKF_IsPCRel; bool IsResolved = false; if (IsPCRel) { if (Target.getSymB()) { IsResolved = false; } else if (!Target.getSymA()) { IsResolved = false; } else { const MCSymbolRefExpr *A = Target.getSymA(); const MCSymbol &SA = A->getSymbol(); if (A->getKind() != MCSymbolRefExpr::VK_None || SA.isUndefined()) { IsResolved = false; } else if (auto *Writer = getWriterPtr()) { IsResolved = Writer->isSymbolRefDifferenceFullyResolvedImpl( *this, SA, *DF, false, true); } } } else { IsResolved = Target.isAbsolute(); } Value = Target.getConstant(); if (const MCSymbolRefExpr *A = Target.getSymA()) { const MCSymbol &Sym = A->getSymbol(); if (Sym.isDefined()) Value += Layout.getSymbolOffset(Sym); } if (const MCSymbolRefExpr *B = Target.getSymB()) { const MCSymbol &Sym = B->getSymbol(); if (Sym.isDefined()) Value -= Layout.getSymbolOffset(Sym); } bool ShouldAlignPC = getBackend().getFixupKindInfo(Fixup.getKind()).Flags & MCFixupKindInfo::FKF_IsAlignedDownTo32Bits; assert((ShouldAlignPC ? IsPCRel : true) && "FKF_IsAlignedDownTo32Bits is only allowed on PC-relative fixups!"); if (IsPCRel) { uint32_t Offset = Layout.getFragmentOffset(DF) + Fixup.getOffset(); // A number of ARM fixups in Thumb mode require that the effective PC // address be determined as the 32-bit aligned version of the actual offset. if (ShouldAlignPC) Offset &= ~0x3; Value -= Offset; } // Let the backend force a relocation if needed. if (IsResolved && getBackend().shouldForceRelocation(*this, Fixup, Target)) { IsResolved = false; WasForced = true; } return IsResolved; } uint64_t MCAssembler::computeFragmentSize(const MCAsmLayout &Layout, const MCFragment &F) const { assert(getBackendPtr() && "Requires assembler backend"); switch (F.getKind()) { case MCFragment::FT_Data: return cast(F).getContents().size(); case MCFragment::FT_Relaxable: return cast(F).getContents().size(); case MCFragment::FT_CompactEncodedInst: return cast(F).getContents().size(); case MCFragment::FT_Fill: { auto &FF = cast(F); int64_t NumValues = 0; if (!FF.getNumValues().evaluateAsAbsolute(NumValues, Layout)) { getContext().reportError(FF.getLoc(), "expected assembly-time absolute expression"); return 0; } int64_t Size = NumValues * FF.getValueSize(); if (Size < 0) { getContext().reportError(FF.getLoc(), "invalid number of bytes"); return 0; } return Size; } case MCFragment::FT_LEB: return cast(F).getContents().size(); case MCFragment::FT_Padding: return cast(F).getSize(); case MCFragment::FT_SymbolId: return 4; case MCFragment::FT_Align: { const MCAlignFragment &AF = cast(F); unsigned Offset = Layout.getFragmentOffset(&AF); unsigned Size = OffsetToAlignment(Offset, AF.getAlignment()); // Insert extra Nops for code alignment if the target define // shouldInsertExtraNopBytesForCodeAlign target hook. if (AF.getParent()->UseCodeAlign() && AF.hasEmitNops() && getBackend().shouldInsertExtraNopBytesForCodeAlign(AF, Size)) return Size; // If we are padding with nops, force the padding to be larger than the // minimum nop size. if (Size > 0 && AF.hasEmitNops()) { while (Size % getBackend().getMinimumNopSize()) Size += AF.getAlignment(); } if (Size > AF.getMaxBytesToEmit()) return 0; return Size; } case MCFragment::FT_Org: { const MCOrgFragment &OF = cast(F); MCValue Value; if (!OF.getOffset().evaluateAsValue(Value, Layout)) { getContext().reportError(OF.getLoc(), "expected assembly-time absolute expression"); return 0; } uint64_t FragmentOffset = Layout.getFragmentOffset(&OF); int64_t TargetLocation = Value.getConstant(); if (const MCSymbolRefExpr *A = Value.getSymA()) { uint64_t Val; if (!Layout.getSymbolOffset(A->getSymbol(), Val)) { getContext().reportError(OF.getLoc(), "expected absolute expression"); return 0; } TargetLocation += Val; } int64_t Size = TargetLocation - FragmentOffset; if (Size < 0 || Size >= 0x40000000) { getContext().reportError( OF.getLoc(), "invalid .org offset '" + Twine(TargetLocation) + "' (at offset '" + Twine(FragmentOffset) + "')"); return 0; } return Size; } case MCFragment::FT_Dwarf: return cast(F).getContents().size(); case MCFragment::FT_DwarfFrame: return cast(F).getContents().size(); case MCFragment::FT_CVInlineLines: return cast(F).getContents().size(); case MCFragment::FT_CVDefRange: return cast(F).getContents().size(); case MCFragment::FT_Dummy: llvm_unreachable("Should not have been added"); } llvm_unreachable("invalid fragment kind"); } void MCAsmLayout::layoutFragment(MCFragment *F) { MCFragment *Prev = F->getPrevNode(); // We should never try to recompute something which is valid. assert(!isFragmentValid(F) && "Attempt to recompute a valid fragment!"); // We should never try to compute the fragment layout if its predecessor // isn't valid. assert((!Prev || isFragmentValid(Prev)) && "Attempt to compute fragment before its predecessor!"); ++stats::FragmentLayouts; // Compute fragment offset and size. if (Prev) F->Offset = Prev->Offset + getAssembler().computeFragmentSize(*this, *Prev); else F->Offset = 0; LastValidFragment[F->getParent()] = F; // If bundling is enabled and this fragment has instructions in it, it has to // obey the bundling restrictions. With padding, we'll have: // // // BundlePadding // ||| // ------------------------------------- // Prev |##########| F | // ------------------------------------- // ^ // | // F->Offset // // The fragment's offset will point to after the padding, and its computed // size won't include the padding. // // When the -mc-relax-all flag is used, we optimize bundling by writting the // padding directly into fragments when the instructions are emitted inside // the streamer. When the fragment is larger than the bundle size, we need to // ensure that it's bundle aligned. This means that if we end up with // multiple fragments, we must emit bundle padding between fragments. // // ".align N" is an example of a directive that introduces multiple // fragments. We could add a special case to handle ".align N" by emitting // within-fragment padding (which would produce less padding when N is less // than the bundle size), but for now we don't. // if (Assembler.isBundlingEnabled() && F->hasInstructions()) { assert(isa(F) && "Only MCEncodedFragment implementations have instructions"); MCEncodedFragment *EF = cast(F); uint64_t FSize = Assembler.computeFragmentSize(*this, *EF); if (!Assembler.getRelaxAll() && FSize > Assembler.getBundleAlignSize()) report_fatal_error("Fragment can't be larger than a bundle size"); uint64_t RequiredBundlePadding = computeBundlePadding(Assembler, EF, EF->Offset, FSize); if (RequiredBundlePadding > UINT8_MAX) report_fatal_error("Padding cannot exceed 255 bytes"); EF->setBundlePadding(static_cast(RequiredBundlePadding)); EF->Offset += RequiredBundlePadding; } } void MCAssembler::registerSymbol(const MCSymbol &Symbol, bool *Created) { bool New = !Symbol.isRegistered(); if (Created) *Created = New; if (New) { Symbol.setIsRegistered(true); Symbols.push_back(&Symbol); } } void MCAssembler::writeFragmentPadding(raw_ostream &OS, const MCEncodedFragment &EF, uint64_t FSize) const { assert(getBackendPtr() && "Expected assembler backend"); // Should NOP padding be written out before this fragment? unsigned BundlePadding = EF.getBundlePadding(); if (BundlePadding > 0) { assert(isBundlingEnabled() && "Writing bundle padding with disabled bundling"); assert(EF.hasInstructions() && "Writing bundle padding for a fragment without instructions"); unsigned TotalLength = BundlePadding + static_cast(FSize); if (EF.alignToBundleEnd() && TotalLength > getBundleAlignSize()) { // If the padding itself crosses a bundle boundary, it must be emitted // in 2 pieces, since even nop instructions must not cross boundaries. // v--------------v <- BundleAlignSize // v---------v <- BundlePadding // ---------------------------- // | Prev |####|####| F | // ---------------------------- // ^-------------------^ <- TotalLength unsigned DistanceToBoundary = TotalLength - getBundleAlignSize(); if (!getBackend().writeNopData(OS, DistanceToBoundary)) report_fatal_error("unable to write NOP sequence of " + Twine(DistanceToBoundary) + " bytes"); BundlePadding -= DistanceToBoundary; } if (!getBackend().writeNopData(OS, BundlePadding)) report_fatal_error("unable to write NOP sequence of " + Twine(BundlePadding) + " bytes"); } } /// Write the fragment \p F to the output file. static void writeFragment(raw_ostream &OS, const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment &F) { // FIXME: Embed in fragments instead? uint64_t FragmentSize = Asm.computeFragmentSize(Layout, F); support::endianness Endian = Asm.getBackend().Endian; if (const MCEncodedFragment *EF = dyn_cast(&F)) Asm.writeFragmentPadding(OS, *EF, FragmentSize); // This variable (and its dummy usage) is to participate in the assert at // the end of the function. uint64_t Start = OS.tell(); (void) Start; ++stats::EmittedFragments; switch (F.getKind()) { case MCFragment::FT_Align: { ++stats::EmittedAlignFragments; const MCAlignFragment &AF = cast(F); assert(AF.getValueSize() && "Invalid virtual align in concrete fragment!"); uint64_t Count = FragmentSize / AF.getValueSize(); // FIXME: This error shouldn't actually occur (the front end should emit // multiple .align directives to enforce the semantics it wants), but is // severe enough that we want to report it. How to handle this? if (Count * AF.getValueSize() != FragmentSize) report_fatal_error("undefined .align directive, value size '" + Twine(AF.getValueSize()) + "' is not a divisor of padding size '" + Twine(FragmentSize) + "'"); // See if we are aligning with nops, and if so do that first to try to fill // the Count bytes. Then if that did not fill any bytes or there are any // bytes left to fill use the Value and ValueSize to fill the rest. // If we are aligning with nops, ask that target to emit the right data. if (AF.hasEmitNops()) { if (!Asm.getBackend().writeNopData(OS, Count)) report_fatal_error("unable to write nop sequence of " + Twine(Count) + " bytes"); break; } // Otherwise, write out in multiples of the value size. for (uint64_t i = 0; i != Count; ++i) { switch (AF.getValueSize()) { default: llvm_unreachable("Invalid size!"); case 1: OS << char(AF.getValue()); break; case 2: support::endian::write(OS, AF.getValue(), Endian); break; case 4: support::endian::write(OS, AF.getValue(), Endian); break; case 8: support::endian::write(OS, AF.getValue(), Endian); break; } } break; } case MCFragment::FT_Data: ++stats::EmittedDataFragments; OS << cast(F).getContents(); break; case MCFragment::FT_Relaxable: ++stats::EmittedRelaxableFragments; OS << cast(F).getContents(); break; case MCFragment::FT_CompactEncodedInst: ++stats::EmittedCompactEncodedInstFragments; OS << cast(F).getContents(); break; case MCFragment::FT_Fill: { ++stats::EmittedFillFragments; const MCFillFragment &FF = cast(F); uint64_t V = FF.getValue(); unsigned VSize = FF.getValueSize(); const unsigned MaxChunkSize = 16; char Data[MaxChunkSize]; // Duplicate V into Data as byte vector to reduce number of // writes done. As such, do endian conversion here. for (unsigned I = 0; I != VSize; ++I) { unsigned index = Endian == support::little ? I : (VSize - I - 1); Data[I] = uint8_t(V >> (index * 8)); } for (unsigned I = VSize; I < MaxChunkSize; ++I) Data[I] = Data[I - VSize]; // Set to largest multiple of VSize in Data. const unsigned NumPerChunk = MaxChunkSize / VSize; // Set ChunkSize to largest multiple of VSize in Data const unsigned ChunkSize = VSize * NumPerChunk; // Do copies by chunk. StringRef Ref(Data, ChunkSize); for (uint64_t I = 0, E = FragmentSize / ChunkSize; I != E; ++I) OS << Ref; // do remainder if needed. unsigned TrailingCount = FragmentSize % ChunkSize; if (TrailingCount) OS.write(Data, TrailingCount); break; } case MCFragment::FT_LEB: { const MCLEBFragment &LF = cast(F); OS << LF.getContents(); break; } case MCFragment::FT_Padding: { if (!Asm.getBackend().writeNopData(OS, FragmentSize)) report_fatal_error("unable to write nop sequence of " + Twine(FragmentSize) + " bytes"); break; } case MCFragment::FT_SymbolId: { const MCSymbolIdFragment &SF = cast(F); support::endian::write(OS, SF.getSymbol()->getIndex(), Endian); break; } case MCFragment::FT_Org: { ++stats::EmittedOrgFragments; const MCOrgFragment &OF = cast(F); for (uint64_t i = 0, e = FragmentSize; i != e; ++i) OS << char(OF.getValue()); break; } case MCFragment::FT_Dwarf: { const MCDwarfLineAddrFragment &OF = cast(F); OS << OF.getContents(); break; } case MCFragment::FT_DwarfFrame: { const MCDwarfCallFrameFragment &CF = cast(F); OS << CF.getContents(); break; } case MCFragment::FT_CVInlineLines: { const auto &OF = cast(F); OS << OF.getContents(); break; } case MCFragment::FT_CVDefRange: { const auto &DRF = cast(F); OS << DRF.getContents(); break; } case MCFragment::FT_Dummy: llvm_unreachable("Should not have been added"); } assert(OS.tell() - Start == FragmentSize && "The stream should advance by fragment size"); } void MCAssembler::writeSectionData(raw_ostream &OS, const MCSection *Sec, const MCAsmLayout &Layout) const { assert(getBackendPtr() && "Expected assembler backend"); // Ignore virtual sections. if (Sec->isVirtualSection()) { assert(Layout.getSectionFileSize(Sec) == 0 && "Invalid size for section!"); // Check that contents are only things legal inside a virtual section. for (const MCFragment &F : *Sec) { switch (F.getKind()) { default: llvm_unreachable("Invalid fragment in virtual section!"); case MCFragment::FT_Data: { // Check that we aren't trying to write a non-zero contents (or fixups) // into a virtual section. This is to support clients which use standard // directives to fill the contents of virtual sections. const MCDataFragment &DF = cast(F); if (DF.fixup_begin() != DF.fixup_end()) report_fatal_error("cannot have fixups in virtual section!"); for (unsigned i = 0, e = DF.getContents().size(); i != e; ++i) if (DF.getContents()[i]) { if (auto *ELFSec = dyn_cast(Sec)) report_fatal_error("non-zero initializer found in section '" + ELFSec->getSectionName() + "'"); else report_fatal_error("non-zero initializer found in virtual section"); } break; } case MCFragment::FT_Align: // Check that we aren't trying to write a non-zero value into a virtual // section. assert((cast(F).getValueSize() == 0 || cast(F).getValue() == 0) && "Invalid align in virtual section!"); break; case MCFragment::FT_Fill: assert((cast(F).getValue() == 0) && "Invalid fill in virtual section!"); break; } } return; } uint64_t Start = OS.tell(); (void)Start; for (const MCFragment &F : *Sec) writeFragment(OS, *this, Layout, F); assert(OS.tell() - Start == Layout.getSectionAddressSize(Sec)); } std::tuple MCAssembler::handleFixup(const MCAsmLayout &Layout, MCFragment &F, const MCFixup &Fixup) { // Evaluate the fixup. MCValue Target; uint64_t FixedValue; bool WasForced; bool IsResolved = evaluateFixup(Layout, Fixup, &F, Target, FixedValue, WasForced); if (!IsResolved) { // The fixup was unresolved, we need a relocation. Inform the object // writer of the relocation, and give it an opportunity to adjust the // fixup value if need be. if (Target.getSymA() && Target.getSymB() && getBackend().requiresDiffExpressionRelocations()) { // The fixup represents the difference between two symbols, which the // backend has indicated must be resolved at link time. Split up the fixup // into two relocations, one for the add, and one for the sub, and emit // both of these. The constant will be associated with the add half of the // expression. MCFixup FixupAdd = MCFixup::createAddFor(Fixup); MCValue TargetAdd = MCValue::get(Target.getSymA(), nullptr, Target.getConstant()); getWriter().recordRelocation(*this, Layout, &F, FixupAdd, TargetAdd, FixedValue); MCFixup FixupSub = MCFixup::createSubFor(Fixup); MCValue TargetSub = MCValue::get(Target.getSymB()); getWriter().recordRelocation(*this, Layout, &F, FixupSub, TargetSub, FixedValue); } else { getWriter().recordRelocation(*this, Layout, &F, Fixup, Target, FixedValue); } } return std::make_tuple(Target, FixedValue, IsResolved); } void MCAssembler::layout(MCAsmLayout &Layout) { assert(getBackendPtr() && "Expected assembler backend"); DEBUG_WITH_TYPE("mc-dump", { errs() << "assembler backend - pre-layout\n--\n"; dump(); }); // Create dummy fragments and assign section ordinals. unsigned SectionIndex = 0; for (MCSection &Sec : *this) { // Create dummy fragments to eliminate any empty sections, this simplifies // layout. if (Sec.getFragmentList().empty()) new MCDataFragment(&Sec); Sec.setOrdinal(SectionIndex++); } // Assign layout order indices to sections and fragments. for (unsigned i = 0, e = Layout.getSectionOrder().size(); i != e; ++i) { MCSection *Sec = Layout.getSectionOrder()[i]; Sec->setLayoutOrder(i); unsigned FragmentIndex = 0; for (MCFragment &Frag : *Sec) Frag.setLayoutOrder(FragmentIndex++); } // Layout until everything fits. while (layoutOnce(Layout)) if (getContext().hadError()) return; DEBUG_WITH_TYPE("mc-dump", { errs() << "assembler backend - post-relaxation\n--\n"; dump(); }); // Finalize the layout, including fragment lowering. finishLayout(Layout); DEBUG_WITH_TYPE("mc-dump", { errs() << "assembler backend - final-layout\n--\n"; dump(); }); // Allow the object writer a chance to perform post-layout binding (for // example, to set the index fields in the symbol data). getWriter().executePostLayoutBinding(*this, Layout); // Evaluate and apply the fixups, generating relocation entries as necessary. for (MCSection &Sec : *this) { for (MCFragment &Frag : Sec) { // Data and relaxable fragments both have fixups. So only process // those here. // FIXME: Is there a better way to do this? MCEncodedFragmentWithFixups // being templated makes this tricky. if (isa(&Frag) && isa(&Frag)) continue; if (!isa(&Frag) && !isa(&Frag) && !isa(&Frag)) continue; ArrayRef Fixups; MutableArrayRef Contents; const MCSubtargetInfo *STI = nullptr; if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); STI = FragWithFixups->getSubtargetInfo(); assert(!FragWithFixups->hasInstructions() || STI != nullptr); } else if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); STI = FragWithFixups->getSubtargetInfo(); assert(!FragWithFixups->hasInstructions() || STI != nullptr); } else if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); } else if (auto *FragWithFixups = dyn_cast(&Frag)) { Fixups = FragWithFixups->getFixups(); Contents = FragWithFixups->getContents(); } else if (auto *AF = dyn_cast(&Frag)) { // Insert fixup type for code alignment if the target define // shouldInsertFixupForCodeAlign target hook. if (Sec.UseCodeAlign() && AF->hasEmitNops()) { getBackend().shouldInsertFixupForCodeAlign(*this, Layout, *AF); } continue; + } else if (auto *FragWithFixups = + dyn_cast(&Frag)) { + Fixups = FragWithFixups->getFixups(); + Contents = FragWithFixups->getContents(); } else llvm_unreachable("Unknown fragment with fixups!"); for (const MCFixup &Fixup : Fixups) { uint64_t FixedValue; bool IsResolved; MCValue Target; std::tie(Target, FixedValue, IsResolved) = handleFixup(Layout, Frag, Fixup); getBackend().applyFixup(*this, Fixup, Target, Contents, FixedValue, IsResolved, STI); } } } } void MCAssembler::Finish() { // Create the layout object. MCAsmLayout Layout(*this); layout(Layout); // Write the object file. stats::ObjectBytes += getWriter().writeObject(*this, Layout); } bool MCAssembler::fixupNeedsRelaxation(const MCFixup &Fixup, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const { assert(getBackendPtr() && "Expected assembler backend"); MCValue Target; uint64_t Value; bool WasForced; bool Resolved = evaluateFixup(Layout, Fixup, DF, Target, Value, WasForced); if (Target.getSymA() && Target.getSymA()->getKind() == MCSymbolRefExpr::VK_X86_ABS8 && Fixup.getKind() == FK_Data_1) return false; return getBackend().fixupNeedsRelaxationAdvanced(Fixup, Resolved, Value, DF, Layout, WasForced); } bool MCAssembler::fragmentNeedsRelaxation(const MCRelaxableFragment *F, const MCAsmLayout &Layout) const { assert(getBackendPtr() && "Expected assembler backend"); // If this inst doesn't ever need relaxation, ignore it. This occurs when we // are intentionally pushing out inst fragments, or because we relaxed a // previous instruction to one that doesn't need relaxation. if (!getBackend().mayNeedRelaxation(F->getInst(), *F->getSubtargetInfo())) return false; for (const MCFixup &Fixup : F->getFixups()) if (fixupNeedsRelaxation(Fixup, F, Layout)) return true; return false; } bool MCAssembler::relaxInstruction(MCAsmLayout &Layout, MCRelaxableFragment &F) { assert(getEmitterPtr() && "Expected CodeEmitter defined for relaxInstruction"); if (!fragmentNeedsRelaxation(&F, Layout)) return false; ++stats::RelaxedInstructions; // FIXME-PERF: We could immediately lower out instructions if we can tell // they are fully resolved, to avoid retesting on later passes. // Relax the fragment. MCInst Relaxed; getBackend().relaxInstruction(F.getInst(), *F.getSubtargetInfo(), Relaxed); // Encode the new instruction. // // FIXME-PERF: If it matters, we could let the target do this. It can // probably do so more efficiently in many cases. SmallVector Fixups; SmallString<256> Code; raw_svector_ostream VecOS(Code); getEmitter().encodeInstruction(Relaxed, VecOS, Fixups, *F.getSubtargetInfo()); // Update the fragment. F.setInst(Relaxed); F.getContents() = Code; F.getFixups() = Fixups; return true; } bool MCAssembler::relaxPaddingFragment(MCAsmLayout &Layout, MCPaddingFragment &PF) { assert(getBackendPtr() && "Expected assembler backend"); uint64_t OldSize = PF.getSize(); if (!getBackend().relaxFragment(&PF, Layout)) return false; uint64_t NewSize = PF.getSize(); ++stats::PaddingFragmentsRelaxations; stats::PaddingFragmentsBytes += NewSize; stats::PaddingFragmentsBytes -= OldSize; return true; } bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) { uint64_t OldSize = LF.getContents().size(); int64_t Value; bool Abs = LF.getValue().evaluateKnownAbsolute(Value, Layout); if (!Abs) report_fatal_error("sleb128 and uleb128 expressions must be absolute"); SmallString<8> &Data = LF.getContents(); Data.clear(); raw_svector_ostream OSE(Data); // The compiler can generate EH table assembly that is impossible to assemble // without either adding padding to an LEB fragment or adding extra padding // to a later alignment fragment. To accommodate such tables, relaxation can // only increase an LEB fragment size here, not decrease it. See PR35809. if (LF.isSigned()) encodeSLEB128(Value, OSE, OldSize); else encodeULEB128(Value, OSE, OldSize); return OldSize != LF.getContents().size(); } bool MCAssembler::relaxDwarfLineAddr(MCAsmLayout &Layout, MCDwarfLineAddrFragment &DF) { MCContext &Context = Layout.getAssembler().getContext(); uint64_t OldSize = DF.getContents().size(); int64_t AddrDelta; - bool Abs; - if (getBackend().requiresDiffExpressionRelocations()) - Abs = DF.getAddrDelta().evaluateAsAbsolute(AddrDelta, Layout); - else { - Abs = DF.getAddrDelta().evaluateKnownAbsolute(AddrDelta, Layout); - assert(Abs && "We created a line delta with an invalid expression"); - } + bool Abs = DF.getAddrDelta().evaluateKnownAbsolute(AddrDelta, Layout); + assert(Abs && "We created a line delta with an invalid expression"); + (void)Abs; int64_t LineDelta; LineDelta = DF.getLineDelta(); SmallVectorImpl &Data = DF.getContents(); Data.clear(); raw_svector_ostream OSE(Data); DF.getFixups().clear(); - if (Abs) { + if (!getBackend().requiresDiffExpressionRelocations()) { MCDwarfLineAddr::Encode(Context, getDWARFLinetableParams(), LineDelta, AddrDelta, OSE); } else { uint32_t Offset; uint32_t Size; bool SetDelta = MCDwarfLineAddr::FixedEncode(Context, getDWARFLinetableParams(), LineDelta, AddrDelta, OSE, &Offset, &Size); // Add Fixups for address delta or new address. const MCExpr *FixupExpr; if (SetDelta) { FixupExpr = &DF.getAddrDelta(); } else { const MCBinaryExpr *ABE = cast(&DF.getAddrDelta()); FixupExpr = ABE->getLHS(); } DF.getFixups().push_back( MCFixup::create(Offset, FixupExpr, MCFixup::getKindForSize(Size, false /*isPCRel*/))); } return OldSize != Data.size(); } bool MCAssembler::relaxDwarfCallFrameFragment(MCAsmLayout &Layout, MCDwarfCallFrameFragment &DF) { MCContext &Context = Layout.getAssembler().getContext(); uint64_t OldSize = DF.getContents().size(); int64_t AddrDelta; bool Abs = DF.getAddrDelta().evaluateKnownAbsolute(AddrDelta, Layout); assert(Abs && "We created call frame with an invalid expression"); (void) Abs; - SmallString<8> &Data = DF.getContents(); + SmallVectorImpl &Data = DF.getContents(); Data.clear(); raw_svector_ostream OSE(Data); - MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE); + DF.getFixups().clear(); + + if (getBackend().requiresDiffExpressionRelocations()) { + uint32_t Offset; + uint32_t Size; + MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE, &Offset, + &Size); + if (Size) { + DF.getFixups().push_back(MCFixup::create( + Offset, &DF.getAddrDelta(), + MCFixup::getKindForSizeInBits(Size /*In bits.*/, false /*isPCRel*/))); + } + } else { + MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OSE); + } + return OldSize != Data.size(); } bool MCAssembler::relaxCVInlineLineTable(MCAsmLayout &Layout, MCCVInlineLineTableFragment &F) { unsigned OldSize = F.getContents().size(); getContext().getCVContext().encodeInlineLineTable(Layout, F); return OldSize != F.getContents().size(); } bool MCAssembler::relaxCVDefRange(MCAsmLayout &Layout, MCCVDefRangeFragment &F) { unsigned OldSize = F.getContents().size(); getContext().getCVContext().encodeDefRange(Layout, F); return OldSize != F.getContents().size(); } bool MCAssembler::layoutSectionOnce(MCAsmLayout &Layout, MCSection &Sec) { // Holds the first fragment which needed relaxing during this layout. It will // remain NULL if none were relaxed. // When a fragment is relaxed, all the fragments following it should get // invalidated because their offset is going to change. MCFragment *FirstRelaxedFragment = nullptr; // Attempt to relax all the fragments in the section. for (MCSection::iterator I = Sec.begin(), IE = Sec.end(); I != IE; ++I) { // Check if this is a fragment that needs relaxation. bool RelaxedFrag = false; switch(I->getKind()) { default: break; case MCFragment::FT_Relaxable: assert(!getRelaxAll() && "Did not expect a MCRelaxableFragment in RelaxAll mode"); RelaxedFrag = relaxInstruction(Layout, *cast(I)); break; case MCFragment::FT_Dwarf: RelaxedFrag = relaxDwarfLineAddr(Layout, *cast(I)); break; case MCFragment::FT_DwarfFrame: RelaxedFrag = relaxDwarfCallFrameFragment(Layout, *cast(I)); break; case MCFragment::FT_LEB: RelaxedFrag = relaxLEB(Layout, *cast(I)); break; case MCFragment::FT_Padding: RelaxedFrag = relaxPaddingFragment(Layout, *cast(I)); break; case MCFragment::FT_CVInlineLines: RelaxedFrag = relaxCVInlineLineTable(Layout, *cast(I)); break; case MCFragment::FT_CVDefRange: RelaxedFrag = relaxCVDefRange(Layout, *cast(I)); break; } if (RelaxedFrag && !FirstRelaxedFragment) FirstRelaxedFragment = &*I; } if (FirstRelaxedFragment) { Layout.invalidateFragmentsFrom(FirstRelaxedFragment); return true; } return false; } bool MCAssembler::layoutOnce(MCAsmLayout &Layout) { ++stats::RelaxationSteps; bool WasRelaxed = false; for (iterator it = begin(), ie = end(); it != ie; ++it) { MCSection &Sec = *it; while (layoutSectionOnce(Layout, Sec)) WasRelaxed = true; } return WasRelaxed; } void MCAssembler::finishLayout(MCAsmLayout &Layout) { assert(getBackendPtr() && "Expected assembler backend"); // The layout is done. Mark every fragment as valid. for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { MCSection &Section = *Layout.getSectionOrder()[i]; Layout.getFragmentOffset(&*Section.rbegin()); computeFragmentSize(Layout, *Section.rbegin()); } getBackend().finishLayout(*this, Layout); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void MCAssembler::dump() const{ raw_ostream &OS = errs(); OS << "dump(); } OS << "],\n"; OS << " Symbols:["; for (const_symbol_iterator it = symbol_begin(), ie = symbol_end(); it != ie; ++it) { if (it != symbol_begin()) OS << ",\n "; OS << "("; it->dump(); OS << ", Index:" << it->getIndex() << ", "; OS << ")"; } OS << "]>\n"; } #endif Index: head/contrib/llvm/lib/MC/MCDwarf.cpp =================================================================== --- head/contrib/llvm/lib/MC/MCDwarf.cpp (revision 354468) +++ head/contrib/llvm/lib/MC/MCDwarf.cpp (revision 354469) @@ -1,1922 +1,1950 @@ //===- lib/MC/MCDwarf.cpp - MCDwarf implementation ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/MC/MCDwarf.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Config/config.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include using namespace llvm; /// Manage the .debug_line_str section contents, if we use it. class llvm::MCDwarfLineStr { MCSymbol *LineStrLabel = nullptr; StringTableBuilder LineStrings{StringTableBuilder::DWARF}; bool UseRelocs = false; public: /// Construct an instance that can emit .debug_line_str (for use in a normal /// v5 line table). explicit MCDwarfLineStr(MCContext &Ctx) { UseRelocs = Ctx.getAsmInfo()->doesDwarfUseRelocationsAcrossSections(); if (UseRelocs) LineStrLabel = Ctx.getObjectFileInfo()->getDwarfLineStrSection()->getBeginSymbol(); } /// Emit a reference to the string. void emitRef(MCStreamer *MCOS, StringRef Path); /// Emit the .debug_line_str section if appropriate. void emitSection(MCStreamer *MCOS); }; static inline uint64_t ScaleAddrDelta(MCContext &Context, uint64_t AddrDelta) { unsigned MinInsnLength = Context.getAsmInfo()->getMinInstAlignment(); if (MinInsnLength == 1) return AddrDelta; if (AddrDelta % MinInsnLength != 0) { // TODO: report this error, but really only once. ; } return AddrDelta / MinInsnLength; } // // This is called when an instruction is assembled into the specified section // and if there is information from the last .loc directive that has yet to have // a line entry made for it is made. // void MCDwarfLineEntry::Make(MCObjectStreamer *MCOS, MCSection *Section) { if (!MCOS->getContext().getDwarfLocSeen()) return; // Create a symbol at in the current section for use in the line entry. MCSymbol *LineSym = MCOS->getContext().createTempSymbol(); // Set the value of the symbol to use for the MCDwarfLineEntry. MCOS->EmitLabel(LineSym); // Get the current .loc info saved in the context. const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc(); // Create a (local) line entry with the symbol and the current .loc info. MCDwarfLineEntry LineEntry(LineSym, DwarfLoc); // clear DwarfLocSeen saying the current .loc info is now used. MCOS->getContext().clearDwarfLocSeen(); // Add the line entry to this section's entries. MCOS->getContext() .getMCDwarfLineTable(MCOS->getContext().getDwarfCompileUnitID()) .getMCLineSections() .addLineEntry(LineEntry, Section); } // // This helper routine returns an expression of End - Start + IntVal . // static inline const MCExpr *MakeStartMinusEndExpr(const MCStreamer &MCOS, const MCSymbol &Start, const MCSymbol &End, int IntVal) { MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; const MCExpr *Res = MCSymbolRefExpr::create(&End, Variant, MCOS.getContext()); const MCExpr *RHS = MCSymbolRefExpr::create(&Start, Variant, MCOS.getContext()); const MCExpr *Res1 = MCBinaryExpr::create(MCBinaryExpr::Sub, Res, RHS, MCOS.getContext()); const MCExpr *Res2 = MCConstantExpr::create(IntVal, MCOS.getContext()); const MCExpr *Res3 = MCBinaryExpr::create(MCBinaryExpr::Sub, Res1, Res2, MCOS.getContext()); return Res3; } // // This helper routine returns an expression of Start + IntVal . // static inline const MCExpr * makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) { MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; const MCExpr *LHS = MCSymbolRefExpr::create(&Start, Variant, Ctx); const MCExpr *RHS = MCConstantExpr::create(IntVal, Ctx); const MCExpr *Res = MCBinaryExpr::create(MCBinaryExpr::Add, LHS, RHS, Ctx); return Res; } // // This emits the Dwarf line table for the specified section from the entries // in the LineSection. // static inline void EmitDwarfLineTable(MCObjectStreamer *MCOS, MCSection *Section, const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { unsigned FileNum = 1; unsigned LastLine = 1; unsigned Column = 0; unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; unsigned Isa = 0; unsigned Discriminator = 0; MCSymbol *LastLabel = nullptr; // Loop through each MCDwarfLineEntry and encode the dwarf line number table. for (const MCDwarfLineEntry &LineEntry : LineEntries) { int64_t LineDelta = static_cast(LineEntry.getLine()) - LastLine; if (FileNum != LineEntry.getFileNum()) { FileNum = LineEntry.getFileNum(); MCOS->EmitIntValue(dwarf::DW_LNS_set_file, 1); MCOS->EmitULEB128IntValue(FileNum); } if (Column != LineEntry.getColumn()) { Column = LineEntry.getColumn(); MCOS->EmitIntValue(dwarf::DW_LNS_set_column, 1); MCOS->EmitULEB128IntValue(Column); } if (Discriminator != LineEntry.getDiscriminator() && MCOS->getContext().getDwarfVersion() >= 4) { Discriminator = LineEntry.getDiscriminator(); unsigned Size = getULEB128Size(Discriminator); MCOS->EmitIntValue(dwarf::DW_LNS_extended_op, 1); MCOS->EmitULEB128IntValue(Size + 1); MCOS->EmitIntValue(dwarf::DW_LNE_set_discriminator, 1); MCOS->EmitULEB128IntValue(Discriminator); } if (Isa != LineEntry.getIsa()) { Isa = LineEntry.getIsa(); MCOS->EmitIntValue(dwarf::DW_LNS_set_isa, 1); MCOS->EmitULEB128IntValue(Isa); } if ((LineEntry.getFlags() ^ Flags) & DWARF2_FLAG_IS_STMT) { Flags = LineEntry.getFlags(); MCOS->EmitIntValue(dwarf::DW_LNS_negate_stmt, 1); } if (LineEntry.getFlags() & DWARF2_FLAG_BASIC_BLOCK) MCOS->EmitIntValue(dwarf::DW_LNS_set_basic_block, 1); if (LineEntry.getFlags() & DWARF2_FLAG_PROLOGUE_END) MCOS->EmitIntValue(dwarf::DW_LNS_set_prologue_end, 1); if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) MCOS->EmitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1); MCSymbol *Label = LineEntry.getLabel(); // At this point we want to emit/create the sequence to encode the delta in // line numbers and the increment of the address from the previous Label // and the current Label. const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo(); MCOS->EmitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, asmInfo->getCodePointerSize()); Discriminator = 0; LastLine = LineEntry.getLine(); LastLabel = Label; } // Emit a DW_LNE_end_sequence for the end of the section. // Use the section end label to compute the address delta and use INT64_MAX // as the line delta which is the signal that this is actually a // DW_LNE_end_sequence. MCSymbol *SectionEnd = MCOS->endSection(Section); // Switch back the dwarf line section, in case endSection had to switch the // section. MCContext &Ctx = MCOS->getContext(); MCOS->SwitchSection(Ctx.getObjectFileInfo()->getDwarfLineSection()); const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); MCOS->EmitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd, AsmInfo->getCodePointerSize()); } // // This emits the Dwarf file and the line tables. // void MCDwarfLineTable::Emit(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params) { MCContext &context = MCOS->getContext(); auto &LineTables = context.getMCDwarfLineTables(); // Bail out early so we don't switch to the debug_line section needlessly and // in doing so create an unnecessary (if empty) section. if (LineTables.empty()) return; // In a v5 non-split line table, put the strings in a separate section. Optional LineStr; if (context.getDwarfVersion() >= 5) LineStr = MCDwarfLineStr(context); // Switch to the section where the table will be emitted into. MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfLineSection()); // Handle the rest of the Compile Units. for (const auto &CUIDTablePair : LineTables) { CUIDTablePair.second.EmitCU(MCOS, Params, LineStr); } if (LineStr) LineStr->emitSection(MCOS); } void MCDwarfDwoLineTable::Emit(MCStreamer &MCOS, MCDwarfLineTableParams Params, MCSection *Section) const { if (!HasSplitLineTable) return; Optional NoLineStr(None); MCOS.SwitchSection(Section); MCOS.EmitLabel(Header.Emit(&MCOS, Params, None, NoLineStr).second); } std::pair MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, Optional &LineStr) const { static const char StandardOpcodeLengths[] = { 0, // length of DW_LNS_copy 1, // length of DW_LNS_advance_pc 1, // length of DW_LNS_advance_line 1, // length of DW_LNS_set_file 1, // length of DW_LNS_set_column 0, // length of DW_LNS_negate_stmt 0, // length of DW_LNS_set_basic_block 0, // length of DW_LNS_const_add_pc 1, // length of DW_LNS_fixed_advance_pc 0, // length of DW_LNS_set_prologue_end 0, // length of DW_LNS_set_epilogue_begin 1 // DW_LNS_set_isa }; assert(array_lengthof(StandardOpcodeLengths) >= (Params.DWARF2LineOpcodeBase - 1U)); return Emit( MCOS, Params, makeArrayRef(StandardOpcodeLengths, Params.DWARF2LineOpcodeBase - 1), LineStr); } static const MCExpr *forceExpAbs(MCStreamer &OS, const MCExpr* Expr) { MCContext &Context = OS.getContext(); assert(!isa(Expr)); if (Context.getAsmInfo()->hasAggressiveSymbolFolding()) return Expr; MCSymbol *ABS = Context.createTempSymbol(); OS.EmitAssignment(ABS, Expr); return MCSymbolRefExpr::create(ABS, Context); } static void emitAbsValue(MCStreamer &OS, const MCExpr *Value, unsigned Size) { const MCExpr *ABS = forceExpAbs(OS, Value); OS.EmitValue(ABS, Size); } void MCDwarfLineStr::emitSection(MCStreamer *MCOS) { // Switch to the .debug_line_str section. MCOS->SwitchSection( MCOS->getContext().getObjectFileInfo()->getDwarfLineStrSection()); // Emit the strings without perturbing the offsets we used. LineStrings.finalizeInOrder(); SmallString<0> Data; Data.resize(LineStrings.getSize()); LineStrings.write((uint8_t *)Data.data()); MCOS->EmitBinaryData(Data.str()); } void MCDwarfLineStr::emitRef(MCStreamer *MCOS, StringRef Path) { int RefSize = 4; // FIXME: Support DWARF-64 size_t Offset = LineStrings.add(Path); if (UseRelocs) { MCContext &Ctx = MCOS->getContext(); MCOS->EmitValue(makeStartPlusIntExpr(Ctx, *LineStrLabel, Offset), RefSize); } else MCOS->EmitIntValue(Offset, RefSize); } void MCDwarfLineTableHeader::emitV2FileDirTables(MCStreamer *MCOS) const { // First the directory table. for (auto &Dir : MCDwarfDirs) { MCOS->EmitBytes(Dir); // The DirectoryName, and... MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. } MCOS->EmitIntValue(0, 1); // Terminate the directory list. // Second the file table. for (unsigned i = 1; i < MCDwarfFiles.size(); i++) { assert(!MCDwarfFiles[i].Name.empty()); MCOS->EmitBytes(MCDwarfFiles[i].Name); // FileName and... MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. MCOS->EmitULEB128IntValue(MCDwarfFiles[i].DirIndex); // Directory number. MCOS->EmitIntValue(0, 1); // Last modification timestamp (always 0). MCOS->EmitIntValue(0, 1); // File size (always 0). } MCOS->EmitIntValue(0, 1); // Terminate the file list. } static void emitOneV5FileEntry(MCStreamer *MCOS, const MCDwarfFile &DwarfFile, bool EmitMD5, bool HasSource, Optional &LineStr) { assert(!DwarfFile.Name.empty()); if (LineStr) LineStr->emitRef(MCOS, DwarfFile.Name); else { MCOS->EmitBytes(DwarfFile.Name); // FileName and... MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. } MCOS->EmitULEB128IntValue(DwarfFile.DirIndex); // Directory number. if (EmitMD5) { const MD5::MD5Result &Cksum = *DwarfFile.Checksum; MCOS->EmitBinaryData( StringRef(reinterpret_cast(Cksum.Bytes.data()), Cksum.Bytes.size())); } if (HasSource) { if (LineStr) LineStr->emitRef(MCOS, DwarfFile.Source.getValueOr(StringRef())); else { MCOS->EmitBytes( DwarfFile.Source.getValueOr(StringRef())); // Source and... MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. } } } void MCDwarfLineTableHeader::emitV5FileDirTables( MCStreamer *MCOS, Optional &LineStr) const { // The directory format, which is just a list of the directory paths. In a // non-split object, these are references to .debug_line_str; in a split // object, they are inline strings. MCOS->EmitIntValue(1, 1); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path); MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp : dwarf::DW_FORM_string); MCOS->EmitULEB128IntValue(MCDwarfDirs.size() + 1); // Try not to emit an empty compilation directory. const StringRef CompDir = CompilationDir.empty() ? MCOS->getContext().getCompilationDir() : StringRef(CompilationDir); if (LineStr) { // Record path strings, emit references here. LineStr->emitRef(MCOS, CompDir); for (const auto &Dir : MCDwarfDirs) LineStr->emitRef(MCOS, Dir); } else { // The list of directory paths. Compilation directory comes first. MCOS->EmitBytes(CompDir); MCOS->EmitBytes(StringRef("\0", 1)); for (const auto &Dir : MCDwarfDirs) { MCOS->EmitBytes(Dir); // The DirectoryName, and... MCOS->EmitBytes(StringRef("\0", 1)); // its null terminator. } } // The file format, which is the inline null-terminated filename and a // directory index. We don't track file size/timestamp so don't emit them // in the v5 table. Emit MD5 checksums and source if we have them. uint64_t Entries = 2; if (HasAllMD5) Entries += 1; if (HasSource) Entries += 1; MCOS->EmitIntValue(Entries, 1); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_path); MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp : dwarf::DW_FORM_string); MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_directory_index); MCOS->EmitULEB128IntValue(dwarf::DW_FORM_udata); if (HasAllMD5) { MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_MD5); MCOS->EmitULEB128IntValue(dwarf::DW_FORM_data16); } if (HasSource) { MCOS->EmitULEB128IntValue(dwarf::DW_LNCT_LLVM_source); MCOS->EmitULEB128IntValue(LineStr ? dwarf::DW_FORM_line_strp : dwarf::DW_FORM_string); } // Then the counted list of files. The root file is file #0, then emit the // files as provide by .file directives. // MCDwarfFiles has an unused element [0] so use size() not size()+1. // But sometimes MCDwarfFiles is empty, in which case we still emit one file. MCOS->EmitULEB128IntValue(MCDwarfFiles.empty() ? 1 : MCDwarfFiles.size()); // To accommodate assembler source written for DWARF v4 but trying to emit // v5: If we didn't see a root file explicitly, replicate file #1. assert((!RootFile.Name.empty() || MCDwarfFiles.size() >= 1) && "No root file and no .file directives"); emitOneV5FileEntry(MCOS, RootFile.Name.empty() ? MCDwarfFiles[1] : RootFile, HasAllMD5, HasSource, LineStr); for (unsigned i = 1; i < MCDwarfFiles.size(); ++i) emitOneV5FileEntry(MCOS, MCDwarfFiles[i], HasAllMD5, HasSource, LineStr); } std::pair MCDwarfLineTableHeader::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, ArrayRef StandardOpcodeLengths, Optional &LineStr) const { MCContext &context = MCOS->getContext(); // Create a symbol at the beginning of the line table. MCSymbol *LineStartSym = Label; if (!LineStartSym) LineStartSym = context.createTempSymbol(); // Set the value of the symbol, as we are at the start of the line table. MCOS->EmitLabel(LineStartSym); // Create a symbol for the end of the section (to be set when we get there). MCSymbol *LineEndSym = context.createTempSymbol(); // The first 4 bytes is the total length of the information for this // compilation unit (not including these 4 bytes for the length). emitAbsValue(*MCOS, MakeStartMinusEndExpr(*MCOS, *LineStartSym, *LineEndSym, 4), 4); // Next 2 bytes is the Version. unsigned LineTableVersion = context.getDwarfVersion(); MCOS->EmitIntValue(LineTableVersion, 2); // Keep track of the bytes between the very start and where the header length // comes out. unsigned PreHeaderLengthBytes = 4 + 2; // In v5, we get address info next. if (LineTableVersion >= 5) { MCOS->EmitIntValue(context.getAsmInfo()->getCodePointerSize(), 1); MCOS->EmitIntValue(0, 1); // Segment selector; same as EmitGenDwarfAranges. PreHeaderLengthBytes += 2; } // Create a symbol for the end of the prologue (to be set when we get there). MCSymbol *ProEndSym = context.createTempSymbol(); // Lprologue_end // Length of the prologue, is the next 4 bytes. This is actually the length // from after the length word, to the end of the prologue. emitAbsValue(*MCOS, MakeStartMinusEndExpr(*MCOS, *LineStartSym, *ProEndSym, (PreHeaderLengthBytes + 4)), 4); // Parameters of the state machine, are next. MCOS->EmitIntValue(context.getAsmInfo()->getMinInstAlignment(), 1); // maximum_operations_per_instruction // For non-VLIW architectures this field is always 1. // FIXME: VLIW architectures need to update this field accordingly. if (LineTableVersion >= 4) MCOS->EmitIntValue(1, 1); MCOS->EmitIntValue(DWARF2_LINE_DEFAULT_IS_STMT, 1); MCOS->EmitIntValue(Params.DWARF2LineBase, 1); MCOS->EmitIntValue(Params.DWARF2LineRange, 1); MCOS->EmitIntValue(StandardOpcodeLengths.size() + 1, 1); // Standard opcode lengths for (char Length : StandardOpcodeLengths) MCOS->EmitIntValue(Length, 1); // Put out the directory and file tables. The formats vary depending on // the version. if (LineTableVersion >= 5) emitV5FileDirTables(MCOS, LineStr); else emitV2FileDirTables(MCOS); // This is the end of the prologue, so set the value of the symbol at the // end of the prologue (that was used in a previous expression). MCOS->EmitLabel(ProEndSym); return std::make_pair(LineStartSym, LineEndSym); } void MCDwarfLineTable::EmitCU(MCObjectStreamer *MCOS, MCDwarfLineTableParams Params, Optional &LineStr) const { MCSymbol *LineEndSym = Header.Emit(MCOS, Params, LineStr).second; // Put out the line tables. for (const auto &LineSec : MCLineSections.getMCLineEntries()) EmitDwarfLineTable(MCOS, LineSec.first, LineSec.second); // This is the end of the section, so set the value of the symbol at the end // of this section (that was used in a previous expression). MCOS->EmitLabel(LineEndSym); } Expected MCDwarfLineTable::tryGetFile(StringRef &Directory, StringRef &FileName, Optional Checksum, Optional Source, uint16_t DwarfVersion, unsigned FileNumber) { return Header.tryGetFile(Directory, FileName, Checksum, Source, DwarfVersion, FileNumber); } bool isRootFile(const MCDwarfFile &RootFile, StringRef &Directory, StringRef &FileName, Optional Checksum) { if (RootFile.Name.empty() || RootFile.Name != FileName.data()) return false; return RootFile.Checksum == Checksum; } Expected MCDwarfLineTableHeader::tryGetFile(StringRef &Directory, StringRef &FileName, Optional Checksum, Optional Source, uint16_t DwarfVersion, unsigned FileNumber) { if (Directory == CompilationDir) Directory = ""; if (FileName.empty()) { FileName = ""; Directory = ""; } assert(!FileName.empty()); // Keep track of whether any or all files have an MD5 checksum. // If any files have embedded source, they all must. if (MCDwarfFiles.empty()) { trackMD5Usage(Checksum.hasValue()); HasSource = (Source != None); } if (isRootFile(RootFile, Directory, FileName, Checksum) && DwarfVersion >= 5) return 0; if (FileNumber == 0) { // File numbers start with 1 and/or after any file numbers // allocated by inline-assembler .file directives. FileNumber = MCDwarfFiles.empty() ? 1 : MCDwarfFiles.size(); SmallString<256> Buffer; auto IterBool = SourceIdMap.insert( std::make_pair((Directory + Twine('\0') + FileName).toStringRef(Buffer), FileNumber)); if (!IterBool.second) return IterBool.first->second; } // Make space for this FileNumber in the MCDwarfFiles vector if needed. if (FileNumber >= MCDwarfFiles.size()) MCDwarfFiles.resize(FileNumber + 1); // Get the new MCDwarfFile slot for this FileNumber. MCDwarfFile &File = MCDwarfFiles[FileNumber]; // It is an error to see the same number more than once. if (!File.Name.empty()) return make_error("file number already allocated", inconvertibleErrorCode()); // If any files have embedded source, they all must. if (HasSource != (Source != None)) return make_error("inconsistent use of embedded source", inconvertibleErrorCode()); if (Directory.empty()) { // Separate the directory part from the basename of the FileName. StringRef tFileName = sys::path::filename(FileName); if (!tFileName.empty()) { Directory = sys::path::parent_path(FileName); if (!Directory.empty()) FileName = tFileName; } } // Find or make an entry in the MCDwarfDirs vector for this Directory. // Capture directory name. unsigned DirIndex; if (Directory.empty()) { // For FileNames with no directories a DirIndex of 0 is used. DirIndex = 0; } else { DirIndex = llvm::find(MCDwarfDirs, Directory) - MCDwarfDirs.begin(); if (DirIndex >= MCDwarfDirs.size()) MCDwarfDirs.push_back(Directory); // The DirIndex is one based, as DirIndex of 0 is used for FileNames with // no directories. MCDwarfDirs[] is unlike MCDwarfFiles[] in that the // directory names are stored at MCDwarfDirs[DirIndex-1] where FileNames // are stored at MCDwarfFiles[FileNumber].Name . DirIndex++; } File.Name = FileName; File.DirIndex = DirIndex; File.Checksum = Checksum; trackMD5Usage(Checksum.hasValue()); File.Source = Source; if (Source) HasSource = true; // return the allocated FileNumber. return FileNumber; } /// Utility function to emit the encoding to a streamer. void MCDwarfLineAddr::Emit(MCStreamer *MCOS, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta) { MCContext &Context = MCOS->getContext(); SmallString<256> Tmp; raw_svector_ostream OS(Tmp); MCDwarfLineAddr::Encode(Context, Params, LineDelta, AddrDelta, OS); MCOS->EmitBytes(OS.str()); } /// Given a special op, return the address skip amount (in units of /// DWARF2_LINE_MIN_INSN_LENGTH). static uint64_t SpecialAddr(MCDwarfLineTableParams Params, uint64_t op) { return (op - Params.DWARF2LineOpcodeBase) / Params.DWARF2LineRange; } /// Utility function to encode a Dwarf pair of LineDelta and AddrDeltas. void MCDwarfLineAddr::Encode(MCContext &Context, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta, raw_ostream &OS) { uint64_t Temp, Opcode; bool NeedCopy = false; // The maximum address skip amount that can be encoded with a special op. uint64_t MaxSpecialAddrDelta = SpecialAddr(Params, 255); // Scale the address delta by the minimum instruction length. AddrDelta = ScaleAddrDelta(Context, AddrDelta); // A LineDelta of INT64_MAX is a signal that this is actually a // DW_LNE_end_sequence. We cannot use special opcodes here, since we want the // end_sequence to emit the matrix entry. if (LineDelta == INT64_MAX) { if (AddrDelta == MaxSpecialAddrDelta) OS << char(dwarf::DW_LNS_const_add_pc); else if (AddrDelta) { OS << char(dwarf::DW_LNS_advance_pc); encodeULEB128(AddrDelta, OS); } OS << char(dwarf::DW_LNS_extended_op); OS << char(1); OS << char(dwarf::DW_LNE_end_sequence); return; } // Bias the line delta by the base. Temp = LineDelta - Params.DWARF2LineBase; // If the line increment is out of range of a special opcode, we must encode // it with DW_LNS_advance_line. if (Temp >= Params.DWARF2LineRange || Temp + Params.DWARF2LineOpcodeBase > 255) { OS << char(dwarf::DW_LNS_advance_line); encodeSLEB128(LineDelta, OS); LineDelta = 0; Temp = 0 - Params.DWARF2LineBase; NeedCopy = true; } // Use DW_LNS_copy instead of a "line +0, addr +0" special opcode. if (LineDelta == 0 && AddrDelta == 0) { OS << char(dwarf::DW_LNS_copy); return; } // Bias the opcode by the special opcode base. Temp += Params.DWARF2LineOpcodeBase; // Avoid overflow when addr_delta is large. if (AddrDelta < 256 + MaxSpecialAddrDelta) { // Try using a special opcode. Opcode = Temp + AddrDelta * Params.DWARF2LineRange; if (Opcode <= 255) { OS << char(Opcode); return; } // Try using DW_LNS_const_add_pc followed by special op. Opcode = Temp + (AddrDelta - MaxSpecialAddrDelta) * Params.DWARF2LineRange; if (Opcode <= 255) { OS << char(dwarf::DW_LNS_const_add_pc); OS << char(Opcode); return; } } // Otherwise use DW_LNS_advance_pc. OS << char(dwarf::DW_LNS_advance_pc); encodeULEB128(AddrDelta, OS); if (NeedCopy) OS << char(dwarf::DW_LNS_copy); else { assert(Temp <= 255 && "Buggy special opcode encoding."); OS << char(Temp); } } bool MCDwarfLineAddr::FixedEncode(MCContext &Context, MCDwarfLineTableParams Params, int64_t LineDelta, uint64_t AddrDelta, raw_ostream &OS, uint32_t *Offset, uint32_t *Size) { if (LineDelta != INT64_MAX) { OS << char(dwarf::DW_LNS_advance_line); encodeSLEB128(LineDelta, OS); } // Use address delta to adjust address or use absolute address to adjust // address. bool SetDelta; // According to DWARF spec., the DW_LNS_fixed_advance_pc opcode takes a // single uhalf (unencoded) operand. So, the maximum value of AddrDelta // is 65535. We set a conservative upper bound for it for relaxation. if (AddrDelta > 60000) { const MCAsmInfo *asmInfo = Context.getAsmInfo(); unsigned AddrSize = asmInfo->getCodePointerSize(); OS << char(dwarf::DW_LNS_extended_op); encodeULEB128(1 + AddrSize, OS); OS << char(dwarf::DW_LNE_set_address); // Generate fixup for the address. *Offset = OS.tell(); *Size = AddrSize; SetDelta = false; OS.write_zeros(AddrSize); } else { OS << char(dwarf::DW_LNS_fixed_advance_pc); // Generate fixup for 2-bytes address delta. *Offset = OS.tell(); *Size = 2; SetDelta = true; OS << char(0); OS << char(0); } if (LineDelta == INT64_MAX) { OS << char(dwarf::DW_LNS_extended_op); OS << char(1); OS << char(dwarf::DW_LNE_end_sequence); } else { OS << char(dwarf::DW_LNS_copy); } return SetDelta; } // Utility function to write a tuple for .debug_abbrev. static void EmitAbbrev(MCStreamer *MCOS, uint64_t Name, uint64_t Form) { MCOS->EmitULEB128IntValue(Name); MCOS->EmitULEB128IntValue(Form); } // When generating dwarf for assembly source files this emits // the data for .debug_abbrev section which contains three DIEs. static void EmitGenDwarfAbbrev(MCStreamer *MCOS) { MCContext &context = MCOS->getContext(); MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); // DW_TAG_compile_unit DIE abbrev (1). MCOS->EmitULEB128IntValue(1); MCOS->EmitULEB128IntValue(dwarf::DW_TAG_compile_unit); MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1); EmitAbbrev(MCOS, dwarf::DW_AT_stmt_list, context.getDwarfVersion() >= 4 ? dwarf::DW_FORM_sec_offset : dwarf::DW_FORM_data4); if (context.getGenDwarfSectionSyms().size() > 1 && context.getDwarfVersion() >= 3) { EmitAbbrev(MCOS, dwarf::DW_AT_ranges, context.getDwarfVersion() >= 4 ? dwarf::DW_FORM_sec_offset : dwarf::DW_FORM_data4); } else { EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr); EmitAbbrev(MCOS, dwarf::DW_AT_high_pc, dwarf::DW_FORM_addr); } EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string); if (!context.getCompilationDir().empty()) EmitAbbrev(MCOS, dwarf::DW_AT_comp_dir, dwarf::DW_FORM_string); StringRef DwarfDebugFlags = context.getDwarfDebugFlags(); if (!DwarfDebugFlags.empty()) EmitAbbrev(MCOS, dwarf::DW_AT_APPLE_flags, dwarf::DW_FORM_string); EmitAbbrev(MCOS, dwarf::DW_AT_producer, dwarf::DW_FORM_string); EmitAbbrev(MCOS, dwarf::DW_AT_language, dwarf::DW_FORM_data2); EmitAbbrev(MCOS, 0, 0); // DW_TAG_label DIE abbrev (2). MCOS->EmitULEB128IntValue(2); MCOS->EmitULEB128IntValue(dwarf::DW_TAG_label); MCOS->EmitIntValue(dwarf::DW_CHILDREN_yes, 1); EmitAbbrev(MCOS, dwarf::DW_AT_name, dwarf::DW_FORM_string); EmitAbbrev(MCOS, dwarf::DW_AT_decl_file, dwarf::DW_FORM_data4); EmitAbbrev(MCOS, dwarf::DW_AT_decl_line, dwarf::DW_FORM_data4); EmitAbbrev(MCOS, dwarf::DW_AT_low_pc, dwarf::DW_FORM_addr); EmitAbbrev(MCOS, dwarf::DW_AT_prototyped, dwarf::DW_FORM_flag); EmitAbbrev(MCOS, 0, 0); // DW_TAG_unspecified_parameters DIE abbrev (3). MCOS->EmitULEB128IntValue(3); MCOS->EmitULEB128IntValue(dwarf::DW_TAG_unspecified_parameters); MCOS->EmitIntValue(dwarf::DW_CHILDREN_no, 1); EmitAbbrev(MCOS, 0, 0); // Terminate the abbreviations for this compilation unit. MCOS->EmitIntValue(0, 1); } // When generating dwarf for assembly source files this emits the data for // .debug_aranges section. This section contains a header and a table of pairs // of PointerSize'ed values for the address and size of section(s) with line // table entries. static void EmitGenDwarfAranges(MCStreamer *MCOS, const MCSymbol *InfoSectionSymbol) { MCContext &context = MCOS->getContext(); auto &Sections = context.getGenDwarfSectionSyms(); MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection()); // This will be the length of the .debug_aranges section, first account for // the size of each item in the header (see below where we emit these items). int Length = 4 + 2 + 4 + 1 + 1; // Figure the padding after the header before the table of address and size // pairs who's values are PointerSize'ed. const MCAsmInfo *asmInfo = context.getAsmInfo(); int AddrSize = asmInfo->getCodePointerSize(); int Pad = 2 * AddrSize - (Length & (2 * AddrSize - 1)); if (Pad == 2 * AddrSize) Pad = 0; Length += Pad; // Add the size of the pair of PointerSize'ed values for the address and size // of each section we have in the table. Length += 2 * AddrSize * Sections.size(); // And the pair of terminating zeros. Length += 2 * AddrSize; // Emit the header for this section. // The 4 byte length not including the 4 byte value for the length. MCOS->EmitIntValue(Length - 4, 4); // The 2 byte version, which is 2. MCOS->EmitIntValue(2, 2); // The 4 byte offset to the compile unit in the .debug_info from the start // of the .debug_info. if (InfoSectionSymbol) MCOS->EmitSymbolValue(InfoSectionSymbol, 4, asmInfo->needsDwarfSectionOffsetDirective()); else MCOS->EmitIntValue(0, 4); // The 1 byte size of an address. MCOS->EmitIntValue(AddrSize, 1); // The 1 byte size of a segment descriptor, we use a value of zero. MCOS->EmitIntValue(0, 1); // Align the header with the padding if needed, before we put out the table. for(int i = 0; i < Pad; i++) MCOS->EmitIntValue(0, 1); // Now emit the table of pairs of PointerSize'ed values for the section // addresses and sizes. for (MCSection *Sec : Sections) { const MCSymbol *StartSymbol = Sec->getBeginSymbol(); MCSymbol *EndSymbol = Sec->getEndSymbol(context); assert(StartSymbol && "StartSymbol must not be NULL"); assert(EndSymbol && "EndSymbol must not be NULL"); const MCExpr *Addr = MCSymbolRefExpr::create( StartSymbol, MCSymbolRefExpr::VK_None, context); const MCExpr *Size = MakeStartMinusEndExpr(*MCOS, *StartSymbol, *EndSymbol, 0); MCOS->EmitValue(Addr, AddrSize); emitAbsValue(*MCOS, Size, AddrSize); } // And finally the pair of terminating zeros. MCOS->EmitIntValue(0, AddrSize); MCOS->EmitIntValue(0, AddrSize); } // When generating dwarf for assembly source files this emits the data for // .debug_info section which contains three parts. The header, the compile_unit // DIE and a list of label DIEs. static void EmitGenDwarfInfo(MCStreamer *MCOS, const MCSymbol *AbbrevSectionSymbol, const MCSymbol *LineSectionSymbol, const MCSymbol *RangesSectionSymbol) { MCContext &context = MCOS->getContext(); MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection()); // Create a symbol at the start and end of this section used in here for the // expression to calculate the length in the header. MCSymbol *InfoStart = context.createTempSymbol(); MCOS->EmitLabel(InfoStart); MCSymbol *InfoEnd = context.createTempSymbol(); // First part: the header. // The 4 byte total length of the information for this compilation unit, not // including these 4 bytes. const MCExpr *Length = MakeStartMinusEndExpr(*MCOS, *InfoStart, *InfoEnd, 4); emitAbsValue(*MCOS, Length, 4); // The 2 byte DWARF version. MCOS->EmitIntValue(context.getDwarfVersion(), 2); // The DWARF v5 header has unit type, address size, abbrev offset. // Earlier versions have abbrev offset, address size. const MCAsmInfo &AsmInfo = *context.getAsmInfo(); int AddrSize = AsmInfo.getCodePointerSize(); if (context.getDwarfVersion() >= 5) { MCOS->EmitIntValue(dwarf::DW_UT_compile, 1); MCOS->EmitIntValue(AddrSize, 1); } // The 4 byte offset to the debug abbrevs from the start of the .debug_abbrev, // it is at the start of that section so this is zero. if (AbbrevSectionSymbol == nullptr) MCOS->EmitIntValue(0, 4); else MCOS->EmitSymbolValue(AbbrevSectionSymbol, 4, AsmInfo.needsDwarfSectionOffsetDirective()); if (context.getDwarfVersion() <= 4) MCOS->EmitIntValue(AddrSize, 1); // Second part: the compile_unit DIE. // The DW_TAG_compile_unit DIE abbrev (1). MCOS->EmitULEB128IntValue(1); // DW_AT_stmt_list, a 4 byte offset from the start of the .debug_line section, // which is at the start of that section so this is zero. if (LineSectionSymbol) MCOS->EmitSymbolValue(LineSectionSymbol, 4, AsmInfo.needsDwarfSectionOffsetDirective()); else MCOS->EmitIntValue(0, 4); if (RangesSectionSymbol) { // There are multiple sections containing code, so we must use the // .debug_ranges sections. // AT_ranges, the 4 byte offset from the start of the .debug_ranges section // to the address range list for this compilation unit. MCOS->EmitSymbolValue(RangesSectionSymbol, 4); } else { // If we only have one non-empty code section, we can use the simpler // AT_low_pc and AT_high_pc attributes. // Find the first (and only) non-empty text section auto &Sections = context.getGenDwarfSectionSyms(); const auto TextSection = Sections.begin(); assert(TextSection != Sections.end() && "No text section found"); MCSymbol *StartSymbol = (*TextSection)->getBeginSymbol(); MCSymbol *EndSymbol = (*TextSection)->getEndSymbol(context); assert(StartSymbol && "StartSymbol must not be NULL"); assert(EndSymbol && "EndSymbol must not be NULL"); // AT_low_pc, the first address of the default .text section. const MCExpr *Start = MCSymbolRefExpr::create( StartSymbol, MCSymbolRefExpr::VK_None, context); MCOS->EmitValue(Start, AddrSize); // AT_high_pc, the last address of the default .text section. const MCExpr *End = MCSymbolRefExpr::create( EndSymbol, MCSymbolRefExpr::VK_None, context); MCOS->EmitValue(End, AddrSize); } // AT_name, the name of the source file. Reconstruct from the first directory // and file table entries. const SmallVectorImpl &MCDwarfDirs = context.getMCDwarfDirs(); if (MCDwarfDirs.size() > 0) { MCOS->EmitBytes(MCDwarfDirs[0]); MCOS->EmitBytes(sys::path::get_separator()); } const SmallVectorImpl &MCDwarfFiles = context.getMCDwarfFiles(); // MCDwarfFiles might be empty if we have an empty source file. // If it's not empty, [0] is unused and [1] is the first actual file. assert(MCDwarfFiles.empty() || MCDwarfFiles.size() >= 2); const MCDwarfFile &RootFile = MCDwarfFiles.empty() ? context.getMCDwarfLineTable(/*CUID=*/0).getRootFile() : MCDwarfFiles[1]; MCOS->EmitBytes(RootFile.Name); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. // AT_comp_dir, the working directory the assembly was done in. if (!context.getCompilationDir().empty()) { MCOS->EmitBytes(context.getCompilationDir()); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. } // AT_APPLE_flags, the command line arguments of the assembler tool. StringRef DwarfDebugFlags = context.getDwarfDebugFlags(); if (!DwarfDebugFlags.empty()){ MCOS->EmitBytes(DwarfDebugFlags); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. } // AT_producer, the version of the assembler tool. StringRef DwarfDebugProducer = context.getDwarfDebugProducer(); if (!DwarfDebugProducer.empty()) MCOS->EmitBytes(DwarfDebugProducer); else MCOS->EmitBytes(StringRef("llvm-mc (based on LLVM " PACKAGE_VERSION ")")); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. // AT_language, a 4 byte value. We use DW_LANG_Mips_Assembler as the dwarf2 // draft has no standard code for assembler. MCOS->EmitIntValue(dwarf::DW_LANG_Mips_Assembler, 2); // Third part: the list of label DIEs. // Loop on saved info for dwarf labels and create the DIEs for them. const std::vector &Entries = MCOS->getContext().getMCGenDwarfLabelEntries(); for (const auto &Entry : Entries) { // The DW_TAG_label DIE abbrev (2). MCOS->EmitULEB128IntValue(2); // AT_name, of the label without any leading underbar. MCOS->EmitBytes(Entry.getName()); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. // AT_decl_file, index into the file table. MCOS->EmitIntValue(Entry.getFileNumber(), 4); // AT_decl_line, source line number. MCOS->EmitIntValue(Entry.getLineNumber(), 4); // AT_low_pc, start address of the label. const MCExpr *AT_low_pc = MCSymbolRefExpr::create(Entry.getLabel(), MCSymbolRefExpr::VK_None, context); MCOS->EmitValue(AT_low_pc, AddrSize); // DW_AT_prototyped, a one byte flag value of 0 saying we have no prototype. MCOS->EmitIntValue(0, 1); // The DW_TAG_unspecified_parameters DIE abbrev (3). MCOS->EmitULEB128IntValue(3); // Add the NULL DIE terminating the DW_TAG_unspecified_parameters DIE's. MCOS->EmitIntValue(0, 1); } // Add the NULL DIE terminating the Compile Unit DIE's. MCOS->EmitIntValue(0, 1); // Now set the value of the symbol at the end of the info section. MCOS->EmitLabel(InfoEnd); } // When generating dwarf for assembly source files this emits the data for // .debug_ranges section. We only emit one range list, which spans all of the // executable sections of this file. static void EmitGenDwarfRanges(MCStreamer *MCOS) { MCContext &context = MCOS->getContext(); auto &Sections = context.getGenDwarfSectionSyms(); const MCAsmInfo *AsmInfo = context.getAsmInfo(); int AddrSize = AsmInfo->getCodePointerSize(); MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfRangesSection()); for (MCSection *Sec : Sections) { const MCSymbol *StartSymbol = Sec->getBeginSymbol(); MCSymbol *EndSymbol = Sec->getEndSymbol(context); assert(StartSymbol && "StartSymbol must not be NULL"); assert(EndSymbol && "EndSymbol must not be NULL"); // Emit a base address selection entry for the start of this section const MCExpr *SectionStartAddr = MCSymbolRefExpr::create( StartSymbol, MCSymbolRefExpr::VK_None, context); MCOS->emitFill(AddrSize, 0xFF); MCOS->EmitValue(SectionStartAddr, AddrSize); // Emit a range list entry spanning this section const MCExpr *SectionSize = MakeStartMinusEndExpr(*MCOS, *StartSymbol, *EndSymbol, 0); MCOS->EmitIntValue(0, AddrSize); emitAbsValue(*MCOS, SectionSize, AddrSize); } // Emit end of list entry MCOS->EmitIntValue(0, AddrSize); MCOS->EmitIntValue(0, AddrSize); } // // When generating dwarf for assembly source files this emits the Dwarf // sections. // void MCGenDwarfInfo::Emit(MCStreamer *MCOS) { MCContext &context = MCOS->getContext(); // Create the dwarf sections in this order (.debug_line already created). const MCAsmInfo *AsmInfo = context.getAsmInfo(); bool CreateDwarfSectionSymbols = AsmInfo->doesDwarfUseRelocationsAcrossSections(); MCSymbol *LineSectionSymbol = nullptr; if (CreateDwarfSectionSymbols) LineSectionSymbol = MCOS->getDwarfLineTableSymbol(0); MCSymbol *AbbrevSectionSymbol = nullptr; MCSymbol *InfoSectionSymbol = nullptr; MCSymbol *RangesSectionSymbol = nullptr; // Create end symbols for each section, and remove empty sections MCOS->getContext().finalizeDwarfSections(*MCOS); // If there are no sections to generate debug info for, we don't need // to do anything if (MCOS->getContext().getGenDwarfSectionSyms().empty()) return; // We only use the .debug_ranges section if we have multiple code sections, // and we are emitting a DWARF version which supports it. const bool UseRangesSection = MCOS->getContext().getGenDwarfSectionSyms().size() > 1 && MCOS->getContext().getDwarfVersion() >= 3; CreateDwarfSectionSymbols |= UseRangesSection; MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfInfoSection()); if (CreateDwarfSectionSymbols) { InfoSectionSymbol = context.createTempSymbol(); MCOS->EmitLabel(InfoSectionSymbol); } MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfAbbrevSection()); if (CreateDwarfSectionSymbols) { AbbrevSectionSymbol = context.createTempSymbol(); MCOS->EmitLabel(AbbrevSectionSymbol); } if (UseRangesSection) { MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfRangesSection()); if (CreateDwarfSectionSymbols) { RangesSectionSymbol = context.createTempSymbol(); MCOS->EmitLabel(RangesSectionSymbol); } } assert((RangesSectionSymbol != nullptr) || !UseRangesSection); MCOS->SwitchSection(context.getObjectFileInfo()->getDwarfARangesSection()); // Output the data for .debug_aranges section. EmitGenDwarfAranges(MCOS, InfoSectionSymbol); if (UseRangesSection) EmitGenDwarfRanges(MCOS); // Output the data for .debug_abbrev section. EmitGenDwarfAbbrev(MCOS); // Output the data for .debug_info section. EmitGenDwarfInfo(MCOS, AbbrevSectionSymbol, LineSectionSymbol, RangesSectionSymbol); } // // When generating dwarf for assembly source files this is called when symbol // for a label is created. If this symbol is not a temporary and is in the // section that dwarf is being generated for, save the needed info to create // a dwarf label. // void MCGenDwarfLabelEntry::Make(MCSymbol *Symbol, MCStreamer *MCOS, SourceMgr &SrcMgr, SMLoc &Loc) { // We won't create dwarf labels for temporary symbols. if (Symbol->isTemporary()) return; MCContext &context = MCOS->getContext(); // We won't create dwarf labels for symbols in sections that we are not // generating debug info for. if (!context.getGenDwarfSectionSyms().count(MCOS->getCurrentSectionOnly())) return; // The dwarf label's name does not have the symbol name's leading // underbar if any. StringRef Name = Symbol->getName(); if (Name.startswith("_")) Name = Name.substr(1, Name.size()-1); // Get the dwarf file number to be used for the dwarf label. unsigned FileNumber = context.getGenDwarfFileNumber(); // Finding the line number is the expensive part which is why we just don't // pass it in as for some symbols we won't create a dwarf label. unsigned CurBuffer = SrcMgr.FindBufferContainingLoc(Loc); unsigned LineNumber = SrcMgr.FindLineNumber(Loc, CurBuffer); // We create a temporary symbol for use for the AT_high_pc and AT_low_pc // values so that they don't have things like an ARM thumb bit from the // original symbol. So when used they won't get a low bit set after // relocation. MCSymbol *Label = context.createTempSymbol(); MCOS->EmitLabel(Label); // Create and entry for the info and add it to the other entries. MCOS->getContext().addMCGenDwarfLabelEntry( MCGenDwarfLabelEntry(Name, FileNumber, LineNumber, Label)); } static int getDataAlignmentFactor(MCStreamer &streamer) { MCContext &context = streamer.getContext(); const MCAsmInfo *asmInfo = context.getAsmInfo(); int size = asmInfo->getCalleeSaveStackSlotSize(); if (asmInfo->isStackGrowthDirectionUp()) return size; else return -size; } static unsigned getSizeForEncoding(MCStreamer &streamer, unsigned symbolEncoding) { MCContext &context = streamer.getContext(); unsigned format = symbolEncoding & 0x0f; switch (format) { default: llvm_unreachable("Unknown Encoding"); case dwarf::DW_EH_PE_absptr: case dwarf::DW_EH_PE_signed: return context.getAsmInfo()->getCodePointerSize(); case dwarf::DW_EH_PE_udata2: case dwarf::DW_EH_PE_sdata2: return 2; case dwarf::DW_EH_PE_udata4: case dwarf::DW_EH_PE_sdata4: return 4; case dwarf::DW_EH_PE_udata8: case dwarf::DW_EH_PE_sdata8: return 8; } } static void emitFDESymbol(MCObjectStreamer &streamer, const MCSymbol &symbol, unsigned symbolEncoding, bool isEH) { MCContext &context = streamer.getContext(); const MCAsmInfo *asmInfo = context.getAsmInfo(); const MCExpr *v = asmInfo->getExprForFDESymbol(&symbol, symbolEncoding, streamer); unsigned size = getSizeForEncoding(streamer, symbolEncoding); if (asmInfo->doDwarfFDESymbolsUseAbsDiff() && isEH) emitAbsValue(streamer, v, size); else streamer.EmitValue(v, size); } static void EmitPersonality(MCStreamer &streamer, const MCSymbol &symbol, unsigned symbolEncoding) { MCContext &context = streamer.getContext(); const MCAsmInfo *asmInfo = context.getAsmInfo(); const MCExpr *v = asmInfo->getExprForPersonalitySymbol(&symbol, symbolEncoding, streamer); unsigned size = getSizeForEncoding(streamer, symbolEncoding); streamer.EmitValue(v, size); } namespace { class FrameEmitterImpl { int CFAOffset = 0; int InitialCFAOffset = 0; bool IsEH; MCObjectStreamer &Streamer; public: FrameEmitterImpl(bool IsEH, MCObjectStreamer &Streamer) : IsEH(IsEH), Streamer(Streamer) {} /// Emit the unwind information in a compact way. void EmitCompactUnwind(const MCDwarfFrameInfo &frame); const MCSymbol &EmitCIE(const MCDwarfFrameInfo &F); void EmitFDE(const MCSymbol &cieStart, const MCDwarfFrameInfo &frame, bool LastInSection, const MCSymbol &SectionStart); void EmitCFIInstructions(ArrayRef Instrs, MCSymbol *BaseLabel); void EmitCFIInstruction(const MCCFIInstruction &Instr); }; } // end anonymous namespace static void emitEncodingByte(MCObjectStreamer &Streamer, unsigned Encoding) { Streamer.EmitIntValue(Encoding, 1); } void FrameEmitterImpl::EmitCFIInstruction(const MCCFIInstruction &Instr) { int dataAlignmentFactor = getDataAlignmentFactor(Streamer); auto *MRI = Streamer.getContext().getRegisterInfo(); switch (Instr.getOperation()) { case MCCFIInstruction::OpRegister: { unsigned Reg1 = Instr.getRegister(); unsigned Reg2 = Instr.getRegister2(); if (!IsEH) { Reg1 = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg1); Reg2 = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg2); } Streamer.EmitIntValue(dwarf::DW_CFA_register, 1); Streamer.EmitULEB128IntValue(Reg1); Streamer.EmitULEB128IntValue(Reg2); return; } case MCCFIInstruction::OpWindowSave: Streamer.EmitIntValue(dwarf::DW_CFA_GNU_window_save, 1); return; case MCCFIInstruction::OpNegateRAState: Streamer.EmitIntValue(dwarf::DW_CFA_AARCH64_negate_ra_state, 1); return; case MCCFIInstruction::OpUndefined: { unsigned Reg = Instr.getRegister(); Streamer.EmitIntValue(dwarf::DW_CFA_undefined, 1); Streamer.EmitULEB128IntValue(Reg); return; } case MCCFIInstruction::OpAdjustCfaOffset: case MCCFIInstruction::OpDefCfaOffset: { const bool IsRelative = Instr.getOperation() == MCCFIInstruction::OpAdjustCfaOffset; Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_offset, 1); if (IsRelative) CFAOffset += Instr.getOffset(); else CFAOffset = -Instr.getOffset(); Streamer.EmitULEB128IntValue(CFAOffset); return; } case MCCFIInstruction::OpDefCfa: { unsigned Reg = Instr.getRegister(); if (!IsEH) Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg); Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa, 1); Streamer.EmitULEB128IntValue(Reg); CFAOffset = -Instr.getOffset(); Streamer.EmitULEB128IntValue(CFAOffset); return; } case MCCFIInstruction::OpDefCfaRegister: { unsigned Reg = Instr.getRegister(); if (!IsEH) Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg); Streamer.EmitIntValue(dwarf::DW_CFA_def_cfa_register, 1); Streamer.EmitULEB128IntValue(Reg); return; } case MCCFIInstruction::OpOffset: case MCCFIInstruction::OpRelOffset: { const bool IsRelative = Instr.getOperation() == MCCFIInstruction::OpRelOffset; unsigned Reg = Instr.getRegister(); if (!IsEH) Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg); int Offset = Instr.getOffset(); if (IsRelative) Offset -= CFAOffset; Offset = Offset / dataAlignmentFactor; if (Offset < 0) { Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended_sf, 1); Streamer.EmitULEB128IntValue(Reg); Streamer.EmitSLEB128IntValue(Offset); } else if (Reg < 64) { Streamer.EmitIntValue(dwarf::DW_CFA_offset + Reg, 1); Streamer.EmitULEB128IntValue(Offset); } else { Streamer.EmitIntValue(dwarf::DW_CFA_offset_extended, 1); Streamer.EmitULEB128IntValue(Reg); Streamer.EmitULEB128IntValue(Offset); } return; } case MCCFIInstruction::OpRememberState: Streamer.EmitIntValue(dwarf::DW_CFA_remember_state, 1); return; case MCCFIInstruction::OpRestoreState: Streamer.EmitIntValue(dwarf::DW_CFA_restore_state, 1); return; case MCCFIInstruction::OpSameValue: { unsigned Reg = Instr.getRegister(); Streamer.EmitIntValue(dwarf::DW_CFA_same_value, 1); Streamer.EmitULEB128IntValue(Reg); return; } case MCCFIInstruction::OpRestore: { unsigned Reg = Instr.getRegister(); if (!IsEH) Reg = MRI->getDwarfRegNumFromDwarfEHRegNum(Reg); if (Reg < 64) { Streamer.EmitIntValue(dwarf::DW_CFA_restore | Reg, 1); } else { Streamer.EmitIntValue(dwarf::DW_CFA_restore_extended, 1); Streamer.EmitULEB128IntValue(Reg); } return; } case MCCFIInstruction::OpGnuArgsSize: Streamer.EmitIntValue(dwarf::DW_CFA_GNU_args_size, 1); Streamer.EmitULEB128IntValue(Instr.getOffset()); return; case MCCFIInstruction::OpEscape: Streamer.EmitBytes(Instr.getValues()); return; } llvm_unreachable("Unhandled case in switch"); } /// Emit frame instructions to describe the layout of the frame. void FrameEmitterImpl::EmitCFIInstructions(ArrayRef Instrs, MCSymbol *BaseLabel) { for (const MCCFIInstruction &Instr : Instrs) { MCSymbol *Label = Instr.getLabel(); // Throw out move if the label is invalid. if (Label && !Label->isDefined()) continue; // Not emitted, in dead code. // Advance row if new location. if (BaseLabel && Label) { MCSymbol *ThisSym = Label; if (ThisSym != BaseLabel) { Streamer.EmitDwarfAdvanceFrameAddr(BaseLabel, ThisSym); BaseLabel = ThisSym; } } EmitCFIInstruction(Instr); } } /// Emit the unwind information in a compact way. void FrameEmitterImpl::EmitCompactUnwind(const MCDwarfFrameInfo &Frame) { MCContext &Context = Streamer.getContext(); const MCObjectFileInfo *MOFI = Context.getObjectFileInfo(); // range-start range-length compact-unwind-enc personality-func lsda // _foo LfooEnd-_foo 0x00000023 0 0 // _bar LbarEnd-_bar 0x00000025 __gxx_personality except_tab1 // // .section __LD,__compact_unwind,regular,debug // // # compact unwind for _foo // .quad _foo // .set L1,LfooEnd-_foo // .long L1 // .long 0x01010001 // .quad 0 // .quad 0 // // # compact unwind for _bar // .quad _bar // .set L2,LbarEnd-_bar // .long L2 // .long 0x01020011 // .quad __gxx_personality // .quad except_tab1 uint32_t Encoding = Frame.CompactUnwindEncoding; if (!Encoding) return; bool DwarfEHFrameOnly = (Encoding == MOFI->getCompactUnwindDwarfEHFrameOnly()); // The encoding needs to know we have an LSDA. if (!DwarfEHFrameOnly && Frame.Lsda) Encoding |= 0x40000000; // Range Start unsigned FDEEncoding = MOFI->getFDEEncoding(); unsigned Size = getSizeForEncoding(Streamer, FDEEncoding); Streamer.EmitSymbolValue(Frame.Begin, Size); // Range Length const MCExpr *Range = MakeStartMinusEndExpr(Streamer, *Frame.Begin, *Frame.End, 0); emitAbsValue(Streamer, Range, 4); // Compact Encoding Size = getSizeForEncoding(Streamer, dwarf::DW_EH_PE_udata4); Streamer.EmitIntValue(Encoding, Size); // Personality Function Size = getSizeForEncoding(Streamer, dwarf::DW_EH_PE_absptr); if (!DwarfEHFrameOnly && Frame.Personality) Streamer.EmitSymbolValue(Frame.Personality, Size); else Streamer.EmitIntValue(0, Size); // No personality fn // LSDA Size = getSizeForEncoding(Streamer, Frame.LsdaEncoding); if (!DwarfEHFrameOnly && Frame.Lsda) Streamer.EmitSymbolValue(Frame.Lsda, Size); else Streamer.EmitIntValue(0, Size); // No LSDA } static unsigned getCIEVersion(bool IsEH, unsigned DwarfVersion) { if (IsEH) return 1; switch (DwarfVersion) { case 2: return 1; case 3: return 3; case 4: case 5: return 4; } llvm_unreachable("Unknown version"); } const MCSymbol &FrameEmitterImpl::EmitCIE(const MCDwarfFrameInfo &Frame) { MCContext &context = Streamer.getContext(); const MCRegisterInfo *MRI = context.getRegisterInfo(); const MCObjectFileInfo *MOFI = context.getObjectFileInfo(); MCSymbol *sectionStart = context.createTempSymbol(); Streamer.EmitLabel(sectionStart); MCSymbol *sectionEnd = context.createTempSymbol(); // Length const MCExpr *Length = MakeStartMinusEndExpr(Streamer, *sectionStart, *sectionEnd, 4); emitAbsValue(Streamer, Length, 4); // CIE ID unsigned CIE_ID = IsEH ? 0 : -1; Streamer.EmitIntValue(CIE_ID, 4); // Version uint8_t CIEVersion = getCIEVersion(IsEH, context.getDwarfVersion()); Streamer.EmitIntValue(CIEVersion, 1); if (IsEH) { SmallString<8> Augmentation; Augmentation += "z"; if (Frame.Personality) Augmentation += "P"; if (Frame.Lsda) Augmentation += "L"; Augmentation += "R"; if (Frame.IsSignalFrame) Augmentation += "S"; if (Frame.IsBKeyFrame) Augmentation += "B"; Streamer.EmitBytes(Augmentation); } Streamer.EmitIntValue(0, 1); if (CIEVersion >= 4) { // Address Size Streamer.EmitIntValue(context.getAsmInfo()->getCodePointerSize(), 1); // Segment Descriptor Size Streamer.EmitIntValue(0, 1); } // Code Alignment Factor Streamer.EmitULEB128IntValue(context.getAsmInfo()->getMinInstAlignment()); // Data Alignment Factor Streamer.EmitSLEB128IntValue(getDataAlignmentFactor(Streamer)); // Return Address Register unsigned RAReg = Frame.RAReg; if (RAReg == static_cast(INT_MAX)) RAReg = MRI->getDwarfRegNum(MRI->getRARegister(), IsEH); if (CIEVersion == 1) { assert(RAReg <= 255 && "DWARF 2 encodes return_address_register in one byte"); Streamer.EmitIntValue(RAReg, 1); } else { Streamer.EmitULEB128IntValue(RAReg); } // Augmentation Data Length (optional) unsigned augmentationLength = 0; if (IsEH) { if (Frame.Personality) { // Personality Encoding augmentationLength += 1; // Personality augmentationLength += getSizeForEncoding(Streamer, Frame.PersonalityEncoding); } if (Frame.Lsda) augmentationLength += 1; // Encoding of the FDE pointers augmentationLength += 1; Streamer.EmitULEB128IntValue(augmentationLength); // Augmentation Data (optional) if (Frame.Personality) { // Personality Encoding emitEncodingByte(Streamer, Frame.PersonalityEncoding); // Personality EmitPersonality(Streamer, *Frame.Personality, Frame.PersonalityEncoding); } if (Frame.Lsda) emitEncodingByte(Streamer, Frame.LsdaEncoding); // Encoding of the FDE pointers emitEncodingByte(Streamer, MOFI->getFDEEncoding()); } // Initial Instructions const MCAsmInfo *MAI = context.getAsmInfo(); if (!Frame.IsSimple) { const std::vector &Instructions = MAI->getInitialFrameState(); EmitCFIInstructions(Instructions, nullptr); } InitialCFAOffset = CFAOffset; // Padding Streamer.EmitValueToAlignment(IsEH ? 4 : MAI->getCodePointerSize()); Streamer.EmitLabel(sectionEnd); return *sectionStart; } void FrameEmitterImpl::EmitFDE(const MCSymbol &cieStart, const MCDwarfFrameInfo &frame, bool LastInSection, const MCSymbol &SectionStart) { MCContext &context = Streamer.getContext(); MCSymbol *fdeStart = context.createTempSymbol(); MCSymbol *fdeEnd = context.createTempSymbol(); const MCObjectFileInfo *MOFI = context.getObjectFileInfo(); CFAOffset = InitialCFAOffset; // Length const MCExpr *Length = MakeStartMinusEndExpr(Streamer, *fdeStart, *fdeEnd, 0); emitAbsValue(Streamer, Length, 4); Streamer.EmitLabel(fdeStart); // CIE Pointer const MCAsmInfo *asmInfo = context.getAsmInfo(); if (IsEH) { const MCExpr *offset = MakeStartMinusEndExpr(Streamer, cieStart, *fdeStart, 0); emitAbsValue(Streamer, offset, 4); } else if (!asmInfo->doesDwarfUseRelocationsAcrossSections()) { const MCExpr *offset = MakeStartMinusEndExpr(Streamer, SectionStart, cieStart, 0); emitAbsValue(Streamer, offset, 4); } else { Streamer.EmitSymbolValue(&cieStart, 4); } // PC Begin unsigned PCEncoding = IsEH ? MOFI->getFDEEncoding() : (unsigned)dwarf::DW_EH_PE_absptr; unsigned PCSize = getSizeForEncoding(Streamer, PCEncoding); emitFDESymbol(Streamer, *frame.Begin, PCEncoding, IsEH); // PC Range const MCExpr *Range = MakeStartMinusEndExpr(Streamer, *frame.Begin, *frame.End, 0); emitAbsValue(Streamer, Range, PCSize); if (IsEH) { // Augmentation Data Length unsigned augmentationLength = 0; if (frame.Lsda) augmentationLength += getSizeForEncoding(Streamer, frame.LsdaEncoding); Streamer.EmitULEB128IntValue(augmentationLength); // Augmentation Data if (frame.Lsda) emitFDESymbol(Streamer, *frame.Lsda, frame.LsdaEncoding, true); } // Call Frame Instructions EmitCFIInstructions(frame.Instructions, frame.Begin); // Padding // The size of a .eh_frame section has to be a multiple of the alignment // since a null CIE is interpreted as the end. Old systems overaligned // .eh_frame, so we do too and account for it in the last FDE. unsigned Align = LastInSection ? asmInfo->getCodePointerSize() : PCSize; Streamer.EmitValueToAlignment(Align); Streamer.EmitLabel(fdeEnd); } namespace { struct CIEKey { static const CIEKey getEmptyKey() { return CIEKey(nullptr, 0, -1, false, false, static_cast(INT_MAX), false); } static const CIEKey getTombstoneKey() { return CIEKey(nullptr, -1, 0, false, false, static_cast(INT_MAX), false); } CIEKey(const MCSymbol *Personality, unsigned PersonalityEncoding, unsigned LSDAEncoding, bool IsSignalFrame, bool IsSimple, unsigned RAReg, bool IsBKeyFrame) : Personality(Personality), PersonalityEncoding(PersonalityEncoding), LsdaEncoding(LSDAEncoding), IsSignalFrame(IsSignalFrame), IsSimple(IsSimple), RAReg(RAReg), IsBKeyFrame(IsBKeyFrame) {} explicit CIEKey(const MCDwarfFrameInfo &Frame) : Personality(Frame.Personality), PersonalityEncoding(Frame.PersonalityEncoding), LsdaEncoding(Frame.LsdaEncoding), IsSignalFrame(Frame.IsSignalFrame), IsSimple(Frame.IsSimple), RAReg(Frame.RAReg), IsBKeyFrame(Frame.IsBKeyFrame) {} StringRef PersonalityName() const { if (!Personality) return StringRef(); return Personality->getName(); } bool operator<(const CIEKey &Other) const { return std::make_tuple(PersonalityName(), PersonalityEncoding, LsdaEncoding, IsSignalFrame, IsSimple, RAReg) < std::make_tuple(Other.PersonalityName(), Other.PersonalityEncoding, Other.LsdaEncoding, Other.IsSignalFrame, Other.IsSimple, Other.RAReg); } const MCSymbol *Personality; unsigned PersonalityEncoding; unsigned LsdaEncoding; bool IsSignalFrame; bool IsSimple; unsigned RAReg; bool IsBKeyFrame; }; } // end anonymous namespace namespace llvm { template <> struct DenseMapInfo { static CIEKey getEmptyKey() { return CIEKey::getEmptyKey(); } static CIEKey getTombstoneKey() { return CIEKey::getTombstoneKey(); } static unsigned getHashValue(const CIEKey &Key) { return static_cast(hash_combine( Key.Personality, Key.PersonalityEncoding, Key.LsdaEncoding, Key.IsSignalFrame, Key.IsSimple, Key.RAReg, Key.IsBKeyFrame)); } static bool isEqual(const CIEKey &LHS, const CIEKey &RHS) { return LHS.Personality == RHS.Personality && LHS.PersonalityEncoding == RHS.PersonalityEncoding && LHS.LsdaEncoding == RHS.LsdaEncoding && LHS.IsSignalFrame == RHS.IsSignalFrame && LHS.IsSimple == RHS.IsSimple && LHS.RAReg == RHS.RAReg && LHS.IsBKeyFrame == RHS.IsBKeyFrame; } }; } // end namespace llvm void MCDwarfFrameEmitter::Emit(MCObjectStreamer &Streamer, MCAsmBackend *MAB, bool IsEH) { Streamer.generateCompactUnwindEncodings(MAB); MCContext &Context = Streamer.getContext(); const MCObjectFileInfo *MOFI = Context.getObjectFileInfo(); const MCAsmInfo *AsmInfo = Context.getAsmInfo(); FrameEmitterImpl Emitter(IsEH, Streamer); ArrayRef FrameArray = Streamer.getDwarfFrameInfos(); // Emit the compact unwind info if available. bool NeedsEHFrameSection = !MOFI->getSupportsCompactUnwindWithoutEHFrame(); if (IsEH && MOFI->getCompactUnwindSection()) { bool SectionEmitted = false; for (const MCDwarfFrameInfo &Frame : FrameArray) { if (Frame.CompactUnwindEncoding == 0) continue; if (!SectionEmitted) { Streamer.SwitchSection(MOFI->getCompactUnwindSection()); Streamer.EmitValueToAlignment(AsmInfo->getCodePointerSize()); SectionEmitted = true; } NeedsEHFrameSection |= Frame.CompactUnwindEncoding == MOFI->getCompactUnwindDwarfEHFrameOnly(); Emitter.EmitCompactUnwind(Frame); } } if (!NeedsEHFrameSection) return; MCSection &Section = IsEH ? *const_cast(MOFI)->getEHFrameSection() : *MOFI->getDwarfFrameSection(); Streamer.SwitchSection(&Section); MCSymbol *SectionStart = Context.createTempSymbol(); Streamer.EmitLabel(SectionStart); DenseMap CIEStarts; const MCSymbol *DummyDebugKey = nullptr; bool CanOmitDwarf = MOFI->getOmitDwarfIfHaveCompactUnwind(); // Sort the FDEs by their corresponding CIE before we emit them. // This isn't technically necessary according to the DWARF standard, // but the Android libunwindstack rejects eh_frame sections where // an FDE refers to a CIE other than the closest previous CIE. std::vector FrameArrayX(FrameArray.begin(), FrameArray.end()); llvm::stable_sort(FrameArrayX, [](const MCDwarfFrameInfo &X, const MCDwarfFrameInfo &Y) { return CIEKey(X) < CIEKey(Y); }); for (auto I = FrameArrayX.begin(), E = FrameArrayX.end(); I != E;) { const MCDwarfFrameInfo &Frame = *I; ++I; if (CanOmitDwarf && Frame.CompactUnwindEncoding != MOFI->getCompactUnwindDwarfEHFrameOnly()) // Don't generate an EH frame if we don't need one. I.e., it's taken care // of by the compact unwind encoding. continue; CIEKey Key(Frame); const MCSymbol *&CIEStart = IsEH ? CIEStarts[Key] : DummyDebugKey; if (!CIEStart) CIEStart = &Emitter.EmitCIE(Frame); Emitter.EmitFDE(*CIEStart, Frame, I == E, *SectionStart); } } void MCDwarfFrameEmitter::EmitAdvanceLoc(MCObjectStreamer &Streamer, uint64_t AddrDelta) { MCContext &Context = Streamer.getContext(); SmallString<256> Tmp; raw_svector_ostream OS(Tmp); MCDwarfFrameEmitter::EncodeAdvanceLoc(Context, AddrDelta, OS); Streamer.EmitBytes(OS.str()); } void MCDwarfFrameEmitter::EncodeAdvanceLoc(MCContext &Context, - uint64_t AddrDelta, - raw_ostream &OS) { + uint64_t AddrDelta, raw_ostream &OS, + uint32_t *Offset, uint32_t *Size) { // Scale the address delta by the minimum instruction length. AddrDelta = ScaleAddrDelta(Context, AddrDelta); + bool WithFixups = false; + if (Offset && Size) + WithFixups = true; + support::endianness E = Context.getAsmInfo()->isLittleEndian() ? support::little : support::big; if (AddrDelta == 0) { + if (WithFixups) { + *Offset = 0; + *Size = 0; + } } else if (isUIntN(6, AddrDelta)) { uint8_t Opcode = dwarf::DW_CFA_advance_loc | AddrDelta; - OS << Opcode; + if (WithFixups) { + *Offset = OS.tell(); + *Size = 6; + OS << uint8_t(dwarf::DW_CFA_advance_loc); + } else + OS << Opcode; } else if (isUInt<8>(AddrDelta)) { OS << uint8_t(dwarf::DW_CFA_advance_loc1); - OS << uint8_t(AddrDelta); + if (WithFixups) { + *Offset = OS.tell(); + *Size = 8; + OS.write_zeros(1); + } else + OS << uint8_t(AddrDelta); } else if (isUInt<16>(AddrDelta)) { OS << uint8_t(dwarf::DW_CFA_advance_loc2); - support::endian::write(OS, AddrDelta, E); + if (WithFixups) { + *Offset = OS.tell(); + *Size = 16; + OS.write_zeros(2); + } else + support::endian::write(OS, AddrDelta, E); } else { assert(isUInt<32>(AddrDelta)); OS << uint8_t(dwarf::DW_CFA_advance_loc4); - support::endian::write(OS, AddrDelta, E); + if (WithFixups) { + *Offset = OS.tell(); + *Size = 32; + OS.write_zeros(4); + } else + support::endian::write(OS, AddrDelta, E); } } Index: head/contrib/llvm/lib/MC/MCExpr.cpp =================================================================== --- head/contrib/llvm/lib/MC/MCExpr.cpp (revision 354468) +++ head/contrib/llvm/lib/MC/MCExpr.cpp (revision 354469) @@ -1,914 +1,931 @@ //===- MCExpr.cpp - Assembly Level Expression Implementation --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/MC/MCExpr.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; #define DEBUG_TYPE "mcexpr" namespace { namespace stats { STATISTIC(MCExprEvaluate, "Number of MCExpr evaluations"); } // end namespace stats } // end anonymous namespace void MCExpr::print(raw_ostream &OS, const MCAsmInfo *MAI, bool InParens) const { switch (getKind()) { case MCExpr::Target: return cast(this)->printImpl(OS, MAI); case MCExpr::Constant: { auto Value = cast(*this).getValue(); auto PrintInHex = cast(*this).useHexFormat(); if (PrintInHex) OS << "0x" << Twine::utohexstr(Value); else OS << Value; return; } case MCExpr::SymbolRef: { const MCSymbolRefExpr &SRE = cast(*this); const MCSymbol &Sym = SRE.getSymbol(); // Parenthesize names that start with $ so that they don't look like // absolute names. bool UseParens = !InParens && !Sym.getName().empty() && Sym.getName()[0] == '$'; if (UseParens) { OS << '('; Sym.print(OS, MAI); OS << ')'; } else Sym.print(OS, MAI); if (SRE.getKind() != MCSymbolRefExpr::VK_None) SRE.printVariantKind(OS); return; } case MCExpr::Unary: { const MCUnaryExpr &UE = cast(*this); switch (UE.getOpcode()) { case MCUnaryExpr::LNot: OS << '!'; break; case MCUnaryExpr::Minus: OS << '-'; break; case MCUnaryExpr::Not: OS << '~'; break; case MCUnaryExpr::Plus: OS << '+'; break; } bool Binary = UE.getSubExpr()->getKind() == MCExpr::Binary; if (Binary) OS << "("; UE.getSubExpr()->print(OS, MAI); if (Binary) OS << ")"; return; } case MCExpr::Binary: { const MCBinaryExpr &BE = cast(*this); // Only print parens around the LHS if it is non-trivial. if (isa(BE.getLHS()) || isa(BE.getLHS())) { BE.getLHS()->print(OS, MAI); } else { OS << '('; BE.getLHS()->print(OS, MAI); OS << ')'; } switch (BE.getOpcode()) { case MCBinaryExpr::Add: // Print "X-42" instead of "X+-42". if (const MCConstantExpr *RHSC = dyn_cast(BE.getRHS())) { if (RHSC->getValue() < 0) { OS << RHSC->getValue(); return; } } OS << '+'; break; case MCBinaryExpr::AShr: OS << ">>"; break; case MCBinaryExpr::And: OS << '&'; break; case MCBinaryExpr::Div: OS << '/'; break; case MCBinaryExpr::EQ: OS << "=="; break; case MCBinaryExpr::GT: OS << '>'; break; case MCBinaryExpr::GTE: OS << ">="; break; case MCBinaryExpr::LAnd: OS << "&&"; break; case MCBinaryExpr::LOr: OS << "||"; break; case MCBinaryExpr::LShr: OS << ">>"; break; case MCBinaryExpr::LT: OS << '<'; break; case MCBinaryExpr::LTE: OS << "<="; break; case MCBinaryExpr::Mod: OS << '%'; break; case MCBinaryExpr::Mul: OS << '*'; break; case MCBinaryExpr::NE: OS << "!="; break; case MCBinaryExpr::Or: OS << '|'; break; case MCBinaryExpr::Shl: OS << "<<"; break; case MCBinaryExpr::Sub: OS << '-'; break; case MCBinaryExpr::Xor: OS << '^'; break; } // Only print parens around the LHS if it is non-trivial. if (isa(BE.getRHS()) || isa(BE.getRHS())) { BE.getRHS()->print(OS, MAI); } else { OS << '('; BE.getRHS()->print(OS, MAI); OS << ')'; } return; } } llvm_unreachable("Invalid expression kind!"); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void MCExpr::dump() const { dbgs() << *this; dbgs() << '\n'; } #endif /* *** */ const MCBinaryExpr *MCBinaryExpr::create(Opcode Opc, const MCExpr *LHS, const MCExpr *RHS, MCContext &Ctx, SMLoc Loc) { return new (Ctx) MCBinaryExpr(Opc, LHS, RHS, Loc); } const MCUnaryExpr *MCUnaryExpr::create(Opcode Opc, const MCExpr *Expr, MCContext &Ctx, SMLoc Loc) { return new (Ctx) MCUnaryExpr(Opc, Expr, Loc); } const MCConstantExpr *MCConstantExpr::create(int64_t Value, MCContext &Ctx, bool PrintInHex) { return new (Ctx) MCConstantExpr(Value, PrintInHex); } /* *** */ MCSymbolRefExpr::MCSymbolRefExpr(const MCSymbol *Symbol, VariantKind Kind, const MCAsmInfo *MAI, SMLoc Loc) : MCExpr(MCExpr::SymbolRef, Loc), Kind(Kind), UseParensForSymbolVariant(MAI->useParensForSymbolVariant()), HasSubsectionsViaSymbols(MAI->hasSubsectionsViaSymbols()), Symbol(Symbol) { assert(Symbol); } const MCSymbolRefExpr *MCSymbolRefExpr::create(const MCSymbol *Sym, VariantKind Kind, MCContext &Ctx, SMLoc Loc) { return new (Ctx) MCSymbolRefExpr(Sym, Kind, Ctx.getAsmInfo(), Loc); } const MCSymbolRefExpr *MCSymbolRefExpr::create(StringRef Name, VariantKind Kind, MCContext &Ctx) { return create(Ctx.getOrCreateSymbol(Name), Kind, Ctx); } StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { switch (Kind) { case VK_Invalid: return "<>"; case VK_None: return "<>"; case VK_DTPOFF: return "DTPOFF"; case VK_DTPREL: return "DTPREL"; case VK_GOT: return "GOT"; case VK_GOTOFF: return "GOTOFF"; case VK_GOTREL: return "GOTREL"; case VK_GOTPCREL: return "GOTPCREL"; case VK_GOTTPOFF: return "GOTTPOFF"; case VK_INDNTPOFF: return "INDNTPOFF"; case VK_NTPOFF: return "NTPOFF"; case VK_GOTNTPOFF: return "GOTNTPOFF"; case VK_PLT: return "PLT"; case VK_TLSGD: return "TLSGD"; case VK_TLSLD: return "TLSLD"; case VK_TLSLDM: return "TLSLDM"; case VK_TPOFF: return "TPOFF"; case VK_TPREL: return "TPREL"; case VK_TLSCALL: return "tlscall"; case VK_TLSDESC: return "tlsdesc"; case VK_TLVP: return "TLVP"; case VK_TLVPPAGE: return "TLVPPAGE"; case VK_TLVPPAGEOFF: return "TLVPPAGEOFF"; case VK_PAGE: return "PAGE"; case VK_PAGEOFF: return "PAGEOFF"; case VK_GOTPAGE: return "GOTPAGE"; case VK_GOTPAGEOFF: return "GOTPAGEOFF"; case VK_SECREL: return "SECREL32"; case VK_SIZE: return "SIZE"; case VK_WEAKREF: return "WEAKREF"; case VK_X86_ABS8: return "ABS8"; case VK_ARM_NONE: return "none"; case VK_ARM_GOT_PREL: return "GOT_PREL"; case VK_ARM_TARGET1: return "target1"; case VK_ARM_TARGET2: return "target2"; case VK_ARM_PREL31: return "prel31"; case VK_ARM_SBREL: return "sbrel"; case VK_ARM_TLSLDO: return "tlsldo"; case VK_ARM_TLSDESCSEQ: return "tlsdescseq"; case VK_AVR_NONE: return "none"; case VK_AVR_LO8: return "lo8"; case VK_AVR_HI8: return "hi8"; case VK_AVR_HLO8: return "hlo8"; case VK_AVR_DIFF8: return "diff8"; case VK_AVR_DIFF16: return "diff16"; case VK_AVR_DIFF32: return "diff32"; case VK_PPC_LO: return "l"; case VK_PPC_HI: return "h"; case VK_PPC_HA: return "ha"; case VK_PPC_HIGH: return "high"; case VK_PPC_HIGHA: return "higha"; case VK_PPC_HIGHER: return "higher"; case VK_PPC_HIGHERA: return "highera"; case VK_PPC_HIGHEST: return "highest"; case VK_PPC_HIGHESTA: return "highesta"; case VK_PPC_GOT_LO: return "got@l"; case VK_PPC_GOT_HI: return "got@h"; case VK_PPC_GOT_HA: return "got@ha"; case VK_PPC_TOCBASE: return "tocbase"; case VK_PPC_TOC: return "toc"; case VK_PPC_TOC_LO: return "toc@l"; case VK_PPC_TOC_HI: return "toc@h"; case VK_PPC_TOC_HA: return "toc@ha"; case VK_PPC_DTPMOD: return "dtpmod"; case VK_PPC_TPREL_LO: return "tprel@l"; case VK_PPC_TPREL_HI: return "tprel@h"; case VK_PPC_TPREL_HA: return "tprel@ha"; case VK_PPC_TPREL_HIGH: return "tprel@high"; case VK_PPC_TPREL_HIGHA: return "tprel@higha"; case VK_PPC_TPREL_HIGHER: return "tprel@higher"; case VK_PPC_TPREL_HIGHERA: return "tprel@highera"; case VK_PPC_TPREL_HIGHEST: return "tprel@highest"; case VK_PPC_TPREL_HIGHESTA: return "tprel@highesta"; case VK_PPC_DTPREL_LO: return "dtprel@l"; case VK_PPC_DTPREL_HI: return "dtprel@h"; case VK_PPC_DTPREL_HA: return "dtprel@ha"; case VK_PPC_DTPREL_HIGH: return "dtprel@high"; case VK_PPC_DTPREL_HIGHA: return "dtprel@higha"; case VK_PPC_DTPREL_HIGHER: return "dtprel@higher"; case VK_PPC_DTPREL_HIGHERA: return "dtprel@highera"; case VK_PPC_DTPREL_HIGHEST: return "dtprel@highest"; case VK_PPC_DTPREL_HIGHESTA: return "dtprel@highesta"; case VK_PPC_GOT_TPREL: return "got@tprel"; case VK_PPC_GOT_TPREL_LO: return "got@tprel@l"; case VK_PPC_GOT_TPREL_HI: return "got@tprel@h"; case VK_PPC_GOT_TPREL_HA: return "got@tprel@ha"; case VK_PPC_GOT_DTPREL: return "got@dtprel"; case VK_PPC_GOT_DTPREL_LO: return "got@dtprel@l"; case VK_PPC_GOT_DTPREL_HI: return "got@dtprel@h"; case VK_PPC_GOT_DTPREL_HA: return "got@dtprel@ha"; case VK_PPC_TLS: return "tls"; case VK_PPC_GOT_TLSGD: return "got@tlsgd"; case VK_PPC_GOT_TLSGD_LO: return "got@tlsgd@l"; case VK_PPC_GOT_TLSGD_HI: return "got@tlsgd@h"; case VK_PPC_GOT_TLSGD_HA: return "got@tlsgd@ha"; case VK_PPC_TLSGD: return "tlsgd"; case VK_PPC_GOT_TLSLD: return "got@tlsld"; case VK_PPC_GOT_TLSLD_LO: return "got@tlsld@l"; case VK_PPC_GOT_TLSLD_HI: return "got@tlsld@h"; case VK_PPC_GOT_TLSLD_HA: return "got@tlsld@ha"; case VK_PPC_TLSLD: return "tlsld"; case VK_PPC_LOCAL: return "local"; case VK_COFF_IMGREL32: return "IMGREL"; case VK_Hexagon_PCREL: return "PCREL"; case VK_Hexagon_LO16: return "LO16"; case VK_Hexagon_HI16: return "HI16"; case VK_Hexagon_GPREL: return "GPREL"; case VK_Hexagon_GD_GOT: return "GDGOT"; case VK_Hexagon_LD_GOT: return "LDGOT"; case VK_Hexagon_GD_PLT: return "GDPLT"; case VK_Hexagon_LD_PLT: return "LDPLT"; case VK_Hexagon_IE: return "IE"; case VK_Hexagon_IE_GOT: return "IEGOT"; case VK_WASM_TYPEINDEX: return "TYPEINDEX"; case VK_WASM_MBREL: return "MBREL"; case VK_WASM_TBREL: return "TBREL"; case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo"; case VK_AMDGPU_GOTPCREL32_HI: return "gotpcrel32@hi"; case VK_AMDGPU_REL32_LO: return "rel32@lo"; case VK_AMDGPU_REL32_HI: return "rel32@hi"; case VK_AMDGPU_REL64: return "rel64"; case VK_AMDGPU_ABS32_LO: return "abs32@lo"; case VK_AMDGPU_ABS32_HI: return "abs32@hi"; } llvm_unreachable("Invalid variant kind"); } MCSymbolRefExpr::VariantKind MCSymbolRefExpr::getVariantKindForName(StringRef Name) { return StringSwitch(Name.lower()) .Case("dtprel", VK_DTPREL) .Case("dtpoff", VK_DTPOFF) .Case("got", VK_GOT) .Case("gotoff", VK_GOTOFF) .Case("gotrel", VK_GOTREL) .Case("gotpcrel", VK_GOTPCREL) .Case("gottpoff", VK_GOTTPOFF) .Case("indntpoff", VK_INDNTPOFF) .Case("ntpoff", VK_NTPOFF) .Case("gotntpoff", VK_GOTNTPOFF) .Case("plt", VK_PLT) .Case("tlscall", VK_TLSCALL) .Case("tlsdesc", VK_TLSDESC) .Case("tlsgd", VK_TLSGD) .Case("tlsld", VK_TLSLD) .Case("tlsldm", VK_TLSLDM) .Case("tpoff", VK_TPOFF) .Case("tprel", VK_TPREL) .Case("tlvp", VK_TLVP) .Case("tlvppage", VK_TLVPPAGE) .Case("tlvppageoff", VK_TLVPPAGEOFF) .Case("page", VK_PAGE) .Case("pageoff", VK_PAGEOFF) .Case("gotpage", VK_GOTPAGE) .Case("gotpageoff", VK_GOTPAGEOFF) .Case("imgrel", VK_COFF_IMGREL32) .Case("secrel32", VK_SECREL) .Case("size", VK_SIZE) .Case("abs8", VK_X86_ABS8) .Case("l", VK_PPC_LO) .Case("h", VK_PPC_HI) .Case("ha", VK_PPC_HA) .Case("high", VK_PPC_HIGH) .Case("higha", VK_PPC_HIGHA) .Case("higher", VK_PPC_HIGHER) .Case("highera", VK_PPC_HIGHERA) .Case("highest", VK_PPC_HIGHEST) .Case("highesta", VK_PPC_HIGHESTA) .Case("got@l", VK_PPC_GOT_LO) .Case("got@h", VK_PPC_GOT_HI) .Case("got@ha", VK_PPC_GOT_HA) .Case("local", VK_PPC_LOCAL) .Case("tocbase", VK_PPC_TOCBASE) .Case("toc", VK_PPC_TOC) .Case("toc@l", VK_PPC_TOC_LO) .Case("toc@h", VK_PPC_TOC_HI) .Case("toc@ha", VK_PPC_TOC_HA) .Case("tls", VK_PPC_TLS) .Case("dtpmod", VK_PPC_DTPMOD) .Case("tprel@l", VK_PPC_TPREL_LO) .Case("tprel@h", VK_PPC_TPREL_HI) .Case("tprel@ha", VK_PPC_TPREL_HA) .Case("tprel@high", VK_PPC_TPREL_HIGH) .Case("tprel@higha", VK_PPC_TPREL_HIGHA) .Case("tprel@higher", VK_PPC_TPREL_HIGHER) .Case("tprel@highera", VK_PPC_TPREL_HIGHERA) .Case("tprel@highest", VK_PPC_TPREL_HIGHEST) .Case("tprel@highesta", VK_PPC_TPREL_HIGHESTA) .Case("dtprel@l", VK_PPC_DTPREL_LO) .Case("dtprel@h", VK_PPC_DTPREL_HI) .Case("dtprel@ha", VK_PPC_DTPREL_HA) .Case("dtprel@high", VK_PPC_DTPREL_HIGH) .Case("dtprel@higha", VK_PPC_DTPREL_HIGHA) .Case("dtprel@higher", VK_PPC_DTPREL_HIGHER) .Case("dtprel@highera", VK_PPC_DTPREL_HIGHERA) .Case("dtprel@highest", VK_PPC_DTPREL_HIGHEST) .Case("dtprel@highesta", VK_PPC_DTPREL_HIGHESTA) .Case("got@tprel", VK_PPC_GOT_TPREL) .Case("got@tprel@l", VK_PPC_GOT_TPREL_LO) .Case("got@tprel@h", VK_PPC_GOT_TPREL_HI) .Case("got@tprel@ha", VK_PPC_GOT_TPREL_HA) .Case("got@dtprel", VK_PPC_GOT_DTPREL) .Case("got@dtprel@l", VK_PPC_GOT_DTPREL_LO) .Case("got@dtprel@h", VK_PPC_GOT_DTPREL_HI) .Case("got@dtprel@ha", VK_PPC_GOT_DTPREL_HA) .Case("got@tlsgd", VK_PPC_GOT_TLSGD) .Case("got@tlsgd@l", VK_PPC_GOT_TLSGD_LO) .Case("got@tlsgd@h", VK_PPC_GOT_TLSGD_HI) .Case("got@tlsgd@ha", VK_PPC_GOT_TLSGD_HA) .Case("got@tlsld", VK_PPC_GOT_TLSLD) .Case("got@tlsld@l", VK_PPC_GOT_TLSLD_LO) .Case("got@tlsld@h", VK_PPC_GOT_TLSLD_HI) .Case("got@tlsld@ha", VK_PPC_GOT_TLSLD_HA) .Case("gdgot", VK_Hexagon_GD_GOT) .Case("gdplt", VK_Hexagon_GD_PLT) .Case("iegot", VK_Hexagon_IE_GOT) .Case("ie", VK_Hexagon_IE) .Case("ldgot", VK_Hexagon_LD_GOT) .Case("ldplt", VK_Hexagon_LD_PLT) .Case("pcrel", VK_Hexagon_PCREL) .Case("none", VK_ARM_NONE) .Case("got_prel", VK_ARM_GOT_PREL) .Case("target1", VK_ARM_TARGET1) .Case("target2", VK_ARM_TARGET2) .Case("prel31", VK_ARM_PREL31) .Case("sbrel", VK_ARM_SBREL) .Case("tlsldo", VK_ARM_TLSLDO) .Case("lo8", VK_AVR_LO8) .Case("hi8", VK_AVR_HI8) .Case("hlo8", VK_AVR_HLO8) .Case("typeindex", VK_WASM_TYPEINDEX) .Case("tbrel", VK_WASM_TBREL) .Case("mbrel", VK_WASM_MBREL) .Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO) .Case("gotpcrel32@hi", VK_AMDGPU_GOTPCREL32_HI) .Case("rel32@lo", VK_AMDGPU_REL32_LO) .Case("rel32@hi", VK_AMDGPU_REL32_HI) .Case("rel64", VK_AMDGPU_REL64) .Case("abs32@lo", VK_AMDGPU_ABS32_LO) .Case("abs32@hi", VK_AMDGPU_ABS32_HI) .Default(VK_Invalid); } void MCSymbolRefExpr::printVariantKind(raw_ostream &OS) const { if (UseParensForSymbolVariant) OS << '(' << MCSymbolRefExpr::getVariantKindName(getKind()) << ')'; else OS << '@' << MCSymbolRefExpr::getVariantKindName(getKind()); } /* *** */ void MCTargetExpr::anchor() {} /* *** */ bool MCExpr::evaluateAsAbsolute(int64_t &Res) const { return evaluateAsAbsolute(Res, nullptr, nullptr, nullptr); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAsmLayout &Layout) const { return evaluateAsAbsolute(Res, &Layout.getAssembler(), &Layout, nullptr); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAsmLayout &Layout, const SectionAddrMap &Addrs) const { return evaluateAsAbsolute(Res, &Layout.getAssembler(), &Layout, &Addrs); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler &Asm) const { return evaluateAsAbsolute(Res, &Asm, nullptr, nullptr); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm) const { return evaluateAsAbsolute(Res, Asm, nullptr, nullptr); } bool MCExpr::evaluateKnownAbsolute(int64_t &Res, const MCAsmLayout &Layout) const { return evaluateAsAbsolute(Res, &Layout.getAssembler(), &Layout, nullptr, true); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs) const { // FIXME: The use if InSet = Addrs is a hack. Setting InSet causes us // absolutize differences across sections and that is what the MachO writer // uses Addrs for. return evaluateAsAbsolute(Res, Asm, Layout, Addrs, Addrs); } bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet) const { MCValue Value; // Fast path constants. if (const MCConstantExpr *CE = dyn_cast(this)) { Res = CE->getValue(); return true; } bool IsRelocatable = evaluateAsRelocatableImpl(Value, Asm, Layout, nullptr, Addrs, InSet); // Record the current value. Res = Value.getConstant(); return IsRelocatable && Value.isAbsolute(); } /// Helper method for \see EvaluateSymbolAdd(). static void AttemptToFoldSymbolOffsetDifference( const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet, const MCSymbolRefExpr *&A, const MCSymbolRefExpr *&B, int64_t &Addend) { if (!A || !B) return; const MCSymbol &SA = A->getSymbol(); const MCSymbol &SB = B->getSymbol(); if (SA.isUndefined() || SB.isUndefined()) return; if (!Asm->getWriter().isSymbolRefDifferenceFullyResolved(*Asm, A, B, InSet)) return; if (SA.getFragment() == SB.getFragment() && !SA.isVariable() && !SA.isUnset() && !SB.isVariable() && !SB.isUnset()) { Addend += (SA.getOffset() - SB.getOffset()); // Pointers to Thumb symbols need to have their low-bit set to allow // for interworking. if (Asm->isThumbFunc(&SA)) Addend |= 1; // If symbol is labeled as micromips, we set low-bit to ensure // correct offset in .gcc_except_table if (Asm->getBackend().isMicroMips(&SA)) Addend |= 1; // Clear the symbol expr pointers to indicate we have folded these // operands. A = B = nullptr; return; } if (!Layout) return; const MCSection &SecA = *SA.getFragment()->getParent(); const MCSection &SecB = *SB.getFragment()->getParent(); if ((&SecA != &SecB) && !Addrs) return; // Eagerly evaluate. Addend += Layout->getSymbolOffset(A->getSymbol()) - Layout->getSymbolOffset(B->getSymbol()); if (Addrs && (&SecA != &SecB)) Addend += (Addrs->lookup(&SecA) - Addrs->lookup(&SecB)); // Pointers to Thumb symbols need to have their low-bit set to allow // for interworking. if (Asm->isThumbFunc(&SA)) Addend |= 1; // If symbol is labeled as micromips, we set low-bit to ensure // correct offset in .gcc_except_table if (Asm->getBackend().isMicroMips(&SA)) Addend |= 1; // Clear the symbol expr pointers to indicate we have folded these // operands. A = B = nullptr; } +static bool canFold(const MCAssembler *Asm, const MCSymbolRefExpr *A, + const MCSymbolRefExpr *B, bool InSet) { + if (InSet) + return true; + + if (!Asm->getBackend().requiresDiffExpressionRelocations()) + return true; + + const MCSymbol &CheckSym = A ? A->getSymbol() : B->getSymbol(); + if (!CheckSym.isInSection()) + return true; + + if (!CheckSym.getSection().hasInstructions()) + return true; + + return false; +} + /// Evaluate the result of an add between (conceptually) two MCValues. /// /// This routine conceptually attempts to construct an MCValue: /// Result = (Result_A - Result_B + Result_Cst) /// from two MCValue's LHS and RHS where /// Result = LHS + RHS /// and /// Result = (LHS_A - LHS_B + LHS_Cst) + (RHS_A - RHS_B + RHS_Cst). /// /// This routine attempts to aggresively fold the operands such that the result /// is representable in an MCValue, but may not always succeed. /// /// \returns True on success, false if the result is not representable in an /// MCValue. /// NOTE: It is really important to have both the Asm and Layout arguments. /// They might look redundant, but this function can be used before layout /// is done (see the object streamer for example) and having the Asm argument /// lets us avoid relaxations early. static bool EvaluateSymbolicAdd(const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet, const MCValue &LHS, const MCSymbolRefExpr *RHS_A, const MCSymbolRefExpr *RHS_B, int64_t RHS_Cst, MCValue &Res) { // FIXME: This routine (and other evaluation parts) are *incredibly* sloppy // about dealing with modifiers. This will ultimately bite us, one day. const MCSymbolRefExpr *LHS_A = LHS.getSymA(); const MCSymbolRefExpr *LHS_B = LHS.getSymB(); int64_t LHS_Cst = LHS.getConstant(); // Fold the result constant immediately. int64_t Result_Cst = LHS_Cst + RHS_Cst; assert((!Layout || Asm) && "Must have an assembler object if layout is given!"); // If we have a layout, we can fold resolved differences. Do not do this if // the backend requires this to be emitted as individual relocations, unless // the InSet flag is set to get the current difference anyway (used for // example to calculate symbol sizes). - if (Asm && - (InSet || !Asm->getBackend().requiresDiffExpressionRelocations())) { + if (Asm && canFold(Asm, LHS_A, LHS_B, InSet)) { // First, fold out any differences which are fully resolved. By // reassociating terms in // Result = (LHS_A - LHS_B + LHS_Cst) + (RHS_A - RHS_B + RHS_Cst). // we have the four possible differences: // (LHS_A - LHS_B), // (LHS_A - RHS_B), // (RHS_A - LHS_B), // (RHS_A - RHS_B). // Since we are attempting to be as aggressive as possible about folding, we // attempt to evaluate each possible alternative. AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, LHS_A, LHS_B, Result_Cst); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, LHS_A, RHS_B, Result_Cst); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, RHS_A, LHS_B, Result_Cst); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, RHS_A, RHS_B, Result_Cst); } // We can't represent the addition or subtraction of two symbols. if ((LHS_A && RHS_A) || (LHS_B && RHS_B)) return false; // At this point, we have at most one additive symbol and one subtractive // symbol -- find them. const MCSymbolRefExpr *A = LHS_A ? LHS_A : RHS_A; const MCSymbolRefExpr *B = LHS_B ? LHS_B : RHS_B; Res = MCValue::get(A, B, Result_Cst); return true; } bool MCExpr::evaluateAsRelocatable(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { MCAssembler *Assembler = Layout ? &Layout->getAssembler() : nullptr; return evaluateAsRelocatableImpl(Res, Assembler, Layout, Fixup, nullptr, false); } bool MCExpr::evaluateAsValue(MCValue &Res, const MCAsmLayout &Layout) const { MCAssembler *Assembler = &Layout.getAssembler(); return evaluateAsRelocatableImpl(Res, Assembler, &Layout, nullptr, nullptr, true); } static bool canExpand(const MCSymbol &Sym, bool InSet) { const MCExpr *Expr = Sym.getVariableValue(); const auto *Inner = dyn_cast(Expr); if (Inner) { if (Inner->getKind() == MCSymbolRefExpr::VK_WEAKREF) return false; } if (InSet) return true; return !Sym.isInSection(); } bool MCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const MCFixup *Fixup, const SectionAddrMap *Addrs, bool InSet) const { ++stats::MCExprEvaluate; switch (getKind()) { case Target: return cast(this)->evaluateAsRelocatableImpl(Res, Layout, Fixup); case Constant: Res = MCValue::get(cast(this)->getValue()); return true; case SymbolRef: { const MCSymbolRefExpr *SRE = cast(this); const MCSymbol &Sym = SRE->getSymbol(); // Evaluate recursively if this is a variable. if (Sym.isVariable() && SRE->getKind() == MCSymbolRefExpr::VK_None && canExpand(Sym, InSet)) { bool IsMachO = SRE->hasSubsectionsViaSymbols(); if (Sym.getVariableValue()->evaluateAsRelocatableImpl( Res, Asm, Layout, Fixup, Addrs, InSet || IsMachO)) { if (!IsMachO) return true; const MCSymbolRefExpr *A = Res.getSymA(); const MCSymbolRefExpr *B = Res.getSymB(); // FIXME: This is small hack. Given // a = b + 4 // .long a // the OS X assembler will completely drop the 4. We should probably // include it in the relocation or produce an error if that is not // possible. // Allow constant expressions. if (!A && !B) return true; // Allows aliases with zero offset. if (Res.getConstant() == 0 && (!A || !B)) return true; } } Res = MCValue::get(SRE, nullptr, 0); return true; } case Unary: { const MCUnaryExpr *AUE = cast(this); MCValue Value; if (!AUE->getSubExpr()->evaluateAsRelocatableImpl(Value, Asm, Layout, Fixup, Addrs, InSet)) return false; switch (AUE->getOpcode()) { case MCUnaryExpr::LNot: if (!Value.isAbsolute()) return false; Res = MCValue::get(!Value.getConstant()); break; case MCUnaryExpr::Minus: /// -(a - b + const) ==> (b - a - const) if (Value.getSymA() && !Value.getSymB()) return false; // The cast avoids undefined behavior if the constant is INT64_MIN. Res = MCValue::get(Value.getSymB(), Value.getSymA(), -(uint64_t)Value.getConstant()); break; case MCUnaryExpr::Not: if (!Value.isAbsolute()) return false; Res = MCValue::get(~Value.getConstant()); break; case MCUnaryExpr::Plus: Res = Value; break; } return true; } case Binary: { const MCBinaryExpr *ABE = cast(this); MCValue LHSValue, RHSValue; if (!ABE->getLHS()->evaluateAsRelocatableImpl(LHSValue, Asm, Layout, Fixup, Addrs, InSet) || !ABE->getRHS()->evaluateAsRelocatableImpl(RHSValue, Asm, Layout, Fixup, Addrs, InSet)) { // Check if both are Target Expressions, see if we can compare them. if (const MCTargetExpr *L = dyn_cast(ABE->getLHS())) if (const MCTargetExpr *R = cast(ABE->getRHS())) { switch (ABE->getOpcode()) { case MCBinaryExpr::EQ: Res = MCValue::get((L->isEqualTo(R)) ? -1 : 0); return true; case MCBinaryExpr::NE: Res = MCValue::get((R->isEqualTo(R)) ? 0 : -1); return true; default: break; } } return false; } // We only support a few operations on non-constant expressions, handle // those first. if (!LHSValue.isAbsolute() || !RHSValue.isAbsolute()) { switch (ABE->getOpcode()) { default: return false; case MCBinaryExpr::Sub: // Negate RHS and add. // The cast avoids undefined behavior if the constant is INT64_MIN. return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, RHSValue.getSymB(), RHSValue.getSymA(), -(uint64_t)RHSValue.getConstant(), Res); case MCBinaryExpr::Add: return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, RHSValue.getSymA(), RHSValue.getSymB(), RHSValue.getConstant(), Res); } } // FIXME: We need target hooks for the evaluation. It may be limited in // width, and gas defines the result of comparisons differently from // Apple as. int64_t LHS = LHSValue.getConstant(), RHS = RHSValue.getConstant(); int64_t Result = 0; auto Op = ABE->getOpcode(); switch (Op) { case MCBinaryExpr::AShr: Result = LHS >> RHS; break; case MCBinaryExpr::Add: Result = LHS + RHS; break; case MCBinaryExpr::And: Result = LHS & RHS; break; case MCBinaryExpr::Div: case MCBinaryExpr::Mod: // Handle division by zero. gas just emits a warning and keeps going, // we try to be stricter. // FIXME: Currently the caller of this function has no way to understand // we're bailing out because of 'division by zero'. Therefore, it will // emit a 'expected relocatable expression' error. It would be nice to // change this code to emit a better diagnostic. if (RHS == 0) return false; if (ABE->getOpcode() == MCBinaryExpr::Div) Result = LHS / RHS; else Result = LHS % RHS; break; case MCBinaryExpr::EQ: Result = LHS == RHS; break; case MCBinaryExpr::GT: Result = LHS > RHS; break; case MCBinaryExpr::GTE: Result = LHS >= RHS; break; case MCBinaryExpr::LAnd: Result = LHS && RHS; break; case MCBinaryExpr::LOr: Result = LHS || RHS; break; case MCBinaryExpr::LShr: Result = uint64_t(LHS) >> uint64_t(RHS); break; case MCBinaryExpr::LT: Result = LHS < RHS; break; case MCBinaryExpr::LTE: Result = LHS <= RHS; break; case MCBinaryExpr::Mul: Result = LHS * RHS; break; case MCBinaryExpr::NE: Result = LHS != RHS; break; case MCBinaryExpr::Or: Result = LHS | RHS; break; case MCBinaryExpr::Shl: Result = uint64_t(LHS) << uint64_t(RHS); break; case MCBinaryExpr::Sub: Result = LHS - RHS; break; case MCBinaryExpr::Xor: Result = LHS ^ RHS; break; } switch (Op) { default: Res = MCValue::get(Result); break; case MCBinaryExpr::EQ: case MCBinaryExpr::GT: case MCBinaryExpr::GTE: case MCBinaryExpr::LT: case MCBinaryExpr::LTE: case MCBinaryExpr::NE: // A comparison operator returns a -1 if true and 0 if false. Res = MCValue::get(Result ? -1 : 0); break; } return true; } } llvm_unreachable("Invalid assembly expression kind!"); } MCFragment *MCExpr::findAssociatedFragment() const { switch (getKind()) { case Target: // We never look through target specific expressions. return cast(this)->findAssociatedFragment(); case Constant: return MCSymbol::AbsolutePseudoFragment; case SymbolRef: { const MCSymbolRefExpr *SRE = cast(this); const MCSymbol &Sym = SRE->getSymbol(); return Sym.getFragment(); } case Unary: return cast(this)->getSubExpr()->findAssociatedFragment(); case Binary: { const MCBinaryExpr *BE = cast(this); MCFragment *LHS_F = BE->getLHS()->findAssociatedFragment(); MCFragment *RHS_F = BE->getRHS()->findAssociatedFragment(); // If either is absolute, return the other. if (LHS_F == MCSymbol::AbsolutePseudoFragment) return RHS_F; if (RHS_F == MCSymbol::AbsolutePseudoFragment) return LHS_F; // Not always correct, but probably the best we can do without more context. if (BE->getOpcode() == MCBinaryExpr::Sub) return MCSymbol::AbsolutePseudoFragment; // Otherwise, return the first non-null fragment. return LHS_F ? LHS_F : RHS_F; } } llvm_unreachable("Invalid assembly expression kind!"); } Index: head/contrib/llvm/lib/Object/RelocationResolver.cpp =================================================================== --- head/contrib/llvm/lib/Object/RelocationResolver.cpp (revision 354468) +++ head/contrib/llvm/lib/Object/RelocationResolver.cpp (revision 354469) @@ -1,550 +1,556 @@ //===- RelocationResolver.cpp ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines utilities to resolve relocations in object files. // //===----------------------------------------------------------------------===// #include "llvm/Object/RelocationResolver.h" namespace llvm { namespace object { static int64_t getELFAddend(RelocationRef R) { Expected AddendOrErr = ELFRelocationRef(R).getAddend(); handleAllErrors(AddendOrErr.takeError(), [](const ErrorInfoBase &EI) { report_fatal_error(EI.message()); }); return *AddendOrErr; } static bool supportsX86_64(uint64_t Type) { switch (Type) { case ELF::R_X86_64_NONE: case ELF::R_X86_64_64: case ELF::R_X86_64_DTPOFF32: case ELF::R_X86_64_DTPOFF64: case ELF::R_X86_64_PC32: case ELF::R_X86_64_32: case ELF::R_X86_64_32S: return true; default: return false; } } static uint64_t resolveX86_64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_X86_64_NONE: return A; case ELF::R_X86_64_64: case ELF::R_X86_64_DTPOFF32: case ELF::R_X86_64_DTPOFF64: return S + getELFAddend(R); case ELF::R_X86_64_PC32: return S + getELFAddend(R) - R.getOffset(); case ELF::R_X86_64_32: case ELF::R_X86_64_32S: return (S + getELFAddend(R)) & 0xFFFFFFFF; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsAArch64(uint64_t Type) { switch (Type) { case ELF::R_AARCH64_ABS32: case ELF::R_AARCH64_ABS64: return true; default: return false; } } static uint64_t resolveAArch64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_AARCH64_ABS32: return (S + getELFAddend(R)) & 0xFFFFFFFF; case ELF::R_AARCH64_ABS64: return S + getELFAddend(R); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsBPF(uint64_t Type) { switch (Type) { case ELF::R_BPF_64_32: case ELF::R_BPF_64_64: return true; default: return false; } } static uint64_t resolveBPF(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_BPF_64_32: return (S + A) & 0xFFFFFFFF; case ELF::R_BPF_64_64: return S + A; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsMips64(uint64_t Type) { switch (Type) { case ELF::R_MIPS_32: case ELF::R_MIPS_64: case ELF::R_MIPS_TLS_DTPREL64: return true; default: return false; } } static uint64_t resolveMips64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_MIPS_32: return (S + getELFAddend(R)) & 0xFFFFFFFF; case ELF::R_MIPS_64: return S + getELFAddend(R); case ELF::R_MIPS_TLS_DTPREL64: return S + getELFAddend(R) - 0x8000; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsPPC64(uint64_t Type) { switch (Type) { case ELF::R_PPC64_ADDR32: case ELF::R_PPC64_ADDR64: return true; default: return false; } } static uint64_t resolvePPC64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_PPC64_ADDR32: return (S + getELFAddend(R)) & 0xFFFFFFFF; case ELF::R_PPC64_ADDR64: return S + getELFAddend(R); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsSystemZ(uint64_t Type) { switch (Type) { case ELF::R_390_32: case ELF::R_390_64: return true; default: return false; } } static uint64_t resolveSystemZ(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_390_32: return (S + getELFAddend(R)) & 0xFFFFFFFF; case ELF::R_390_64: return S + getELFAddend(R); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsSparc64(uint64_t Type) { switch (Type) { case ELF::R_SPARC_32: case ELF::R_SPARC_64: case ELF::R_SPARC_UA32: case ELF::R_SPARC_UA64: return true; default: return false; } } static uint64_t resolveSparc64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_SPARC_32: case ELF::R_SPARC_64: case ELF::R_SPARC_UA32: case ELF::R_SPARC_UA64: return S + getELFAddend(R); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsAmdgpu(uint64_t Type) { switch (Type) { case ELF::R_AMDGPU_ABS32: case ELF::R_AMDGPU_ABS64: return true; default: return false; } } static uint64_t resolveAmdgpu(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_AMDGPU_ABS32: case ELF::R_AMDGPU_ABS64: return S + getELFAddend(R); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsX86(uint64_t Type) { switch (Type) { case ELF::R_386_NONE: case ELF::R_386_32: case ELF::R_386_PC32: return true; default: return false; } } static uint64_t resolveX86(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_386_NONE: return A; case ELF::R_386_32: return S + A; case ELF::R_386_PC32: return S - R.getOffset() + A; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsPPC32(uint64_t Type) { return Type == ELF::R_PPC_ADDR32; } static uint64_t resolvePPC32(RelocationRef R, uint64_t S, uint64_t A) { if (R.getType() == ELF::R_PPC_ADDR32) return (S + getELFAddend(R)) & 0xFFFFFFFF; llvm_unreachable("Invalid relocation type"); } static bool supportsARM(uint64_t Type) { return Type == ELF::R_ARM_ABS32; } static uint64_t resolveARM(RelocationRef R, uint64_t S, uint64_t A) { if (R.getType() == ELF::R_ARM_ABS32) return (S + A) & 0xFFFFFFFF; llvm_unreachable("Invalid relocation type"); } static bool supportsAVR(uint64_t Type) { switch (Type) { case ELF::R_AVR_16: case ELF::R_AVR_32: return true; default: return false; } } static uint64_t resolveAVR(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case ELF::R_AVR_16: return (S + getELFAddend(R)) & 0xFFFF; case ELF::R_AVR_32: return (S + getELFAddend(R)) & 0xFFFFFFFF; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsLanai(uint64_t Type) { return Type == ELF::R_LANAI_32; } static uint64_t resolveLanai(RelocationRef R, uint64_t S, uint64_t A) { if (R.getType() == ELF::R_LANAI_32) return (S + getELFAddend(R)) & 0xFFFFFFFF; llvm_unreachable("Invalid relocation type"); } static bool supportsMips32(uint64_t Type) { switch (Type) { case ELF::R_MIPS_32: case ELF::R_MIPS_TLS_DTPREL32: return true; default: return false; } } static uint64_t resolveMips32(RelocationRef R, uint64_t S, uint64_t A) { // FIXME: Take in account implicit addends to get correct results. uint32_t Rel = R.getType(); if (Rel == ELF::R_MIPS_32) return (S + A) & 0xFFFFFFFF; if (Rel == ELF::R_MIPS_TLS_DTPREL32) return (S + A) & 0xFFFFFFFF; llvm_unreachable("Invalid relocation type"); } static bool supportsSparc32(uint64_t Type) { switch (Type) { case ELF::R_SPARC_32: case ELF::R_SPARC_UA32: return true; default: return false; } } static uint64_t resolveSparc32(RelocationRef R, uint64_t S, uint64_t A) { uint32_t Rel = R.getType(); if (Rel == ELF::R_SPARC_32 || Rel == ELF::R_SPARC_UA32) return S + getELFAddend(R); return A; } static bool supportsHexagon(uint64_t Type) { return Type == ELF::R_HEX_32; } static uint64_t resolveHexagon(RelocationRef R, uint64_t S, uint64_t A) { if (R.getType() == ELF::R_HEX_32) return S + getELFAddend(R); llvm_unreachable("Invalid relocation type"); } static bool supportsRISCV(uint64_t Type) { switch (Type) { case ELF::R_RISCV_NONE: case ELF::R_RISCV_32: case ELF::R_RISCV_64: + case ELF::R_RISCV_SET6: + case ELF::R_RISCV_SUB6: case ELF::R_RISCV_ADD8: case ELF::R_RISCV_SUB8: case ELF::R_RISCV_ADD16: case ELF::R_RISCV_SUB16: case ELF::R_RISCV_ADD32: case ELF::R_RISCV_SUB32: case ELF::R_RISCV_ADD64: case ELF::R_RISCV_SUB64: return true; default: return false; } } static uint64_t resolveRISCV(RelocationRef R, uint64_t S, uint64_t A) { int64_t RA = getELFAddend(R); switch (R.getType()) { case ELF::R_RISCV_NONE: return A; case ELF::R_RISCV_32: return (S + RA) & 0xFFFFFFFF; case ELF::R_RISCV_64: return S + RA; + case ELF::R_RISCV_SET6: + return (A + (S + RA)) & 0xFF; + case ELF::R_RISCV_SUB6: + return (A - (S + RA)) & 0xFF; case ELF::R_RISCV_ADD8: return (A + (S + RA)) & 0xFF; case ELF::R_RISCV_SUB8: return (A - (S + RA)) & 0xFF; case ELF::R_RISCV_ADD16: return (A + (S + RA)) & 0xFFFF; case ELF::R_RISCV_SUB16: return (A - (S + RA)) & 0xFFFF; case ELF::R_RISCV_ADD32: return (A + (S + RA)) & 0xFFFFFFFF; case ELF::R_RISCV_SUB32: return (A - (S + RA)) & 0xFFFFFFFF; case ELF::R_RISCV_ADD64: return (A + (S + RA)); case ELF::R_RISCV_SUB64: return (A - (S + RA)); default: llvm_unreachable("Invalid relocation type"); } } static bool supportsCOFFX86(uint64_t Type) { switch (Type) { case COFF::IMAGE_REL_I386_SECREL: case COFF::IMAGE_REL_I386_DIR32: return true; default: return false; } } static uint64_t resolveCOFFX86(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case COFF::IMAGE_REL_I386_SECREL: case COFF::IMAGE_REL_I386_DIR32: return (S + A) & 0xFFFFFFFF; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsCOFFX86_64(uint64_t Type) { switch (Type) { case COFF::IMAGE_REL_AMD64_SECREL: case COFF::IMAGE_REL_AMD64_ADDR64: return true; default: return false; } } static uint64_t resolveCOFFX86_64(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case COFF::IMAGE_REL_AMD64_SECREL: return (S + A) & 0xFFFFFFFF; case COFF::IMAGE_REL_AMD64_ADDR64: return S + A; default: llvm_unreachable("Invalid relocation type"); } } static bool supportsMachOX86_64(uint64_t Type) { return Type == MachO::X86_64_RELOC_UNSIGNED; } static uint64_t resolveMachOX86_64(RelocationRef R, uint64_t S, uint64_t A) { if (R.getType() == MachO::X86_64_RELOC_UNSIGNED) return S; llvm_unreachable("Invalid relocation type"); } static bool supportsWasm32(uint64_t Type) { switch (Type) { case wasm::R_WASM_FUNCTION_INDEX_LEB: case wasm::R_WASM_TABLE_INDEX_SLEB: case wasm::R_WASM_TABLE_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_TYPE_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_FUNCTION_OFFSET_I32: case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_EVENT_INDEX_LEB: return true; default: return false; } } static uint64_t resolveWasm32(RelocationRef R, uint64_t S, uint64_t A) { switch (R.getType()) { case wasm::R_WASM_FUNCTION_INDEX_LEB: case wasm::R_WASM_TABLE_INDEX_SLEB: case wasm::R_WASM_TABLE_INDEX_I32: case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_I32: case wasm::R_WASM_TYPE_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_FUNCTION_OFFSET_I32: case wasm::R_WASM_SECTION_OFFSET_I32: case wasm::R_WASM_EVENT_INDEX_LEB: // For wasm section, its offset at 0 -- ignoring Value return A; default: llvm_unreachable("Invalid relocation type"); } } std::pair getRelocationResolver(const ObjectFile &Obj) { if (Obj.isCOFF()) { if (Obj.getBytesInAddress() == 8) return {supportsCOFFX86_64, resolveCOFFX86_64}; return {supportsCOFFX86, resolveCOFFX86}; } else if (Obj.isELF()) { if (Obj.getBytesInAddress() == 8) { switch (Obj.getArch()) { case Triple::x86_64: return {supportsX86_64, resolveX86_64}; case Triple::aarch64: case Triple::aarch64_be: return {supportsAArch64, resolveAArch64}; case Triple::bpfel: case Triple::bpfeb: return {supportsBPF, resolveBPF}; case Triple::mips64el: case Triple::mips64: return {supportsMips64, resolveMips64}; case Triple::ppc64le: case Triple::ppc64: return {supportsPPC64, resolvePPC64}; case Triple::systemz: return {supportsSystemZ, resolveSystemZ}; case Triple::sparcv9: return {supportsSparc64, resolveSparc64}; case Triple::amdgcn: return {supportsAmdgpu, resolveAmdgpu}; case Triple::riscv64: return {supportsRISCV, resolveRISCV}; default: return {nullptr, nullptr}; } } // 32-bit object file assert(Obj.getBytesInAddress() == 4 && "Invalid word size in object file"); switch (Obj.getArch()) { case Triple::x86: return {supportsX86, resolveX86}; case Triple::ppc: return {supportsPPC32, resolvePPC32}; case Triple::arm: case Triple::armeb: return {supportsARM, resolveARM}; case Triple::avr: return {supportsAVR, resolveAVR}; case Triple::lanai: return {supportsLanai, resolveLanai}; case Triple::mipsel: case Triple::mips: return {supportsMips32, resolveMips32}; case Triple::sparc: return {supportsSparc32, resolveSparc32}; case Triple::hexagon: return {supportsHexagon, resolveHexagon}; case Triple::riscv32: return {supportsRISCV, resolveRISCV}; default: return {nullptr, nullptr}; } } else if (Obj.isMachO()) { if (Obj.getArch() == Triple::x86_64) return {supportsMachOX86_64, resolveMachOX86_64}; return {nullptr, nullptr}; } else if (Obj.isWasm()) { if (Obj.getArch() == Triple::wasm32) return {supportsWasm32, resolveWasm32}; return {nullptr, nullptr}; } llvm_unreachable("Invalid object file"); } } // namespace object } // namespace llvm Index: head/contrib/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (revision 354469) @@ -1,1793 +1,1870 @@ //===-- RISCVAsmParser.cpp - Parse RISCV assembly to MCInst instructions --===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MCTargetDesc/RISCVAsmBackend.h" #include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "MCTargetDesc/RISCVTargetStreamer.h" #include "TargetInfo/RISCVTargetInfo.h" #include "Utils/RISCVBaseInfo.h" #include "Utils/RISCVMatInt.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Casting.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/TargetRegistry.h" #include using namespace llvm; // Include the auto-generated portion of the compress emitter. #define GEN_COMPRESS_INSTR #include "RISCVGenCompressInstEmitter.inc" namespace { struct RISCVOperand; class RISCVAsmParser : public MCTargetAsmParser { SmallVector FeatureBitStack; SMLoc getLoc() const { return getParser().getTok().getLoc(); } bool isRV64() const { return getSTI().hasFeature(RISCV::Feature64Bit); } bool isRV32E() const { return getSTI().hasFeature(RISCV::FeatureRV32E); } RISCVTargetStreamer &getTargetStreamer() { MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); return static_cast(TS); } unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t Upper, Twine Msg); bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) override; bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; bool ParseDirective(AsmToken DirectiveID) override; // Helper to actually emit an instruction to the MCStreamer. Also, when // possible, compression of the instruction is performed. void emitToStreamer(MCStreamer &S, const MCInst &Inst); // Helper to emit a combination of LUI, ADDI(W), and SLLI instructions that // synthesize the desired immedate value into the destination register. void emitLoadImm(unsigned DestReg, int64_t Value, MCStreamer &Out); // Helper to emit a combination of AUIPC and SecondOpcode. Used to implement // helpers such as emitLoadLocalAddress and emitLoadAddress. void emitAuipcInstPair(MCOperand DestReg, MCOperand TmpReg, const MCExpr *Symbol, RISCVMCExpr::VariantKind VKHi, unsigned SecondOpcode, SMLoc IDLoc, MCStreamer &Out); // Helper to emit pseudo instruction "lla" used in PC-rel addressing. void emitLoadLocalAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); // Helper to emit pseudo instruction "la" used in GOT/PC-rel addressing. void emitLoadAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); // Helper to emit pseudo instruction "la.tls.ie" used in initial-exec TLS // addressing. void emitLoadTLSIEAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); // Helper to emit pseudo instruction "la.tls.gd" used in global-dynamic TLS // addressing. void emitLoadTLSGDAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); // Helper to emit pseudo load/store instruction with a symbol. void emitLoadStoreSymbol(MCInst &Inst, unsigned Opcode, SMLoc IDLoc, MCStreamer &Out, bool HasTmpReg); // Checks that a PseudoAddTPRel is using x4/tp in its second input operand. // Enforcing this using a restricted register class for the second input // operand of PseudoAddTPRel results in a poor diagnostic due to the fact // 'add' is an overloaded mnemonic. bool checkPseudoAddTPRel(MCInst &Inst, OperandVector &Operands); /// Helper for processing MC instructions that have been successfully matched /// by MatchAndEmitInstruction. Modifications to the emitted instructions, /// like the expansion of pseudo instructions (e.g., "li"), can be performed /// in this method. bool processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands, MCStreamer &Out); // Auto-generated instruction matching functions #define GET_ASSEMBLER_HEADER #include "RISCVGenAsmMatcher.inc" OperandMatchResultTy parseCSRSystemRegister(OperandVector &Operands); OperandMatchResultTy parseImmediate(OperandVector &Operands); OperandMatchResultTy parseRegister(OperandVector &Operands, bool AllowParens = false); OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands); + OperandMatchResultTy parseAtomicMemOp(OperandVector &Operands); OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); OperandMatchResultTy parseBareSymbol(OperandVector &Operands); OperandMatchResultTy parseCallSymbol(OperandVector &Operands); OperandMatchResultTy parseJALOffset(OperandVector &Operands); bool parseOperand(OperandVector &Operands, StringRef Mnemonic); bool parseDirectiveOption(); void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().getFeatureBits()[Feature])) { MCSubtargetInfo &STI = copySTI(); setAvailableFeatures( ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); } } void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { if (getSTI().getFeatureBits()[Feature]) { MCSubtargetInfo &STI = copySTI(); setAvailableFeatures( ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); } } void pushFeatureBits() { FeatureBitStack.push_back(getSTI().getFeatureBits()); } bool popFeatureBits() { if (FeatureBitStack.empty()) return true; FeatureBitset FeatureBits = FeatureBitStack.pop_back_val(); copySTI().setFeatureBits(FeatureBits); setAvailableFeatures(ComputeAvailableFeatures(FeatureBits)); return false; } public: enum RISCVMatchResultTy { Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, #define GET_OPERAND_DIAGNOSTIC_TYPES #include "RISCVGenAsmMatcher.inc" #undef GET_OPERAND_DIAGNOSTIC_TYPES }; static bool classifySymbolRef(const MCExpr *Expr, RISCVMCExpr::VariantKind &Kind, int64_t &Addend); RISCVAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, STI, MII) { Parser.addAliasForDirective(".half", ".2byte"); Parser.addAliasForDirective(".hword", ".2byte"); Parser.addAliasForDirective(".word", ".4byte"); Parser.addAliasForDirective(".dword", ".8byte"); setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); } }; /// RISCVOperand - Instances of this class represent a parsed machine /// instruction struct RISCVOperand : public MCParsedAsmOperand { enum KindTy { Token, Register, Immediate, SystemRegister } Kind; bool IsRV64; struct RegOp { unsigned RegNum; }; struct ImmOp { const MCExpr *Val; }; struct SysRegOp { const char *Data; unsigned Length; unsigned Encoding; // FIXME: Add the Encoding parsed fields as needed for checks, // e.g.: read/write or user/supervisor/machine privileges. }; SMLoc StartLoc, EndLoc; union { StringRef Tok; RegOp Reg; ImmOp Imm; struct SysRegOp SysReg; }; RISCVOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} public: RISCVOperand(const RISCVOperand &o) : MCParsedAsmOperand() { Kind = o.Kind; IsRV64 = o.IsRV64; StartLoc = o.StartLoc; EndLoc = o.EndLoc; switch (Kind) { case Register: Reg = o.Reg; break; case Immediate: Imm = o.Imm; break; case Token: Tok = o.Tok; break; case SystemRegister: SysReg = o.SysReg; break; } } bool isToken() const override { return Kind == Token; } bool isReg() const override { return Kind == Register; } bool isImm() const override { return Kind == Immediate; } bool isMem() const override { return false; } bool isSystemRegister() const { return Kind == SystemRegister; } static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm, RISCVMCExpr::VariantKind &VK) { if (auto *RE = dyn_cast(Expr)) { VK = RE->getKind(); return RE->evaluateAsConstant(Imm); } if (auto CE = dyn_cast(Expr)) { VK = RISCVMCExpr::VK_RISCV_None; Imm = CE->getValue(); return true; } return false; } // True if operand is a symbol with no modifiers, or a constant with no // modifiers and isShiftedInt(Op). template bool isBareSimmNLsb0() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); bool IsValid; if (!IsConstantImm) IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); else IsValid = isShiftedInt(Imm); return IsValid && VK == RISCVMCExpr::VK_RISCV_None; } // Predicate methods for AsmOperands defined in RISCVInstrInfo.td bool isBareSymbol() const { int64_t Imm; RISCVMCExpr::VariantKind VK; // Must be of 'immediate' type but not a constant. if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) return false; return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isCallSymbol() const { int64_t Imm; RISCVMCExpr::VariantKind VK; // Must be of 'immediate' type but not a constant. if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) return false; return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && (VK == RISCVMCExpr::VK_RISCV_CALL || VK == RISCVMCExpr::VK_RISCV_CALL_PLT); } bool isTPRelAddSymbol() const { int64_t Imm; RISCVMCExpr::VariantKind VK; // Must be of 'immediate' type but not a constant. if (!isImm() || evaluateConstantImm(getImm(), Imm, VK)) return false; return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && VK == RISCVMCExpr::VK_RISCV_TPREL_ADD; } bool isCSRSystemRegister() const { return isSystemRegister(); } /// Return true if the operand is a valid for the fence instruction e.g. /// ('iorw'). bool isFenceArg() const { if (!isImm()) return false; const MCExpr *Val = getImm(); auto *SVal = dyn_cast(Val); if (!SVal || SVal->getKind() != MCSymbolRefExpr::VK_None) return false; StringRef Str = SVal->getSymbol().getName(); // Letters must be unique, taken from 'iorw', and in ascending order. This // holds as long as each individual character is one of 'iorw' and is // greater than the previous character. char Prev = '\0'; for (char c : Str) { if (c != 'i' && c != 'o' && c != 'r' && c != 'w') return false; if (c <= Prev) return false; Prev = c; } return true; } /// Return true if the operand is a valid floating point rounding mode. bool isFRMArg() const { if (!isImm()) return false; const MCExpr *Val = getImm(); auto *SVal = dyn_cast(Val); if (!SVal || SVal->getKind() != MCSymbolRefExpr::VK_None) return false; StringRef Str = SVal->getSymbol().getName(); return RISCVFPRndMode::stringToRoundingMode(Str) != RISCVFPRndMode::Invalid; } bool isImmXLenLI() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); if (VK == RISCVMCExpr::VK_RISCV_LO || VK == RISCVMCExpr::VK_RISCV_PCREL_LO) return true; // Given only Imm, ensuring that the actually specified constant is either // a signed or unsigned 64-bit number is unfortunately impossible. bool IsInRange = isRV64() ? true : isInt<32>(Imm) || isUInt<32>(Imm); return IsConstantImm && IsInRange && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImmLog2XLen() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; if (!evaluateConstantImm(getImm(), Imm, VK) || VK != RISCVMCExpr::VK_RISCV_None) return false; return (isRV64() && isUInt<6>(Imm)) || isUInt<5>(Imm); } bool isUImmLog2XLenNonZero() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; if (!evaluateConstantImm(getImm(), Imm, VK) || VK != RISCVMCExpr::VK_RISCV_None) return false; if (Imm == 0) return false; return (isRV64() && isUInt<6>(Imm)) || isUInt<5>(Imm); } bool isUImm5() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm5NonZero() const { int64_t Imm; RISCVMCExpr::VariantKind VK; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isUInt<5>(Imm) && (Imm != 0) && VK == RISCVMCExpr::VK_RISCV_None; } bool isSImm6() const { if (!isImm()) return false; RISCVMCExpr::VariantKind VK; int64_t Imm; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isInt<6>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isSImm6NonZero() const { if (!isImm()) return false; RISCVMCExpr::VariantKind VK; int64_t Imm; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isInt<6>(Imm) && (Imm != 0) && VK == RISCVMCExpr::VK_RISCV_None; } bool isCLUIImm() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && (Imm != 0) && (isUInt<5>(Imm) || (Imm >= 0xfffe0 && Imm <= 0xfffff)) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm7Lsb00() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isShiftedUInt<5, 2>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm8Lsb00() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isShiftedUInt<6, 2>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm8Lsb000() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isShiftedUInt<5, 3>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isSImm9Lsb0() const { return isBareSimmNLsb0<9>(); } bool isUImm9Lsb000() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isShiftedUInt<6, 3>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm10Lsb00NonZero() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && isShiftedUInt<8, 2>(Imm) && (Imm != 0) && VK == RISCVMCExpr::VK_RISCV_None; } bool isSImm12() const { RISCVMCExpr::VariantKind VK; int64_t Imm; bool IsValid; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); if (!IsConstantImm) IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); else IsValid = isInt<12>(Imm); return IsValid && ((IsConstantImm && VK == RISCVMCExpr::VK_RISCV_None) || VK == RISCVMCExpr::VK_RISCV_LO || VK == RISCVMCExpr::VK_RISCV_PCREL_LO || VK == RISCVMCExpr::VK_RISCV_TPREL_LO); } bool isSImm12Lsb0() const { return isBareSimmNLsb0<12>(); } bool isSImm13Lsb0() const { return isBareSimmNLsb0<13>(); } bool isSImm10Lsb0000NonZero() const { if (!isImm()) return false; int64_t Imm; RISCVMCExpr::VariantKind VK; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); return IsConstantImm && (Imm != 0) && isShiftedInt<6, 4>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } bool isUImm20LUI() const { RISCVMCExpr::VariantKind VK; int64_t Imm; bool IsValid; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); if (!IsConstantImm) { IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); return IsValid && (VK == RISCVMCExpr::VK_RISCV_HI || VK == RISCVMCExpr::VK_RISCV_TPREL_HI); } else { return isUInt<20>(Imm) && (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_HI || VK == RISCVMCExpr::VK_RISCV_TPREL_HI); } } bool isUImm20AUIPC() const { RISCVMCExpr::VariantKind VK; int64_t Imm; bool IsValid; if (!isImm()) return false; bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); if (!IsConstantImm) { IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm); return IsValid && (VK == RISCVMCExpr::VK_RISCV_PCREL_HI || VK == RISCVMCExpr::VK_RISCV_GOT_HI || VK == RISCVMCExpr::VK_RISCV_TLS_GOT_HI || VK == RISCVMCExpr::VK_RISCV_TLS_GD_HI); } else { return isUInt<20>(Imm) && (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_PCREL_HI || VK == RISCVMCExpr::VK_RISCV_GOT_HI || VK == RISCVMCExpr::VK_RISCV_TLS_GOT_HI || VK == RISCVMCExpr::VK_RISCV_TLS_GD_HI); } } bool isSImm21Lsb0JAL() const { return isBareSimmNLsb0<21>(); } + bool isImmZero() const { + if (!isImm()) + return false; + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && (Imm == 0) && VK == RISCVMCExpr::VK_RISCV_None; + } + /// getStartLoc - Gets location of the first token of this operand SMLoc getStartLoc() const override { return StartLoc; } /// getEndLoc - Gets location of the last token of this operand SMLoc getEndLoc() const override { return EndLoc; } /// True if this operand is for an RV64 instruction bool isRV64() const { return IsRV64; } unsigned getReg() const override { assert(Kind == Register && "Invalid type access!"); return Reg.RegNum; } StringRef getSysReg() const { assert(Kind == SystemRegister && "Invalid access!"); return StringRef(SysReg.Data, SysReg.Length); } const MCExpr *getImm() const { assert(Kind == Immediate && "Invalid type access!"); return Imm.Val; } StringRef getToken() const { assert(Kind == Token && "Invalid type access!"); return Tok; } void print(raw_ostream &OS) const override { switch (Kind) { case Immediate: OS << *getImm(); break; case Register: OS << ""; break; case Token: OS << "'" << getToken() << "'"; break; case SystemRegister: OS << "'; break; } } static std::unique_ptr createToken(StringRef Str, SMLoc S, bool IsRV64) { auto Op = make_unique(Token); Op->Tok = Str; Op->StartLoc = S; Op->EndLoc = S; Op->IsRV64 = IsRV64; return Op; } static std::unique_ptr createReg(unsigned RegNo, SMLoc S, SMLoc E, bool IsRV64) { auto Op = make_unique(Register); Op->Reg.RegNum = RegNo; Op->StartLoc = S; Op->EndLoc = E; Op->IsRV64 = IsRV64; return Op; } static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, SMLoc E, bool IsRV64) { auto Op = make_unique(Immediate); Op->Imm.Val = Val; Op->StartLoc = S; Op->EndLoc = E; Op->IsRV64 = IsRV64; return Op; } static std::unique_ptr createSysReg(StringRef Str, SMLoc S, unsigned Encoding, bool IsRV64) { auto Op = make_unique(SystemRegister); Op->SysReg.Data = Str.data(); Op->SysReg.Length = Str.size(); Op->SysReg.Encoding = Encoding; Op->StartLoc = S; Op->IsRV64 = IsRV64; return Op; } void addExpr(MCInst &Inst, const MCExpr *Expr) const { assert(Expr && "Expr shouldn't be null!"); int64_t Imm = 0; RISCVMCExpr::VariantKind VK; bool IsConstant = evaluateConstantImm(Expr, Imm, VK); if (IsConstant) Inst.addOperand(MCOperand::createImm(Imm)); else Inst.addOperand(MCOperand::createExpr(Expr)); } // Used by the TableGen Code void addRegOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createReg(getReg())); } void addImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); addExpr(Inst, getImm()); } void addFenceArgOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // isFenceArg has validated the operand, meaning this cast is safe auto SE = cast(getImm()); unsigned Imm = 0; for (char c : SE->getSymbol().getName()) { switch (c) { default: llvm_unreachable("FenceArg must contain only [iorw]"); case 'i': Imm |= RISCVFenceField::I; break; case 'o': Imm |= RISCVFenceField::O; break; case 'r': Imm |= RISCVFenceField::R; break; case 'w': Imm |= RISCVFenceField::W; break; } } Inst.addOperand(MCOperand::createImm(Imm)); } void addCSRSystemRegisterOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(SysReg.Encoding)); } // Returns the rounding mode represented by this RISCVOperand. Should only // be called after checking isFRMArg. RISCVFPRndMode::RoundingMode getRoundingMode() const { // isFRMArg has validated the operand, meaning this cast is safe. auto SE = cast(getImm()); RISCVFPRndMode::RoundingMode FRM = RISCVFPRndMode::stringToRoundingMode(SE->getSymbol().getName()); assert(FRM != RISCVFPRndMode::Invalid && "Invalid rounding mode"); return FRM; } void addFRMArgOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(getRoundingMode())); } }; } // end anonymous namespace. #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #include "RISCVGenAsmMatcher.inc" // Return the matching FPR64 register for the given FPR32. // FIXME: Ideally this function could be removed in favour of using // information from TableGen. unsigned convertFPR32ToFPR64(unsigned Reg) { switch (Reg) { default: llvm_unreachable("Not a recognised FPR32 register"); case RISCV::F0_32: return RISCV::F0_64; case RISCV::F1_32: return RISCV::F1_64; case RISCV::F2_32: return RISCV::F2_64; case RISCV::F3_32: return RISCV::F3_64; case RISCV::F4_32: return RISCV::F4_64; case RISCV::F5_32: return RISCV::F5_64; case RISCV::F6_32: return RISCV::F6_64; case RISCV::F7_32: return RISCV::F7_64; case RISCV::F8_32: return RISCV::F8_64; case RISCV::F9_32: return RISCV::F9_64; case RISCV::F10_32: return RISCV::F10_64; case RISCV::F11_32: return RISCV::F11_64; case RISCV::F12_32: return RISCV::F12_64; case RISCV::F13_32: return RISCV::F13_64; case RISCV::F14_32: return RISCV::F14_64; case RISCV::F15_32: return RISCV::F15_64; case RISCV::F16_32: return RISCV::F16_64; case RISCV::F17_32: return RISCV::F17_64; case RISCV::F18_32: return RISCV::F18_64; case RISCV::F19_32: return RISCV::F19_64; case RISCV::F20_32: return RISCV::F20_64; case RISCV::F21_32: return RISCV::F21_64; case RISCV::F22_32: return RISCV::F22_64; case RISCV::F23_32: return RISCV::F23_64; case RISCV::F24_32: return RISCV::F24_64; case RISCV::F25_32: return RISCV::F25_64; case RISCV::F26_32: return RISCV::F26_64; case RISCV::F27_32: return RISCV::F27_64; case RISCV::F28_32: return RISCV::F28_64; case RISCV::F29_32: return RISCV::F29_64; case RISCV::F30_32: return RISCV::F30_64; case RISCV::F31_32: return RISCV::F31_64; } } unsigned RISCVAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, unsigned Kind) { RISCVOperand &Op = static_cast(AsmOp); if (!Op.isReg()) return Match_InvalidOperand; unsigned Reg = Op.getReg(); bool IsRegFPR32 = RISCVMCRegisterClasses[RISCV::FPR32RegClassID].contains(Reg); bool IsRegFPR32C = RISCVMCRegisterClasses[RISCV::FPR32CRegClassID].contains(Reg); // As the parser couldn't differentiate an FPR32 from an FPR64, coerce the // register from FPR32 to FPR64 or FPR32C to FPR64C if necessary. if ((IsRegFPR32 && Kind == MCK_FPR64) || (IsRegFPR32C && Kind == MCK_FPR64C)) { Op.Reg.RegNum = convertFPR32ToFPR64(Reg); return Match_Success; } return Match_InvalidOperand; } bool RISCVAsmParser::generateImmOutOfRangeError( OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t Upper, Twine Msg = "immediate must be an integer in the range") { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, Msg + " [" + Twine(Lower) + ", " + Twine(Upper) + "]"); } bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) { MCInst Inst; auto Result = MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); switch (Result) { default: break; case Match_Success: return processInstruction(Inst, IDLoc, Operands, Out); case Match_MissingFeature: return Error(IDLoc, "instruction use requires an option to be enabled"); case Match_MnemonicFail: return Error(IDLoc, "unrecognized instruction mnemonic"); case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0U) { if (ErrorInfo >= Operands.size()) return Error(ErrorLoc, "too few operands for instruction"); ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; } return Error(ErrorLoc, "invalid operand for instruction"); } } // Handle the case when the error message is of specific type // other than the generic Match_InvalidOperand, and the // corresponding operand is missing. if (Result > FIRST_TARGET_MATCH_RESULT_TY) { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0U && ErrorInfo >= Operands.size()) return Error(ErrorLoc, "too few operands for instruction"); } switch(Result) { default: break; case Match_InvalidImmXLenLI: if (isRV64()) { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a constant 64-bit integer"); } return generateImmOutOfRangeError(Operands, ErrorInfo, std::numeric_limits::min(), std::numeric_limits::max()); case Match_InvalidUImmLog2XLen: if (isRV64()) return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 6) - 1); return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); case Match_InvalidUImmLog2XLenNonZero: if (isRV64()) return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 6) - 1); return generateImmOutOfRangeError(Operands, ErrorInfo, 1, (1 << 5) - 1); case Match_InvalidUImm5: return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1); case Match_InvalidSImm6: return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 5), (1 << 5) - 1); case Match_InvalidSImm6NonZero: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 5), (1 << 5) - 1, "immediate must be non-zero in the range"); case Match_InvalidCLUIImm: return generateImmOutOfRangeError( Operands, ErrorInfo, 1, (1 << 5) - 1, "immediate must be in [0xfffe0, 0xfffff] or"); case Match_InvalidUImm7Lsb00: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 7) - 4, "immediate must be a multiple of 4 bytes in the range"); case Match_InvalidUImm8Lsb00: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 8) - 4, "immediate must be a multiple of 4 bytes in the range"); case Match_InvalidUImm8Lsb000: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 8) - 8, "immediate must be a multiple of 8 bytes in the range"); case Match_InvalidSImm9Lsb0: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 8), (1 << 8) - 2, "immediate must be a multiple of 2 bytes in the range"); case Match_InvalidUImm9Lsb000: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 9) - 8, "immediate must be a multiple of 8 bytes in the range"); case Match_InvalidUImm10Lsb00NonZero: return generateImmOutOfRangeError( Operands, ErrorInfo, 4, (1 << 10) - 4, "immediate must be a multiple of 4 bytes in the range"); case Match_InvalidSImm10Lsb0000NonZero: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 9), (1 << 9) - 16, "immediate must be a multiple of 16 bytes and non-zero in the range"); case Match_InvalidSImm12: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 11), (1 << 11) - 1, "operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an " "integer in the range"); case Match_InvalidSImm12Lsb0: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 11), (1 << 11) - 2, "immediate must be a multiple of 2 bytes in the range"); case Match_InvalidSImm13Lsb0: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 12), (1 << 12) - 2, "immediate must be a multiple of 2 bytes in the range"); case Match_InvalidUImm20LUI: return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 20) - 1, "operand must be a symbol with " "%hi/%tprel_hi modifier or an integer in " "the range"); case Match_InvalidUImm20AUIPC: return generateImmOutOfRangeError( Operands, ErrorInfo, 0, (1 << 20) - 1, "operand must be a symbol with a " "%pcrel_hi/%got_pcrel_hi/%tls_ie_pcrel_hi/%tls_gd_pcrel_hi modifier or " "an integer in the range"); case Match_InvalidSImm21Lsb0JAL: return generateImmOutOfRangeError( Operands, ErrorInfo, -(1 << 20), (1 << 20) - 2, "immediate must be a multiple of 2 bytes in the range"); case Match_InvalidCSRSystemRegister: { return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 12) - 1, "operand must be a valid system register " "name or an integer in the range"); } case Match_InvalidFenceArg: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error( ErrorLoc, "operand must be formed of letters selected in-order from 'iorw'"); } case Match_InvalidFRMArg: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error( ErrorLoc, "operand must be a valid floating point rounding mode mnemonic"); } case Match_InvalidBareSymbol: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); } case Match_InvalidCallSymbol: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a bare symbol name"); } case Match_InvalidTPRelAddSymbol: { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); return Error(ErrorLoc, "operand must be a symbol with %tprel_add modifier"); } } llvm_unreachable("Unknown match type detected!"); } // Attempts to match Name as a register (either using the default name or // alternative ABI names), setting RegNo to the matching register. Upon // failure, returns true and sets RegNo to 0. If IsRV32E then registers // x16-x31 will be rejected. static bool matchRegisterNameHelper(bool IsRV32E, unsigned &RegNo, StringRef Name) { RegNo = MatchRegisterName(Name); if (RegNo == 0) RegNo = MatchRegisterAltName(Name); if (IsRV32E && RegNo >= RISCV::X16 && RegNo <= RISCV::X31) RegNo = 0; return RegNo == 0; } bool RISCVAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { const AsmToken &Tok = getParser().getTok(); StartLoc = Tok.getLoc(); EndLoc = Tok.getEndLoc(); RegNo = 0; StringRef Name = getLexer().getTok().getIdentifier(); if (matchRegisterNameHelper(isRV32E(), RegNo, Name)) return Error(StartLoc, "invalid register name"); getParser().Lex(); // Eat identifier token. return false; } OperandMatchResultTy RISCVAsmParser::parseRegister(OperandVector &Operands, bool AllowParens) { SMLoc FirstS = getLoc(); bool HadParens = false; AsmToken LParen; // If this is an LParen and a parenthesised register name is allowed, parse it // atomically. if (AllowParens && getLexer().is(AsmToken::LParen)) { AsmToken Buf[2]; size_t ReadCount = getLexer().peekTokens(Buf); if (ReadCount == 2 && Buf[1].getKind() == AsmToken::RParen) { HadParens = true; LParen = getParser().getTok(); getParser().Lex(); // Eat '(' } } switch (getLexer().getKind()) { default: if (HadParens) getLexer().UnLex(LParen); return MatchOperand_NoMatch; case AsmToken::Identifier: StringRef Name = getLexer().getTok().getIdentifier(); unsigned RegNo; matchRegisterNameHelper(isRV32E(), RegNo, Name); if (RegNo == 0) { if (HadParens) getLexer().UnLex(LParen); return MatchOperand_NoMatch; } if (HadParens) Operands.push_back(RISCVOperand::createToken("(", FirstS, isRV64())); SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); getLexer().Lex(); Operands.push_back(RISCVOperand::createReg(RegNo, S, E, isRV64())); } if (HadParens) { getParser().Lex(); // Eat ')' Operands.push_back(RISCVOperand::createToken(")", getLoc(), isRV64())); } return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseCSRSystemRegister(OperandVector &Operands) { SMLoc S = getLoc(); const MCExpr *Res; switch (getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::LParen: case AsmToken::Minus: case AsmToken::Plus: case AsmToken::Exclaim: case AsmToken::Tilde: case AsmToken::Integer: case AsmToken::String: { if (getParser().parseExpression(Res)) return MatchOperand_ParseFail; auto *CE = dyn_cast(Res); if (CE) { int64_t Imm = CE->getValue(); if (isUInt<12>(Imm)) { auto SysReg = RISCVSysReg::lookupSysRegByEncoding(Imm); // Accept an immediate representing a named or un-named Sys Reg // if the range is valid, regardless of the required features. Operands.push_back(RISCVOperand::createSysReg( SysReg ? SysReg->Name : "", S, Imm, isRV64())); return MatchOperand_Success; } } Twine Msg = "immediate must be an integer in the range"; Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 12) - 1) + "]"); return MatchOperand_ParseFail; } case AsmToken::Identifier: { StringRef Identifier; if (getParser().parseIdentifier(Identifier)) return MatchOperand_ParseFail; auto SysReg = RISCVSysReg::lookupSysRegByName(Identifier); // Accept a named Sys Reg if the required features are present. if (SysReg) { if (!SysReg->haveRequiredFeatures(getSTI().getFeatureBits())) { Error(S, "system register use requires an option to be enabled"); return MatchOperand_ParseFail; } Operands.push_back(RISCVOperand::createSysReg( Identifier, S, SysReg->Encoding, isRV64())); return MatchOperand_Success; } Twine Msg = "operand must be a valid system register name " "or an integer in the range"; Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 12) - 1) + "]"); return MatchOperand_ParseFail; } case AsmToken::Percent: { // Discard operand with modifier. Twine Msg = "immediate must be an integer in the range"; Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 12) - 1) + "]"); return MatchOperand_ParseFail; } } return MatchOperand_NoMatch; } OperandMatchResultTy RISCVAsmParser::parseImmediate(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); const MCExpr *Res; switch (getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::LParen: case AsmToken::Dot: case AsmToken::Minus: case AsmToken::Plus: case AsmToken::Exclaim: case AsmToken::Tilde: case AsmToken::Integer: case AsmToken::String: case AsmToken::Identifier: if (getParser().parseExpression(Res)) return MatchOperand_ParseFail; break; case AsmToken::Percent: return parseOperandWithModifier(Operands); } Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseOperandWithModifier(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); if (getLexer().getKind() != AsmToken::Percent) { Error(getLoc(), "expected '%' for operand modifier"); return MatchOperand_ParseFail; } getParser().Lex(); // Eat '%' if (getLexer().getKind() != AsmToken::Identifier) { Error(getLoc(), "expected valid identifier for operand modifier"); return MatchOperand_ParseFail; } StringRef Identifier = getParser().getTok().getIdentifier(); RISCVMCExpr::VariantKind VK = RISCVMCExpr::getVariantKindForName(Identifier); if (VK == RISCVMCExpr::VK_RISCV_Invalid) { Error(getLoc(), "unrecognized operand modifier"); return MatchOperand_ParseFail; } getParser().Lex(); // Eat the identifier if (getLexer().getKind() != AsmToken::LParen) { Error(getLoc(), "expected '('"); return MatchOperand_ParseFail; } getParser().Lex(); // Eat '(' const MCExpr *SubExpr; if (getParser().parseParenExpression(SubExpr, E)) { return MatchOperand_ParseFail; } const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, VK, getContext()); Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, isRV64())); return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseBareSymbol(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); const MCExpr *Res; if (getLexer().getKind() != AsmToken::Identifier) return MatchOperand_NoMatch; StringRef Identifier; AsmToken Tok = getLexer().getTok(); if (getParser().parseIdentifier(Identifier)) return MatchOperand_ParseFail; if (Identifier.consume_back("@plt")) { Error(getLoc(), "'@plt' operand not valid for instruction"); return MatchOperand_ParseFail; } MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); if (Sym->isVariable()) { const MCExpr *V = Sym->getVariableValue(/*SetUsed=*/false); if (!isa(V)) { getLexer().UnLex(Tok); // Put back if it's not a bare symbol. return MatchOperand_NoMatch; } Res = V; } else Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); MCBinaryExpr::Opcode Opcode; switch (getLexer().getKind()) { default: Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); return MatchOperand_Success; case AsmToken::Plus: Opcode = MCBinaryExpr::Add; break; case AsmToken::Minus: Opcode = MCBinaryExpr::Sub; break; } const MCExpr *Expr; if (getParser().parseExpression(Expr)) return MatchOperand_ParseFail; Res = MCBinaryExpr::create(Opcode, Res, Expr, getContext()); Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseCallSymbol(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); const MCExpr *Res; if (getLexer().getKind() != AsmToken::Identifier) return MatchOperand_NoMatch; // Avoid parsing the register in `call rd, foo` as a call symbol. if (getLexer().peekTok().getKind() != AsmToken::EndOfStatement) return MatchOperand_NoMatch; StringRef Identifier; if (getParser().parseIdentifier(Identifier)) return MatchOperand_ParseFail; RISCVMCExpr::VariantKind Kind = RISCVMCExpr::VK_RISCV_CALL; if (Identifier.consume_back("@plt")) Kind = RISCVMCExpr::VK_RISCV_CALL_PLT; MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); Res = RISCVMCExpr::create(Res, Kind, getContext()); Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64())); return MatchOperand_Success; } OperandMatchResultTy RISCVAsmParser::parseJALOffset(OperandVector &Operands) { // Parsing jal operands is fiddly due to the `jal foo` and `jal ra, foo` // both being acceptable forms. When parsing `jal ra, foo` this function // will be called for the `ra` register operand in an attempt to match the // single-operand alias. parseJALOffset must fail for this case. It would // seem logical to try parse the operand using parseImmediate and return // NoMatch if the next token is a comma (meaning we must be parsing a jal in // the second form rather than the first). We can't do this as there's no // way of rewinding the lexer state. Instead, return NoMatch if this operand // is an identifier and is followed by a comma. if (getLexer().is(AsmToken::Identifier) && getLexer().peekTok().is(AsmToken::Comma)) return MatchOperand_NoMatch; return parseImmediate(Operands); } OperandMatchResultTy RISCVAsmParser::parseMemOpBaseReg(OperandVector &Operands) { if (getLexer().isNot(AsmToken::LParen)) { Error(getLoc(), "expected '('"); return MatchOperand_ParseFail; } getParser().Lex(); // Eat '(' Operands.push_back(RISCVOperand::createToken("(", getLoc(), isRV64())); if (parseRegister(Operands) != MatchOperand_Success) { Error(getLoc(), "expected register"); return MatchOperand_ParseFail; } if (getLexer().isNot(AsmToken::RParen)) { Error(getLoc(), "expected ')'"); return MatchOperand_ParseFail; } getParser().Lex(); // Eat ')' Operands.push_back(RISCVOperand::createToken(")", getLoc(), isRV64())); + + return MatchOperand_Success; +} + +OperandMatchResultTy RISCVAsmParser::parseAtomicMemOp(OperandVector &Operands) { + // Atomic operations such as lr.w, sc.w, and amo*.w accept a "memory operand" + // as one of their register operands, such as `(a0)`. This just denotes that + // the register (in this case `a0`) contains a memory address. + // + // Normally, we would be able to parse these by putting the parens into the + // instruction string. However, GNU as also accepts a zero-offset memory + // operand (such as `0(a0)`), and ignores the 0. Normally this would be parsed + // with parseImmediate followed by parseMemOpBaseReg, but these instructions + // do not accept an immediate operand, and we do not want to add a "dummy" + // operand that is silently dropped. + // + // Instead, we use this custom parser. This will: allow (and discard) an + // offset if it is zero; require (and discard) parentheses; and add only the + // parsed register operand to `Operands`. + // + // These operands are printed with RISCVInstPrinter::printAtomicMemOp, which + // will only print the register surrounded by parentheses (which GNU as also + // uses as its canonical representation for these operands). + std::unique_ptr OptionalImmOp; + + if (getLexer().isNot(AsmToken::LParen)) { + // Parse an Integer token. We do not accept arbritrary constant expressions + // in the offset field (because they may include parens, which complicates + // parsing a lot). + int64_t ImmVal; + SMLoc ImmStart = getLoc(); + if (getParser().parseIntToken(ImmVal, + "expected '(' or optional integer offset")) + return MatchOperand_ParseFail; + + // Create a RISCVOperand for checking later (so the error messages are + // nicer), but we don't add it to Operands. + SMLoc ImmEnd = getLoc(); + OptionalImmOp = + RISCVOperand::createImm(MCConstantExpr::create(ImmVal, getContext()), + ImmStart, ImmEnd, isRV64()); + } + + if (getLexer().isNot(AsmToken::LParen)) { + Error(getLoc(), OptionalImmOp ? "expected '(' after optional integer offset" + : "expected '(' or optional integer offset"); + return MatchOperand_ParseFail; + } + getParser().Lex(); // Eat '(' + + if (parseRegister(Operands) != MatchOperand_Success) { + Error(getLoc(), "expected register"); + return MatchOperand_ParseFail; + } + + if (getLexer().isNot(AsmToken::RParen)) { + Error(getLoc(), "expected ')'"); + return MatchOperand_ParseFail; + } + getParser().Lex(); // Eat ')' + + // Deferred Handling of non-zero offsets. This makes the error messages nicer. + if (OptionalImmOp && !OptionalImmOp->isImmZero()) { + Error(OptionalImmOp->getStartLoc(), "optional integer offset must be 0", + SMRange(OptionalImmOp->getStartLoc(), OptionalImmOp->getEndLoc())); + return MatchOperand_ParseFail; + } return MatchOperand_Success; } /// Looks at a token type and creates the relevant operand from this /// information, adding to Operands. If operand was parsed, returns false, else /// true. bool RISCVAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { // Check if the current operand has a custom associated parser, if so, try to // custom parse the operand, or fallback to the general approach. OperandMatchResultTy Result = MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true); if (Result == MatchOperand_Success) return false; if (Result == MatchOperand_ParseFail) return true; // Attempt to parse token as a register. if (parseRegister(Operands, true) == MatchOperand_Success) return false; // Attempt to parse token as an immediate if (parseImmediate(Operands) == MatchOperand_Success) { // Parse memory base register if present if (getLexer().is(AsmToken::LParen)) return parseMemOpBaseReg(Operands) != MatchOperand_Success; return false; } // Finally we have exhausted all options and must declare defeat. Error(getLoc(), "unknown operand"); return true; } bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { // Ensure that if the instruction occurs when relaxation is enabled, // relocations are forced for the file. Ideally this would be done when there // is enough information to reliably determine if the instruction itself may // cause relaxations. Unfortunately instruction processing stage occurs in the // same pass as relocation emission, so it's too late to set a 'sticky bit' // for the entire file. if (getSTI().getFeatureBits()[RISCV::FeatureRelax]) { auto *Assembler = getTargetStreamer().getStreamer().getAssemblerPtr(); if (Assembler != nullptr) { RISCVAsmBackend &MAB = static_cast(Assembler->getBackend()); MAB.setForceRelocs(); } } // First operand is token for instruction Operands.push_back(RISCVOperand::createToken(Name, NameLoc, isRV64())); // If there are no more operands, then finish if (getLexer().is(AsmToken::EndOfStatement)) return false; // Parse first operand if (parseOperand(Operands, Name)) return true; // Parse until end of statement, consuming commas between operands unsigned OperandIdx = 1; while (getLexer().is(AsmToken::Comma)) { // Consume comma token getLexer().Lex(); // Parse next operand if (parseOperand(Operands, Name)) return true; ++OperandIdx; } if (getLexer().isNot(AsmToken::EndOfStatement)) { SMLoc Loc = getLexer().getLoc(); getParser().eatToEndOfStatement(); return Error(Loc, "unexpected token"); } getParser().Lex(); // Consume the EndOfStatement. return false; } bool RISCVAsmParser::classifySymbolRef(const MCExpr *Expr, RISCVMCExpr::VariantKind &Kind, int64_t &Addend) { Kind = RISCVMCExpr::VK_RISCV_None; Addend = 0; if (const RISCVMCExpr *RE = dyn_cast(Expr)) { Kind = RE->getKind(); Expr = RE->getSubExpr(); } // It's a simple symbol reference or constant with no addend. if (isa(Expr) || isa(Expr)) return true; const MCBinaryExpr *BE = dyn_cast(Expr); if (!BE) return false; if (!isa(BE->getLHS())) return false; if (BE->getOpcode() != MCBinaryExpr::Add && BE->getOpcode() != MCBinaryExpr::Sub) return false; // We are able to support the subtraction of two symbol references if (BE->getOpcode() == MCBinaryExpr::Sub && isa(BE->getRHS())) return true; // See if the addend is a constant, otherwise there's more going // on here than we can deal with. auto AddendExpr = dyn_cast(BE->getRHS()); if (!AddendExpr) return false; Addend = AddendExpr->getValue(); if (BE->getOpcode() == MCBinaryExpr::Sub) Addend = -Addend; // It's some symbol reference + a constant addend return Kind != RISCVMCExpr::VK_RISCV_Invalid; } bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { // This returns false if this function recognizes the directive // regardless of whether it is successfully handles or reports an // error. Otherwise it returns true to give the generic parser a // chance at recognizing it. StringRef IDVal = DirectiveID.getString(); if (IDVal == ".option") return parseDirectiveOption(); return true; } bool RISCVAsmParser::parseDirectiveOption() { MCAsmParser &Parser = getParser(); // Get the option token. AsmToken Tok = Parser.getTok(); // At the moment only identifiers are supported. if (Tok.isNot(AsmToken::Identifier)) return Error(Parser.getTok().getLoc(), "unexpected token, expected identifier"); StringRef Option = Tok.getIdentifier(); if (Option == "push") { getTargetStreamer().emitDirectiveOptionPush(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); pushFeatureBits(); return false; } if (Option == "pop") { SMLoc StartLoc = Parser.getTok().getLoc(); getTargetStreamer().emitDirectiveOptionPop(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); if (popFeatureBits()) return Error(StartLoc, ".option pop with no .option push"); return false; } if (Option == "rvc") { getTargetStreamer().emitDirectiveOptionRVC(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); setFeatureBits(RISCV::FeatureStdExtC, "c"); return false; } if (Option == "norvc") { getTargetStreamer().emitDirectiveOptionNoRVC(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); clearFeatureBits(RISCV::FeatureStdExtC, "c"); return false; } if (Option == "relax") { getTargetStreamer().emitDirectiveOptionRelax(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); setFeatureBits(RISCV::FeatureRelax, "relax"); return false; } if (Option == "norelax") { getTargetStreamer().emitDirectiveOptionNoRelax(); Parser.Lex(); if (Parser.getTok().isNot(AsmToken::EndOfStatement)) return Error(Parser.getTok().getLoc(), "unexpected token, expected end of statement"); clearFeatureBits(RISCV::FeatureRelax, "relax"); return false; } // Unknown option. Warning(Parser.getTok().getLoc(), "unknown option, expected 'push', 'pop', 'rvc', 'norvc', 'relax' or " "'norelax'"); Parser.eatToEndOfStatement(); return false; } void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); CInst.setLoc(Inst.getLoc()); S.EmitInstruction((Res ? CInst : Inst), getSTI()); } void RISCVAsmParser::emitLoadImm(unsigned DestReg, int64_t Value, MCStreamer &Out) { RISCVMatInt::InstSeq Seq; RISCVMatInt::generateInstSeq(Value, isRV64(), Seq); unsigned SrcReg = RISCV::X0; for (RISCVMatInt::Inst &Inst : Seq) { if (Inst.Opc == RISCV::LUI) { emitToStreamer( Out, MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Inst.Imm)); } else { emitToStreamer( Out, MCInstBuilder(Inst.Opc).addReg(DestReg).addReg(SrcReg).addImm( Inst.Imm)); } // Only the first instruction has X0 as its source. SrcReg = DestReg; } } void RISCVAsmParser::emitAuipcInstPair(MCOperand DestReg, MCOperand TmpReg, const MCExpr *Symbol, RISCVMCExpr::VariantKind VKHi, unsigned SecondOpcode, SMLoc IDLoc, MCStreamer &Out) { // A pair of instructions for PC-relative addressing; expands to // TmpLabel: AUIPC TmpReg, VKHi(symbol) // OP DestReg, TmpReg, %pcrel_lo(TmpLabel) MCContext &Ctx = getContext(); MCSymbol *TmpLabel = Ctx.createTempSymbol( "pcrel_hi", /* AlwaysAddSuffix */ true, /* CanBeUnnamed */ false); Out.EmitLabel(TmpLabel); const RISCVMCExpr *SymbolHi = RISCVMCExpr::create(Symbol, VKHi, Ctx); emitToStreamer( Out, MCInstBuilder(RISCV::AUIPC).addOperand(TmpReg).addExpr(SymbolHi)); const MCExpr *RefToLinkTmpLabel = RISCVMCExpr::create(MCSymbolRefExpr::create(TmpLabel, Ctx), RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx); emitToStreamer(Out, MCInstBuilder(SecondOpcode) .addOperand(DestReg) .addOperand(TmpReg) .addExpr(RefToLinkTmpLabel)); } void RISCVAsmParser::emitLoadLocalAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out) { // The load local address pseudo-instruction "lla" is used in PC-relative // addressing of local symbols: // lla rdest, symbol // expands to // TmpLabel: AUIPC rdest, %pcrel_hi(symbol) // ADDI rdest, rdest, %pcrel_lo(TmpLabel) MCOperand DestReg = Inst.getOperand(0); const MCExpr *Symbol = Inst.getOperand(1).getExpr(); emitAuipcInstPair(DestReg, DestReg, Symbol, RISCVMCExpr::VK_RISCV_PCREL_HI, RISCV::ADDI, IDLoc, Out); } void RISCVAsmParser::emitLoadAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out) { // The load address pseudo-instruction "la" is used in PC-relative and // GOT-indirect addressing of global symbols: // la rdest, symbol // expands to either (for non-PIC) // TmpLabel: AUIPC rdest, %pcrel_hi(symbol) // ADDI rdest, rdest, %pcrel_lo(TmpLabel) // or (for PIC) // TmpLabel: AUIPC rdest, %got_pcrel_hi(symbol) // Lx rdest, %pcrel_lo(TmpLabel)(rdest) MCOperand DestReg = Inst.getOperand(0); const MCExpr *Symbol = Inst.getOperand(1).getExpr(); unsigned SecondOpcode; RISCVMCExpr::VariantKind VKHi; // FIXME: Should check .option (no)pic when implemented if (getContext().getObjectFileInfo()->isPositionIndependent()) { SecondOpcode = isRV64() ? RISCV::LD : RISCV::LW; VKHi = RISCVMCExpr::VK_RISCV_GOT_HI; } else { SecondOpcode = RISCV::ADDI; VKHi = RISCVMCExpr::VK_RISCV_PCREL_HI; } emitAuipcInstPair(DestReg, DestReg, Symbol, VKHi, SecondOpcode, IDLoc, Out); } void RISCVAsmParser::emitLoadTLSIEAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out) { // The load TLS IE address pseudo-instruction "la.tls.ie" is used in // initial-exec TLS model addressing of global symbols: // la.tls.ie rdest, symbol // expands to // TmpLabel: AUIPC rdest, %tls_ie_pcrel_hi(symbol) // Lx rdest, %pcrel_lo(TmpLabel)(rdest) MCOperand DestReg = Inst.getOperand(0); const MCExpr *Symbol = Inst.getOperand(1).getExpr(); unsigned SecondOpcode = isRV64() ? RISCV::LD : RISCV::LW; emitAuipcInstPair(DestReg, DestReg, Symbol, RISCVMCExpr::VK_RISCV_TLS_GOT_HI, SecondOpcode, IDLoc, Out); } void RISCVAsmParser::emitLoadTLSGDAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out) { // The load TLS GD address pseudo-instruction "la.tls.gd" is used in // global-dynamic TLS model addressing of global symbols: // la.tls.gd rdest, symbol // expands to // TmpLabel: AUIPC rdest, %tls_gd_pcrel_hi(symbol) // ADDI rdest, rdest, %pcrel_lo(TmpLabel) MCOperand DestReg = Inst.getOperand(0); const MCExpr *Symbol = Inst.getOperand(1).getExpr(); emitAuipcInstPair(DestReg, DestReg, Symbol, RISCVMCExpr::VK_RISCV_TLS_GD_HI, RISCV::ADDI, IDLoc, Out); } void RISCVAsmParser::emitLoadStoreSymbol(MCInst &Inst, unsigned Opcode, SMLoc IDLoc, MCStreamer &Out, bool HasTmpReg) { // The load/store pseudo-instruction does a pc-relative load with // a symbol. // // The expansion looks like this // // TmpLabel: AUIPC tmp, %pcrel_hi(symbol) // [S|L]X rd, %pcrel_lo(TmpLabel)(tmp) MCOperand DestReg = Inst.getOperand(0); unsigned SymbolOpIdx = HasTmpReg ? 2 : 1; unsigned TmpRegOpIdx = HasTmpReg ? 1 : 0; MCOperand TmpReg = Inst.getOperand(TmpRegOpIdx); const MCExpr *Symbol = Inst.getOperand(SymbolOpIdx).getExpr(); emitAuipcInstPair(DestReg, TmpReg, Symbol, RISCVMCExpr::VK_RISCV_PCREL_HI, Opcode, IDLoc, Out); } bool RISCVAsmParser::checkPseudoAddTPRel(MCInst &Inst, OperandVector &Operands) { assert(Inst.getOpcode() == RISCV::PseudoAddTPRel && "Invalid instruction"); assert(Inst.getOperand(2).isReg() && "Unexpected second operand kind"); if (Inst.getOperand(2).getReg() != RISCV::X4) { SMLoc ErrorLoc = ((RISCVOperand &)*Operands[3]).getStartLoc(); return Error(ErrorLoc, "the second input operand must be tp/x4 when using " "%tprel_add modifier"); } return false; } bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands, MCStreamer &Out) { Inst.setLoc(IDLoc); switch (Inst.getOpcode()) { default: break; case RISCV::PseudoLI: { unsigned Reg = Inst.getOperand(0).getReg(); const MCOperand &Op1 = Inst.getOperand(1); if (Op1.isExpr()) { // We must have li reg, %lo(sym) or li reg, %pcrel_lo(sym) or similar. // Just convert to an addi. This allows compatibility with gas. emitToStreamer(Out, MCInstBuilder(RISCV::ADDI) .addReg(Reg) .addReg(RISCV::X0) .addExpr(Op1.getExpr())); return false; } int64_t Imm = Inst.getOperand(1).getImm(); // On RV32 the immediate here can either be a signed or an unsigned // 32-bit number. Sign extension has to be performed to ensure that Imm // represents the expected signed 64-bit number. if (!isRV64()) Imm = SignExtend64<32>(Imm); emitLoadImm(Reg, Imm, Out); return false; } case RISCV::PseudoLLA: emitLoadLocalAddress(Inst, IDLoc, Out); return false; case RISCV::PseudoLA: emitLoadAddress(Inst, IDLoc, Out); return false; case RISCV::PseudoLA_TLS_IE: emitLoadTLSIEAddress(Inst, IDLoc, Out); return false; case RISCV::PseudoLA_TLS_GD: emitLoadTLSGDAddress(Inst, IDLoc, Out); return false; case RISCV::PseudoLB: emitLoadStoreSymbol(Inst, RISCV::LB, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLBU: emitLoadStoreSymbol(Inst, RISCV::LBU, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLH: emitLoadStoreSymbol(Inst, RISCV::LH, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLHU: emitLoadStoreSymbol(Inst, RISCV::LHU, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLW: emitLoadStoreSymbol(Inst, RISCV::LW, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLWU: emitLoadStoreSymbol(Inst, RISCV::LWU, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoLD: emitLoadStoreSymbol(Inst, RISCV::LD, IDLoc, Out, /*HasTmpReg=*/false); return false; case RISCV::PseudoFLW: emitLoadStoreSymbol(Inst, RISCV::FLW, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoFLD: emitLoadStoreSymbol(Inst, RISCV::FLD, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoSB: emitLoadStoreSymbol(Inst, RISCV::SB, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoSH: emitLoadStoreSymbol(Inst, RISCV::SH, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoSW: emitLoadStoreSymbol(Inst, RISCV::SW, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoSD: emitLoadStoreSymbol(Inst, RISCV::SD, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoFSW: emitLoadStoreSymbol(Inst, RISCV::FSW, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoFSD: emitLoadStoreSymbol(Inst, RISCV::FSD, IDLoc, Out, /*HasTmpReg=*/true); return false; case RISCV::PseudoAddTPRel: if (checkPseudoAddTPRel(Inst, Operands)) return true; break; } emitToStreamer(Out, Inst); return false; } extern "C" void LLVMInitializeRISCVAsmParser() { RegisterMCAsmParser X(getTheRISCV32Target()); RegisterMCAsmParser Y(getTheRISCV64Target()); } Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (revision 354469) @@ -1,369 +1,377 @@ //===-- RISCVAsmBackend.cpp - RISCV Assembler Backend ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "RISCVAsmBackend.h" #include "RISCVMCExpr.h" #include "llvm/ADT/APInt.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; // If linker relaxation is enabled, or the relax option had previously been // enabled, always emit relocations even if the fixup can be resolved. This is // necessary for correctness as offsets may change during relaxation. bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) { bool ShouldForce = false; switch ((unsigned)Fixup.getKind()) { default: break; + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + if (Target.isAbsolute()) + return false; + break; case RISCV::fixup_riscv_got_hi20: case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: return true; case RISCV::fixup_riscv_pcrel_lo12_i: case RISCV::fixup_riscv_pcrel_lo12_s: // For pcrel_lo12, force a relocation if the target of the corresponding // pcrel_hi20 is not in the same fragment. const MCFixup *T = cast(Fixup.getValue())->getPCRelHiFixup(); if (!T) { Asm.getContext().reportError(Fixup.getLoc(), "could not find corresponding %pcrel_hi"); return false; } switch ((unsigned)T->getKind()) { default: llvm_unreachable("Unexpected fixup kind for pcrel_lo12"); break; case RISCV::fixup_riscv_got_hi20: case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: ShouldForce = true; break; case RISCV::fixup_riscv_pcrel_hi20: ShouldForce = T->getValue()->findAssociatedFragment() != Fixup.getValue()->findAssociatedFragment(); break; } break; } return ShouldForce || STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs; } bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout, const bool WasForced) const { // Return true if the symbol is actually unresolved. // Resolved could be always false when shouldForceRelocation return true. // We use !WasForced to indicate that the symbol is unresolved and not forced // by shouldForceRelocation. if (!Resolved && !WasForced) return true; int64_t Offset = int64_t(Value); switch ((unsigned)Fixup.getKind()) { default: return false; case RISCV::fixup_riscv_rvc_branch: // For compressed branch instructions the immediate must be // in the range [-256, 254]. return Offset > 254 || Offset < -256; case RISCV::fixup_riscv_rvc_jump: // For compressed jump instructions the immediate must be // in the range [-2048, 2046]. return Offset > 2046 || Offset < -2048; } } void RISCVAsmBackend::relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const { // TODO: replace this with call to auto generated uncompressinstr() function. switch (Inst.getOpcode()) { default: llvm_unreachable("Opcode not expected!"); case RISCV::C_BEQZ: // c.beqz $rs1, $imm -> beq $rs1, X0, $imm. Res.setOpcode(RISCV::BEQ); Res.addOperand(Inst.getOperand(0)); Res.addOperand(MCOperand::createReg(RISCV::X0)); Res.addOperand(Inst.getOperand(1)); break; case RISCV::C_BNEZ: // c.bnez $rs1, $imm -> bne $rs1, X0, $imm. Res.setOpcode(RISCV::BNE); Res.addOperand(Inst.getOperand(0)); Res.addOperand(MCOperand::createReg(RISCV::X0)); Res.addOperand(Inst.getOperand(1)); break; case RISCV::C_J: // c.j $imm -> jal X0, $imm. Res.setOpcode(RISCV::JAL); Res.addOperand(MCOperand::createReg(RISCV::X0)); Res.addOperand(Inst.getOperand(0)); break; case RISCV::C_JAL: // c.jal $imm -> jal X1, $imm. Res.setOpcode(RISCV::JAL); Res.addOperand(MCOperand::createReg(RISCV::X1)); Res.addOperand(Inst.getOperand(0)); break; } } // Given a compressed control flow instruction this function returns // the expanded instruction. unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const { switch (Op) { default: return Op; case RISCV::C_BEQZ: return RISCV::BEQ; case RISCV::C_BNEZ: return RISCV::BNE; case RISCV::C_J: case RISCV::C_JAL: // fall through. return RISCV::JAL; } } bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const { return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode(); } bool RISCVAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { bool HasStdExtC = STI.getFeatureBits()[RISCV::FeatureStdExtC]; unsigned MinNopLen = HasStdExtC ? 2 : 4; if ((Count % MinNopLen) != 0) return false; // The canonical nop on RISC-V is addi x0, x0, 0. for (; Count >= 4; Count -= 4) OS.write("\x13\0\0\0", 4); // The canonical nop on RVC is c.nop. if (Count && HasStdExtC) OS.write("\x01\0", 2); return true; } static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, MCContext &Ctx) { unsigned Kind = Fixup.getKind(); switch (Kind) { default: llvm_unreachable("Unknown fixup kind!"); case RISCV::fixup_riscv_got_hi20: case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: llvm_unreachable("Relocation should be unconditionally forced\n"); case FK_Data_1: case FK_Data_2: case FK_Data_4: case FK_Data_8: + case FK_Data_6b: return Value; case RISCV::fixup_riscv_lo12_i: case RISCV::fixup_riscv_pcrel_lo12_i: case RISCV::fixup_riscv_tprel_lo12_i: return Value & 0xfff; case RISCV::fixup_riscv_lo12_s: case RISCV::fixup_riscv_pcrel_lo12_s: case RISCV::fixup_riscv_tprel_lo12_s: return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7); case RISCV::fixup_riscv_hi20: case RISCV::fixup_riscv_pcrel_hi20: case RISCV::fixup_riscv_tprel_hi20: // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. return ((Value + 0x800) >> 12) & 0xfffff; case RISCV::fixup_riscv_jal: { if (!isInt<21>(Value)) Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); if (Value & 0x1) Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); // Need to produce imm[19|10:1|11|19:12] from the 21-bit Value. unsigned Sbit = (Value >> 20) & 0x1; unsigned Hi8 = (Value >> 12) & 0xff; unsigned Mid1 = (Value >> 11) & 0x1; unsigned Lo10 = (Value >> 1) & 0x3ff; // Inst{31} = Sbit; // Inst{30-21} = Lo10; // Inst{20} = Mid1; // Inst{19-12} = Hi8; Value = (Sbit << 19) | (Lo10 << 9) | (Mid1 << 8) | Hi8; return Value; } case RISCV::fixup_riscv_branch: { if (!isInt<13>(Value)) Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); if (Value & 0x1) Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); // Need to extract imm[12], imm[10:5], imm[4:1], imm[11] from the 13-bit // Value. unsigned Sbit = (Value >> 12) & 0x1; unsigned Hi1 = (Value >> 11) & 0x1; unsigned Mid6 = (Value >> 5) & 0x3f; unsigned Lo4 = (Value >> 1) & 0xf; // Inst{31} = Sbit; // Inst{30-25} = Mid6; // Inst{11-8} = Lo4; // Inst{7} = Hi1; Value = (Sbit << 31) | (Mid6 << 25) | (Lo4 << 8) | (Hi1 << 7); return Value; } case RISCV::fixup_riscv_call: case RISCV::fixup_riscv_call_plt: { // Jalr will add UpperImm with the sign-extended 12-bit LowerImm, // we need to add 0x800ULL before extract upper bits to reflect the // effect of the sign extension. uint64_t UpperImm = (Value + 0x800ULL) & 0xfffff000ULL; uint64_t LowerImm = Value & 0xfffULL; return UpperImm | ((LowerImm << 20) << 32); } case RISCV::fixup_riscv_rvc_jump: { // Need to produce offset[11|4|9:8|10|6|7|3:1|5] from the 11-bit Value. unsigned Bit11 = (Value >> 11) & 0x1; unsigned Bit4 = (Value >> 4) & 0x1; unsigned Bit9_8 = (Value >> 8) & 0x3; unsigned Bit10 = (Value >> 10) & 0x1; unsigned Bit6 = (Value >> 6) & 0x1; unsigned Bit7 = (Value >> 7) & 0x1; unsigned Bit3_1 = (Value >> 1) & 0x7; unsigned Bit5 = (Value >> 5) & 0x1; Value = (Bit11 << 10) | (Bit4 << 9) | (Bit9_8 << 7) | (Bit10 << 6) | (Bit6 << 5) | (Bit7 << 4) | (Bit3_1 << 1) | Bit5; return Value; } case RISCV::fixup_riscv_rvc_branch: { // Need to produce offset[8|4:3], [reg 3 bit], offset[7:6|2:1|5] unsigned Bit8 = (Value >> 8) & 0x1; unsigned Bit7_6 = (Value >> 6) & 0x3; unsigned Bit5 = (Value >> 5) & 0x1; unsigned Bit4_3 = (Value >> 3) & 0x3; unsigned Bit2_1 = (Value >> 1) & 0x3; Value = (Bit8 << 12) | (Bit4_3 << 10) | (Bit7_6 << 5) | (Bit2_1 << 3) | (Bit5 << 2); return Value; } } } void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const { MCContext &Ctx = Asm.getContext(); MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); if (!Value) return; // Doesn't change encoding. // Apply any target-specific value adjustments. Value = adjustFixupValue(Fixup, Value, Ctx); // Shift the value into position. Value <<= Info.TargetOffset; unsigned Offset = Fixup.getOffset(); unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8; assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); // For each byte of the fragment that the fixup touches, mask in the // bits from the fixup value. for (unsigned i = 0; i != NumBytes; ++i) { Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); } } // Linker relaxation may change code size. We have to insert Nops // for .align directive when linker relaxation enabled. So then Linker // could satisfy alignment by removing Nops. // The function return the total Nops Size we need to insert. bool RISCVAsmBackend::shouldInsertExtraNopBytesForCodeAlign( const MCAlignFragment &AF, unsigned &Size) { // Calculate Nops Size only when linker relaxation enabled. if (!STI.getFeatureBits()[RISCV::FeatureRelax]) return false; bool HasStdExtC = STI.getFeatureBits()[RISCV::FeatureStdExtC]; unsigned MinNopLen = HasStdExtC ? 2 : 4; if (AF.getAlignment() <= MinNopLen) { return false; } else { Size = AF.getAlignment() - MinNopLen; return true; } } // We need to insert R_RISCV_ALIGN relocation type to indicate the // position of Nops and the total bytes of the Nops have been inserted // when linker relaxation enabled. // The function insert fixup_riscv_align fixup which eventually will // transfer to R_RISCV_ALIGN relocation type. bool RISCVAsmBackend::shouldInsertFixupForCodeAlign(MCAssembler &Asm, const MCAsmLayout &Layout, MCAlignFragment &AF) { // Insert the fixup only when linker relaxation enabled. if (!STI.getFeatureBits()[RISCV::FeatureRelax]) return false; // Calculate total Nops we need to insert. If there are none to insert // then simply return. unsigned Count; if (!shouldInsertExtraNopBytesForCodeAlign(AF, Count) || (Count == 0)) return false; MCContext &Ctx = Asm.getContext(); const MCExpr *Dummy = MCConstantExpr::create(0, Ctx); // Create fixup_riscv_align fixup. MCFixup Fixup = MCFixup::create(0, Dummy, MCFixupKind(RISCV::fixup_riscv_align), SMLoc()); uint64_t FixedValue = 0; MCValue NopBytes = MCValue::get(Count); Asm.getWriter().recordRelocation(Asm, Layout, &AF, Fixup, NopBytes, FixedValue); return true; } std::unique_ptr RISCVAsmBackend::createObjectTargetWriter() const { return createRISCVELFObjectWriter(OSABI, Is64Bit); } MCAsmBackend *llvm::createRISCVAsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { const Triple &TT = STI.getTargetTriple(); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TT.getOS()); return new RISCVAsmBackend(STI, OSABI, TT.isArch64Bit(), Options); } Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp (revision 354469) @@ -1,133 +1,142 @@ //===-- RISCVELFObjectWriter.cpp - RISCV ELF Writer -----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MCTargetDesc/RISCVFixupKinds.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; namespace { class RISCVELFObjectWriter : public MCELFObjectTargetWriter { public: RISCVELFObjectWriter(uint8_t OSABI, bool Is64Bit); ~RISCVELFObjectWriter() override; // Return true if the given relocation must be with a symbol rather than // section plus offset. bool needsRelocateWithSymbol(const MCSymbol &Sym, unsigned Type) const override { // TODO: this is very conservative, update once RISC-V psABI requirements // are clarified. return true; } protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; }; } RISCVELFObjectWriter::RISCVELFObjectWriter(uint8_t OSABI, bool Is64Bit) : MCELFObjectTargetWriter(Is64Bit, OSABI, ELF::EM_RISCV, /*HasRelocationAddend*/ true) {} RISCVELFObjectWriter::~RISCVELFObjectWriter() {} unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const { + const MCExpr *Expr = Fixup.getValue(); // Determine the type of the relocation unsigned Kind = Fixup.getKind(); if (IsPCRel) { switch (Kind) { default: llvm_unreachable("invalid fixup kind!"); case FK_Data_4: case FK_PCRel_4: return ELF::R_RISCV_32_PCREL; case RISCV::fixup_riscv_pcrel_hi20: return ELF::R_RISCV_PCREL_HI20; case RISCV::fixup_riscv_pcrel_lo12_i: return ELF::R_RISCV_PCREL_LO12_I; case RISCV::fixup_riscv_pcrel_lo12_s: return ELF::R_RISCV_PCREL_LO12_S; case RISCV::fixup_riscv_got_hi20: return ELF::R_RISCV_GOT_HI20; case RISCV::fixup_riscv_tls_got_hi20: return ELF::R_RISCV_TLS_GOT_HI20; case RISCV::fixup_riscv_tls_gd_hi20: return ELF::R_RISCV_TLS_GD_HI20; case RISCV::fixup_riscv_jal: return ELF::R_RISCV_JAL; case RISCV::fixup_riscv_branch: return ELF::R_RISCV_BRANCH; case RISCV::fixup_riscv_rvc_jump: return ELF::R_RISCV_RVC_JUMP; case RISCV::fixup_riscv_rvc_branch: return ELF::R_RISCV_RVC_BRANCH; case RISCV::fixup_riscv_call: return ELF::R_RISCV_CALL; case RISCV::fixup_riscv_call_plt: return ELF::R_RISCV_CALL_PLT; } } switch (Kind) { default: llvm_unreachable("invalid fixup kind!"); case FK_Data_4: + if (Expr->getKind() == MCExpr::Target && + cast(Expr)->getKind() == RISCVMCExpr::VK_RISCV_32_PCREL) + return ELF::R_RISCV_32_PCREL; return ELF::R_RISCV_32; case FK_Data_8: return ELF::R_RISCV_64; case FK_Data_Add_1: return ELF::R_RISCV_ADD8; case FK_Data_Add_2: return ELF::R_RISCV_ADD16; case FK_Data_Add_4: return ELF::R_RISCV_ADD32; case FK_Data_Add_8: return ELF::R_RISCV_ADD64; + case FK_Data_Add_6b: + return ELF::R_RISCV_SET6; case FK_Data_Sub_1: return ELF::R_RISCV_SUB8; case FK_Data_Sub_2: return ELF::R_RISCV_SUB16; case FK_Data_Sub_4: return ELF::R_RISCV_SUB32; case FK_Data_Sub_8: return ELF::R_RISCV_SUB64; + case FK_Data_Sub_6b: + return ELF::R_RISCV_SUB6; case RISCV::fixup_riscv_hi20: return ELF::R_RISCV_HI20; case RISCV::fixup_riscv_lo12_i: return ELF::R_RISCV_LO12_I; case RISCV::fixup_riscv_lo12_s: return ELF::R_RISCV_LO12_S; case RISCV::fixup_riscv_tprel_hi20: return ELF::R_RISCV_TPREL_HI20; case RISCV::fixup_riscv_tprel_lo12_i: return ELF::R_RISCV_TPREL_LO12_I; case RISCV::fixup_riscv_tprel_lo12_s: return ELF::R_RISCV_TPREL_LO12_S; case RISCV::fixup_riscv_tprel_add: return ELF::R_RISCV_TPREL_ADD; case RISCV::fixup_riscv_relax: return ELF::R_RISCV_RELAX; case RISCV::fixup_riscv_align: return ELF::R_RISCV_ALIGN; } } std::unique_ptr llvm::createRISCVELFObjectWriter(uint8_t OSABI, bool Is64Bit) { return llvm::make_unique(OSABI, Is64Bit); } Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp (revision 354469) @@ -1,114 +1,126 @@ //===-- RISCVInstPrinter.cpp - Convert RISCV MCInst to asm syntax ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This class prints an RISCV MCInst to a .s file. // //===----------------------------------------------------------------------===// #include "RISCVInstPrinter.h" #include "MCTargetDesc/RISCVMCExpr.h" #include "Utils/RISCVBaseInfo.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" using namespace llvm; #define DEBUG_TYPE "asm-printer" // Include the auto-generated portion of the assembly writer. #define PRINT_ALIAS_INSTR #include "RISCVGenAsmWriter.inc" // Include the auto-generated portion of the compress emitter. #define GEN_UNCOMPRESS_INSTR #include "RISCVGenCompressInstEmitter.inc" static cl::opt NoAliases("riscv-no-aliases", cl::desc("Disable the emission of assembler pseudo instructions"), cl::init(false), cl::Hidden); void RISCVInstPrinter::printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, const MCSubtargetInfo &STI) { bool Res = false; const MCInst *NewMI = MI; MCInst UncompressedMI; if (!NoAliases) Res = uncompressInst(UncompressedMI, *MI, MRI, STI); if (Res) NewMI = const_cast(&UncompressedMI); if (NoAliases || !printAliasInstr(NewMI, STI, O)) printInstruction(NewMI, STI, O); printAnnotation(O, Annot); } void RISCVInstPrinter::printRegName(raw_ostream &O, unsigned RegNo) const { O << getRegisterName(RegNo); } void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O, const char *Modifier) { assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported"); const MCOperand &MO = MI->getOperand(OpNo); if (MO.isReg()) { printRegName(O, MO.getReg()); return; } if (MO.isImm()) { O << MO.getImm(); return; } assert(MO.isExpr() && "Unknown operand kind in printOperand"); MO.getExpr()->print(O, &MAI); } void RISCVInstPrinter::printCSRSystemRegister(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O) { unsigned Imm = MI->getOperand(OpNo).getImm(); auto SysReg = RISCVSysReg::lookupSysRegByEncoding(Imm); if (SysReg && SysReg->haveRequiredFeatures(STI.getFeatureBits())) O << SysReg->Name; else O << Imm; } void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O) { unsigned FenceArg = MI->getOperand(OpNo).getImm(); assert (((FenceArg >> 4) == 0) && "Invalid immediate in printFenceArg"); if ((FenceArg & RISCVFenceField::I) != 0) O << 'i'; if ((FenceArg & RISCVFenceField::O) != 0) O << 'o'; if ((FenceArg & RISCVFenceField::R) != 0) O << 'r'; if ((FenceArg & RISCVFenceField::W) != 0) O << 'w'; if (FenceArg == 0) O << "unknown"; } void RISCVInstPrinter::printFRMArg(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O) { auto FRMArg = static_cast(MI->getOperand(OpNo).getImm()); O << RISCVFPRndMode::roundingModeToString(FRMArg); } + +void RISCVInstPrinter::printAtomicMemOp(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, + raw_ostream &O) { + const MCOperand &MO = MI->getOperand(OpNo); + + assert(MO.isReg() && "printAtomicMemOp can only print register operands"); + O << "("; + printRegName(O, MO.getReg()); + O << ")"; + return; +} Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h (revision 354469) @@ -1,54 +1,56 @@ //===-- RISCVInstPrinter.h - Convert RISCV MCInst to asm syntax ---*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This class prints a RISCV MCInst to a .s file. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVINSTPRINTER_H #define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVINSTPRINTER_H #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/MC/MCInstPrinter.h" namespace llvm { class MCOperand; class RISCVInstPrinter : public MCInstPrinter { public: RISCVInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, const MCRegisterInfo &MRI) : MCInstPrinter(MAI, MII, MRI) {} void printInst(const MCInst *MI, raw_ostream &O, StringRef Annot, const MCSubtargetInfo &STI) override; void printRegName(raw_ostream &O, unsigned RegNo) const override; void printOperand(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O, const char *Modifier = nullptr); void printCSRSystemRegister(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O); void printFenceArg(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O); void printFRMArg(const MCInst *MI, unsigned OpNo, const MCSubtargetInfo &STI, raw_ostream &O); + void printAtomicMemOp(const MCInst *MI, unsigned OpNo, + const MCSubtargetInfo &STI, raw_ostream &O); // Autogenerated by tblgen. void printInstruction(const MCInst *MI, const MCSubtargetInfo &STI, raw_ostream &O); bool printAliasInstr(const MCInst *MI, const MCSubtargetInfo &STI, raw_ostream &O); void printCustomAliasOperand(const MCInst *MI, unsigned OpIdx, unsigned PrintMethodIdx, const MCSubtargetInfo &STI, raw_ostream &O); static const char *getRegisterName(unsigned RegNo, unsigned AltIdx = RISCV::ABIRegAltName); }; } // namespace llvm #endif Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.cpp (revision 354469) @@ -1,27 +1,47 @@ //===-- RISCVMCAsmInfo.cpp - RISCV Asm properties -------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains the declarations of the RISCVMCAsmInfo properties. // //===----------------------------------------------------------------------===// #include "RISCVMCAsmInfo.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/MC/MCStreamer.h" using namespace llvm; void RISCVMCAsmInfo::anchor() {} RISCVMCAsmInfo::RISCVMCAsmInfo(const Triple &TT) { CodePointerSize = CalleeSaveStackSlotSize = TT.isArch64Bit() ? 8 : 4; CommentString = "#"; AlignmentIsInBytes = false; SupportsDebugInformation = true; ExceptionsType = ExceptionHandling::DwarfCFI; Data16bitsDirective = "\t.half\t"; Data32bitsDirective = "\t.word\t"; +} + +const MCExpr *RISCVMCAsmInfo::getExprForFDESymbol(const MCSymbol *Sym, + unsigned Encoding, + MCStreamer &Streamer) const { + if (!(Encoding & dwarf::DW_EH_PE_pcrel)) + return MCAsmInfo::getExprForFDESymbol(Sym, Encoding, Streamer); + + // The default symbol subtraction results in an ADD/SUB relocation pair. + // Processing this relocation pair is problematic when linker relaxation is + // enabled, so we follow binutils in using the R_RISCV_32_PCREL relocation + // for the FDE initial location. + MCContext &Ctx = Streamer.getContext(); + const MCExpr *ME = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx); + assert(Encoding & dwarf::DW_EH_PE_sdata4 && "Unexpected encoding"); + return RISCVMCExpr::create(ME, RISCVMCExpr::VK_RISCV_32_PCREL, Ctx); } Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h (revision 354469) @@ -1,30 +1,33 @@ //===-- RISCVMCAsmInfo.h - RISCV Asm Info ----------------------*- C++ -*--===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains the declaration of the RISCVMCAsmInfo class. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCASMINFO_H #define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCASMINFO_H #include "llvm/MC/MCAsmInfoELF.h" namespace llvm { class Triple; class RISCVMCAsmInfo : public MCAsmInfoELF { void anchor() override; public: explicit RISCVMCAsmInfo(const Triple &TargetTriple); + + const MCExpr *getExprForFDESymbol(const MCSymbol *Sym, unsigned Encoding, + MCStreamer &Streamer) const override; }; } // namespace llvm #endif Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp (revision 354469) @@ -1,369 +1,370 @@ //===-- RISCVMCCodeEmitter.cpp - Convert RISCV code to machine code -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the RISCVMCCodeEmitter class. // //===----------------------------------------------------------------------===// #include "MCTargetDesc/RISCVFixupKinds.h" #include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "Utils/RISCVBaseInfo.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Casting.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "mccodeemitter" STATISTIC(MCNumEmitted, "Number of MC instructions emitted"); STATISTIC(MCNumFixups, "Number of MC fixups created"); namespace { class RISCVMCCodeEmitter : public MCCodeEmitter { RISCVMCCodeEmitter(const RISCVMCCodeEmitter &) = delete; void operator=(const RISCVMCCodeEmitter &) = delete; MCContext &Ctx; MCInstrInfo const &MCII; public: RISCVMCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) : Ctx(ctx), MCII(MCII) {} ~RISCVMCCodeEmitter() override {} void encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const override; void expandFunctionCall(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; void expandAddTPRel(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; /// TableGen'erated function for getting the binary encoding for an /// instruction. uint64_t getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; /// Return binary encoding of operand. If the machine operand requires /// relocation, record the relocation and return zero. unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; unsigned getImmOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; }; } // end anonymous namespace MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { return new RISCVMCCodeEmitter(Ctx, MCII); } // Expand PseudoCALL(Reg) and PseudoTAIL to AUIPC and JALR with relocation // types. We expand PseudoCALL(Reg) and PseudoTAIL while encoding, meaning AUIPC // and JALR won't go through RISCV MC to MC compressed instruction // transformation. This is acceptable because AUIPC has no 16-bit form and // C_JALR have no immediate operand field. We let linker relaxation deal with // it. When linker relaxation enabled, AUIPC and JALR have chance relax to JAL. // If C extension is enabled, JAL has chance relax to C_JAL. void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { MCInst TmpInst; MCOperand Func; unsigned Ra; if (MI.getOpcode() == RISCV::PseudoTAIL) { Func = MI.getOperand(0); Ra = RISCV::X6; } else if (MI.getOpcode() == RISCV::PseudoCALLReg) { Func = MI.getOperand(1); Ra = MI.getOperand(0).getReg(); } else { Func = MI.getOperand(0); Ra = RISCV::X1; } uint32_t Binary; assert(Func.isExpr() && "Expected expression"); const MCExpr *CallExpr = Func.getExpr(); // Emit AUIPC Ra, Func with R_RISCV_CALL relocation type. TmpInst = MCInstBuilder(RISCV::AUIPC) .addReg(Ra) .addOperand(MCOperand::createExpr(CallExpr)); Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); support::endian::write(OS, Binary, support::little); if (MI.getOpcode() == RISCV::PseudoTAIL) // Emit JALR X0, X6, 0 TmpInst = MCInstBuilder(RISCV::JALR).addReg(RISCV::X0).addReg(Ra).addImm(0); else // Emit JALR Ra, Ra, 0 TmpInst = MCInstBuilder(RISCV::JALR).addReg(Ra).addReg(Ra).addImm(0); Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); support::endian::write(OS, Binary, support::little); } // Expand PseudoAddTPRel to a simple ADD with the correct relocation. void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { MCOperand DestReg = MI.getOperand(0); MCOperand SrcReg = MI.getOperand(1); MCOperand TPReg = MI.getOperand(2); assert(TPReg.isReg() && TPReg.getReg() == RISCV::X4 && "Expected thread pointer as second input to TP-relative add"); MCOperand SrcSymbol = MI.getOperand(3); assert(SrcSymbol.isExpr() && "Expected expression as third input to TP-relative add"); const RISCVMCExpr *Expr = dyn_cast(SrcSymbol.getExpr()); assert(Expr && Expr->getKind() == RISCVMCExpr::VK_RISCV_TPREL_ADD && "Expected tprel_add relocation on TP-relative symbol"); // Emit the correct tprel_add relocation for the symbol. Fixups.push_back(MCFixup::create( 0, Expr, MCFixupKind(RISCV::fixup_riscv_tprel_add), MI.getLoc())); // Emit fixup_riscv_relax for tprel_add where the relax feature is enabled. if (STI.getFeatureBits()[RISCV::FeatureRelax]) { const MCConstantExpr *Dummy = MCConstantExpr::create(0, Ctx); Fixups.push_back(MCFixup::create( 0, Dummy, MCFixupKind(RISCV::fixup_riscv_relax), MI.getLoc())); } // Emit a normal ADD instruction with the given operands. MCInst TmpInst = MCInstBuilder(RISCV::ADD) .addOperand(DestReg) .addOperand(SrcReg) .addOperand(TPReg); uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); support::endian::write(OS, Binary, support::little); } void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); // Get byte count of instruction. unsigned Size = Desc.getSize(); if (MI.getOpcode() == RISCV::PseudoCALLReg || MI.getOpcode() == RISCV::PseudoCALL || MI.getOpcode() == RISCV::PseudoTAIL) { expandFunctionCall(MI, OS, Fixups, STI); MCNumEmitted += 2; return; } if (MI.getOpcode() == RISCV::PseudoAddTPRel) { expandAddTPRel(MI, OS, Fixups, STI); MCNumEmitted += 1; return; } switch (Size) { default: llvm_unreachable("Unhandled encodeInstruction length!"); case 2: { uint16_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); support::endian::write(OS, Bits, support::little); break; } case 4: { uint32_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); support::endian::write(OS, Bits, support::little); break; } } ++MCNumEmitted; // Keep track of the # of mi's emitted. } unsigned RISCVMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { if (MO.isReg()) return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg()); if (MO.isImm()) return static_cast(MO.getImm()); llvm_unreachable("Unhandled expression!"); return 0; } unsigned RISCVMCCodeEmitter::getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { const MCOperand &MO = MI.getOperand(OpNo); if (MO.isImm()) { unsigned Res = MO.getImm(); assert((Res & 1) == 0 && "LSB is non-zero"); return Res >> 1; } return getImmOpValue(MI, OpNo, Fixups, STI); } unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { bool EnableRelax = STI.getFeatureBits()[RISCV::FeatureRelax]; const MCOperand &MO = MI.getOperand(OpNo); MCInstrDesc const &Desc = MCII.get(MI.getOpcode()); unsigned MIFrm = Desc.TSFlags & RISCVII::InstFormatMask; // If the destination is an immediate, there is nothing to do. if (MO.isImm()) return MO.getImm(); assert(MO.isExpr() && "getImmOpValue expects only expressions or immediates"); const MCExpr *Expr = MO.getExpr(); MCExpr::ExprKind Kind = Expr->getKind(); RISCV::Fixups FixupKind = RISCV::fixup_riscv_invalid; bool RelaxCandidate = false; if (Kind == MCExpr::Target) { const RISCVMCExpr *RVExpr = cast(Expr); switch (RVExpr->getKind()) { case RISCVMCExpr::VK_RISCV_None: case RISCVMCExpr::VK_RISCV_Invalid: + case RISCVMCExpr::VK_RISCV_32_PCREL: llvm_unreachable("Unhandled fixup kind!"); case RISCVMCExpr::VK_RISCV_TPREL_ADD: // tprel_add is only used to indicate that a relocation should be emitted // for an add instruction used in TP-relative addressing. It should not be // expanded as if representing an actual instruction operand and so to // encounter it here is an error. llvm_unreachable( "VK_RISCV_TPREL_ADD should not represent an instruction operand"); case RISCVMCExpr::VK_RISCV_LO: if (MIFrm == RISCVII::InstFormatI) FixupKind = RISCV::fixup_riscv_lo12_i; else if (MIFrm == RISCVII::InstFormatS) FixupKind = RISCV::fixup_riscv_lo12_s; else llvm_unreachable("VK_RISCV_LO used with unexpected instruction format"); RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_HI: FixupKind = RISCV::fixup_riscv_hi20; RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_PCREL_LO: if (MIFrm == RISCVII::InstFormatI) FixupKind = RISCV::fixup_riscv_pcrel_lo12_i; else if (MIFrm == RISCVII::InstFormatS) FixupKind = RISCV::fixup_riscv_pcrel_lo12_s; else llvm_unreachable( "VK_RISCV_PCREL_LO used with unexpected instruction format"); RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_PCREL_HI: FixupKind = RISCV::fixup_riscv_pcrel_hi20; RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_GOT_HI: FixupKind = RISCV::fixup_riscv_got_hi20; break; case RISCVMCExpr::VK_RISCV_TPREL_LO: if (MIFrm == RISCVII::InstFormatI) FixupKind = RISCV::fixup_riscv_tprel_lo12_i; else if (MIFrm == RISCVII::InstFormatS) FixupKind = RISCV::fixup_riscv_tprel_lo12_s; else llvm_unreachable( "VK_RISCV_TPREL_LO used with unexpected instruction format"); RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_TPREL_HI: FixupKind = RISCV::fixup_riscv_tprel_hi20; RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_TLS_GOT_HI: FixupKind = RISCV::fixup_riscv_tls_got_hi20; break; case RISCVMCExpr::VK_RISCV_TLS_GD_HI: FixupKind = RISCV::fixup_riscv_tls_gd_hi20; break; case RISCVMCExpr::VK_RISCV_CALL: FixupKind = RISCV::fixup_riscv_call; RelaxCandidate = true; break; case RISCVMCExpr::VK_RISCV_CALL_PLT: FixupKind = RISCV::fixup_riscv_call_plt; RelaxCandidate = true; break; } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { if (Desc.getOpcode() == RISCV::JAL) { FixupKind = RISCV::fixup_riscv_jal; } else if (MIFrm == RISCVII::InstFormatB) { FixupKind = RISCV::fixup_riscv_branch; } else if (MIFrm == RISCVII::InstFormatCJ) { FixupKind = RISCV::fixup_riscv_rvc_jump; } else if (MIFrm == RISCVII::InstFormatCB) { FixupKind = RISCV::fixup_riscv_rvc_branch; } } assert(FixupKind != RISCV::fixup_riscv_invalid && "Unhandled expression!"); Fixups.push_back( MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc())); ++MCNumFixups; // Ensure an R_RISCV_RELAX relocation will be emitted if linker relaxation is // enabled and the current fixup will result in a relocation that may be // relaxed. if (EnableRelax && RelaxCandidate) { const MCConstantExpr *Dummy = MCConstantExpr::create(0, Ctx); Fixups.push_back( MCFixup::create(0, Dummy, MCFixupKind(RISCV::fixup_riscv_relax), MI.getLoc())); ++MCNumFixups; } return 0; } #include "RISCVGenMCCodeEmitter.inc" Index: head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h (revision 354469) @@ -1,93 +1,94 @@ //===-- RISCVMCExpr.h - RISCV specific MC expression classes ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file describes RISCV-specific MCExprs, used for modifiers like // "%hi" or "%lo" etc., // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H #define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H #include "llvm/MC/MCExpr.h" namespace llvm { class StringRef; class MCOperand; class RISCVMCExpr : public MCTargetExpr { public: enum VariantKind { VK_RISCV_None, VK_RISCV_LO, VK_RISCV_HI, VK_RISCV_PCREL_LO, VK_RISCV_PCREL_HI, VK_RISCV_GOT_HI, VK_RISCV_TPREL_LO, VK_RISCV_TPREL_HI, VK_RISCV_TPREL_ADD, VK_RISCV_TLS_GOT_HI, VK_RISCV_TLS_GD_HI, VK_RISCV_CALL, VK_RISCV_CALL_PLT, + VK_RISCV_32_PCREL, VK_RISCV_Invalid }; private: const MCExpr *Expr; const VariantKind Kind; int64_t evaluateAsInt64(int64_t Value) const; bool evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const; explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} public: static const RISCVMCExpr *create(const MCExpr *Expr, VariantKind Kind, MCContext &Ctx); VariantKind getKind() const { return Kind; } const MCExpr *getSubExpr() const { return Expr; } /// Get the corresponding PC-relative HI fixup that a VK_RISCV_PCREL_LO /// points to. /// /// \returns nullptr if this isn't a VK_RISCV_PCREL_LO pointing to a /// known PC-relative HI fixup. const MCFixup *getPCRelHiFixup() const; void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const override; void visitUsedExpr(MCStreamer &Streamer) const override; MCFragment *findAssociatedFragment() const override { return getSubExpr()->findAssociatedFragment(); } void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override; bool evaluateAsConstant(int64_t &Res) const; static bool classof(const MCExpr *E) { return E->getKind() == MCExpr::Target; } static bool classof(const RISCVMCExpr *) { return true; } static VariantKind getVariantKindForName(StringRef name); static StringRef getVariantKindName(VariantKind Kind); }; } // end namespace llvm. #endif Index: head/contrib/llvm/lib/Target/RISCV/RISCVInstrInfoA.td =================================================================== --- head/contrib/llvm/lib/Target/RISCV/RISCVInstrInfoA.td (revision 354468) +++ head/contrib/llvm/lib/Target/RISCV/RISCVInstrInfoA.td (revision 354469) @@ -1,353 +1,371 @@ //===-- RISCVInstrInfoA.td - RISC-V 'A' instructions -------*- tablegen -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file describes the RISC-V instructions from the standard 'A', Atomic // Instructions extension. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// +// Operand and SDNode transformation definitions. +//===----------------------------------------------------------------------===// + +// A parse method for (${gpr}) or 0(${gpr}), where the 0 is be silently ignored. +// Used for GNU as Compatibility. +def AtomicMemOpOperand : AsmOperandClass { + let Name = "AtomicMemOpOperand"; + let RenderMethod = "addRegOperands"; + let PredicateMethod = "isReg"; + let ParserMethod = "parseAtomicMemOp"; +} + +def GPRMemAtomic : RegisterOperand { + let ParserMatchClass = AtomicMemOpOperand; + let PrintMethod = "printAtomicMemOp"; +} + +//===----------------------------------------------------------------------===// // Instruction class templates //===----------------------------------------------------------------------===// let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in class LR_r funct3, string opcodestr> : RVInstRAtomic<0b00010, aq, rl, funct3, OPC_AMO, - (outs GPR:$rd), (ins GPR:$rs1), - opcodestr, "$rd, (${rs1})"> { + (outs GPR:$rd), (ins GPRMemAtomic:$rs1), + opcodestr, "$rd, $rs1"> { let rs2 = 0; } multiclass LR_r_aq_rl funct3, string opcodestr> { def "" : LR_r<0, 0, funct3, opcodestr>; def _AQ : LR_r<1, 0, funct3, opcodestr # ".aq">; def _RL : LR_r<0, 1, funct3, opcodestr # ".rl">; def _AQ_RL : LR_r<1, 1, funct3, opcodestr # ".aqrl">; } let hasSideEffects = 0, mayLoad = 1, mayStore = 1 in class AMO_rr funct5, bit aq, bit rl, bits<3> funct3, string opcodestr> : RVInstRAtomic; + (outs GPR:$rd), (ins GPRMemAtomic:$rs1, GPR:$rs2), + opcodestr, "$rd, $rs2, $rs1">; multiclass AMO_rr_aq_rl funct5, bits<3> funct3, string opcodestr> { def "" : AMO_rr; def _AQ : AMO_rr; def _RL : AMO_rr; def _AQ_RL : AMO_rr; } multiclass AtomicStPat { def : Pat<(StoreOp GPR:$rs1, StTy:$rs2), (Inst StTy:$rs2, GPR:$rs1, 0)>; def : Pat<(StoreOp AddrFI:$rs1, StTy:$rs2), (Inst StTy:$rs2, AddrFI:$rs1, 0)>; def : Pat<(StoreOp (add GPR:$rs1, simm12:$imm12), StTy:$rs2), (Inst StTy:$rs2, GPR:$rs1, simm12:$imm12)>; def : Pat<(StoreOp (add AddrFI:$rs1, simm12:$imm12), StTy:$rs2), (Inst StTy:$rs2, AddrFI:$rs1, simm12:$imm12)>; def : Pat<(StoreOp (IsOrAdd AddrFI:$rs1, simm12:$imm12), StTy:$rs2), (Inst StTy:$rs2, AddrFI:$rs1, simm12:$imm12)>; } //===----------------------------------------------------------------------===// // Instructions //===----------------------------------------------------------------------===// let Predicates = [HasStdExtA] in { defm LR_W : LR_r_aq_rl<0b010, "lr.w">; defm SC_W : AMO_rr_aq_rl<0b00011, 0b010, "sc.w">; defm AMOSWAP_W : AMO_rr_aq_rl<0b00001, 0b010, "amoswap.w">; defm AMOADD_W : AMO_rr_aq_rl<0b00000, 0b010, "amoadd.w">; defm AMOXOR_W : AMO_rr_aq_rl<0b00100, 0b010, "amoxor.w">; defm AMOAND_W : AMO_rr_aq_rl<0b01100, 0b010, "amoand.w">; defm AMOOR_W : AMO_rr_aq_rl<0b01000, 0b010, "amoor.w">; defm AMOMIN_W : AMO_rr_aq_rl<0b10000, 0b010, "amomin.w">; defm AMOMAX_W : AMO_rr_aq_rl<0b10100, 0b010, "amomax.w">; defm AMOMINU_W : AMO_rr_aq_rl<0b11000, 0b010, "amominu.w">; defm AMOMAXU_W : AMO_rr_aq_rl<0b11100, 0b010, "amomaxu.w">; } // Predicates = [HasStdExtA] let Predicates = [HasStdExtA, IsRV64] in { defm LR_D : LR_r_aq_rl<0b011, "lr.d">; defm SC_D : AMO_rr_aq_rl<0b00011, 0b011, "sc.d">; defm AMOSWAP_D : AMO_rr_aq_rl<0b00001, 0b011, "amoswap.d">; defm AMOADD_D : AMO_rr_aq_rl<0b00000, 0b011, "amoadd.d">; defm AMOXOR_D : AMO_rr_aq_rl<0b00100, 0b011, "amoxor.d">; defm AMOAND_D : AMO_rr_aq_rl<0b01100, 0b011, "amoand.d">; defm AMOOR_D : AMO_rr_aq_rl<0b01000, 0b011, "amoor.d">; defm AMOMIN_D : AMO_rr_aq_rl<0b10000, 0b011, "amomin.d">; defm AMOMAX_D : AMO_rr_aq_rl<0b10100, 0b011, "amomax.d">; defm AMOMINU_D : AMO_rr_aq_rl<0b11000, 0b011, "amominu.d">; defm AMOMAXU_D : AMO_rr_aq_rl<0b11100, 0b011, "amomaxu.d">; } // Predicates = [HasStdExtA, IsRV64] //===----------------------------------------------------------------------===// // Pseudo-instructions and codegen patterns //===----------------------------------------------------------------------===// let Predicates = [HasStdExtA] in { /// Atomic loads and stores // Fences will be inserted for atomic load/stores according to the logic in // RISCVTargetLowering::{emitLeadingFence,emitTrailingFence}. defm : LdPat; defm : LdPat; defm : LdPat; defm : AtomicStPat; defm : AtomicStPat; defm : AtomicStPat; /// AMOs multiclass AMOPat { def : PatGprGpr(AtomicOp#"_monotonic"), !cast(BaseInst)>; def : PatGprGpr(AtomicOp#"_acquire"), !cast(BaseInst#"_AQ")>; def : PatGprGpr(AtomicOp#"_release"), !cast(BaseInst#"_RL")>; def : PatGprGpr(AtomicOp#"_acq_rel"), !cast(BaseInst#"_AQ_RL")>; def : PatGprGpr(AtomicOp#"_seq_cst"), !cast(BaseInst#"_AQ_RL")>; } defm : AMOPat<"atomic_swap_32", "AMOSWAP_W">; defm : AMOPat<"atomic_load_add_32", "AMOADD_W">; defm : AMOPat<"atomic_load_and_32", "AMOAND_W">; defm : AMOPat<"atomic_load_or_32", "AMOOR_W">; defm : AMOPat<"atomic_load_xor_32", "AMOXOR_W">; defm : AMOPat<"atomic_load_max_32", "AMOMAX_W">; defm : AMOPat<"atomic_load_min_32", "AMOMIN_W">; defm : AMOPat<"atomic_load_umax_32", "AMOMAXU_W">; defm : AMOPat<"atomic_load_umin_32", "AMOMINU_W">; def : Pat<(atomic_load_sub_32_monotonic GPR:$addr, GPR:$incr), (AMOADD_W GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_32_acquire GPR:$addr, GPR:$incr), (AMOADD_W_AQ GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_32_release GPR:$addr, GPR:$incr), (AMOADD_W_RL GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_32_acq_rel GPR:$addr, GPR:$incr), (AMOADD_W_AQ_RL GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_32_seq_cst GPR:$addr, GPR:$incr), (AMOADD_W_AQ_RL GPR:$addr, (SUB X0, GPR:$incr))>; /// Pseudo AMOs class PseudoAMO : Pseudo<(outs GPR:$res, GPR:$scratch), (ins GPR:$addr, GPR:$incr, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } def PseudoAtomicLoadNand32 : PseudoAMO; // Ordering constants must be kept in sync with the AtomicOrdering enum in // AtomicOrdering.h. def : Pat<(atomic_load_nand_32_monotonic GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand32 GPR:$addr, GPR:$incr, 2)>; def : Pat<(atomic_load_nand_32_acquire GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand32 GPR:$addr, GPR:$incr, 4)>; def : Pat<(atomic_load_nand_32_release GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand32 GPR:$addr, GPR:$incr, 5)>; def : Pat<(atomic_load_nand_32_acq_rel GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand32 GPR:$addr, GPR:$incr, 6)>; def : Pat<(atomic_load_nand_32_seq_cst GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand32 GPR:$addr, GPR:$incr, 7)>; class PseudoMaskedAMO : Pseudo<(outs GPR:$res, GPR:$scratch), (ins GPR:$addr, GPR:$incr, GPR:$mask, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } class PseudoMaskedAMOMinMax : Pseudo<(outs GPR:$res, GPR:$scratch1, GPR:$scratch2), (ins GPR:$addr, GPR:$incr, GPR:$mask, ixlenimm:$sextshamt, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch1," "@earlyclobber $scratch2"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } class PseudoMaskedAMOUMinUMax : Pseudo<(outs GPR:$res, GPR:$scratch1, GPR:$scratch2), (ins GPR:$addr, GPR:$incr, GPR:$mask, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch1," "@earlyclobber $scratch2"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } class PseudoMaskedAMOPat : Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, imm:$ordering), (AMOInst GPR:$addr, GPR:$incr, GPR:$mask, imm:$ordering)>; class PseudoMaskedAMOMinMaxPat : Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt, imm:$ordering), (AMOInst GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt, imm:$ordering)>; def PseudoMaskedAtomicSwap32 : PseudoMaskedAMO; def : PseudoMaskedAMOPat; def PseudoMaskedAtomicLoadAdd32 : PseudoMaskedAMO; def : PseudoMaskedAMOPat; def PseudoMaskedAtomicLoadSub32 : PseudoMaskedAMO; def : PseudoMaskedAMOPat; def PseudoMaskedAtomicLoadNand32 : PseudoMaskedAMO; def : PseudoMaskedAMOPat; def PseudoMaskedAtomicLoadMax32 : PseudoMaskedAMOMinMax; def : PseudoMaskedAMOMinMaxPat; def PseudoMaskedAtomicLoadMin32 : PseudoMaskedAMOMinMax; def : PseudoMaskedAMOMinMaxPat; def PseudoMaskedAtomicLoadUMax32 : PseudoMaskedAMOUMinUMax; def : PseudoMaskedAMOPat; def PseudoMaskedAtomicLoadUMin32 : PseudoMaskedAMOUMinUMax; def : PseudoMaskedAMOPat; /// Compare and exchange class PseudoCmpXchg : Pseudo<(outs GPR:$res, GPR:$scratch), (ins GPR:$addr, GPR:$cmpval, GPR:$newval, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } // Ordering constants must be kept in sync with the AtomicOrdering enum in // AtomicOrdering.h. multiclass PseudoCmpXchgPat { def : Pat<(!cast(Op#"_monotonic") GPR:$addr, GPR:$cmp, GPR:$new), (CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 2)>; def : Pat<(!cast(Op#"_acquire") GPR:$addr, GPR:$cmp, GPR:$new), (CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 4)>; def : Pat<(!cast(Op#"_release") GPR:$addr, GPR:$cmp, GPR:$new), (CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 5)>; def : Pat<(!cast(Op#"_acq_rel") GPR:$addr, GPR:$cmp, GPR:$new), (CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 6)>; def : Pat<(!cast(Op#"_seq_cst") GPR:$addr, GPR:$cmp, GPR:$new), (CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 7)>; } def PseudoCmpXchg32 : PseudoCmpXchg; defm : PseudoCmpXchgPat<"atomic_cmp_swap_32", PseudoCmpXchg32>; def PseudoMaskedCmpXchg32 : Pseudo<(outs GPR:$res, GPR:$scratch), (ins GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, ixlenimm:$ordering), []> { let Constraints = "@earlyclobber $res,@earlyclobber $scratch"; let mayLoad = 1; let mayStore = 1; let hasSideEffects = 0; } def : Pat<(int_riscv_masked_cmpxchg_i32 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering), (PseudoMaskedCmpXchg32 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering)>; } // Predicates = [HasStdExtA] let Predicates = [HasStdExtA, IsRV64] in { /// 64-bit atomic loads and stores // Fences will be inserted for atomic load/stores according to the logic in // RISCVTargetLowering::{emitLeadingFence,emitTrailingFence}. defm : LdPat; defm : AtomicStPat; defm : AMOPat<"atomic_swap_64", "AMOSWAP_D">; defm : AMOPat<"atomic_load_add_64", "AMOADD_D">; defm : AMOPat<"atomic_load_and_64", "AMOAND_D">; defm : AMOPat<"atomic_load_or_64", "AMOOR_D">; defm : AMOPat<"atomic_load_xor_64", "AMOXOR_D">; defm : AMOPat<"atomic_load_max_64", "AMOMAX_D">; defm : AMOPat<"atomic_load_min_64", "AMOMIN_D">; defm : AMOPat<"atomic_load_umax_64", "AMOMAXU_D">; defm : AMOPat<"atomic_load_umin_64", "AMOMINU_D">; /// 64-bit AMOs def : Pat<(atomic_load_sub_64_monotonic GPR:$addr, GPR:$incr), (AMOADD_D GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_64_acquire GPR:$addr, GPR:$incr), (AMOADD_D_AQ GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_64_release GPR:$addr, GPR:$incr), (AMOADD_D_RL GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_64_acq_rel GPR:$addr, GPR:$incr), (AMOADD_D_AQ_RL GPR:$addr, (SUB X0, GPR:$incr))>; def : Pat<(atomic_load_sub_64_seq_cst GPR:$addr, GPR:$incr), (AMOADD_D_AQ_RL GPR:$addr, (SUB X0, GPR:$incr))>; /// 64-bit pseudo AMOs def PseudoAtomicLoadNand64 : PseudoAMO; // Ordering constants must be kept in sync with the AtomicOrdering enum in // AtomicOrdering.h. def : Pat<(atomic_load_nand_64_monotonic GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand64 GPR:$addr, GPR:$incr, 2)>; def : Pat<(atomic_load_nand_64_acquire GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand64 GPR:$addr, GPR:$incr, 4)>; def : Pat<(atomic_load_nand_64_release GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand64 GPR:$addr, GPR:$incr, 5)>; def : Pat<(atomic_load_nand_64_acq_rel GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand64 GPR:$addr, GPR:$incr, 6)>; def : Pat<(atomic_load_nand_64_seq_cst GPR:$addr, GPR:$incr), (PseudoAtomicLoadNand64 GPR:$addr, GPR:$incr, 7)>; def : PseudoMaskedAMOPat; def : PseudoMaskedAMOPat; def : PseudoMaskedAMOPat; def : PseudoMaskedAMOPat; def : PseudoMaskedAMOMinMaxPat; def : PseudoMaskedAMOMinMaxPat; def : PseudoMaskedAMOPat; def : PseudoMaskedAMOPat; /// 64-bit compare and exchange def PseudoCmpXchg64 : PseudoCmpXchg; defm : PseudoCmpXchgPat<"atomic_cmp_swap_64", PseudoCmpXchg64>; def : Pat<(int_riscv_masked_cmpxchg_i64 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering), (PseudoMaskedCmpXchg32 GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, imm:$ordering)>; } // Predicates = [HasStdExtA, IsRV64] Index: head/contrib/llvm/tools/clang/lib/Basic/Targets.cpp =================================================================== --- head/contrib/llvm/tools/clang/lib/Basic/Targets.cpp (revision 354468) +++ head/contrib/llvm/tools/clang/lib/Basic/Targets.cpp (revision 354469) @@ -1,664 +1,675 @@ //===--- Targets.cpp - Implement target feature support -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements construction of a TargetInfo object from a // target triple. // //===----------------------------------------------------------------------===// #include "Targets.h" #include "Targets/AArch64.h" #include "Targets/AMDGPU.h" #include "Targets/ARC.h" #include "Targets/ARM.h" #include "Targets/AVR.h" #include "Targets/BPF.h" #include "Targets/Hexagon.h" #include "Targets/Lanai.h" #include "Targets/Le64.h" #include "Targets/MSP430.h" #include "Targets/Mips.h" #include "Targets/NVPTX.h" #include "Targets/OSTargets.h" #include "Targets/PNaCl.h" #include "Targets/PPC.h" #include "Targets/RISCV.h" #include "Targets/SPIR.h" #include "Targets/Sparc.h" #include "Targets/SystemZ.h" #include "Targets/TCE.h" #include "Targets/WebAssembly.h" #include "Targets/X86.h" #include "Targets/XCore.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" using namespace clang; namespace clang { namespace targets { //===----------------------------------------------------------------------===// // Common code shared among targets. //===----------------------------------------------------------------------===// /// DefineStd - Define a macro name and standard variants. For example if /// MacroName is "unix", then this will define "__unix", "__unix__", and "unix" /// when in GNU mode. void DefineStd(MacroBuilder &Builder, StringRef MacroName, const LangOptions &Opts) { assert(MacroName[0] != '_' && "Identifier should be in the user's namespace"); // If in GNU mode (e.g. -std=gnu99 but not -std=c99) define the raw identifier // in the user's namespace. if (Opts.GNUMode) Builder.defineMacro(MacroName); // Define __unix. Builder.defineMacro("__" + MacroName); // Define __unix__. Builder.defineMacro("__" + MacroName + "__"); } void defineCPUMacros(MacroBuilder &Builder, StringRef CPUName, bool Tuning) { Builder.defineMacro("__" + CPUName); Builder.defineMacro("__" + CPUName + "__"); if (Tuning) Builder.defineMacro("__tune_" + CPUName + "__"); } void addCygMingDefines(const LangOptions &Opts, MacroBuilder &Builder) { // Mingw and cygwin define __declspec(a) to __attribute__((a)). Clang // supports __declspec natively under -fms-extensions, but we define a no-op // __declspec macro anyway for pre-processor compatibility. if (Opts.MicrosoftExt) Builder.defineMacro("__declspec", "__declspec"); else Builder.defineMacro("__declspec(a)", "__attribute__((a))"); if (!Opts.MicrosoftExt) { // Provide macros for all the calling convention keywords. Provide both // single and double underscore prefixed variants. These are available on // x64 as well as x86, even though they have no effect. const char *CCs[] = {"cdecl", "stdcall", "fastcall", "thiscall", "pascal"}; for (const char *CC : CCs) { std::string GCCSpelling = "__attribute__((__"; GCCSpelling += CC; GCCSpelling += "__))"; Builder.defineMacro(Twine("_") + CC, GCCSpelling); Builder.defineMacro(Twine("__") + CC, GCCSpelling); } } } //===----------------------------------------------------------------------===// // Driver code //===----------------------------------------------------------------------===// TargetInfo *AllocateTarget(const llvm::Triple &Triple, const TargetOptions &Opts) { llvm::Triple::OSType os = Triple.getOS(); switch (Triple.getArch()) { default: return nullptr; case llvm::Triple::arc: return new ARCTargetInfo(Triple, Opts); case llvm::Triple::xcore: return new XCoreTargetInfo(Triple, Opts); case llvm::Triple::hexagon: return new HexagonTargetInfo(Triple, Opts); case llvm::Triple::lanai: return new LanaiTargetInfo(Triple, Opts); case llvm::Triple::aarch64: if (Triple.isOSDarwin()) return new DarwinAArch64TargetInfo(Triple, Opts); switch (os) { case llvm::Triple::CloudABI: return new CloudABITargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::Fuchsia: return new FuchsiaTargetInfo(Triple, Opts); case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::Win32: switch (Triple.getEnvironment()) { case llvm::Triple::GNU: return new MinGWARM64TargetInfo(Triple, Opts); case llvm::Triple::MSVC: default: // Assume MSVC for unknown environments return new MicrosoftARM64TargetInfo(Triple, Opts); } default: return new AArch64leTargetInfo(Triple, Opts); } case llvm::Triple::aarch64_be: switch (os) { case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::Fuchsia: return new FuchsiaTargetInfo(Triple, Opts); case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); default: return new AArch64beTargetInfo(Triple, Opts); } case llvm::Triple::arm: case llvm::Triple::thumb: if (Triple.isOSBinFormatMachO()) return new DarwinARMTargetInfo(Triple, Opts); switch (os) { case llvm::Triple::CloudABI: return new CloudABITargetInfo(Triple, Opts); case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); case llvm::Triple::Win32: switch (Triple.getEnvironment()) { case llvm::Triple::Cygnus: return new CygwinARMTargetInfo(Triple, Opts); case llvm::Triple::GNU: return new MinGWARMTargetInfo(Triple, Opts); case llvm::Triple::Itanium: return new ItaniumWindowsARMleTargetInfo(Triple, Opts); case llvm::Triple::MSVC: default: // Assume MSVC for unknown environments return new MicrosoftARMleTargetInfo(Triple, Opts); } default: return new ARMleTargetInfo(Triple, Opts); } case llvm::Triple::armeb: case llvm::Triple::thumbeb: if (Triple.isOSDarwin()) return new DarwinARMTargetInfo(Triple, Opts); switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); default: return new ARMbeTargetInfo(Triple, Opts); } case llvm::Triple::avr: return new AVRTargetInfo(Triple, Opts); case llvm::Triple::bpfeb: case llvm::Triple::bpfel: return new BPFTargetInfo(Triple, Opts); case llvm::Triple::msp430: return new MSP430TargetInfo(Triple, Opts); case llvm::Triple::mips: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); default: return new MipsTargetInfo(Triple, Opts); } case llvm::Triple::mipsel: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); default: return new MipsTargetInfo(Triple, Opts); } case llvm::Triple::mips64: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); default: return new MipsTargetInfo(Triple, Opts); } case llvm::Triple::mips64el: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); default: return new MipsTargetInfo(Triple, Opts); } case llvm::Triple::le32: switch (os) { case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); default: return nullptr; } case llvm::Triple::le64: return new Le64TargetInfo(Triple, Opts); case llvm::Triple::ppc: if (Triple.isOSDarwin()) return new DarwinPPC32TargetInfo(Triple, Opts); switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); case llvm::Triple::AIX: return new AIXPPC32TargetInfo(Triple, Opts); default: return new PPC32TargetInfo(Triple, Opts); } case llvm::Triple::ppc64: if (Triple.isOSDarwin()) return new DarwinPPC64TargetInfo(Triple, Opts); switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::Lv2: return new PS3PPUTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::AIX: return new AIXPPC64TargetInfo(Triple, Opts); default: return new PPC64TargetInfo(Triple, Opts); } case llvm::Triple::ppc64le: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); default: return new PPC64TargetInfo(Triple, Opts); } case llvm::Triple::nvptx: return new NVPTXTargetInfo(Triple, Opts, /*TargetPointerWidth=*/32); case llvm::Triple::nvptx64: return new NVPTXTargetInfo(Triple, Opts, /*TargetPointerWidth=*/64); case llvm::Triple::amdgcn: case llvm::Triple::r600: return new AMDGPUTargetInfo(Triple, Opts); case llvm::Triple::riscv32: - // TODO: add cases for FreeBSD, NetBSD, RTEMS once tested. - if (os == llvm::Triple::Linux) + // TODO: add cases for NetBSD, RTEMS once tested. + switch (os) { + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); - return new RISCV32TargetInfo(Triple, Opts); + default: + return new RISCV32TargetInfo(Triple, Opts); + } + case llvm::Triple::riscv64: - // TODO: add cases for FreeBSD, NetBSD, RTEMS once tested. - if (os == llvm::Triple::Linux) + // TODO: add cases for NetBSD, RTEMS once tested. + switch (os) { + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); - return new RISCV64TargetInfo(Triple, Opts); + default: + return new RISCV64TargetInfo(Triple, Opts); + } case llvm::Triple::sparc: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::Solaris: return new SolarisTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); default: return new SparcV8TargetInfo(Triple, Opts); } // The 'sparcel' architecture copies all the above cases except for Solaris. case llvm::Triple::sparcel: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSTargetInfo(Triple, Opts); default: return new SparcV8elTargetInfo(Triple, Opts); } case llvm::Triple::sparcv9: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::Solaris: return new SolarisTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDTargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); default: return new SparcV9TargetInfo(Triple, Opts); } case llvm::Triple::systemz: switch (os) { case llvm::Triple::Linux: return new LinuxTargetInfo(Triple, Opts); default: return new SystemZTargetInfo(Triple, Opts); } case llvm::Triple::tce: return new TCETargetInfo(Triple, Opts); case llvm::Triple::tcele: return new TCELETargetInfo(Triple, Opts); case llvm::Triple::x86: if (Triple.isOSDarwin()) return new DarwinI386TargetInfo(Triple, Opts); switch (os) { case llvm::Triple::Ananas: return new AnanasTargetInfo(Triple, Opts); case llvm::Triple::CloudABI: return new CloudABITargetInfo(Triple, Opts); case llvm::Triple::Linux: { switch (Triple.getEnvironment()) { default: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::Android: return new AndroidX86_32TargetInfo(Triple, Opts); } } case llvm::Triple::DragonFly: return new DragonFlyBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDI386TargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDI386TargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::KFreeBSD: return new KFreeBSDTargetInfo(Triple, Opts); case llvm::Triple::Minix: return new MinixTargetInfo(Triple, Opts); case llvm::Triple::Solaris: return new SolarisTargetInfo(Triple, Opts); case llvm::Triple::Win32: { switch (Triple.getEnvironment()) { case llvm::Triple::Cygnus: return new CygwinX86_32TargetInfo(Triple, Opts); case llvm::Triple::GNU: return new MinGWX86_32TargetInfo(Triple, Opts); case llvm::Triple::Itanium: case llvm::Triple::MSVC: default: // Assume MSVC for unknown environments return new MicrosoftX86_32TargetInfo(Triple, Opts); } } case llvm::Triple::Haiku: return new HaikuX86_32TargetInfo(Triple, Opts); case llvm::Triple::RTEMS: return new RTEMSX86_32TargetInfo(Triple, Opts); case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); case llvm::Triple::ELFIAMCU: return new MCUX86_32TargetInfo(Triple, Opts); case llvm::Triple::Hurd: return new HurdTargetInfo(Triple, Opts); default: return new X86_32TargetInfo(Triple, Opts); } case llvm::Triple::x86_64: if (Triple.isOSDarwin() || Triple.isOSBinFormatMachO()) return new DarwinX86_64TargetInfo(Triple, Opts); switch (os) { case llvm::Triple::Ananas: return new AnanasTargetInfo(Triple, Opts); case llvm::Triple::CloudABI: return new CloudABITargetInfo(Triple, Opts); case llvm::Triple::Linux: { switch (Triple.getEnvironment()) { default: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::Android: return new AndroidX86_64TargetInfo(Triple, Opts); } } case llvm::Triple::DragonFly: return new DragonFlyBSDTargetInfo(Triple, Opts); case llvm::Triple::NetBSD: return new NetBSDTargetInfo(Triple, Opts); case llvm::Triple::OpenBSD: return new OpenBSDX86_64TargetInfo(Triple, Opts); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo(Triple, Opts); case llvm::Triple::Fuchsia: return new FuchsiaTargetInfo(Triple, Opts); case llvm::Triple::KFreeBSD: return new KFreeBSDTargetInfo(Triple, Opts); case llvm::Triple::Solaris: return new SolarisTargetInfo(Triple, Opts); case llvm::Triple::Win32: { switch (Triple.getEnvironment()) { case llvm::Triple::Cygnus: return new CygwinX86_64TargetInfo(Triple, Opts); case llvm::Triple::GNU: return new MinGWX86_64TargetInfo(Triple, Opts); case llvm::Triple::MSVC: default: // Assume MSVC for unknown environments return new MicrosoftX86_64TargetInfo(Triple, Opts); } } case llvm::Triple::Haiku: return new HaikuTargetInfo(Triple, Opts); case llvm::Triple::NaCl: return new NaClTargetInfo(Triple, Opts); case llvm::Triple::PS4: return new PS4OSTargetInfo(Triple, Opts); default: return new X86_64TargetInfo(Triple, Opts); } case llvm::Triple::spir: { if (Triple.getOS() != llvm::Triple::UnknownOS || Triple.getEnvironment() != llvm::Triple::UnknownEnvironment) return nullptr; return new SPIR32TargetInfo(Triple, Opts); } case llvm::Triple::spir64: { if (Triple.getOS() != llvm::Triple::UnknownOS || Triple.getEnvironment() != llvm::Triple::UnknownEnvironment) return nullptr; return new SPIR64TargetInfo(Triple, Opts); } case llvm::Triple::wasm32: if (Triple.getSubArch() != llvm::Triple::NoSubArch || Triple.getVendor() != llvm::Triple::UnknownVendor || !Triple.isOSBinFormatWasm()) return nullptr; switch (Triple.getOS()) { case llvm::Triple::WASI: return new WASITargetInfo(Triple, Opts); case llvm::Triple::Emscripten: return new EmscriptenTargetInfo(Triple, Opts); case llvm::Triple::UnknownOS: return new WebAssemblyOSTargetInfo(Triple, Opts); default: return nullptr; } case llvm::Triple::wasm64: if (Triple.getSubArch() != llvm::Triple::NoSubArch || Triple.getVendor() != llvm::Triple::UnknownVendor || !Triple.isOSBinFormatWasm()) return nullptr; switch (Triple.getOS()) { case llvm::Triple::WASI: return new WASITargetInfo(Triple, Opts); case llvm::Triple::Emscripten: return new EmscriptenTargetInfo(Triple, Opts); case llvm::Triple::UnknownOS: return new WebAssemblyOSTargetInfo(Triple, Opts); default: return nullptr; } case llvm::Triple::renderscript32: return new LinuxTargetInfo(Triple, Opts); case llvm::Triple::renderscript64: return new LinuxTargetInfo(Triple, Opts); } } } // namespace targets } // namespace clang using namespace clang::targets; /// CreateTargetInfo - Return the target info object for the specified target /// options. TargetInfo * TargetInfo::CreateTargetInfo(DiagnosticsEngine &Diags, const std::shared_ptr &Opts) { llvm::Triple Triple(Opts->Triple); // Construct the target std::unique_ptr Target(AllocateTarget(Triple, *Opts)); if (!Target) { Diags.Report(diag::err_target_unknown_triple) << Triple.str(); return nullptr; } Target->TargetOpts = Opts; // Set the target CPU if specified. if (!Opts->CPU.empty() && !Target->setCPU(Opts->CPU)) { Diags.Report(diag::err_target_unknown_cpu) << Opts->CPU; SmallVector ValidList; Target->fillValidCPUList(ValidList); if (!ValidList.empty()) Diags.Report(diag::note_valid_options) << llvm::join(ValidList, ", "); return nullptr; } // Set the target ABI if specified. if (!Opts->ABI.empty() && !Target->setABI(Opts->ABI)) { Diags.Report(diag::err_target_unknown_abi) << Opts->ABI; return nullptr; } // Set the fp math unit. if (!Opts->FPMath.empty() && !Target->setFPMath(Opts->FPMath)) { Diags.Report(diag::err_target_unknown_fpmath) << Opts->FPMath; return nullptr; } // Compute the default target features, we need the target to handle this // because features may have dependencies on one another. llvm::StringMap Features; if (!Target->initFeatureMap(Features, Diags, Opts->CPU, Opts->FeaturesAsWritten)) return nullptr; // Add the features to the compile options. Opts->Features.clear(); for (const auto &F : Features) Opts->Features.push_back((F.getValue() ? "+" : "-") + F.getKey().str()); // Sort here, so we handle the features in a predictable order. (This matters // when we're dealing with features that overlap.) llvm::sort(Opts->Features); if (!Target->handleTargetFeatures(Opts->Features, Diags)) return nullptr; Target->setSupportedOpenCLOpts(); Target->setOpenCLExtensionOpts(); Target->setMaxAtomicWidth(); if (!Target->validateTarget(Diags)) return nullptr; Target->CheckFixedPointBits(); return Target.release(); } Index: head/contrib/llvm/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp =================================================================== --- head/contrib/llvm/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp (revision 354468) +++ head/contrib/llvm/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp (revision 354469) @@ -1,438 +1,446 @@ //===--- FreeBSD.cpp - FreeBSD ToolChain Implementations --------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "FreeBSD.h" #include "Arch/ARM.h" #include "Arch/Mips.h" #include "Arch/Sparc.h" #include "CommonArgs.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Options.h" #include "clang/Driver/SanitizerArgs.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/VirtualFileSystem.h" using namespace clang::driver; using namespace clang::driver::tools; using namespace clang::driver::toolchains; using namespace clang; using namespace llvm::opt; void freebsd::Assembler::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { claimNoWarnArgs(Args); ArgStringList CmdArgs; // When building 32-bit code on FreeBSD/amd64, we have to explicitly // instruct as in the base system to assemble 32-bit code. switch (getToolChain().getArch()) { default: break; case llvm::Triple::x86: CmdArgs.push_back("--32"); break; case llvm::Triple::ppc: CmdArgs.push_back("-a32"); break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: { StringRef CPUName; StringRef ABIName; mips::getMipsCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName); CmdArgs.push_back("-march"); CmdArgs.push_back(CPUName.data()); CmdArgs.push_back("-mabi"); CmdArgs.push_back(mips::getGnuCompatibleMipsABIName(ABIName).data()); if (getToolChain().getTriple().isLittleEndian()) CmdArgs.push_back("-EL"); else CmdArgs.push_back("-EB"); if (Arg *A = Args.getLastArg(options::OPT_G)) { StringRef v = A->getValue(); CmdArgs.push_back(Args.MakeArgString("-G" + v)); A->claim(); } AddAssemblerKPIC(getToolChain(), Args, CmdArgs); break; } case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: { arm::FloatABI ABI = arm::getARMFloatABI(getToolChain(), Args); if (ABI == arm::FloatABI::Hard) CmdArgs.push_back("-mfpu=vfp"); else CmdArgs.push_back("-mfpu=softvfp"); switch (getToolChain().getTriple().getEnvironment()) { case llvm::Triple::GNUEABIHF: case llvm::Triple::GNUEABI: case llvm::Triple::EABI: CmdArgs.push_back("-meabi=5"); break; default: CmdArgs.push_back("-matpcs"); } break; } case llvm::Triple::sparc: case llvm::Triple::sparcel: case llvm::Triple::sparcv9: { std::string CPU = getCPUName(Args, getToolChain().getTriple()); CmdArgs.push_back(sparc::getSparcAsmModeForCPU(CPU, getToolChain().getTriple())); AddAssemblerKPIC(getToolChain(), Args, CmdArgs); break; } } Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); for (const auto &II : Inputs) CmdArgs.push_back(II.getFilename()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("as")); C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } void freebsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const toolchains::FreeBSD &ToolChain = static_cast(getToolChain()); const Driver &D = ToolChain.getDriver(); const llvm::Triple::ArchType Arch = ToolChain.getArch(); const bool IsPIE = !Args.hasArg(options::OPT_shared) && (Args.hasArg(options::OPT_pie) || ToolChain.isPIEDefault()); ArgStringList CmdArgs; // Silence warning for "clang -g foo.o -o foo" Args.ClaimAllArgs(options::OPT_g_Group); // and "clang -emit-llvm foo.o -o foo" Args.ClaimAllArgs(options::OPT_emit_llvm); // and for "clang -w foo.o -o foo". Other warning options are already // handled somewhere else. Args.ClaimAllArgs(options::OPT_w); if (!D.SysRoot.empty()) CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); if (IsPIE) CmdArgs.push_back("-pie"); CmdArgs.push_back("--eh-frame-hdr"); if (Args.hasArg(options::OPT_static)) { CmdArgs.push_back("-Bstatic"); } else { if (Args.hasArg(options::OPT_rdynamic)) CmdArgs.push_back("-export-dynamic"); if (Args.hasArg(options::OPT_shared)) { CmdArgs.push_back("-Bshareable"); } else { CmdArgs.push_back("-dynamic-linker"); CmdArgs.push_back("/libexec/ld-elf.so.1"); } if (ToolChain.getTriple().getOSMajorVersion() >= 9) { if (Arch == llvm::Triple::arm || Arch == llvm::Triple::sparc || Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) { CmdArgs.push_back("--hash-style=both"); } } CmdArgs.push_back("--enable-new-dtags"); } // Explicitly set the linker emulation for platforms that might not // be the default emulation for the linker. switch (Arch) { case llvm::Triple::x86: CmdArgs.push_back("-m"); CmdArgs.push_back("elf_i386_fbsd"); break; case llvm::Triple::ppc: CmdArgs.push_back("-m"); CmdArgs.push_back("elf32ppc_fbsd"); break; case llvm::Triple::mips: CmdArgs.push_back("-m"); CmdArgs.push_back("elf32btsmip_fbsd"); break; case llvm::Triple::mipsel: CmdArgs.push_back("-m"); CmdArgs.push_back("elf32ltsmip_fbsd"); break; case llvm::Triple::mips64: CmdArgs.push_back("-m"); if (tools::mips::hasMipsAbiArg(Args, "n32")) CmdArgs.push_back("elf32btsmipn32_fbsd"); else CmdArgs.push_back("elf64btsmip_fbsd"); break; case llvm::Triple::mips64el: CmdArgs.push_back("-m"); if (tools::mips::hasMipsAbiArg(Args, "n32")) CmdArgs.push_back("elf32ltsmipn32_fbsd"); else CmdArgs.push_back("elf64ltsmip_fbsd"); break; + case llvm::Triple::riscv32: + CmdArgs.push_back("-m"); + CmdArgs.push_back("elf32lriscv"); + break; + case llvm::Triple::riscv64: + CmdArgs.push_back("-m"); + CmdArgs.push_back("elf64lriscv"); + break; default: break; } if (Arg *A = Args.getLastArg(options::OPT_G)) { if (ToolChain.getTriple().isMIPS()) { StringRef v = A->getValue(); CmdArgs.push_back(Args.MakeArgString("-G" + v)); A->claim(); } } if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); } else { assert(Output.isNothing() && "Invalid output."); } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { const char *crt1 = nullptr; if (!Args.hasArg(options::OPT_shared)) { if (Args.hasArg(options::OPT_pg)) crt1 = "gcrt1.o"; else if (IsPIE) crt1 = "Scrt1.o"; else crt1 = "crt1.o"; } if (crt1) CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crt1))); CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crti.o"))); const char *crtbegin = nullptr; if (Args.hasArg(options::OPT_static)) crtbegin = "crtbeginT.o"; else if (Args.hasArg(options::OPT_shared) || IsPIE) crtbegin = "crtbeginS.o"; else crtbegin = "crtbegin.o"; CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); } Args.AddAllArgs(CmdArgs, options::OPT_L); ToolChain.AddFilePathLibArgs(Args, CmdArgs); Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); Args.AddAllArgs(CmdArgs, options::OPT_s); Args.AddAllArgs(CmdArgs, options::OPT_t); Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); Args.AddAllArgs(CmdArgs, options::OPT_r); if (D.isUsingLTO()) { assert(!Inputs.empty() && "Must have at least one input."); AddGoldPlugin(ToolChain, Args, CmdArgs, Output, Inputs[0], D.getLTOMode() == LTOK_Thin); } bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { addOpenMPRuntime(CmdArgs, ToolChain, Args); if (D.CCCIsCXX()) { if (ToolChain.ShouldLinkCXXStdlib(Args)) ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lm_p"); else CmdArgs.push_back("-lm"); } if (NeedsSanitizerDeps) linkSanitizerRuntimeDeps(ToolChain, CmdArgs); if (NeedsXRayDeps) linkXRayRuntimeDeps(ToolChain, CmdArgs); // FIXME: For some reason GCC passes -lgcc and -lgcc_s before adding // the default system libraries. Just mimic this for now. if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lgcc_p"); else CmdArgs.push_back("-lgcc"); if (Args.hasArg(options::OPT_static)) { CmdArgs.push_back("-lgcc_eh"); } else if (Args.hasArg(options::OPT_pg)) { CmdArgs.push_back("-lgcc_eh_p"); } else { CmdArgs.push_back("--as-needed"); CmdArgs.push_back("-lgcc_s"); CmdArgs.push_back("--no-as-needed"); } if (Args.hasArg(options::OPT_pthread)) { if (Args.hasArg(options::OPT_pg)) CmdArgs.push_back("-lpthread_p"); else CmdArgs.push_back("-lpthread"); } if (Args.hasArg(options::OPT_pg)) { if (Args.hasArg(options::OPT_shared)) CmdArgs.push_back("-lc"); else CmdArgs.push_back("-lc_p"); CmdArgs.push_back("-lgcc_p"); } else { CmdArgs.push_back("-lc"); CmdArgs.push_back("-lgcc"); } if (Args.hasArg(options::OPT_static)) { CmdArgs.push_back("-lgcc_eh"); } else if (Args.hasArg(options::OPT_pg)) { CmdArgs.push_back("-lgcc_eh_p"); } else { CmdArgs.push_back("--as-needed"); CmdArgs.push_back("-lgcc_s"); CmdArgs.push_back("--no-as-needed"); } } if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) { if (Args.hasArg(options::OPT_shared) || IsPIE) CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtendS.o"))); else CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o"))); CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtn.o"))); } ToolChain.addProfileRTLibs(Args, CmdArgs); const char *Exec = Args.MakeArgString(getToolChain().GetLinkerPath()); C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } /// FreeBSD - FreeBSD tool chain which can call as(1) and ld(1) directly. FreeBSD::FreeBSD(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : Generic_ELF(D, Triple, Args) { // When targeting 32-bit platforms, look for '/usr/lib32/crt1.o' and fall // back to '/usr/lib' if it doesn't exist. if ((Triple.getArch() == llvm::Triple::x86 || Triple.isMIPS32() || Triple.getArch() == llvm::Triple::ppc) && D.getVFS().exists(getDriver().SysRoot + "/usr/lib32/crt1.o")) getFilePaths().push_back(getDriver().SysRoot + "/usr/lib32"); else getFilePaths().push_back(getDriver().SysRoot + "/usr/lib"); } ToolChain::CXXStdlibType FreeBSD::GetDefaultCXXStdlibType() const { if (getTriple().getOSMajorVersion() >= 10) return ToolChain::CST_Libcxx; return ToolChain::CST_Libstdcxx; } unsigned FreeBSD::GetDefaultDwarfVersion() const { // Default to use DWARF 2 before FreeBSD 13. if (getTriple().getOSMajorVersion() < 13) return 2; return 4; } void FreeBSD::addLibStdCxxIncludePaths( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const { addLibStdCXXIncludePaths(getDriver().SysRoot, "/usr/include/c++/4.2", "", "", "", "", DriverArgs, CC1Args); } void FreeBSD::AddCXXStdlibLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const { CXXStdlibType Type = GetCXXStdlibType(Args); bool Profiling = Args.hasArg(options::OPT_pg); switch (Type) { case ToolChain::CST_Libcxx: CmdArgs.push_back(Profiling ? "-lc++_p" : "-lc++"); break; case ToolChain::CST_Libstdcxx: CmdArgs.push_back(Profiling ? "-lstdc++_p" : "-lstdc++"); break; } } Tool *FreeBSD::buildAssembler() const { return new tools::freebsd::Assembler(*this); } Tool *FreeBSD::buildLinker() const { return new tools::freebsd::Linker(*this); } llvm::ExceptionHandling FreeBSD::GetExceptionModel(const ArgList &Args) const { // FreeBSD uses SjLj exceptions on ARM oabi. switch (getTriple().getEnvironment()) { case llvm::Triple::GNUEABIHF: case llvm::Triple::GNUEABI: case llvm::Triple::EABI: return llvm::ExceptionHandling::None; default: if (getTriple().getArch() == llvm::Triple::arm || getTriple().getArch() == llvm::Triple::thumb) return llvm::ExceptionHandling::SjLj; return llvm::ExceptionHandling::None; } } bool FreeBSD::HasNativeLLVMSupport() const { return true; } bool FreeBSD::IsUnwindTablesDefault(const ArgList &Args) const { return true; } bool FreeBSD::isPIEDefault() const { return getSanitizerArgs().requiresPIE(); } SanitizerMask FreeBSD::getSupportedSanitizers() const { const bool IsX86 = getTriple().getArch() == llvm::Triple::x86; const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; const bool IsMIPS64 = getTriple().isMIPS64(); SanitizerMask Res = ToolChain::getSupportedSanitizers(); Res |= SanitizerKind::Address; Res |= SanitizerKind::PointerCompare; Res |= SanitizerKind::PointerSubtract; Res |= SanitizerKind::Vptr; if (IsX86_64 || IsMIPS64) { Res |= SanitizerKind::Leak; Res |= SanitizerKind::Thread; } if (IsX86 || IsX86_64) { Res |= SanitizerKind::Function; Res |= SanitizerKind::SafeStack; Res |= SanitizerKind::Fuzzer; Res |= SanitizerKind::FuzzerNoLink; } if (IsX86_64) Res |= SanitizerKind::Memory; return Res; }