diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h index 20525a65b7e3..a7d7db834b50 100644 --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -1,186 +1,189 @@ //===- llvm/MC/MCAsmBackend.h - MC Asm Backend ------------------*- 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_MCASMBACKEND_H #define LLVM_MC_MCASMBACKEND_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCFragment.h" #include "llvm/Support/Endian.h" #include namespace llvm { class MCAsmLayout; class MCAssembler; class MCCFIInstruction; struct MCFixupKindInfo; class MCFragment; class MCInst; class MCObjectStreamer; class MCObjectTargetWriter; class MCObjectWriter; class MCRelaxableFragment; class MCSubtargetInfo; class MCValue; class raw_pwrite_stream; /// Generic interface to target specific assembler backends. class MCAsmBackend { protected: // Can only create subclasses. MCAsmBackend(support::endianness Endian); public: MCAsmBackend(const MCAsmBackend &) = delete; MCAsmBackend &operator=(const MCAsmBackend &) = delete; virtual ~MCAsmBackend(); const support::endianness Endian; + virtual void alignBranchesBegin(MCObjectStreamer &OS, const MCInst &Inst) {} + virtual void alignBranchesEnd(MCObjectStreamer &OS, const MCInst &Inst) {} + /// lifetime management virtual void reset() {} /// Create a new MCObjectWriter instance for use by the assembler backend to /// emit the final object file. std::unique_ptr createObjectWriter(raw_pwrite_stream &OS) const; /// Create an MCObjectWriter that writes two object files: a .o file which is /// linked into the final program and a .dwo file which is used by debuggers. /// This function is only supported with ELF targets. std::unique_ptr createDwoObjectWriter(raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS) const; virtual std::unique_ptr createObjectTargetWriter() const = 0; /// \name Target Fixup Interfaces /// @{ /// Get the number of target specific fixup kinds. virtual unsigned getNumFixupKinds() const = 0; /// Map a relocation name used in .reloc to a fixup kind. virtual Optional getFixupKind(StringRef Name) const; /// Get information on a fixup kind. virtual const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const; /// Hook to check if a relocation is needed for some target specific reason. virtual bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) { return false; } /// Hook to check if extra nop bytes must be inserted for alignment directive. /// For some targets this may be necessary in order to support linker /// relaxation. The number of bytes to insert are returned in Size. virtual bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF, unsigned &Size) { return false; } /// Hook which indicates if the target requires a fixup to be generated when /// handling an align directive in an executable section virtual bool shouldInsertFixupForCodeAlign(MCAssembler &Asm, const MCAsmLayout &Layout, MCAlignFragment &AF) { return false; } /// Apply the \p Value for given \p Fixup into the provided data fragment, at /// the offset specified by the fixup and following the fixup kind as /// appropriate. Errors (such as an out of range fixup value) should be /// reported via \p Ctx. /// The \p STI is present only for fragments of type MCRelaxableFragment and /// MCDataFragment with hasInstructions() == true. virtual void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const = 0; /// Check whether the given target requires emitting differences of two /// symbols as a set of relocations. virtual bool requiresDiffExpressionRelocations() const { return false; } /// @} /// \name Target Relaxation Interfaces /// @{ /// Check whether the given instruction may need relaxation. /// /// \param Inst - The instruction to test. /// \param STI - The MCSubtargetInfo in effect when the instruction was /// encoded. virtual bool mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const = 0; /// Target specific predicate for whether a given fixup requires the /// associated instruction to be relaxed. virtual bool fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout, const bool WasForced) const; /// Simple predicate for targets where !Resolved implies requiring relaxation virtual bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const = 0; /// Relax the instruction in the given fragment to the next wider instruction. /// /// \param Inst The instruction to relax, which may be the same as the /// output. /// \param STI the subtarget information for the associated instruction. /// \param [out] Res On return, the relaxed instruction. virtual void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const = 0; /// @} /// Returns the minimum size of a nop in bytes on this target. The assembler /// will use this to emit excess padding in situations where the padding /// required for simple alignment would be less than the minimum nop size. /// virtual unsigned getMinimumNopSize() const { return 1; } /// Write an (optimal) nop sequence of Count bytes to the given output. If the /// target cannot generate such a sequence, it should return an error. /// /// \return - True on success. virtual bool writeNopData(raw_ostream &OS, uint64_t Count) const = 0; /// Give backend an opportunity to finish layout after relaxation virtual void finishLayout(MCAssembler const &Asm, MCAsmLayout &Layout) const {} /// Handle any target-specific assembler flags. By default, do nothing. virtual void handleAssemblerFlag(MCAssemblerFlag Flag) {} /// Generate the compact unwind encoding for the CFI instructions. virtual uint32_t generateCompactUnwindEncoding(ArrayRef) const { return 0; } /// Check whether a given symbol has been flagged with MICROMIPS flag. virtual bool isMicroMips(const MCSymbol *Sym) const { return false; } }; } // end namespace llvm #endif // LLVM_MC_MCASMBACKEND_H diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h index 3fbeb62c7f8f..8c76f30222e5 100644 --- a/llvm/include/llvm/MC/MCAssembler.h +++ b/llvm/include/llvm/MC/MCAssembler.h @@ -1,466 +1,465 @@ //===- MCAssembler.h - Object File Generation -------------------*- 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_MCASSEMBLER_H #define LLVM_MC_MCASSEMBLER_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCFragment.h" #include "llvm/MC/MCLinkerOptimizationHint.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/VersionTuple.h" #include #include #include #include #include #include namespace llvm { class MCAsmBackend; class MCAsmLayout; class MCContext; class MCCodeEmitter; class MCFragment; class MCObjectWriter; class MCSection; class MCValue; // FIXME: This really doesn't belong here. See comments below. struct IndirectSymbolData { MCSymbol *Symbol; MCSection *Section; }; // FIXME: Ditto this. Purely so the Streamer and the ObjectWriter can talk // to one another. struct DataRegionData { // This enum should be kept in sync w/ the mach-o definition in // llvm/Object/MachOFormat.h. enum KindTy { Data = 1, JumpTable8, JumpTable16, JumpTable32 } Kind; MCSymbol *Start; MCSymbol *End; }; class MCAssembler { friend class MCAsmLayout; public: using SectionListType = std::vector; using SymbolDataListType = std::vector; using const_iterator = pointee_iterator; using iterator = pointee_iterator; using const_symbol_iterator = pointee_iterator; using symbol_iterator = pointee_iterator; using symbol_range = iterator_range; using const_symbol_range = iterator_range; using const_indirect_symbol_iterator = std::vector::const_iterator; using indirect_symbol_iterator = std::vector::iterator; using const_data_region_iterator = std::vector::const_iterator; using data_region_iterator = std::vector::iterator; /// MachO specific deployment target version info. // A Major version of 0 indicates that no version information was supplied // and so the corresponding load command should not be emitted. using VersionInfoType = struct { bool EmitBuildVersion; union { MCVersionMinType Type; ///< Used when EmitBuildVersion==false. MachO::PlatformType Platform; ///< Used when EmitBuildVersion==true. } TypeOrPlatform; unsigned Major; unsigned Minor; unsigned Update; /// An optional version of the SDK that was used to build the source. VersionTuple SDKVersion; }; private: MCContext &Context; std::unique_ptr Backend; std::unique_ptr Emitter; std::unique_ptr Writer; SectionListType Sections; SymbolDataListType Symbols; std::vector IndirectSymbols; std::vector DataRegions; /// The list of linker options to propagate into the object file. std::vector> LinkerOptions; /// List of declared file names std::vector FileNames; MCDwarfLineTableParams LTParams; /// The set of function symbols for which a .thumb_func directive has /// been seen. // // FIXME: We really would like this in target specific code rather than // here. Maybe when the relocation stuff moves to target specific, // this can go with it? The streamer would need some target specific // refactoring too. mutable SmallPtrSet ThumbFuncs; /// The bundle alignment size currently set in the assembler. /// /// By default it's 0, which means bundling is disabled. unsigned BundleAlignSize; bool RelaxAll : 1; bool SubsectionsViaSymbols : 1; bool IncrementalLinkerCompatible : 1; /// ELF specific e_header flags // It would be good if there were an MCELFAssembler class to hold this. // ELF header flags are used both by the integrated and standalone assemblers. // Access to the flags is necessary in cases where assembler directives affect // which flags to be set. unsigned ELFHeaderEFlags; /// Used to communicate Linker Optimization Hint information between /// the Streamer and the .o writer MCLOHContainer LOHContainer; VersionInfoType VersionInfo; /// Evaluate a fixup to a relocatable expression and the value which should be /// placed into the fixup. /// /// \param Layout The layout to use for evaluation. /// \param Fixup The fixup to evaluate. /// \param DF The fragment the fixup is inside. /// \param Target [out] On return, the relocatable expression the fixup /// evaluates to. /// \param Value [out] On return, the value of the fixup as currently laid /// out. /// \param WasForced [out] On return, the value in the fixup is set to the /// correct value if WasForced is true, even if evaluateFixup returns false. /// \return Whether the fixup value was fully resolved. This is true if the /// \p Value result is fixed, otherwise the value may change due to /// relocation. bool evaluateFixup(const MCAsmLayout &Layout, const MCFixup &Fixup, const MCFragment *DF, MCValue &Target, uint64_t &Value, bool &WasForced) const; /// Check whether a fixup can be satisfied, or whether it needs to be relaxed /// (increased in size, in order to hold its value correctly). bool fixupNeedsRelaxation(const MCFixup &Fixup, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const; /// Check whether the given fragment needs relaxation. bool fragmentNeedsRelaxation(const MCRelaxableFragment *IF, const MCAsmLayout &Layout) const; /// Perform one layout iteration and return true if any offsets /// were adjusted. bool layoutOnce(MCAsmLayout &Layout); /// Perform one layout iteration of the given section and return true /// if any offsets were adjusted. bool layoutSectionOnce(MCAsmLayout &Layout, MCSection &Sec); bool relaxInstruction(MCAsmLayout &Layout, MCRelaxableFragment &IF); - bool relaxLEB(MCAsmLayout &Layout, MCLEBFragment &IF); - + bool relaxBoundaryAlign(MCAsmLayout &Layout, MCBoundaryAlignFragment &BF); bool relaxDwarfLineAddr(MCAsmLayout &Layout, MCDwarfLineAddrFragment &DF); bool relaxDwarfCallFrameFragment(MCAsmLayout &Layout, MCDwarfCallFrameFragment &DF); bool relaxCVInlineLineTable(MCAsmLayout &Layout, MCCVInlineLineTableFragment &DF); bool relaxCVDefRange(MCAsmLayout &Layout, MCCVDefRangeFragment &DF); /// finishLayout - Finalize a layout, including fragment lowering. void finishLayout(MCAsmLayout &Layout); std::tuple handleFixup(const MCAsmLayout &Layout, MCFragment &F, const MCFixup &Fixup); public: std::vector> Symvers; /// Construct a new assembler instance. // // FIXME: How are we going to parameterize this? Two obvious options are stay // concrete and require clients to pass in a target like object. The other // option is to make this abstract, and have targets provide concrete // implementations as we do with AsmParser. MCAssembler(MCContext &Context, std::unique_ptr Backend, std::unique_ptr Emitter, std::unique_ptr Writer); MCAssembler(const MCAssembler &) = delete; MCAssembler &operator=(const MCAssembler &) = delete; ~MCAssembler(); /// Compute the effective fragment size assuming it is laid out at the given /// \p SectionAddress and \p FragmentOffset. uint64_t computeFragmentSize(const MCAsmLayout &Layout, const MCFragment &F) const; /// Find the symbol which defines the atom containing the given symbol, or /// null if there is no such symbol. const MCSymbol *getAtom(const MCSymbol &S) const; /// Check whether a particular symbol is visible to the linker and is required /// in the symbol table, or whether it can be discarded by the assembler. This /// also effects whether the assembler treats the label as potentially /// defining a separate atom. bool isSymbolLinkerVisible(const MCSymbol &SD) const; /// Emit the section contents to \p OS. void writeSectionData(raw_ostream &OS, const MCSection *Section, const MCAsmLayout &Layout) const; /// Check whether a given symbol has been flagged with .thumb_func. bool isThumbFunc(const MCSymbol *Func) const; /// Flag a function symbol as the target of a .thumb_func directive. void setIsThumbFunc(const MCSymbol *Func) { ThumbFuncs.insert(Func); } /// ELF e_header flags unsigned getELFHeaderEFlags() const { return ELFHeaderEFlags; } void setELFHeaderEFlags(unsigned Flags) { ELFHeaderEFlags = Flags; } /// MachO deployment target version information. const VersionInfoType &getVersionInfo() const { return VersionInfo; } void setVersionMin(MCVersionMinType Type, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion = VersionTuple()) { VersionInfo.EmitBuildVersion = false; VersionInfo.TypeOrPlatform.Type = Type; VersionInfo.Major = Major; VersionInfo.Minor = Minor; VersionInfo.Update = Update; VersionInfo.SDKVersion = SDKVersion; } void setBuildVersion(MachO::PlatformType Platform, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion = VersionTuple()) { VersionInfo.EmitBuildVersion = true; VersionInfo.TypeOrPlatform.Platform = Platform; VersionInfo.Major = Major; VersionInfo.Minor = Minor; VersionInfo.Update = Update; VersionInfo.SDKVersion = SDKVersion; } /// Reuse an assembler instance /// void reset(); MCContext &getContext() const { return Context; } MCAsmBackend *getBackendPtr() const { return Backend.get(); } MCCodeEmitter *getEmitterPtr() const { return Emitter.get(); } MCObjectWriter *getWriterPtr() const { return Writer.get(); } MCAsmBackend &getBackend() const { return *Backend; } MCCodeEmitter &getEmitter() const { return *Emitter; } MCObjectWriter &getWriter() const { return *Writer; } MCDwarfLineTableParams getDWARFLinetableParams() const { return LTParams; } void setDWARFLinetableParams(MCDwarfLineTableParams P) { LTParams = P; } /// Finish - Do final processing and write the object to the output stream. /// \p Writer is used for custom object writer (as the MCJIT does), /// if not specified it is automatically created from backend. void Finish(); // Layout all section and prepare them for emission. void layout(MCAsmLayout &Layout); // FIXME: This does not belong here. bool getSubsectionsViaSymbols() const { return SubsectionsViaSymbols; } void setSubsectionsViaSymbols(bool Value) { SubsectionsViaSymbols = Value; } bool isIncrementalLinkerCompatible() const { return IncrementalLinkerCompatible; } void setIncrementalLinkerCompatible(bool Value) { IncrementalLinkerCompatible = Value; } bool getRelaxAll() const { return RelaxAll; } void setRelaxAll(bool Value) { RelaxAll = Value; } bool isBundlingEnabled() const { return BundleAlignSize != 0; } unsigned getBundleAlignSize() const { return BundleAlignSize; } void setBundleAlignSize(unsigned Size) { assert((Size == 0 || !(Size & (Size - 1))) && "Expect a power-of-two bundle align size"); BundleAlignSize = Size; } /// \name Section List Access /// @{ iterator begin() { return Sections.begin(); } const_iterator begin() const { return Sections.begin(); } iterator end() { return Sections.end(); } const_iterator end() const { return Sections.end(); } size_t size() const { return Sections.size(); } /// @} /// \name Symbol List Access /// @{ symbol_iterator symbol_begin() { return Symbols.begin(); } const_symbol_iterator symbol_begin() const { return Symbols.begin(); } symbol_iterator symbol_end() { return Symbols.end(); } const_symbol_iterator symbol_end() const { return Symbols.end(); } symbol_range symbols() { return make_range(symbol_begin(), symbol_end()); } const_symbol_range symbols() const { return make_range(symbol_begin(), symbol_end()); } size_t symbol_size() const { return Symbols.size(); } /// @} /// \name Indirect Symbol List Access /// @{ // FIXME: This is a total hack, this should not be here. Once things are // factored so that the streamer has direct access to the .o writer, it can // disappear. std::vector &getIndirectSymbols() { return IndirectSymbols; } indirect_symbol_iterator indirect_symbol_begin() { return IndirectSymbols.begin(); } const_indirect_symbol_iterator indirect_symbol_begin() const { return IndirectSymbols.begin(); } indirect_symbol_iterator indirect_symbol_end() { return IndirectSymbols.end(); } const_indirect_symbol_iterator indirect_symbol_end() const { return IndirectSymbols.end(); } size_t indirect_symbol_size() const { return IndirectSymbols.size(); } /// @} /// \name Linker Option List Access /// @{ std::vector> &getLinkerOptions() { return LinkerOptions; } /// @} /// \name Data Region List Access /// @{ // FIXME: This is a total hack, this should not be here. Once things are // factored so that the streamer has direct access to the .o writer, it can // disappear. std::vector &getDataRegions() { return DataRegions; } data_region_iterator data_region_begin() { return DataRegions.begin(); } const_data_region_iterator data_region_begin() const { return DataRegions.begin(); } data_region_iterator data_region_end() { return DataRegions.end(); } const_data_region_iterator data_region_end() const { return DataRegions.end(); } size_t data_region_size() const { return DataRegions.size(); } /// @} /// \name Data Region List Access /// @{ // FIXME: This is a total hack, this should not be here. Once things are // factored so that the streamer has direct access to the .o writer, it can // disappear. MCLOHContainer &getLOHContainer() { return LOHContainer; } const MCLOHContainer &getLOHContainer() const { return const_cast(this)->getLOHContainer(); } struct CGProfileEntry { const MCSymbolRefExpr *From; const MCSymbolRefExpr *To; uint64_t Count; }; std::vector CGProfile; /// @} /// \name Backend Data Access /// @{ bool registerSection(MCSection &Section); void registerSymbol(const MCSymbol &Symbol, bool *Created = nullptr); ArrayRef getFileNames() { return FileNames; } void addFileName(StringRef FileName) { if (!is_contained(FileNames, FileName)) FileNames.push_back(FileName); } /// Write the necessary bundle padding to \p OS. /// Expects a fragment \p F containing instructions and its size \p FSize. void writeFragmentPadding(raw_ostream &OS, const MCEncodedFragment &F, uint64_t FSize) const; /// @} void dump() const; }; /// Compute the amount of padding required before the fragment \p F to /// obey bundling restrictions, where \p FOffset is the fragment's offset in /// its section and \p FSize is the fragment's size. uint64_t computeBundlePadding(const MCAssembler &Assembler, const MCEncodedFragment *F, uint64_t FOffset, uint64_t FSize); } // end namespace llvm #endif // LLVM_MC_MCASSEMBLER_H diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h index 46f40ae7aba3..5e3d5b783365 100644 --- a/llvm/include/llvm/MC/MCFragment.h +++ b/llvm/include/llvm/MC/MCFragment.h @@ -1,568 +1,613 @@ //===- 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/Alignment.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_BoundaryAlign, 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); 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_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; } }; 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 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; public: MCDwarfCallFrameFragment(const MCExpr &AddrDelta, MCSection *Sec = nullptr) : MCEncodedFragmentWithFixups<8, 1>(FT_DwarfFrame, false, Sec), AddrDelta(&AddrDelta) {} /// \name Accessors /// @{ const MCExpr &getAddrDelta() const { return *AddrDelta; } /// @} 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; } }; +class MCBoundaryAlignFragment : public MCFragment { +private: + /// The size of the MCBoundaryAlignFragment. + /// Note: The size is lazily set during relaxation, and is not meaningful + /// before that. + uint64_t Size = 0; + /// The alignment requirement of the branch to be aligned. + Align AlignBoundary; + /// Flag to indicate whether the branch is fused. + bool Fused : 1; + /// Flag to indicate whether NOPs should be emitted. + bool EmitNops : 1; + +public: + MCBoundaryAlignFragment(Align AlignBoundary, bool Fused = false, + bool EmitNops = false, MCSection *Sec = nullptr) + : MCFragment(FT_BoundaryAlign, false, Sec), AlignBoundary(AlignBoundary), + Fused(Fused), EmitNops(EmitNops) {} + + /// \name Accessors + /// @{ + + Align getAlignment() const { return AlignBoundary; } + + uint64_t getSize() const { return Size; } + + bool canEmitNops() const { return EmitNops; } + + bool isFused() const { return Fused; } + + void setFused(bool Value) { Fused = Value; } + + void setEmitNops(bool Value) { EmitNops = Value; } + + void setSize(uint64_t Value) { Size = Value; } + + /// @} + // + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_BoundaryAlign; + } +}; } // end namespace llvm #endif // LLVM_MC_MCFRAGMENT_H diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h index d8ea75a0ee7c..9e3f87565e26 100644 --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -1,208 +1,209 @@ //===- MCObjectStreamer.h - MCStreamer Object File Interface ----*- 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_MCOBJECTSTREAMER_H #define LLVM_MC_MCOBJECTSTREAMER_H #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" namespace llvm { class MCAssembler; class MCCodeEmitter; class MCSubtargetInfo; class MCExpr; class MCFragment; class MCDataFragment; class MCAsmBackend; class raw_ostream; class raw_pwrite_stream; /// Streaming object file generation interface. /// /// This class provides an implementation of the MCStreamer interface which is /// suitable for use with the assembler backend. Specific object file formats /// are expected to subclass this interface to implement directives specific /// to that file format or custom semantics expected by the object writer /// implementation. class MCObjectStreamer : public MCStreamer { std::unique_ptr Assembler; MCSection::iterator CurInsertionPoint; bool EmitEHFrame; bool EmitDebugFrame; SmallVector PendingLabels; SmallVector PendingLabelSections; unsigned CurSubsectionIdx; struct PendingMCFixup { const MCSymbol *Sym; MCFixup Fixup; MCDataFragment *DF; PendingMCFixup(const MCSymbol *McSym, MCDataFragment *F, MCFixup McFixup) : Sym(McSym), Fixup(McFixup), DF(F) {} }; SmallVector PendingFixups; virtual void EmitInstToData(const MCInst &Inst, const MCSubtargetInfo&) = 0; void EmitCFIStartProcImpl(MCDwarfFrameInfo &Frame) override; void EmitCFIEndProcImpl(MCDwarfFrameInfo &Frame) override; MCSymbol *EmitCFILabel() override; + void EmitInstructionImpl(const MCInst &Inst, const MCSubtargetInfo &STI); void resolvePendingFixups(); protected: MCObjectStreamer(MCContext &Context, std::unique_ptr TAB, std::unique_ptr OW, std::unique_ptr Emitter); ~MCObjectStreamer(); public: /// state management void reset() override; /// Object streamers require the integrated assembler. bool isIntegratedAssemblerRequired() const override { return true; } void EmitFrames(MCAsmBackend *MAB); void EmitCFISections(bool EH, bool Debug) override; MCFragment *getCurrentFragment() const; void insert(MCFragment *F) { flushPendingLabels(F); MCSection *CurSection = getCurrentSectionOnly(); CurSection->getFragmentList().insert(CurInsertionPoint, F); F->setParent(CurSection); } /// Get a data fragment to write into, creating a new one if the current /// fragment is not a data fragment. /// Optionally a \p STI can be passed in so that a new fragment is created /// if the Subtarget differs from the current fragment. MCDataFragment *getOrCreateDataFragment(const MCSubtargetInfo* STI = nullptr); protected: bool changeSectionImpl(MCSection *Section, const MCExpr *Subsection); /// Assign a label to the current Section and Subsection even though a /// fragment is not yet present. Use flushPendingLabels(F) to associate /// a fragment with this label. void addPendingLabel(MCSymbol* label); /// If any labels have been emitted but not assigned fragments in the current /// Section and Subsection, ensure that they get assigned, either to fragment /// F if possible or to a new data fragment. Optionally, one can provide an /// offset \p FOffset as a symbol offset within the fragment. void flushPendingLabels(MCFragment *F, uint64_t FOffset = 0); public: void visitUsedSymbol(const MCSymbol &Sym) override; /// Create a data fragment for any pending labels across all Sections /// and Subsections. void flushPendingLabels(); MCAssembler &getAssembler() { return *Assembler; } MCAssembler *getAssemblerPtr() override; /// \name MCStreamer Interface /// @{ void EmitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; virtual void EmitLabelAtPos(MCSymbol *Symbol, SMLoc Loc, MCFragment *F, uint64_t Offset); void EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; void EmitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc = SMLoc()) override; void EmitULEB128Value(const MCExpr *Value) override; void EmitSLEB128Value(const MCExpr *Value) override; void EmitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; void ChangeSection(MCSection *Section, const MCExpr *Subsection) override; void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; /// Emit an instruction to a special fragment, because this instruction /// can change its size during relaxation. virtual void EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &); void EmitBundleAlignMode(unsigned AlignPow2) override; void EmitBundleLock(bool AlignToEnd) override; void EmitBundleUnlock() override; void EmitBytes(StringRef Data) override; void EmitValueToAlignment(unsigned ByteAlignment, int64_t Value = 0, unsigned ValueSize = 1, unsigned MaxBytesToEmit = 0) override; void EmitCodeAlignment(unsigned ByteAlignment, unsigned MaxBytesToEmit = 0) override; void emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) override; void EmitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, unsigned Isa, unsigned Discriminator, StringRef FileName) override; void EmitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel, const MCSymbol *Label, unsigned PointerSize); void EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, const MCSymbol *Label); void EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, StringRef FileName, SMLoc Loc) override; void EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) override; void EmitCVInlineLinetableDirective(unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) override; void EmitCVDefRangeDirective( ArrayRef> Ranges, StringRef FixedSizePortion) override; void EmitCVStringTableDirective() override; void EmitCVFileChecksumsDirective() override; void EmitCVFileChecksumOffsetDirective(unsigned FileNo) override; void EmitDTPRel32Value(const MCExpr *Value) override; void EmitDTPRel64Value(const MCExpr *Value) override; void EmitTPRel32Value(const MCExpr *Value) override; void EmitTPRel64Value(const MCExpr *Value) override; void EmitGPRel32Value(const MCExpr *Value) override; void EmitGPRel64Value(const MCExpr *Value) override; bool EmitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc Loc, const MCSubtargetInfo &STI) override; using MCStreamer::emitFill; void emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc = SMLoc()) override; void emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr, SMLoc Loc = SMLoc()) override; void EmitFileDirective(StringRef Filename) override; void EmitAddrsig() override; void EmitAddrsigSym(const MCSymbol *Sym) override; void FinishImpl() override; /// Emit the absolute difference between two symbols if possible. /// /// Emit the absolute difference between \c Hi and \c Lo, as long as we can /// compute it. Currently, that requires that both symbols are in the same /// data fragment and that the target has not specified that diff expressions /// require relocations to be emitted. Otherwise, do nothing and return /// \c false. /// /// \pre Offset of \c Hi is greater than the offset \c Lo. void emitAbsoluteSymbolDiff(const MCSymbol *Hi, const MCSymbol *Lo, unsigned Size) override; void emitAbsoluteSymbolDiffAsULEB128(const MCSymbol *Hi, const MCSymbol *Lo) override; bool mayHaveInstructions(MCSection &Sec) const override; }; } // end namespace llvm #endif diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index 2e9cfc73cf47..df1c49b2a06a 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -1,1124 +1,1204 @@ //===- 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/Alignment.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"); } // 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_BoundaryAlign: + 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, Align(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_BoundaryAlign: { + 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::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(); } +/// Check if the branch crosses the boundary. +/// +/// \param StartAddr start address of the fused/unfused branch. +/// \param Size size of the fused/unfused branch. +/// \param BoundaryAlignment aligment requirement of the branch. +/// \returns true if the branch cross the boundary. +static bool mayCrossBoundary(uint64_t StartAddr, uint64_t Size, + Align BoundaryAlignment) { + uint64_t EndAddr = StartAddr + Size; + return (StartAddr >> Log2(BoundaryAlignment)) != + ((EndAddr - 1) >> Log2(BoundaryAlignment)); +} + +/// Check if the branch is against the boundary. +/// +/// \param StartAddr start address of the fused/unfused branch. +/// \param Size size of the fused/unfused branch. +/// \param BoundaryAlignment aligment requirement of the branch. +/// \returns true if the branch is against the boundary. +static bool isAgainstBoundary(uint64_t StartAddr, uint64_t Size, + Align BoundaryAlignment) { + uint64_t EndAddr = StartAddr + Size; + return (EndAddr & (BoundaryAlignment.value() - 1)) == 0; +} + +/// Check if the branch needs padding. +/// +/// \param StartAddr start address of the fused/unfused branch. +/// \param Size size of the fused/unfused branch. +/// \param BoundaryAlignment aligment requirement of the branch. +/// \returns true if the branch needs padding. +static bool needPadding(uint64_t StartAddr, uint64_t Size, + Align BoundaryAlignment) { + return mayCrossBoundary(StartAddr, Size, BoundaryAlignment) || + isAgainstBoundary(StartAddr, Size, BoundaryAlignment); +} + +bool MCAssembler::relaxBoundaryAlign(MCAsmLayout &Layout, + MCBoundaryAlignFragment &BF) { + // The MCBoundaryAlignFragment that doesn't emit NOP should not be relaxed. + if (!BF.canEmitNops()) + return false; + + uint64_t AlignedOffset = Layout.getFragmentOffset(BF.getNextNode()); + uint64_t AlignedSize = 0; + const MCFragment *F = BF.getNextNode(); + // If the branch is unfused, it is emitted into one fragment, otherwise it is + // emitted into two fragments at most, the next MCBoundaryAlignFragment(if + // exists) also marks the end of the branch. + for (auto i = 0, N = BF.isFused() ? 2 : 1; + i != N && !isa(F); ++i, F = F->getNextNode()) { + AlignedSize += computeFragmentSize(Layout, *F); + } + uint64_t OldSize = BF.getSize(); + AlignedOffset -= OldSize; + Align BoundaryAlignment = BF.getAlignment(); + uint64_t NewSize = needPadding(AlignedOffset, AlignedSize, BoundaryAlignment) + ? offsetToAlignment(AlignedOffset, BoundaryAlignment) + : 0U; + if (NewSize == OldSize) + return false; + BF.setSize(NewSize); + Layout.invalidateFragmentsFrom(&BF); + return true; +} + bool MCAssembler::relaxDwarfLineAddr(MCAsmLayout &Layout, MCDwarfLineAddrFragment &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 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 (!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; SmallVectorImpl &Data = DF.getContents(); Data.clear(); raw_svector_ostream OSE(Data); 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_BoundaryAlign: + RelaxedFrag = + relaxBoundaryAlign(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 diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp index 98017a9bfa6f..1f0615c2bb02 100644 --- a/llvm/lib/MC/MCFragment.cpp +++ b/llvm/lib/MC/MCFragment.cpp @@ -1,448 +1,465 @@ //===- lib/MC/MCFragment.cpp - Assembler Fragment 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/MCFragment.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/llvm-config.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; MCAsmLayout::MCAsmLayout(MCAssembler &Asm) : Assembler(Asm) { // Compute the section layout order. Virtual sections must go last. for (MCSection &Sec : Asm) if (!Sec.isVirtualSection()) SectionOrder.push_back(&Sec); for (MCSection &Sec : Asm) if (Sec.isVirtualSection()) SectionOrder.push_back(&Sec); } bool MCAsmLayout::isFragmentValid(const MCFragment *F) const { const MCSection *Sec = F->getParent(); const MCFragment *LastValid = LastValidFragment.lookup(Sec); if (!LastValid) return false; assert(LastValid->getParent() == Sec); return F->getLayoutOrder() <= LastValid->getLayoutOrder(); } void MCAsmLayout::invalidateFragmentsFrom(MCFragment *F) { // If this fragment wasn't already valid, we don't need to do anything. if (!isFragmentValid(F)) return; // Otherwise, reset the last valid fragment to the previous fragment // (if this is the first fragment, it will be NULL). LastValidFragment[F->getParent()] = F->getPrevNode(); } void MCAsmLayout::ensureValid(const MCFragment *F) const { MCSection *Sec = F->getParent(); MCSection::iterator I; if (MCFragment *Cur = LastValidFragment[Sec]) I = ++MCSection::iterator(Cur); else I = Sec->begin(); // Advance the layout position until the fragment is valid. while (!isFragmentValid(F)) { assert(I != Sec->end() && "Layout bookkeeping error"); const_cast(this)->layoutFragment(&*I); ++I; } } uint64_t MCAsmLayout::getFragmentOffset(const MCFragment *F) const { ensureValid(F); assert(F->Offset != ~UINT64_C(0) && "Address not set!"); return F->Offset; } // Simple getSymbolOffset helper for the non-variable case. static bool getLabelOffset(const MCAsmLayout &Layout, const MCSymbol &S, bool ReportError, uint64_t &Val) { if (!S.getFragment()) { if (ReportError) report_fatal_error("unable to evaluate offset to undefined symbol '" + S.getName() + "'"); return false; } Val = Layout.getFragmentOffset(S.getFragment()) + S.getOffset(); return true; } static bool getSymbolOffsetImpl(const MCAsmLayout &Layout, const MCSymbol &S, bool ReportError, uint64_t &Val) { if (!S.isVariable()) return getLabelOffset(Layout, S, ReportError, Val); // If SD is a variable, evaluate it. MCValue Target; if (!S.getVariableValue()->evaluateAsValue(Target, Layout)) report_fatal_error("unable to evaluate offset for variable '" + S.getName() + "'"); uint64_t Offset = Target.getConstant(); const MCSymbolRefExpr *A = Target.getSymA(); if (A) { uint64_t ValA; if (!getLabelOffset(Layout, A->getSymbol(), ReportError, ValA)) return false; Offset += ValA; } const MCSymbolRefExpr *B = Target.getSymB(); if (B) { uint64_t ValB; if (!getLabelOffset(Layout, B->getSymbol(), ReportError, ValB)) return false; Offset -= ValB; } Val = Offset; return true; } bool MCAsmLayout::getSymbolOffset(const MCSymbol &S, uint64_t &Val) const { return getSymbolOffsetImpl(*this, S, false, Val); } uint64_t MCAsmLayout::getSymbolOffset(const MCSymbol &S) const { uint64_t Val; getSymbolOffsetImpl(*this, S, true, Val); return Val; } const MCSymbol *MCAsmLayout::getBaseSymbol(const MCSymbol &Symbol) const { if (!Symbol.isVariable()) return &Symbol; const MCExpr *Expr = Symbol.getVariableValue(); MCValue Value; if (!Expr->evaluateAsValue(Value, *this)) { Assembler.getContext().reportError( Expr->getLoc(), "expression could not be evaluated"); return nullptr; } const MCSymbolRefExpr *RefB = Value.getSymB(); if (RefB) { Assembler.getContext().reportError( Expr->getLoc(), Twine("symbol '") + RefB->getSymbol().getName() + "' could not be evaluated in a subtraction expression"); return nullptr; } const MCSymbolRefExpr *A = Value.getSymA(); if (!A) return nullptr; const MCSymbol &ASym = A->getSymbol(); const MCAssembler &Asm = getAssembler(); if (ASym.isCommon()) { Asm.getContext().reportError(Expr->getLoc(), "Common symbol '" + ASym.getName() + "' cannot be used in assignment expr"); return nullptr; } return &ASym; } uint64_t MCAsmLayout::getSectionAddressSize(const MCSection *Sec) const { // The size is the last fragment's end offset. const MCFragment &F = Sec->getFragmentList().back(); return getFragmentOffset(&F) + getAssembler().computeFragmentSize(*this, F); } uint64_t MCAsmLayout::getSectionFileSize(const MCSection *Sec) const { // Virtual sections have no file size. if (Sec->isVirtualSection()) return 0; // Otherwise, the file size is the same as the address space size. return getSectionAddressSize(Sec); } uint64_t llvm::computeBundlePadding(const MCAssembler &Assembler, const MCEncodedFragment *F, uint64_t FOffset, uint64_t FSize) { uint64_t BundleSize = Assembler.getBundleAlignSize(); assert(BundleSize > 0 && "computeBundlePadding should only be called if bundling is enabled"); uint64_t BundleMask = BundleSize - 1; uint64_t OffsetInBundle = FOffset & BundleMask; uint64_t EndOfFragment = OffsetInBundle + FSize; // There are two kinds of bundling restrictions: // // 1) For alignToBundleEnd(), add padding to ensure that the fragment will // *end* on a bundle boundary. // 2) Otherwise, check if the fragment would cross a bundle boundary. If it // would, add padding until the end of the bundle so that the fragment // will start in a new one. if (F->alignToBundleEnd()) { // Three possibilities here: // // A) The fragment just happens to end at a bundle boundary, so we're good. // B) The fragment ends before the current bundle boundary: pad it just // enough to reach the boundary. // C) The fragment ends after the current bundle boundary: pad it until it // reaches the end of the next bundle boundary. // // Note: this code could be made shorter with some modulo trickery, but it's // intentionally kept in its more explicit form for simplicity. if (EndOfFragment == BundleSize) return 0; else if (EndOfFragment < BundleSize) return BundleSize - EndOfFragment; else { // EndOfFragment > BundleSize return 2 * BundleSize - EndOfFragment; } } else if (OffsetInBundle > 0 && EndOfFragment > BundleSize) return BundleSize - OffsetInBundle; else return 0; } /* *** */ void ilist_alloc_traits::deleteNode(MCFragment *V) { V->destroy(); } MCFragment::MCFragment(FragmentType Kind, bool HasInstructions, MCSection *Parent) : Kind(Kind), HasInstructions(HasInstructions), LayoutOrder(0), Parent(Parent), Atom(nullptr), Offset(~UINT64_C(0)) { if (Parent && !isDummy()) Parent->getFragmentList().push_back(this); } void MCFragment::destroy() { // First check if we are the sentinal. if (Kind == FragmentType(~0)) { delete this; return; } switch (Kind) { case FT_Align: delete cast(this); return; case FT_Data: delete cast(this); return; case FT_CompactEncodedInst: delete cast(this); return; case FT_Fill: delete cast(this); return; case FT_Relaxable: delete cast(this); return; case FT_Org: delete cast(this); return; case FT_Dwarf: delete cast(this); return; case FT_DwarfFrame: delete cast(this); return; case FT_LEB: delete cast(this); return; + case FT_BoundaryAlign: + delete cast(this); + return; case FT_SymbolId: delete cast(this); return; case FT_CVInlineLines: delete cast(this); return; case FT_CVDefRange: delete cast(this); return; case FT_Dummy: delete cast(this); return; } } // Debugging methods namespace llvm { raw_ostream &operator<<(raw_ostream &OS, const MCFixup &AF) { OS << ""; return OS; } } // end namespace llvm #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void MCFragment::dump() const { raw_ostream &OS = errs(); OS << "<"; switch (getKind()) { case MCFragment::FT_Align: OS << "MCAlignFragment"; break; case MCFragment::FT_Data: OS << "MCDataFragment"; break; case MCFragment::FT_CompactEncodedInst: OS << "MCCompactEncodedInstFragment"; break; case MCFragment::FT_Fill: OS << "MCFillFragment"; break; case MCFragment::FT_Relaxable: OS << "MCRelaxableFragment"; break; case MCFragment::FT_Org: OS << "MCOrgFragment"; break; case MCFragment::FT_Dwarf: OS << "MCDwarfFragment"; break; case MCFragment::FT_DwarfFrame: OS << "MCDwarfCallFrameFragment"; break; case MCFragment::FT_LEB: OS << "MCLEBFragment"; break; + case MCFragment::FT_BoundaryAlign: OS<<"MCBoundaryAlignFragment"; break; case MCFragment::FT_SymbolId: OS << "MCSymbolIdFragment"; break; case MCFragment::FT_CVInlineLines: OS << "MCCVInlineLineTableFragment"; break; case MCFragment::FT_CVDefRange: OS << "MCCVDefRangeTableFragment"; break; case MCFragment::FT_Dummy: OS << "MCDummyFragment"; break; } OS << "(this)) OS << " BundlePadding:" << static_cast(EF->getBundlePadding()); OS << ">"; switch (getKind()) { case MCFragment::FT_Align: { const MCAlignFragment *AF = cast(this); if (AF->hasEmitNops()) OS << " (emit nops)"; OS << "\n "; OS << " Alignment:" << AF->getAlignment() << " Value:" << AF->getValue() << " ValueSize:" << AF->getValueSize() << " MaxBytesToEmit:" << AF->getMaxBytesToEmit() << ">"; break; } case MCFragment::FT_Data: { const MCDataFragment *DF = cast(this); OS << "\n "; OS << " Contents:["; const SmallVectorImpl &Contents = DF->getContents(); for (unsigned i = 0, e = Contents.size(); i != e; ++i) { if (i) OS << ","; OS << hexdigit((Contents[i] >> 4) & 0xF) << hexdigit(Contents[i] & 0xF); } OS << "] (" << Contents.size() << " bytes)"; if (DF->fixup_begin() != DF->fixup_end()) { OS << ",\n "; OS << " Fixups:["; for (MCDataFragment::const_fixup_iterator it = DF->fixup_begin(), ie = DF->fixup_end(); it != ie; ++it) { if (it != DF->fixup_begin()) OS << ",\n "; OS << *it; } OS << "]"; } break; } case MCFragment::FT_CompactEncodedInst: { const MCCompactEncodedInstFragment *CEIF = cast(this); OS << "\n "; OS << " Contents:["; const SmallVectorImpl &Contents = CEIF->getContents(); for (unsigned i = 0, e = Contents.size(); i != e; ++i) { if (i) OS << ","; OS << hexdigit((Contents[i] >> 4) & 0xF) << hexdigit(Contents[i] & 0xF); } OS << "] (" << Contents.size() << " bytes)"; break; } case MCFragment::FT_Fill: { const MCFillFragment *FF = cast(this); OS << " Value:" << static_cast(FF->getValue()) << " ValueSize:" << static_cast(FF->getValueSize()) << " NumValues:" << FF->getNumValues(); break; } case MCFragment::FT_Relaxable: { const MCRelaxableFragment *F = cast(this); OS << "\n "; OS << " Inst:"; F->getInst().dump_pretty(OS); break; } case MCFragment::FT_Org: { const MCOrgFragment *OF = cast(this); OS << "\n "; OS << " Offset:" << OF->getOffset() << " Value:" << static_cast(OF->getValue()); break; } case MCFragment::FT_Dwarf: { const MCDwarfLineAddrFragment *OF = cast(this); OS << "\n "; OS << " AddrDelta:" << OF->getAddrDelta() << " LineDelta:" << OF->getLineDelta(); break; } case MCFragment::FT_DwarfFrame: { const MCDwarfCallFrameFragment *CF = cast(this); OS << "\n "; OS << " AddrDelta:" << CF->getAddrDelta(); break; } case MCFragment::FT_LEB: { const MCLEBFragment *LF = cast(this); OS << "\n "; OS << " Value:" << LF->getValue() << " Signed:" << LF->isSigned(); break; } + case MCFragment::FT_BoundaryAlign: { + const auto *BF = cast(this); + if (BF->canEmitNops()) + OS << " (can emit nops to align"; + if (BF->isFused()) + OS << " fused branch)"; + else + OS << " unfused branch)"; + OS << "\n "; + OS << " BoundarySize:" << BF->getAlignment().value() + << " Size:" << BF->getSize(); + break; + } case MCFragment::FT_SymbolId: { const MCSymbolIdFragment *F = cast(this); OS << "\n "; OS << " Sym:" << F->getSymbol(); break; } case MCFragment::FT_CVInlineLines: { const auto *F = cast(this); OS << "\n "; OS << " Sym:" << *F->getFnStartSym(); break; } case MCFragment::FT_CVDefRange: { const auto *F = cast(this); OS << "\n "; for (std::pair RangeStartEnd : F->getRanges()) { OS << " RangeStart:" << RangeStartEnd.first; OS << " RangeEnd:" << RangeStartEnd.second; } break; } case MCFragment::FT_Dummy: break; } OS << ">"; } #endif diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 1ed6ec1015b2..5c42667f9910 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -1,765 +1,772 @@ //===- lib/MC/MCObjectStreamer.cpp - Object File MCStreamer Interface -----===// // // 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/MCObjectStreamer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.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/MCObjectWriter.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" using namespace llvm; MCObjectStreamer::MCObjectStreamer(MCContext &Context, std::unique_ptr TAB, std::unique_ptr OW, std::unique_ptr Emitter) : MCStreamer(Context), Assembler(std::make_unique( Context, std::move(TAB), std::move(Emitter), std::move(OW))), EmitEHFrame(true), EmitDebugFrame(false) {} MCObjectStreamer::~MCObjectStreamer() {} // AssemblerPtr is used for evaluation of expressions and causes // difference between asm and object outputs. Return nullptr to in // inline asm mode to limit divergence to assembly inputs. MCAssembler *MCObjectStreamer::getAssemblerPtr() { if (getUseAssemblerInfoForParsing()) return Assembler.get(); return nullptr; } void MCObjectStreamer::addPendingLabel(MCSymbol* S) { MCSection *CurSection = getCurrentSectionOnly(); if (CurSection) { // Register labels that have not yet been assigned to a Section. if (!PendingLabels.empty()) { for (MCSymbol* Sym : PendingLabels) CurSection->addPendingLabel(Sym); PendingLabels.clear(); } // Add this label to the current Section / Subsection. CurSection->addPendingLabel(S, CurSubsectionIdx); // Add this Section to the list of PendingLabelSections. auto SecIt = std::find(PendingLabelSections.begin(), PendingLabelSections.end(), CurSection); if (SecIt == PendingLabelSections.end()) PendingLabelSections.push_back(CurSection); } else // There is no Section / Subsection for this label yet. PendingLabels.push_back(S); } void MCObjectStreamer::flushPendingLabels(MCFragment *F, uint64_t FOffset) { MCSection *CurSection = getCurrentSectionOnly(); if (!CurSection) { assert(PendingLabels.empty()); return; } // Register labels that have not yet been assigned to a Section. if (!PendingLabels.empty()) { for (MCSymbol* Sym : PendingLabels) CurSection->addPendingLabel(Sym, CurSubsectionIdx); PendingLabels.clear(); } // Associate a fragment with this label, either the supplied fragment // or an empty data fragment. if (F) CurSection->flushPendingLabels(F, FOffset, CurSubsectionIdx); else CurSection->flushPendingLabels(nullptr, 0, CurSubsectionIdx); } void MCObjectStreamer::flushPendingLabels() { // Register labels that have not yet been assigned to a Section. if (!PendingLabels.empty()) { MCSection *CurSection = getCurrentSectionOnly(); assert(CurSection); for (MCSymbol* Sym : PendingLabels) CurSection->addPendingLabel(Sym, CurSubsectionIdx); PendingLabels.clear(); } // Assign an empty data fragment to all remaining pending labels. for (MCSection* Section : PendingLabelSections) Section->flushPendingLabels(); } // When fixup's offset is a forward declared label, e.g.: // // .reloc 1f, R_MIPS_JALR, foo // 1: nop // // postpone adding it to Fixups vector until the label is defined and its offset // is known. void MCObjectStreamer::resolvePendingFixups() { for (PendingMCFixup &PendingFixup : PendingFixups) { if (!PendingFixup.Sym || PendingFixup.Sym->isUndefined ()) { getContext().reportError(PendingFixup.Fixup.getLoc(), "unresolved relocation offset"); continue; } flushPendingLabels(PendingFixup.DF, PendingFixup.DF->getContents().size()); PendingFixup.Fixup.setOffset(PendingFixup.Sym->getOffset()); PendingFixup.DF->getFixups().push_back(PendingFixup.Fixup); } PendingFixups.clear(); } // As a compile-time optimization, avoid allocating and evaluating an MCExpr // tree for (Hi - Lo) when Hi and Lo are offsets into the same fragment. static Optional absoluteSymbolDiff(MCAssembler &Asm, const MCSymbol *Hi, const MCSymbol *Lo) { assert(Hi && Lo); if (Asm.getBackendPtr()->requiresDiffExpressionRelocations()) return None; if (!Hi->getFragment() || Hi->getFragment() != Lo->getFragment() || Hi->isVariable() || Lo->isVariable()) return None; return Hi->getOffset() - Lo->getOffset(); } void MCObjectStreamer::emitAbsoluteSymbolDiff(const MCSymbol *Hi, const MCSymbol *Lo, unsigned Size) { if (Optional Diff = absoluteSymbolDiff(getAssembler(), Hi, Lo)) { EmitIntValue(*Diff, Size); return; } MCStreamer::emitAbsoluteSymbolDiff(Hi, Lo, Size); } void MCObjectStreamer::emitAbsoluteSymbolDiffAsULEB128(const MCSymbol *Hi, const MCSymbol *Lo) { if (Optional Diff = absoluteSymbolDiff(getAssembler(), Hi, Lo)) { EmitULEB128IntValue(*Diff); return; } MCStreamer::emitAbsoluteSymbolDiffAsULEB128(Hi, Lo); } void MCObjectStreamer::reset() { if (Assembler) Assembler->reset(); CurInsertionPoint = MCSection::iterator(); EmitEHFrame = true; EmitDebugFrame = false; PendingLabels.clear(); PendingLabelSections.clear(); MCStreamer::reset(); } void MCObjectStreamer::EmitFrames(MCAsmBackend *MAB) { if (!getNumFrameInfos()) return; if (EmitEHFrame) MCDwarfFrameEmitter::Emit(*this, MAB, true); if (EmitDebugFrame) MCDwarfFrameEmitter::Emit(*this, MAB, false); } MCFragment *MCObjectStreamer::getCurrentFragment() const { assert(getCurrentSectionOnly() && "No current section!"); if (CurInsertionPoint != getCurrentSectionOnly()->getFragmentList().begin()) return &*std::prev(CurInsertionPoint); return nullptr; } static bool CanReuseDataFragment(const MCDataFragment &F, const MCAssembler &Assembler, const MCSubtargetInfo *STI) { if (!F.hasInstructions()) return true; // When bundling is enabled, we don't want to add data to a fragment that // already has instructions (see MCELFStreamer::EmitInstToData for details) if (Assembler.isBundlingEnabled()) return Assembler.getRelaxAll(); // If the subtarget is changed mid fragment we start a new fragment to record // the new STI. return !STI || F.getSubtargetInfo() == STI; } MCDataFragment * MCObjectStreamer::getOrCreateDataFragment(const MCSubtargetInfo *STI) { MCDataFragment *F = dyn_cast_or_null(getCurrentFragment()); if (!F || !CanReuseDataFragment(*F, *Assembler, STI)) { F = new MCDataFragment(); insert(F); } return F; } void MCObjectStreamer::visitUsedSymbol(const MCSymbol &Sym) { Assembler->registerSymbol(Sym); } void MCObjectStreamer::EmitCFISections(bool EH, bool Debug) { MCStreamer::EmitCFISections(EH, Debug); EmitEHFrame = EH; EmitDebugFrame = Debug; } void MCObjectStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) { MCStreamer::EmitValueImpl(Value, Size, Loc); MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); MCDwarfLineEntry::Make(this, getCurrentSectionOnly()); // Avoid fixups when possible. int64_t AbsValue; if (Value->evaluateAsAbsolute(AbsValue, getAssemblerPtr())) { if (!isUIntN(8 * Size, AbsValue) && !isIntN(8 * Size, AbsValue)) { getContext().reportError( Loc, "value evaluated as " + Twine(AbsValue) + " is out of range."); return; } EmitIntValue(AbsValue, Size); return; } DF->getFixups().push_back( MCFixup::create(DF->getContents().size(), Value, MCFixup::getKindForSize(Size, false), Loc)); DF->getContents().resize(DF->getContents().size() + Size, 0); } MCSymbol *MCObjectStreamer::EmitCFILabel() { MCSymbol *Label = getContext().createTempSymbol("cfi", true); EmitLabel(Label); return Label; } void MCObjectStreamer::EmitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { // We need to create a local symbol to avoid relocations. Frame.Begin = getContext().createTempSymbol(); EmitLabel(Frame.Begin); } void MCObjectStreamer::EmitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { Frame.End = getContext().createTempSymbol(); EmitLabel(Frame.End); } void MCObjectStreamer::EmitLabel(MCSymbol *Symbol, SMLoc Loc) { MCStreamer::EmitLabel(Symbol, Loc); getAssembler().registerSymbol(*Symbol); // If there is a current fragment, mark the symbol as pointing into it. // Otherwise queue the label and set its fragment pointer when we emit the // next fragment. auto *F = dyn_cast_or_null(getCurrentFragment()); if (F && !(getAssembler().isBundlingEnabled() && getAssembler().getRelaxAll())) { Symbol->setFragment(F); Symbol->setOffset(F->getContents().size()); } else { // Assign all pending labels to offset 0 within the dummy "pending" // fragment. (They will all be reassigned to a real fragment in // flushPendingLabels()) Symbol->setOffset(0); addPendingLabel(Symbol); } } // Emit a label at a previously emitted fragment/offset position. This must be // within the currently-active section. void MCObjectStreamer::EmitLabelAtPos(MCSymbol *Symbol, SMLoc Loc, MCFragment *F, uint64_t Offset) { assert(F->getParent() == getCurrentSectionOnly()); MCStreamer::EmitLabel(Symbol, Loc); getAssembler().registerSymbol(*Symbol); auto *DF = dyn_cast_or_null(F); Symbol->setOffset(Offset); if (DF) { Symbol->setFragment(F); } else { assert(isa(F) && "F must either be an MCDataFragment or the pending MCDummyFragment"); assert(Offset == 0); addPendingLabel(Symbol); } } void MCObjectStreamer::EmitULEB128Value(const MCExpr *Value) { int64_t IntValue; if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) { EmitULEB128IntValue(IntValue); return; } insert(new MCLEBFragment(*Value, false)); } void MCObjectStreamer::EmitSLEB128Value(const MCExpr *Value) { int64_t IntValue; if (Value->evaluateAsAbsolute(IntValue, getAssemblerPtr())) { EmitSLEB128IntValue(IntValue); return; } insert(new MCLEBFragment(*Value, true)); } void MCObjectStreamer::EmitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) { report_fatal_error("This file format doesn't support weak aliases."); } void MCObjectStreamer::ChangeSection(MCSection *Section, const MCExpr *Subsection) { changeSectionImpl(Section, Subsection); } bool MCObjectStreamer::changeSectionImpl(MCSection *Section, const MCExpr *Subsection) { assert(Section && "Cannot switch to a null section!"); getContext().clearDwarfLocSeen(); bool Created = getAssembler().registerSection(*Section); int64_t IntSubsection = 0; if (Subsection && !Subsection->evaluateAsAbsolute(IntSubsection, getAssemblerPtr())) report_fatal_error("Cannot evaluate subsection number"); if (IntSubsection < 0 || IntSubsection > 8192) report_fatal_error("Subsection number out of range"); CurSubsectionIdx = unsigned(IntSubsection); CurInsertionPoint = Section->getSubsectionInsertionPoint(CurSubsectionIdx); return Created; } void MCObjectStreamer::EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) { getAssembler().registerSymbol(*Symbol); MCStreamer::EmitAssignment(Symbol, Value); } bool MCObjectStreamer::mayHaveInstructions(MCSection &Sec) const { return Sec.hasInstructions(); } void MCObjectStreamer::EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { + getAssembler().getBackend().alignBranchesBegin(*this, Inst); + EmitInstructionImpl(Inst, STI); + getAssembler().getBackend().alignBranchesEnd(*this, Inst); +} + +void MCObjectStreamer::EmitInstructionImpl(const MCInst &Inst, + const MCSubtargetInfo &STI) { MCStreamer::EmitInstruction(Inst, STI); MCSection *Sec = getCurrentSectionOnly(); Sec->setHasInstructions(true); // Now that a machine instruction has been assembled into this section, make // a line entry for any .loc directive that has been seen. MCDwarfLineEntry::Make(this, getCurrentSectionOnly()); // If this instruction doesn't need relaxation, just emit it as data. MCAssembler &Assembler = getAssembler(); if (!Assembler.getBackend().mayNeedRelaxation(Inst, STI)) { EmitInstToData(Inst, STI); return; } // Otherwise, relax and emit it as data if either: // - The RelaxAll flag was passed // - Bundling is enabled and this instruction is inside a bundle-locked // group. We want to emit all such instructions into the same data // fragment. if (Assembler.getRelaxAll() || (Assembler.isBundlingEnabled() && Sec->isBundleLocked())) { MCInst Relaxed; getAssembler().getBackend().relaxInstruction(Inst, STI, Relaxed); while (getAssembler().getBackend().mayNeedRelaxation(Relaxed, STI)) getAssembler().getBackend().relaxInstruction(Relaxed, STI, Relaxed); EmitInstToData(Relaxed, STI); return; } // Otherwise emit to a separate fragment. EmitInstToFragment(Inst, STI); } void MCObjectStreamer::EmitInstToFragment(const MCInst &Inst, const MCSubtargetInfo &STI) { if (getAssembler().getRelaxAll() && getAssembler().isBundlingEnabled()) llvm_unreachable("All instructions should have already been relaxed"); // Always create a new, separate fragment here, because its size can change // during relaxation. MCRelaxableFragment *IF = new MCRelaxableFragment(Inst, STI); insert(IF); SmallString<128> Code; raw_svector_ostream VecOS(Code); getAssembler().getEmitter().encodeInstruction(Inst, VecOS, IF->getFixups(), STI); IF->getContents().append(Code.begin(), Code.end()); } #ifndef NDEBUG static const char *const BundlingNotImplementedMsg = "Aligned bundling is not implemented for this object format"; #endif void MCObjectStreamer::EmitBundleAlignMode(unsigned AlignPow2) { llvm_unreachable(BundlingNotImplementedMsg); } void MCObjectStreamer::EmitBundleLock(bool AlignToEnd) { llvm_unreachable(BundlingNotImplementedMsg); } void MCObjectStreamer::EmitBundleUnlock() { llvm_unreachable(BundlingNotImplementedMsg); } void MCObjectStreamer::EmitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, unsigned Isa, unsigned Discriminator, StringRef FileName) { // In case we see two .loc directives in a row, make sure the // first one gets a line entry. MCDwarfLineEntry::Make(this, getCurrentSectionOnly()); this->MCStreamer::EmitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, Discriminator, FileName); } static const MCExpr *buildSymbolDiff(MCObjectStreamer &OS, const MCSymbol *A, const MCSymbol *B) { MCContext &Context = OS.getContext(); MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; const MCExpr *ARef = MCSymbolRefExpr::create(A, Variant, Context); const MCExpr *BRef = MCSymbolRefExpr::create(B, Variant, Context); const MCExpr *AddrDelta = MCBinaryExpr::create(MCBinaryExpr::Sub, ARef, BRef, Context); return AddrDelta; } static void emitDwarfSetLineAddr(MCObjectStreamer &OS, MCDwarfLineTableParams Params, int64_t LineDelta, const MCSymbol *Label, int PointerSize) { // emit the sequence to set the address OS.EmitIntValue(dwarf::DW_LNS_extended_op, 1); OS.EmitULEB128IntValue(PointerSize + 1); OS.EmitIntValue(dwarf::DW_LNE_set_address, 1); OS.EmitSymbolValue(Label, PointerSize); // emit the sequence for the LineDelta (from 1) and a zero address delta. MCDwarfLineAddr::Emit(&OS, Params, LineDelta, 0); } void MCObjectStreamer::EmitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel, const MCSymbol *Label, unsigned PointerSize) { if (!LastLabel) { emitDwarfSetLineAddr(*this, Assembler->getDWARFLinetableParams(), LineDelta, Label, PointerSize); return; } const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel); int64_t Res; if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) { MCDwarfLineAddr::Emit(this, Assembler->getDWARFLinetableParams(), LineDelta, Res); return; } insert(new MCDwarfLineAddrFragment(LineDelta, *AddrDelta)); } void MCObjectStreamer::EmitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, const MCSymbol *Label) { const MCExpr *AddrDelta = buildSymbolDiff(*this, Label, LastLabel); int64_t Res; if (AddrDelta->evaluateAsAbsolute(Res, getAssemblerPtr())) { MCDwarfFrameEmitter::EmitAdvanceLoc(*this, Res); return; } insert(new MCDwarfCallFrameFragment(*AddrDelta)); } void MCObjectStreamer::EmitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, StringRef FileName, SMLoc Loc) { // Validate the directive. if (!checkCVLocSection(FunctionId, FileNo, Loc)) return; // Emit a label at the current position and record it in the CodeViewContext. MCSymbol *LineSym = getContext().createTempSymbol(); EmitLabel(LineSym); getContext().getCVContext().recordCVLoc(getContext(), LineSym, FunctionId, FileNo, Line, Column, PrologueEnd, IsStmt); } void MCObjectStreamer::EmitCVLinetableDirective(unsigned FunctionId, const MCSymbol *Begin, const MCSymbol *End) { getContext().getCVContext().emitLineTableForFunction(*this, FunctionId, Begin, End); this->MCStreamer::EmitCVLinetableDirective(FunctionId, Begin, End); } void MCObjectStreamer::EmitCVInlineLinetableDirective( unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) { getContext().getCVContext().emitInlineLineTableForFunction( *this, PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); this->MCStreamer::EmitCVInlineLinetableDirective( PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); } void MCObjectStreamer::EmitCVDefRangeDirective( ArrayRef> Ranges, StringRef FixedSizePortion) { MCFragment *Frag = getContext().getCVContext().emitDefRange(*this, Ranges, FixedSizePortion); // Attach labels that were pending before we created the defrange fragment to // the beginning of the new fragment. flushPendingLabels(Frag, 0); this->MCStreamer::EmitCVDefRangeDirective(Ranges, FixedSizePortion); } void MCObjectStreamer::EmitCVStringTableDirective() { getContext().getCVContext().emitStringTable(*this); } void MCObjectStreamer::EmitCVFileChecksumsDirective() { getContext().getCVContext().emitFileChecksums(*this); } void MCObjectStreamer::EmitCVFileChecksumOffsetDirective(unsigned FileNo) { getContext().getCVContext().emitFileChecksumOffset(*this, FileNo); } void MCObjectStreamer::EmitBytes(StringRef Data) { MCDwarfLineEntry::Make(this, getCurrentSectionOnly()); MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getContents().append(Data.begin(), Data.end()); // EmitBytes might not cover all possible ways we emit data (or could be used // to emit executable code in some cases), but is the best method we have // right now for checking this. MCSection *Sec = getCurrentSectionOnly(); Sec->setHasData(true); } void MCObjectStreamer::EmitValueToAlignment(unsigned ByteAlignment, int64_t Value, unsigned ValueSize, unsigned MaxBytesToEmit) { if (MaxBytesToEmit == 0) MaxBytesToEmit = ByteAlignment; insert(new MCAlignFragment(ByteAlignment, Value, ValueSize, MaxBytesToEmit)); // Update the maximum alignment on the current section if necessary. MCSection *CurSec = getCurrentSectionOnly(); if (ByteAlignment > CurSec->getAlignment()) CurSec->setAlignment(Align(ByteAlignment)); } void MCObjectStreamer::EmitCodeAlignment(unsigned ByteAlignment, unsigned MaxBytesToEmit) { EmitValueToAlignment(ByteAlignment, 0, 1, MaxBytesToEmit); cast(getCurrentFragment())->setEmitNops(true); } void MCObjectStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) { insert(new MCOrgFragment(*Offset, Value, Loc)); } // Associate DTPRel32 fixup with data and resize data area void MCObjectStreamer::EmitDTPRel32Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), Value, FK_DTPRel_4)); DF->getContents().resize(DF->getContents().size() + 4, 0); } // Associate DTPRel64 fixup with data and resize data area void MCObjectStreamer::EmitDTPRel64Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), Value, FK_DTPRel_8)); DF->getContents().resize(DF->getContents().size() + 8, 0); } // Associate TPRel32 fixup with data and resize data area void MCObjectStreamer::EmitTPRel32Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), Value, FK_TPRel_4)); DF->getContents().resize(DF->getContents().size() + 4, 0); } // Associate TPRel64 fixup with data and resize data area void MCObjectStreamer::EmitTPRel64Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back(MCFixup::create(DF->getContents().size(), Value, FK_TPRel_8)); DF->getContents().resize(DF->getContents().size() + 8, 0); } // Associate GPRel32 fixup with data and resize data area void MCObjectStreamer::EmitGPRel32Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back( MCFixup::create(DF->getContents().size(), Value, FK_GPRel_4)); DF->getContents().resize(DF->getContents().size() + 4, 0); } // Associate GPRel64 fixup with data and resize data area void MCObjectStreamer::EmitGPRel64Value(const MCExpr *Value) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); DF->getFixups().push_back( MCFixup::create(DF->getContents().size(), Value, FK_GPRel_4)); DF->getContents().resize(DF->getContents().size() + 8, 0); } bool MCObjectStreamer::EmitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc Loc, const MCSubtargetInfo &STI) { Optional MaybeKind = Assembler->getBackend().getFixupKind(Name); if (!MaybeKind.hasValue()) return true; MCFixupKind Kind = *MaybeKind; if (Expr == nullptr) Expr = MCSymbolRefExpr::create(getContext().createTempSymbol(), getContext()); MCDataFragment *DF = getOrCreateDataFragment(&STI); flushPendingLabels(DF, DF->getContents().size()); int64_t OffsetValue; if (Offset.evaluateAsAbsolute(OffsetValue)) { if (OffsetValue < 0) llvm_unreachable(".reloc offset is negative"); DF->getFixups().push_back(MCFixup::create(OffsetValue, Expr, Kind, Loc)); return false; } if (Offset.getKind() != llvm::MCExpr::SymbolRef) llvm_unreachable(".reloc offset is not absolute nor a label"); const MCSymbolRefExpr &SRE = cast(Offset); if (SRE.getSymbol().isDefined()) { DF->getFixups().push_back(MCFixup::create(SRE.getSymbol().getOffset(), Expr, Kind, Loc)); return false; } PendingFixups.emplace_back(&SRE.getSymbol(), DF, MCFixup::create(-1, Expr, Kind, Loc)); return false; } void MCObjectStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc) { MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); assert(getCurrentSectionOnly() && "need a section"); insert(new MCFillFragment(FillValue, 1, NumBytes, Loc)); } void MCObjectStreamer::emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr, SMLoc Loc) { int64_t IntNumValues; // Do additional checking now if we can resolve the value. if (NumValues.evaluateAsAbsolute(IntNumValues, getAssemblerPtr())) { if (IntNumValues < 0) { getContext().getSourceManager()->PrintMessage( Loc, SourceMgr::DK_Warning, "'.fill' directive with negative repeat count has no effect"); return; } // Emit now if we can for better errors. int64_t NonZeroSize = Size > 4 ? 4 : Size; Expr &= ~0ULL >> (64 - NonZeroSize * 8); for (uint64_t i = 0, e = IntNumValues; i != e; ++i) { EmitIntValue(Expr, NonZeroSize); if (NonZeroSize < Size) EmitIntValue(0, Size - NonZeroSize); } return; } // Otherwise emit as fragment. MCDataFragment *DF = getOrCreateDataFragment(); flushPendingLabels(DF, DF->getContents().size()); assert(getCurrentSectionOnly() && "need a section"); insert(new MCFillFragment(Expr, Size, NumValues, Loc)); } void MCObjectStreamer::EmitFileDirective(StringRef Filename) { getAssembler().addFileName(Filename); } void MCObjectStreamer::EmitAddrsig() { getAssembler().getWriter().emitAddrsigSection(); } void MCObjectStreamer::EmitAddrsigSym(const MCSymbol *Sym) { getAssembler().registerSymbol(*Sym); getAssembler().getWriter().addAddrsigSymbol(Sym); } void MCObjectStreamer::FinishImpl() { getContext().RemapDebugPaths(); // If we are generating dwarf for assembly source files dump out the sections. if (getContext().getGenDwarfForAssembly()) MCGenDwarfInfo::Emit(this); // Dump out the dwarf file & directory tables and line tables. MCDwarfLineTable::Emit(this, getAssembler().getDWARFLinetableParams()); // Update any remaining pending labels with empty data fragments. flushPendingLabels(); resolvePendingFixups(); getAssembler().Finish(); } diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp index 1ccb9b7cbf74..afcd244b1441 100644 --- a/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp +++ b/llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp @@ -1,894 +1,1179 @@ //===-- X86AsmBackend.cpp - X86 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 "MCTargetDesc/X86BaseInfo.h" #include "MCTargetDesc/X86FixupKinds.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCMachObjectWriter.h" +#include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCValue.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/TargetRegistry.h" + using namespace llvm; static unsigned getFixupKindSize(unsigned Kind) { switch (Kind) { default: llvm_unreachable("invalid fixup kind!"); case FK_NONE: return 0; case FK_PCRel_1: case FK_SecRel_1: case FK_Data_1: return 1; case FK_PCRel_2: case FK_SecRel_2: case FK_Data_2: return 2; case FK_PCRel_4: case X86::reloc_riprel_4byte: case X86::reloc_riprel_4byte_relax: case X86::reloc_riprel_4byte_relax_rex: case X86::reloc_riprel_4byte_movq_load: case X86::reloc_signed_4byte: case X86::reloc_signed_4byte_relax: case X86::reloc_global_offset_table: case X86::reloc_branch_4byte_pcrel: case FK_SecRel_4: case FK_Data_4: return 4; case FK_PCRel_8: case FK_SecRel_8: case FK_Data_8: case X86::reloc_global_offset_table8: return 8; } } namespace { +class X86AlignBranchKind { +private: + uint8_t AlignBranchKind = 0; + +public: + enum Flag : uint8_t { + AlignBranchNone = 0, + AlignBranchFused = 1U << 0, + AlignBranchJcc = 1U << 1, + AlignBranchJmp = 1U << 2, + AlignBranchCall = 1U << 3, + AlignBranchRet = 1U << 4, + AlignBranchIndirect = 1U << 5 + }; + + void operator=(const std::string &Val) { + if (Val.empty()) + return; + SmallVector BranchTypes; + StringRef(Val).split(BranchTypes, '+', -1, false); + for (auto BranchType : BranchTypes) { + if (BranchType == "fused") + addKind(AlignBranchFused); + else if (BranchType == "jcc") + addKind(AlignBranchJcc); + else if (BranchType == "jmp") + addKind(AlignBranchJmp); + else if (BranchType == "call") + addKind(AlignBranchCall); + else if (BranchType == "ret") + addKind(AlignBranchRet); + else if (BranchType == "indirect") + addKind(AlignBranchIndirect); + else { + report_fatal_error( + "'-x86-align-branch 'The branches's type is combination of jcc, " + "fused, jmp, call, ret, indirect.(plus separated)", + false); + } + } + } + + operator uint8_t() const { return AlignBranchKind; } + void addKind(Flag Value) { AlignBranchKind |= Value; } +}; + +X86AlignBranchKind X86AlignBranchKindLoc; + +cl::opt X86AlignBranchBoundary( + "x86-align-branch-boundary", cl::init(0), + cl::desc( + "Control how the assembler should align branches with NOP. If the " + "boundary's size is not 0, it should be a power of 2 and no less " + "than 32. Branches will be aligned within the boundary of specified " + "size. -x86-align-branch-boundary=0 doesn't align branches.")); + +cl::opt> X86AlignBranch( + "x86-align-branch", + cl::desc("Specify types of branches to align (plus separated list of " + "types). The branches's type is combination of jcc, fused, " + "jmp, call, ret, indirect."), + cl::value_desc("jcc(conditional jump), fused(fused conditional jump), " + "jmp(unconditional jump); call(call); ret(ret), " + "indirect(indirect jump)."), + cl::location(X86AlignBranchKindLoc)); class X86ELFObjectWriter : public MCELFObjectTargetWriter { public: X86ELFObjectWriter(bool is64Bit, uint8_t OSABI, uint16_t EMachine, bool HasRelocationAddend, bool foobar) : MCELFObjectTargetWriter(is64Bit, OSABI, EMachine, HasRelocationAddend) {} }; class X86AsmBackend : public MCAsmBackend { const MCSubtargetInfo &STI; + const MCInstrInfo &MCII; + X86AlignBranchKind AlignBranchType; + Align AlignBoundary; + + bool isFirstMacroFusibleInst(const MCInst &Inst) const; + bool isMacroFused(const MCInst &Cmp, const MCInst &Jcc) const; + bool isRIPRelative(const MCInst &MI) const; + bool hasVariantSymbol(const MCInst &MI) const; + + bool needAlign(MCObjectStreamer &OS) const; + bool needAlignInst(const MCInst &Inst) const; + MCBoundaryAlignFragment * + getOrCreateBoundaryAlignFragment(MCObjectStreamer &OS) const; + MCInst PrevInst; + public: X86AsmBackend(const Target &T, const MCSubtargetInfo &STI) - : MCAsmBackend(support::little), STI(STI) {} + : MCAsmBackend(support::little), STI(STI), + MCII(*(T.createMCInstrInfo())) { + AlignBoundary = assumeAligned(X86AlignBranchBoundary); + AlignBranchType = X86AlignBranchKindLoc; + } + + void alignBranchesBegin(MCObjectStreamer &OS, const MCInst &Inst) override; + void alignBranchesEnd(MCObjectStreamer &OS, const MCInst &Inst) override; unsigned getNumFixupKinds() const override { return X86::NumTargetFixupKinds; } Optional getFixupKind(StringRef Name) const override; const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { const static MCFixupKindInfo Infos[X86::NumTargetFixupKinds] = { {"reloc_riprel_4byte", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, {"reloc_riprel_4byte_movq_load", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, {"reloc_riprel_4byte_relax", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, {"reloc_riprel_4byte_relax_rex", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, {"reloc_signed_4byte", 0, 32, 0}, {"reloc_signed_4byte_relax", 0, 32, 0}, {"reloc_global_offset_table", 0, 32, 0}, {"reloc_global_offset_table8", 0, 64, 0}, {"reloc_branch_4byte_pcrel", 0, 32, MCFixupKindInfo::FKF_IsPCRel}, }; if (Kind < FirstTargetFixupKind) return MCAsmBackend::getFixupKindInfo(Kind); assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && "Invalid kind!"); assert(Infos[Kind - FirstTargetFixupKind].Name && "Empty fixup name!"); return Infos[Kind - FirstTargetFixupKind]; } bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) override; void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const override { unsigned Size = getFixupKindSize(Fixup.getKind()); assert(Fixup.getOffset() + Size <= Data.size() && "Invalid fixup offset!"); int64_t SignedValue = static_cast(Value); if ((Target.isAbsolute() || IsResolved) && getFixupKindInfo(Fixup.getKind()).Flags & MCFixupKindInfo::FKF_IsPCRel) { // check that PC relative fixup fits into the fixup size. if (Size > 0 && !isIntN(Size * 8, SignedValue)) Asm.getContext().reportError( Fixup.getLoc(), "value of " + Twine(SignedValue) + " is too large for field of " + Twine(Size) + ((Size == 1) ? " byte." : " bytes.")); } else { // Check that uppper bits are either all zeros or all ones. // Specifically ignore overflow/underflow as long as the leakage is // limited to the lower bits. This is to remain compatible with // other assemblers. assert((Size == 0 || isIntN(Size * 8 + 1, SignedValue)) && "Value does not fit in the Fixup field"); } for (unsigned i = 0; i != Size; ++i) Data[Fixup.getOffset() + i] = uint8_t(Value >> (i * 8)); } bool mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const override; bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const override; void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const override; bool writeNopData(raw_ostream &OS, uint64_t Count) const override; }; } // end anonymous namespace static unsigned getRelaxedOpcodeBranch(const MCInst &Inst, bool is16BitMode) { unsigned Op = Inst.getOpcode(); switch (Op) { default: return Op; case X86::JCC_1: return (is16BitMode) ? X86::JCC_2 : X86::JCC_4; case X86::JMP_1: return (is16BitMode) ? X86::JMP_2 : X86::JMP_4; } } static unsigned getRelaxedOpcodeArith(const MCInst &Inst) { unsigned Op = Inst.getOpcode(); switch (Op) { default: return Op; // IMUL case X86::IMUL16rri8: return X86::IMUL16rri; case X86::IMUL16rmi8: return X86::IMUL16rmi; case X86::IMUL32rri8: return X86::IMUL32rri; case X86::IMUL32rmi8: return X86::IMUL32rmi; case X86::IMUL64rri8: return X86::IMUL64rri32; case X86::IMUL64rmi8: return X86::IMUL64rmi32; // AND case X86::AND16ri8: return X86::AND16ri; case X86::AND16mi8: return X86::AND16mi; case X86::AND32ri8: return X86::AND32ri; case X86::AND32mi8: return X86::AND32mi; case X86::AND64ri8: return X86::AND64ri32; case X86::AND64mi8: return X86::AND64mi32; // OR case X86::OR16ri8: return X86::OR16ri; case X86::OR16mi8: return X86::OR16mi; case X86::OR32ri8: return X86::OR32ri; case X86::OR32mi8: return X86::OR32mi; case X86::OR64ri8: return X86::OR64ri32; case X86::OR64mi8: return X86::OR64mi32; // XOR case X86::XOR16ri8: return X86::XOR16ri; case X86::XOR16mi8: return X86::XOR16mi; case X86::XOR32ri8: return X86::XOR32ri; case X86::XOR32mi8: return X86::XOR32mi; case X86::XOR64ri8: return X86::XOR64ri32; case X86::XOR64mi8: return X86::XOR64mi32; // ADD case X86::ADD16ri8: return X86::ADD16ri; case X86::ADD16mi8: return X86::ADD16mi; case X86::ADD32ri8: return X86::ADD32ri; case X86::ADD32mi8: return X86::ADD32mi; case X86::ADD64ri8: return X86::ADD64ri32; case X86::ADD64mi8: return X86::ADD64mi32; // ADC case X86::ADC16ri8: return X86::ADC16ri; case X86::ADC16mi8: return X86::ADC16mi; case X86::ADC32ri8: return X86::ADC32ri; case X86::ADC32mi8: return X86::ADC32mi; case X86::ADC64ri8: return X86::ADC64ri32; case X86::ADC64mi8: return X86::ADC64mi32; // SUB case X86::SUB16ri8: return X86::SUB16ri; case X86::SUB16mi8: return X86::SUB16mi; case X86::SUB32ri8: return X86::SUB32ri; case X86::SUB32mi8: return X86::SUB32mi; case X86::SUB64ri8: return X86::SUB64ri32; case X86::SUB64mi8: return X86::SUB64mi32; // SBB case X86::SBB16ri8: return X86::SBB16ri; case X86::SBB16mi8: return X86::SBB16mi; case X86::SBB32ri8: return X86::SBB32ri; case X86::SBB32mi8: return X86::SBB32mi; case X86::SBB64ri8: return X86::SBB64ri32; case X86::SBB64mi8: return X86::SBB64mi32; // CMP case X86::CMP16ri8: return X86::CMP16ri; case X86::CMP16mi8: return X86::CMP16mi; case X86::CMP32ri8: return X86::CMP32ri; case X86::CMP32mi8: return X86::CMP32mi; case X86::CMP64ri8: return X86::CMP64ri32; case X86::CMP64mi8: return X86::CMP64mi32; // PUSH case X86::PUSH32i8: return X86::PUSHi32; case X86::PUSH16i8: return X86::PUSHi16; case X86::PUSH64i8: return X86::PUSH64i32; } } static unsigned getRelaxedOpcode(const MCInst &Inst, bool is16BitMode) { unsigned R = getRelaxedOpcodeArith(Inst); if (R != Inst.getOpcode()) return R; return getRelaxedOpcodeBranch(Inst, is16BitMode); } +static X86::CondCode getCondFromBranch(const MCInst &MI, + const MCInstrInfo &MCII) { + unsigned Opcode = MI.getOpcode(); + switch (Opcode) { + default: + return X86::COND_INVALID; + case X86::JCC_1: { + const MCInstrDesc &Desc = MCII.get(Opcode); + return static_cast( + MI.getOperand(Desc.getNumOperands() - 1).getImm()); + } + } +} + +static X86::SecondMacroFusionInstKind +classifySecondInstInMacroFusion(const MCInst &MI, const MCInstrInfo &MCII) { + X86::CondCode CC = getCondFromBranch(MI, MCII); + return classifySecondCondCodeInMacroFusion(CC); +} + +/// Check if the instruction is valid as the first instruction in macro fusion. +bool X86AsmBackend::isFirstMacroFusibleInst(const MCInst &Inst) const { + // An Intel instruction with RIP relative addressing is not macro fusible. + if (isRIPRelative(Inst)) + return false; + X86::FirstMacroFusionInstKind FIK = + X86::classifyFirstOpcodeInMacroFusion(Inst.getOpcode()); + return FIK != X86::FirstMacroFusionInstKind::Invalid; +} + +/// Check if the two instructions are macro-fused. +bool X86AsmBackend::isMacroFused(const MCInst &Cmp, const MCInst &Jcc) const { + const MCInstrDesc &InstDesc = MCII.get(Jcc.getOpcode()); + if (!InstDesc.isConditionalBranch()) + return false; + if (!isFirstMacroFusibleInst(Cmp)) + return false; + const X86::FirstMacroFusionInstKind CmpKind = + X86::classifyFirstOpcodeInMacroFusion(Cmp.getOpcode()); + const X86::SecondMacroFusionInstKind BranchKind = + classifySecondInstInMacroFusion(Jcc, MCII); + return X86::isMacroFused(CmpKind, BranchKind); +} + +/// Check if the instruction is RIP relative addressing. +bool X86AsmBackend::isRIPRelative(const MCInst &MI) const { + unsigned Opcode = MI.getOpcode(); + const MCInstrDesc &Desc = MCII.get(Opcode); + uint64_t TSFlags = Desc.TSFlags; + unsigned CurOp = X86II::getOperandBias(Desc); + int MemoryOperand = X86II::getMemoryOperandNo(TSFlags); + if (MemoryOperand >= 0) { + unsigned BaseRegNum = MemoryOperand + CurOp + X86::AddrBaseReg; + unsigned BaseReg = MI.getOperand(BaseRegNum).getReg(); + if (BaseReg == X86::RIP) + return true; + } + return false; +} + +/// Check if the instruction has variant symbol operand. +bool X86AsmBackend::hasVariantSymbol(const MCInst &MI) const { + + for (auto &Operand : MI) { + if (Operand.isExpr()) { + const MCExpr &Expr = *Operand.getExpr(); + if (Expr.getKind() == MCExpr::SymbolRef && + cast(*Operand.getExpr()).getKind() != + MCSymbolRefExpr::VK_None) + return true; + } + } + return false; +} + +bool X86AsmBackend::needAlign(MCObjectStreamer &OS) const { + if (AlignBoundary == Align::None() || + AlignBranchType == X86AlignBranchKind::AlignBranchNone) + return false; + + MCAssembler &Assembler = OS.getAssembler(); + MCSection *Sec = OS.getCurrentSectionOnly(); + // To be Done: Currently don't deal with Bundle cases. + if (Assembler.isBundlingEnabled() && Sec->isBundleLocked()) + return false; + + // Branches only need to be aligned in 32-bit or 64-bit mode. + if (!(STI.getFeatureBits()[X86::Mode64Bit] || + STI.getFeatureBits()[X86::Mode32Bit])) + return false; + + return true; +} + +/// Check if the instruction operand needs to be aligned. Padding is disabled +/// before intruction which may be rewritten by linker(e.g. TLSCALL). +bool X86AsmBackend::needAlignInst(const MCInst &Inst) const { + // Linker may rewrite the instruction with variant symbol operand. + if (hasVariantSymbol(Inst)) + return false; + + const MCInstrDesc &InstDesc = MCII.get(Inst.getOpcode()); + return (InstDesc.isConditionalBranch() && + (AlignBranchType & X86AlignBranchKind::AlignBranchJcc)) || + (InstDesc.isUnconditionalBranch() && + (AlignBranchType & X86AlignBranchKind::AlignBranchJmp)) || + (InstDesc.isCall() && + (AlignBranchType & X86AlignBranchKind::AlignBranchCall)) || + (InstDesc.isReturn() && + (AlignBranchType & X86AlignBranchKind::AlignBranchRet)) || + (InstDesc.isIndirectBranch() && + (AlignBranchType & X86AlignBranchKind::AlignBranchIndirect)); +} + +static bool canReuseBoundaryAlignFragment(const MCBoundaryAlignFragment &F) { + // If a MCBoundaryAlignFragment has not been used to emit NOP,we can reuse it. + return !F.canEmitNops(); +} + +MCBoundaryAlignFragment * +X86AsmBackend::getOrCreateBoundaryAlignFragment(MCObjectStreamer &OS) const { + auto *F = dyn_cast_or_null(OS.getCurrentFragment()); + if (!F || !canReuseBoundaryAlignFragment(*F)) { + F = new MCBoundaryAlignFragment(AlignBoundary); + OS.insert(F); + } + return F; +} + +/// Insert MCBoundaryAlignFragment before instructions to align branches. +void X86AsmBackend::alignBranchesBegin(MCObjectStreamer &OS, + const MCInst &Inst) { + if (!needAlign(OS)) + return; + + MCFragment *CF = OS.getCurrentFragment(); + bool NeedAlignFused = AlignBranchType & X86AlignBranchKind::AlignBranchFused; + if (NeedAlignFused && isMacroFused(PrevInst, Inst) && CF) { + // Macro fusion actually happens and there is no other fragment inserted + // after the previous instruction. NOP can be emitted in PF to align fused + // jcc. + if (auto *PF = + dyn_cast_or_null(CF->getPrevNode())) { + const_cast(PF)->setEmitNops(true); + const_cast(PF)->setFused(true); + } + } else if (needAlignInst(Inst)) { + // Note: When there is at least one fragment, such as MCAlignFragment, + // inserted after the previous instruction, e.g. + // + // \code + // cmp %rax %rcx + // .align 16 + // je .Label0 + // \ endcode + // + // We will treat the JCC as a unfused branch although it may be fused + // with the CMP. + auto *F = getOrCreateBoundaryAlignFragment(OS); + F->setEmitNops(true); + F->setFused(false); + } else if (NeedAlignFused && isFirstMacroFusibleInst(Inst)) { + // We don't know if macro fusion happens until the reaching the next + // instruction, so a place holder is put here if necessary. + getOrCreateBoundaryAlignFragment(OS); + } + + PrevInst = Inst; +} + +/// Insert a MCBoundaryAlignFragment to mark the end of the branch to be aligned +/// if necessary. +void X86AsmBackend::alignBranchesEnd(MCObjectStreamer &OS, const MCInst &Inst) { + if (!needAlign(OS)) + return; + // If the branch is emitted into a MCRelaxableFragment, we can determine the + // size of the branch easily in MCAssembler::relaxBoundaryAlign. When the + // branch is fused, the fused branch(macro fusion pair) must be emitted into + // two fragments. Or when the branch is unfused, the branch must be emitted + // into one fragment. The MCRelaxableFragment naturally marks the end of the + // fused or unfused branch. + // Otherwise, we need to insert a MCBoundaryAlignFragment to mark the end of + // the branch. This MCBoundaryAlignFragment may be reused to emit NOP to align + // other branch. + if (needAlignInst(Inst) && !isa(OS.getCurrentFragment())) + OS.insert(new MCBoundaryAlignFragment(AlignBoundary)); + + // Update the maximum alignment on the current section if necessary. + MCSection *Sec = OS.getCurrentSectionOnly(); + if (AlignBoundary.value() > Sec->getAlignment()) + Sec->setAlignment(AlignBoundary); +} + Optional X86AsmBackend::getFixupKind(StringRef Name) const { if (STI.getTargetTriple().isOSBinFormatELF()) { if (STI.getTargetTriple().getArch() == Triple::x86_64) { if (Name == "R_X86_64_NONE") return FK_NONE; } else { if (Name == "R_386_NONE") return FK_NONE; } } return MCAsmBackend::getFixupKind(Name); } bool X86AsmBackend::shouldForceRelocation(const MCAssembler &, const MCFixup &Fixup, const MCValue &) { return Fixup.getKind() == FK_NONE; } bool X86AsmBackend::mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const { // Branches can always be relaxed in either mode. if (getRelaxedOpcodeBranch(Inst, false) != Inst.getOpcode()) return true; // Check if this instruction is ever relaxable. if (getRelaxedOpcodeArith(Inst) == Inst.getOpcode()) return false; // Check if the relaxable operand has an expression. For the current set of // relaxable instructions, the relaxable operand is always the last operand. unsigned RelaxableOp = Inst.getNumOperands() - 1; if (Inst.getOperand(RelaxableOp).isExpr()) return true; return false; } bool X86AsmBackend::fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const { // Relax if the value is too big for a (signed) i8. return !isInt<8>(Value); } // FIXME: Can tblgen help at all here to verify there aren't other instructions // we can relax? void X86AsmBackend::relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const { // The only relaxations X86 does is from a 1byte pcrel to a 4byte pcrel. bool is16BitMode = STI.getFeatureBits()[X86::Mode16Bit]; unsigned RelaxedOp = getRelaxedOpcode(Inst, is16BitMode); if (RelaxedOp == Inst.getOpcode()) { SmallString<256> Tmp; raw_svector_ostream OS(Tmp); Inst.dump_pretty(OS); OS << "\n"; report_fatal_error("unexpected instruction to relax: " + OS.str()); } Res = Inst; Res.setOpcode(RelaxedOp); } /// Write a sequence of optimal nops to the output, covering \p Count /// bytes. /// \return - true on success, false on failure bool X86AsmBackend::writeNopData(raw_ostream &OS, uint64_t Count) const { static const char Nops[10][11] = { // nop "\x90", // xchg %ax,%ax "\x66\x90", // nopl (%[re]ax) "\x0f\x1f\x00", // nopl 0(%[re]ax) "\x0f\x1f\x40\x00", // nopl 0(%[re]ax,%[re]ax,1) "\x0f\x1f\x44\x00\x00", // nopw 0(%[re]ax,%[re]ax,1) "\x66\x0f\x1f\x44\x00\x00", // nopl 0L(%[re]ax) "\x0f\x1f\x80\x00\x00\x00\x00", // nopl 0L(%[re]ax,%[re]ax,1) "\x0f\x1f\x84\x00\x00\x00\x00\x00", // nopw 0L(%[re]ax,%[re]ax,1) "\x66\x0f\x1f\x84\x00\x00\x00\x00\x00", // nopw %cs:0L(%[re]ax,%[re]ax,1) "\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00", }; // This CPU doesn't support long nops. If needed add more. // FIXME: We could generated something better than plain 0x90. if (!STI.getFeatureBits()[X86::FeatureNOPL]) { for (uint64_t i = 0; i < Count; ++i) OS << '\x90'; return true; } // 15-bytes is the longest single NOP instruction, but 10-bytes is // commonly the longest that can be efficiently decoded. uint64_t MaxNopLength = 10; if (STI.getFeatureBits()[X86::ProcIntelSLM]) MaxNopLength = 7; else if (STI.getFeatureBits()[X86::FeatureFast15ByteNOP]) MaxNopLength = 15; else if (STI.getFeatureBits()[X86::FeatureFast11ByteNOP]) MaxNopLength = 11; // Emit as many MaxNopLength NOPs as needed, then emit a NOP of the remaining // length. do { const uint8_t ThisNopLength = (uint8_t) std::min(Count, MaxNopLength); const uint8_t Prefixes = ThisNopLength <= 10 ? 0 : ThisNopLength - 10; for (uint8_t i = 0; i < Prefixes; i++) OS << '\x66'; const uint8_t Rest = ThisNopLength - Prefixes; if (Rest != 0) OS.write(Nops[Rest - 1], Rest); Count -= ThisNopLength; } while (Count != 0); return true; } /* *** */ namespace { class ELFX86AsmBackend : public X86AsmBackend { public: uint8_t OSABI; ELFX86AsmBackend(const Target &T, uint8_t OSABI, const MCSubtargetInfo &STI) : X86AsmBackend(T, STI), OSABI(OSABI) {} }; class ELFX86_32AsmBackend : public ELFX86AsmBackend { public: ELFX86_32AsmBackend(const Target &T, uint8_t OSABI, const MCSubtargetInfo &STI) : ELFX86AsmBackend(T, OSABI, STI) {} std::unique_ptr createObjectTargetWriter() const override { return createX86ELFObjectWriter(/*IsELF64*/ false, OSABI, ELF::EM_386); } }; class ELFX86_X32AsmBackend : public ELFX86AsmBackend { public: ELFX86_X32AsmBackend(const Target &T, uint8_t OSABI, const MCSubtargetInfo &STI) : ELFX86AsmBackend(T, OSABI, STI) {} std::unique_ptr createObjectTargetWriter() const override { return createX86ELFObjectWriter(/*IsELF64*/ false, OSABI, ELF::EM_X86_64); } }; class ELFX86_IAMCUAsmBackend : public ELFX86AsmBackend { public: ELFX86_IAMCUAsmBackend(const Target &T, uint8_t OSABI, const MCSubtargetInfo &STI) : ELFX86AsmBackend(T, OSABI, STI) {} std::unique_ptr createObjectTargetWriter() const override { return createX86ELFObjectWriter(/*IsELF64*/ false, OSABI, ELF::EM_IAMCU); } }; class ELFX86_64AsmBackend : public ELFX86AsmBackend { public: ELFX86_64AsmBackend(const Target &T, uint8_t OSABI, const MCSubtargetInfo &STI) : ELFX86AsmBackend(T, OSABI, STI) {} std::unique_ptr createObjectTargetWriter() const override { return createX86ELFObjectWriter(/*IsELF64*/ true, OSABI, ELF::EM_X86_64); } }; class WindowsX86AsmBackend : public X86AsmBackend { bool Is64Bit; public: WindowsX86AsmBackend(const Target &T, bool is64Bit, const MCSubtargetInfo &STI) : X86AsmBackend(T, STI) , Is64Bit(is64Bit) { } Optional getFixupKind(StringRef Name) const override { return StringSwitch>(Name) .Case("dir32", FK_Data_4) .Case("secrel32", FK_SecRel_4) .Case("secidx", FK_SecRel_2) .Default(MCAsmBackend::getFixupKind(Name)); } std::unique_ptr createObjectTargetWriter() const override { return createX86WinCOFFObjectWriter(Is64Bit); } }; namespace CU { /// Compact unwind encoding values. enum CompactUnwindEncodings { /// [RE]BP based frame where [RE]BP is pused on the stack immediately after /// the return address, then [RE]SP is moved to [RE]BP. UNWIND_MODE_BP_FRAME = 0x01000000, /// A frameless function with a small constant stack size. UNWIND_MODE_STACK_IMMD = 0x02000000, /// A frameless function with a large constant stack size. UNWIND_MODE_STACK_IND = 0x03000000, /// No compact unwind encoding is available. UNWIND_MODE_DWARF = 0x04000000, /// Mask for encoding the frame registers. UNWIND_BP_FRAME_REGISTERS = 0x00007FFF, /// Mask for encoding the frameless registers. UNWIND_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF }; } // end CU namespace class DarwinX86AsmBackend : public X86AsmBackend { const MCRegisterInfo &MRI; /// Number of registers that can be saved in a compact unwind encoding. enum { CU_NUM_SAVED_REGS = 6 }; mutable unsigned SavedRegs[CU_NUM_SAVED_REGS]; bool Is64Bit; unsigned OffsetSize; ///< Offset of a "push" instruction. unsigned MoveInstrSize; ///< Size of a "move" instruction. unsigned StackDivide; ///< Amount to adjust stack size by. protected: /// Size of a "push" instruction for the given register. unsigned PushInstrSize(unsigned Reg) const { switch (Reg) { case X86::EBX: case X86::ECX: case X86::EDX: case X86::EDI: case X86::ESI: case X86::EBP: case X86::RBX: case X86::RBP: return 1; case X86::R12: case X86::R13: case X86::R14: case X86::R15: return 2; } return 1; } /// Implementation of algorithm to generate the compact unwind encoding /// for the CFI instructions. uint32_t generateCompactUnwindEncodingImpl(ArrayRef Instrs) const { if (Instrs.empty()) return 0; // Reset the saved registers. unsigned SavedRegIdx = 0; memset(SavedRegs, 0, sizeof(SavedRegs)); bool HasFP = false; // Encode that we are using EBP/RBP as the frame pointer. uint32_t CompactUnwindEncoding = 0; unsigned SubtractInstrIdx = Is64Bit ? 3 : 2; unsigned InstrOffset = 0; unsigned StackAdjust = 0; unsigned StackSize = 0; unsigned NumDefCFAOffsets = 0; for (unsigned i = 0, e = Instrs.size(); i != e; ++i) { const MCCFIInstruction &Inst = Instrs[i]; switch (Inst.getOperation()) { default: // Any other CFI directives indicate a frame that we aren't prepared // to represent via compact unwind, so just bail out. return 0; case MCCFIInstruction::OpDefCfaRegister: { // Defines a frame pointer. E.g. // // movq %rsp, %rbp // L0: // .cfi_def_cfa_register %rbp // HasFP = true; // If the frame pointer is other than esp/rsp, we do not have a way to // generate a compact unwinding representation, so bail out. if (*MRI.getLLVMRegNum(Inst.getRegister(), true) != (Is64Bit ? X86::RBP : X86::EBP)) return 0; // Reset the counts. memset(SavedRegs, 0, sizeof(SavedRegs)); StackAdjust = 0; SavedRegIdx = 0; InstrOffset += MoveInstrSize; break; } case MCCFIInstruction::OpDefCfaOffset: { // Defines a new offset for the CFA. E.g. // // With frame: // // pushq %rbp // L0: // .cfi_def_cfa_offset 16 // // Without frame: // // subq $72, %rsp // L0: // .cfi_def_cfa_offset 80 // StackSize = std::abs(Inst.getOffset()) / StackDivide; ++NumDefCFAOffsets; break; } case MCCFIInstruction::OpOffset: { // Defines a "push" of a callee-saved register. E.g. // // pushq %r15 // pushq %r14 // pushq %rbx // L0: // subq $120, %rsp // L1: // .cfi_offset %rbx, -40 // .cfi_offset %r14, -32 // .cfi_offset %r15, -24 // if (SavedRegIdx == CU_NUM_SAVED_REGS) // If there are too many saved registers, we cannot use a compact // unwind encoding. return CU::UNWIND_MODE_DWARF; unsigned Reg = *MRI.getLLVMRegNum(Inst.getRegister(), true); SavedRegs[SavedRegIdx++] = Reg; StackAdjust += OffsetSize; InstrOffset += PushInstrSize(Reg); break; } } } StackAdjust /= StackDivide; if (HasFP) { if ((StackAdjust & 0xFF) != StackAdjust) // Offset was too big for a compact unwind encoding. return CU::UNWIND_MODE_DWARF; // Get the encoding of the saved registers when we have a frame pointer. uint32_t RegEnc = encodeCompactUnwindRegistersWithFrame(); if (RegEnc == ~0U) return CU::UNWIND_MODE_DWARF; CompactUnwindEncoding |= CU::UNWIND_MODE_BP_FRAME; CompactUnwindEncoding |= (StackAdjust & 0xFF) << 16; CompactUnwindEncoding |= RegEnc & CU::UNWIND_BP_FRAME_REGISTERS; } else { SubtractInstrIdx += InstrOffset; ++StackAdjust; if ((StackSize & 0xFF) == StackSize) { // Frameless stack with a small stack size. CompactUnwindEncoding |= CU::UNWIND_MODE_STACK_IMMD; // Encode the stack size. CompactUnwindEncoding |= (StackSize & 0xFF) << 16; } else { if ((StackAdjust & 0x7) != StackAdjust) // The extra stack adjustments are too big for us to handle. return CU::UNWIND_MODE_DWARF; // Frameless stack with an offset too large for us to encode compactly. CompactUnwindEncoding |= CU::UNWIND_MODE_STACK_IND; // Encode the offset to the nnnnnn value in the 'subl $nnnnnn, ESP' // instruction. CompactUnwindEncoding |= (SubtractInstrIdx & 0xFF) << 16; // Encode any extra stack adjustments (done via push instructions). CompactUnwindEncoding |= (StackAdjust & 0x7) << 13; } // Encode the number of registers saved. (Reverse the list first.) std::reverse(&SavedRegs[0], &SavedRegs[SavedRegIdx]); CompactUnwindEncoding |= (SavedRegIdx & 0x7) << 10; // Get the encoding of the saved registers when we don't have a frame // pointer. uint32_t RegEnc = encodeCompactUnwindRegistersWithoutFrame(SavedRegIdx); if (RegEnc == ~0U) return CU::UNWIND_MODE_DWARF; // Encode the register encoding. CompactUnwindEncoding |= RegEnc & CU::UNWIND_FRAMELESS_STACK_REG_PERMUTATION; } return CompactUnwindEncoding; } private: /// Get the compact unwind number for a given register. The number /// corresponds to the enum lists in compact_unwind_encoding.h. int getCompactUnwindRegNum(unsigned Reg) const { static const MCPhysReg CU32BitRegs[7] = { X86::EBX, X86::ECX, X86::EDX, X86::EDI, X86::ESI, X86::EBP, 0 }; static const MCPhysReg CU64BitRegs[] = { X86::RBX, X86::R12, X86::R13, X86::R14, X86::R15, X86::RBP, 0 }; const MCPhysReg *CURegs = Is64Bit ? CU64BitRegs : CU32BitRegs; for (int Idx = 1; *CURegs; ++CURegs, ++Idx) if (*CURegs == Reg) return Idx; return -1; } /// Return the registers encoded for a compact encoding with a frame /// pointer. uint32_t encodeCompactUnwindRegistersWithFrame() const { // Encode the registers in the order they were saved --- 3-bits per // register. The list of saved registers is assumed to be in reverse // order. The registers are numbered from 1 to CU_NUM_SAVED_REGS. uint32_t RegEnc = 0; for (int i = 0, Idx = 0; i != CU_NUM_SAVED_REGS; ++i) { unsigned Reg = SavedRegs[i]; if (Reg == 0) break; int CURegNum = getCompactUnwindRegNum(Reg); if (CURegNum == -1) return ~0U; // Encode the 3-bit register number in order, skipping over 3-bits for // each register. RegEnc |= (CURegNum & 0x7) << (Idx++ * 3); } assert((RegEnc & 0x3FFFF) == RegEnc && "Invalid compact register encoding!"); return RegEnc; } /// Create the permutation encoding used with frameless stacks. It is /// passed the number of registers to be saved and an array of the registers /// saved. uint32_t encodeCompactUnwindRegistersWithoutFrame(unsigned RegCount) const { // The saved registers are numbered from 1 to 6. In order to encode the // order in which they were saved, we re-number them according to their // place in the register order. The re-numbering is relative to the last // re-numbered register. E.g., if we have registers {6, 2, 4, 5} saved in // that order: // // Orig Re-Num // ---- ------ // 6 6 // 2 2 // 4 3 // 5 3 // for (unsigned i = 0; i < RegCount; ++i) { int CUReg = getCompactUnwindRegNum(SavedRegs[i]); if (CUReg == -1) return ~0U; SavedRegs[i] = CUReg; } // Reverse the list. std::reverse(&SavedRegs[0], &SavedRegs[CU_NUM_SAVED_REGS]); uint32_t RenumRegs[CU_NUM_SAVED_REGS]; for (unsigned i = CU_NUM_SAVED_REGS - RegCount; i < CU_NUM_SAVED_REGS; ++i){ unsigned Countless = 0; for (unsigned j = CU_NUM_SAVED_REGS - RegCount; j < i; ++j) if (SavedRegs[j] < SavedRegs[i]) ++Countless; RenumRegs[i] = SavedRegs[i] - Countless - 1; } // Take the renumbered values and encode them into a 10-bit number. uint32_t permutationEncoding = 0; switch (RegCount) { case 6: permutationEncoding |= 120 * RenumRegs[0] + 24 * RenumRegs[1] + 6 * RenumRegs[2] + 2 * RenumRegs[3] + RenumRegs[4]; break; case 5: permutationEncoding |= 120 * RenumRegs[1] + 24 * RenumRegs[2] + 6 * RenumRegs[3] + 2 * RenumRegs[4] + RenumRegs[5]; break; case 4: permutationEncoding |= 60 * RenumRegs[2] + 12 * RenumRegs[3] + 3 * RenumRegs[4] + RenumRegs[5]; break; case 3: permutationEncoding |= 20 * RenumRegs[3] + 4 * RenumRegs[4] + RenumRegs[5]; break; case 2: permutationEncoding |= 5 * RenumRegs[4] + RenumRegs[5]; break; case 1: permutationEncoding |= RenumRegs[5]; break; } assert((permutationEncoding & 0x3FF) == permutationEncoding && "Invalid compact register encoding!"); return permutationEncoding; } public: DarwinX86AsmBackend(const Target &T, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI, bool Is64Bit) : X86AsmBackend(T, STI), MRI(MRI), Is64Bit(Is64Bit) { memset(SavedRegs, 0, sizeof(SavedRegs)); OffsetSize = Is64Bit ? 8 : 4; MoveInstrSize = Is64Bit ? 3 : 2; StackDivide = Is64Bit ? 8 : 4; } }; class DarwinX86_32AsmBackend : public DarwinX86AsmBackend { public: DarwinX86_32AsmBackend(const Target &T, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) : DarwinX86AsmBackend(T, MRI, STI, false) {} std::unique_ptr createObjectTargetWriter() const override { return createX86MachObjectWriter(/*Is64Bit=*/false, MachO::CPU_TYPE_I386, MachO::CPU_SUBTYPE_I386_ALL); } /// Generate the compact unwind encoding for the CFI instructions. uint32_t generateCompactUnwindEncoding( ArrayRef Instrs) const override { return generateCompactUnwindEncodingImpl(Instrs); } }; class DarwinX86_64AsmBackend : public DarwinX86AsmBackend { const MachO::CPUSubTypeX86 Subtype; public: DarwinX86_64AsmBackend(const Target &T, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI, MachO::CPUSubTypeX86 st) : DarwinX86AsmBackend(T, MRI, STI, true), Subtype(st) {} std::unique_ptr createObjectTargetWriter() const override { return createX86MachObjectWriter(/*Is64Bit=*/true, MachO::CPU_TYPE_X86_64, Subtype); } /// Generate the compact unwind encoding for the CFI instructions. uint32_t generateCompactUnwindEncoding( ArrayRef Instrs) const override { return generateCompactUnwindEncodingImpl(Instrs); } }; } // end anonymous namespace MCAsmBackend *llvm::createX86_32AsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { const Triple &TheTriple = STI.getTargetTriple(); if (TheTriple.isOSBinFormatMachO()) return new DarwinX86_32AsmBackend(T, MRI, STI); if (TheTriple.isOSWindows() && TheTriple.isOSBinFormatCOFF()) return new WindowsX86AsmBackend(T, false, STI); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); if (TheTriple.isOSIAMCU()) return new ELFX86_IAMCUAsmBackend(T, OSABI, STI); return new ELFX86_32AsmBackend(T, OSABI, STI); } MCAsmBackend *llvm::createX86_64AsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { const Triple &TheTriple = STI.getTargetTriple(); if (TheTriple.isOSBinFormatMachO()) { MachO::CPUSubTypeX86 CS = StringSwitch(TheTriple.getArchName()) .Case("x86_64h", MachO::CPU_SUBTYPE_X86_64_H) .Default(MachO::CPU_SUBTYPE_X86_64_ALL); return new DarwinX86_64AsmBackend(T, MRI, STI, CS); } if (TheTriple.isOSWindows() && TheTriple.isOSBinFormatCOFF()) return new WindowsX86AsmBackend(T, true, STI); uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS()); if (TheTriple.getEnvironment() == Triple::GNUX32) return new ELFX86_X32AsmBackend(T, OSABI, STI); return new ELFX86_64AsmBackend(T, OSABI, STI); } diff --git a/llvm/test/MC/X86/align-branch-32-1a.s b/llvm/test/MC/X86/align-branch-32-1a.s new file mode 100644 index 000000000000..646024e71e93 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-32-1a.s @@ -0,0 +1,38 @@ +# Check NOP padding is disabled before instruction that has variant symbol operand. +# RUN: llvm-mc -filetype=obj -triple i386-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=call %s | llvm-objdump -d - | FileCheck %s + +# CHECK: 00000000 foo: +# CHECK-COUNT-5: : 64 a3 01 00 00 00 movl %eax, %fs:1 +# CHECK: 1e: e8 fc ff ff ff calll {{.*}} +# CHECK-COUNT-4: : 64 a3 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3b: 55 pushl %ebp +# CHECK-NEXT: 3c: ff 91 00 00 00 00 calll *(%ecx) +# CHECK-COUNT-4: : 64 a3 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5a: c1 e9 02 shrl $2, %ecx +# CHECK-NEXT: 5d: 55 pushl %ebp +# CHECK-NEXT: 5e: ff 10 calll *(%eax) +# CHECK-COUNT-5: : 64 a3 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 7e: ff 20 jmpl *(%eax) + .text + .globl foo + .p2align 4 +foo: + .rept 5 + movl %eax, %fs:0x1 + .endr + call ___tls_get_addr@PLT + .rept 4 + movl %eax, %fs:0x1 + .endr + pushl %ebp + call *___tls_get_addr@GOT(%ecx) + .rept 4 + movl %eax, %fs:0x1 + .endr + shrl $2, %ecx + pushl %ebp + call *foo@tlscall(%eax) + .rept 5 + movl %eax, %fs:0x1 + .endr + jmp *foo@tlscall(%eax) diff --git a/llvm/test/MC/X86/align-branch-64-1a.s b/llvm/test/MC/X86/align-branch-64-1a.s new file mode 100644 index 000000000000..c2187f9e73aa --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-1a.s @@ -0,0 +1,83 @@ +# Check only fused conditional jumps, conditional jumps and unconditional jumps are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc+jmp +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc+jmp %s | llvm-objdump -d - > %t1 +# RUN: FileCheck --input-file=%t1 %s + +# Check no branches is aligned with option --x86-align-branch-boundary=0 +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=0 --x86-align-branch=fused+jcc+jmp %s | llvm-objdump -d - > %t2 +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s | llvm-objdump -d - > %t3 +# RUN: cmp %t2 %t3 + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 18: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 1b: 31 c0 xorl %eax, %eax +# CHECK-COUNT-3: : 90 nop +# CHECK: 20: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 23: 74 5d je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3d: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 3f: 90 nop +# CHECK-NEXT: 40: 74 40 je {{.*}} +# CHECK-NEXT: 42: 5d popq %rbp +# CHECK-NEXT: 43: 74 3d je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5d: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 5f: 90 nop +# CHECK-NEXT: 60: eb 26 jmp {{.*}} +# CHECK-NEXT: 62: eb 24 jmp {{.*}} +# CHECK-NEXT: 64: eb 22 jmp {{.*}} +# CHECK-COUNT-2: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 76: 89 45 fc movl %eax, -4(%rbp) +# CHECK-NEXT: 79: 5d popq %rbp +# CHECK-NEXT: 7a: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 7d: 74 03 je {{.*}} +# CHECK-NEXT: 7f: 90 nop +# CHECK-NEXT: 80: eb 06 jmp {{.*}} +# CHECK-NEXT: 82: 8b 45 f4 movl -12(%rbp), %eax +# CHECK-NEXT: 85: 89 45 fc movl %eax, -4(%rbp) +# CHECK-COUNT-10: : 89 b5 50 fb ff ff movl %esi, -1200(%rbp) +# CHECK: c4: eb c2 jmp {{.*}} +# CHECK-NEXT: c6: c3 retq + + .text + .globl foo + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + cmp %rax, %rbp + xorl %eax, %eax + cmp %rax, %rbp + je .L_2 + .rept 3 + movl %eax, %fs:0x1 + .endr + xorl %eax, %eax + je .L_2 + popq %rbp + je .L_2 + .rept 3 + movl %eax, %fs:0x1 + .endr + xorl %eax, %eax + jmp .L_3 + jmp .L_3 + jmp .L_3 + .rept 2 + movl %eax, %fs:0x1 + .endr + movl %eax, -4(%rbp) + popq %rbp + cmp %rax, %rbp + je .L_2 + jmp .L_3 +.L_2: + movl -12(%rbp), %eax + movl %eax, -4(%rbp) +.L_3: + .rept 10 + movl %esi, -1200(%rbp) + .endr + jmp .L_3 + retq diff --git a/llvm/test/MC/X86/align-branch-64-1b.s b/llvm/test/MC/X86/align-branch-64-1b.s new file mode 100644 index 000000000000..3647e4e85be3 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-1b.s @@ -0,0 +1,32 @@ +# Check only fused conditional jumps and conditional jumps are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc %S/align-branch-64-1a.s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 18: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 1b: 31 c0 xorl %eax, %eax +# CHECK-COUNT-3: : 90 nop +# CHECK-NEXT: 20: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 23: 74 5b je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3d: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 3f: 90 nop +# CHECK-NEXT: 40: 74 3e je {{.*}} +# CHECK-NEXT: 42: 5d popq %rbp +# CHECK-NEXT: 43: 74 3b je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5d: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 5f: eb 25 jmp {{.*}} +# CHECK-NEXT: 61: eb 23 jmp {{.*}} +# CHECK-NEXT: 63: eb 21 jmp {{.*}} +# CHECK-COUNT-2: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 75: 89 45 fc movl %eax, -4(%rbp) +# CHECK: 78: 5d popq %rbp +# CHECK-NEXT: 79: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 7c: 74 02 je {{.*}} +# CHECK-NEXT: 7e: eb 06 jmp {{.*}} +# CHECK-NEXT: 80: 8b 45 f4 movl -12(%rbp), %eax +# CHECK-NEXT: 83: 89 45 fc movl %eax, -4(%rbp) +# CHECK-COUNT-10: : 89 b5 50 fb ff ff movl %esi, -1200(%rbp) +# CHECK: c2: eb c2 jmp {{.*}} +# CHECK-NEXT: c4: c3 retq diff --git a/llvm/test/MC/X86/align-branch-64-1c.s b/llvm/test/MC/X86/align-branch-64-1c.s new file mode 100644 index 000000000000..11e0f2be0064 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-1c.s @@ -0,0 +1,31 @@ +# Check only conditional jumps are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=jcc +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=jcc %S/align-branch-64-1a.s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 18: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 1b: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 1d: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 20: 74 5b je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3a: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 3c: 74 3f je {{.*}} +# CHECK-NEXT: 3e: 5d popq %rbp +# CHECK-NEXT: 3f: 90 nop +# CHECK-NEXT: 40: 74 3b je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5a: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 5c: eb 25 jmp {{.*}} +# CHECK-NEXT: 5e: eb 23 jmp {{.*}} +# CHECK-NEXT: 60: eb 21 jmp {{.*}} +# CHECK-COUNT-2: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 72: 89 45 fc movl %eax, -4(%rbp) +# CHECK-NEXT: 75: 5d popq %rbp +# CHECK-NEXT: 76: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 79: 74 02 je {{.*}} +# CHECK-NEXT: 7b: eb 06 jmp {{.*}} +# CHECK-NEXT: 7d: 8b 45 f4 movl -12(%rbp), %eax +# CHECK-NEXT: 80: 89 45 fc movl %eax, -4(%rbp) +# CHECK-COUNT-10: : 89 b5 50 fb ff ff movl %esi, -1200(%rbp) +# CHECK: bf: eb c2 jmp {{.*}} +# CHECK-NEXT: c1: c3 retq diff --git a/llvm/test/MC/X86/align-branch-64-1d.s b/llvm/test/MC/X86/align-branch-64-1d.s new file mode 100644 index 000000000000..be6b5cf94719 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-1d.s @@ -0,0 +1,38 @@ +# Check only conditional jumps and unconditional jumps are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=jcc+jmp +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=jcc+jmp %S/align-branch-64-1a.s | llvm-objdump -d - > %t1 +# RUN: FileCheck --input-file=%t1 %s --check-prefixes=CHECK,SHORT-NOP + +# Check long NOP can be emitted to align branch if the target cpu support long nop. +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 -mcpu=x86-64 --x86-align-branch=jcc+jmp %S/align-branch-64-1a.s | llvm-objdump -d - >%t2 +# RUN: FileCheck --input-file=%t2 %s --check-prefixes=CHECK,LONG-NOP + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 18: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 1b: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 1d: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 20: 74 5d je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3a: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 3c: 74 41 je {{.*}} +# CHECK-NEXT: 3e: 5d popq %rbp +# CHECK-NEXT: 3f: 90 nop +# CHECK-NEXT: 40: 74 3d je {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5a: 31 c0 xorl %eax, %eax +# CHECK-NEXT: 5c: eb 27 jmp {{.*}} +# SHORT-NOP-COUNT-2: : 90 nop +# LONG-NOP: 5e: 66 90 nop +# CHECK-NEXT: 60: eb 23 jmp {{.*}} +# CHECK-NEXT: 62: eb 21 jmp {{.*}} +# CHECK-COUNT-2: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 74: 89 45 fc movl %eax, -4(%rbp) +# CHECK-NEXT: 77: 5d popq %rbp +# CHECK-NEXT: 78: 48 39 c5 cmpq %rax, %rbp +# CHECK-NEXT: 7b: 74 02 je {{.*}} +# CHECK-NEXT: 7d: eb 06 jmp {{.*}} +# CHECK-NEXT: 7f: 8b 45 f4 movl -12(%rbp), %eax +# CHECK-NEXT: 82: 89 45 fc movl %eax, -4(%rbp) +# CHECK-COUNT-10: : 89 b5 50 fb ff ff movl %esi, -1200(%rbp) +# CHECK: c1: eb c2 jmp {{.*}} +# CHECK-NEXT: c3: c3 retq diff --git a/llvm/test/MC/X86/align-branch-64-2a.s b/llvm/test/MC/X86/align-branch-64-2a.s new file mode 100644 index 000000000000..fe38f71c0692 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-2a.s @@ -0,0 +1,44 @@ +# Check only indirect jumps are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=indirect +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=indirect %s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-2: : 89 75 f4 movl %esi, -12(%rbp) +# CHECK-COUNT-2: : 90 nop +# CHECK: 20: ff e0 jmpq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3a: 89 75 f4 movl %esi, -12(%rbp) +# CHECK-NEXT: 3d: 55 pushq %rbp +# CHECK-NEXT: 3e: ff d0 callq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 58: 55 pushq %rbp +# CHECK-NEXT: 59: e8 a2 ff ff ff callq {{.*}} +# CHECK-COUNT-4: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 7e: ff 14 25 00 00 00 00 callq *0 + + .text + .globl foo + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + .rept 2 + movl %esi, -12(%rbp) + .endr + jmp *%rax + .rept 3 + movl %eax, %fs:0x1 + .endr + movl %esi, -12(%rbp) + pushq %rbp + call *%rax + .rept 3 + movl %eax, %fs:0x1 + .endr + pushq %rbp + call foo + .rept 4 + movl %eax, %fs:0x1 + .endr + call *foo diff --git a/llvm/test/MC/X86/align-branch-64-2b.s b/llvm/test/MC/X86/align-branch-64-2b.s new file mode 100644 index 000000000000..c7ffa16922f2 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-2b.s @@ -0,0 +1,17 @@ +# Check only calls are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=call +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=call %S/align-branch-64-2a.s| llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-2: : 89 75 f4 movl %esi, -12(%rbp) +# CHECK: 1e: ff e0 jmpq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 38: 89 75 f4 movl %esi, -12(%rbp) +# CHECK-NEXT: 3b: 55 pushq %rbp +# CHECK-NEXT: 3c: ff d0 callq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 56: 55 pushq %rbp +# CHECK-NEXT: 57: e8 a4 ff ff ff callq {{.*}} +# CHECK-COUNT-4: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-4: : 90 nop +# CHECK: 80: ff 14 25 00 00 00 00 callq *0 diff --git a/llvm/test/MC/X86/align-branch-64-2c.s b/llvm/test/MC/X86/align-branch-64-2c.s new file mode 100644 index 000000000000..71b84e587d8a --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-2c.s @@ -0,0 +1,19 @@ +# Check only indirect jumps and calls are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=indirect+call +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=indirect+call %S/align-branch-64-2a.s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-2: : 89 75 f4 movl %esi, -12(%rbp) +# CHECK-COUNT-2: : 90 nop +# CHECK: 20: ff e0 jmpq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3a: 89 75 f4 movl %esi, -12(%rbp) +# CHECK-NEXT: 3d: 55 pushq %rbp +# CHECK-COUNT-2: : 90 nop +# CHECK: 40: ff d0 callq *%rax +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5a: 55 pushq %rbp +# CHECK-COUNT-5: : 90 nop +# CHECK: 60: e8 9b ff ff ff callq {{.*}} +# CHECK-COUNT-4: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 85: ff 14 25 00 00 00 00 callq *0 diff --git a/llvm/test/MC/X86/align-branch-64-3a.s b/llvm/test/MC/X86/align-branch-64-3a.s new file mode 100644 index 000000000000..47cdd10102f8 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-3a.s @@ -0,0 +1,41 @@ +# Check NOP padding is disabled before instruction that has variant symbol operand. +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=jmp+call %s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-2: : 48 89 e5 movq %rsp, %rbp +# CHECK: 1e: e8 00 00 00 00 callq {{.*}} +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 3b: 55 pushq %rbp +# CHECK-NEXT: 3c: 89 75 f4 movl %esi, -12(%rbp) +# CHECK-NEXT: 3f: ff 15 00 00 00 00 callq *(%rip) +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 5d: ff 15 00 00 00 00 callq *(%rip) +# CHECK-NEXT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 7b: ff 25 00 00 00 00 jmpq *(%rip) + + .text + .globl foo + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + .rept 2 + movq %rsp, %rbp + .endr + call __tls_get_addr@PLT + .rept 3 + movl %eax, %fs:0x1 + .endr + pushq %rbp + movl %esi, -12(%rbp) + call *__tls_get_addr@GOTPCREL(%rip) + .rept 3 + movl %eax, %fs:0x1 + .endr + call *foo@GOTPCREL(%rip) + .rept 3 + movl %eax, %fs:0x1 + .endr + jmp *foo@GOTPCREL(%rip) diff --git a/llvm/test/MC/X86/align-branch-64-4a.s b/llvm/test/MC/X86/align-branch-64-4a.s new file mode 100644 index 000000000000..a1db0e56b2b9 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-4a.s @@ -0,0 +1,33 @@ +# Check only rets are aligned with option --x86-align-branch-boundary=32 --x86-align-branch=ret +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=ret %s | llvm-objdump -d - | FileCheck %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-COUNT-2: : 48 89 e5 movq %rsp, %rbp +# CHECK: 1e: 5a popq %rdx +# CHECK-NEXT: 1f: 90 nop +# CHECK-NEXT: 20: c3 retq +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 39: 89 75 f4 movl %esi, -12(%rbp) +# CHECK-NEXT: 3c: 31 c0 xorl %eax, %eax +# CHECK-COUNT-2: : 90 nop +# CHECK: 40: c2 1e 00 retq $30 + + .text + .globl foo + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + .rept 2 + movq %rsp, %rbp + .endr + popq %rdx + ret + .rept 3 + movl %eax, %fs:0x1 + .endr + movl %esi, -12(%rbp) + xorl %eax, %eax + ret $30 diff --git a/llvm/test/MC/X86/align-branch-64-5a.s b/llvm/test/MC/X86/align-branch-64-5a.s new file mode 100644 index 000000000000..1d4dbd5300ca --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-5a.s @@ -0,0 +1,43 @@ +# Check no nop is inserted if no branch cross or is against the boundary +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc+jmp+indirect+call+ret %s | llvm-objdump -d - > %t1 +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s | llvm-objdump -d - > %t2 +# RUN: cmp %t1 %t2 +# RUN: FileCheck --input-file=%t1 %s + +# CHECK: 0000000000000000 foo: +# CHECK-COUNT-3: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 18: c1 e9 02 shrl $2, %ecx +# CHECK-NEXT: 1b: 89 d1 movl %edx, %ecx +# CHECK-NEXT: 1d: 75 fc jne {{.*}} +# CHECK-NEXT: 1f: 55 pushq %rbp +# CHECK-NEXT: 20: f6 c2 02 testb $2, %dl +# CHECK-NEXT: 23: 75 fa jne {{.*}} +# CHECK-COUNT-2: : 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK: 35: c1 e9 02 shrl $2, %ecx +# CHECK-NEXT: 38: e8 c3 ff ff ff callq {{.*}} +# CHECK-NEXT: 3d: ff e0 jmpq *%rax +# CHECK-NEXT: 3f: 55 pushq %rbp +# CHECK-NEXT: 40: c2 63 00 retq $99 + + .text + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + shrl $2, %ecx +.L1: + movl %edx, %ecx + jne .L1 +.L2: + push %rbp + testb $2, %dl + jne .L2 + .rept 2 + movl %eax, %fs:0x1 + .endr + shrl $2, %ecx + call foo + jmp *%rax + push %rbp + ret $99 diff --git a/llvm/test/MC/X86/align-branch-64-5b.s b/llvm/test/MC/X86/align-branch-64-5b.s new file mode 100644 index 000000000000..4a046f8e0640 --- /dev/null +++ b/llvm/test/MC/X86/align-branch-64-5b.s @@ -0,0 +1,50 @@ +# Check option --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc+jmp+indirect+call+ret can cowork with option --mc-relax-all +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --x86-align-branch-boundary=32 --x86-align-branch=fused+jcc+jmp+indirect+call+ret --mc-relax-all %s | llvm-objdump -d - > %t1 +# RUN: FileCheck --input-file=%t1 %s + +# CHECK: 0000000000000000 foo: +# CHECK-NEXT: 0: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 8: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 10: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 18: c1 e9 02 shrl $2, %ecx +# CHECK-NEXT: 1b: 89 d1 movl %edx, %ecx +# CHECK-NEXT: 1d: 90 nop +# CHECK-NEXT: 1e: 90 nop +# CHECK-NEXT: 1f: 90 nop +# CHECK-NEXT: 20: 0f 85 f5 ff ff ff jne {{.*}} +# CHECK-NEXT: 26: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 2e: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 36: f6 c2 02 testb $2, %dl +# CHECK-NEXT: 39: 0f 85 e7 ff ff ff jne {{.*}} +# CHECK-NEXT: 3f: 90 nop +# CHECK-NEXT: 40: e9 d6 ff ff ff jmp {{.*}} +# CHECK-NEXT: 45: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 4d: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 55: 64 89 04 25 01 00 00 00 movl %eax, %fs:1 +# CHECK-NEXT: 5d: 90 nop +# CHECK-NEXT: 5e: 90 nop +# CHECK-NEXT: 5f: 90 nop +# CHECK-NEXT: 60: e8 9b ff ff ff callq {{.*}} +# CHECK-NEXT: 65: e9 bc ff ff ff jmp {{.*}} + .text + .p2align 4 +foo: + .rept 3 + movl %eax, %fs:0x1 + .endr + shrl $2, %ecx +.L1: + movl %edx, %ecx + jne .L1 +.L2: + .rept 2 + movl %eax, %fs:0x1 + .endr + testb $2, %dl + jne .L2 + jmp .L1 + .rept 3 + movl %eax, %fs:0x1 + .endr + call foo + jmp .L2