Index: llvm/include/llvm/MC/MCAsmBackend.h =================================================================== --- llvm/include/llvm/MC/MCAsmBackend.h +++ llvm/include/llvm/MC/MCAsmBackend.h @@ -179,6 +179,13 @@ virtual bool isMicroMips(const MCSymbol *Sym) const { return false; } + + /// Given the target mask from a .push_align_branch_boundary directive, + /// return the string suitable an assembly file. + virtual std::string getAlignBranchBoundaryMaskStr(unsigned Mask) const { + llvm_unreachable("unsupported target for .push_align_branch_boundary?"); + return ""; + } }; } // end namespace llvm Index: llvm/include/llvm/MC/MCStreamer.h =================================================================== --- llvm/include/llvm/MC/MCStreamer.h +++ llvm/include/llvm/MC/MCStreamer.h @@ -222,6 +222,10 @@ bool UseAssemblerInfoForParsing; + /// This is stack of align_branch_boundary directives. Data is pair of + /// alignment, and target specific instruction kind mask. + SmallVector, 4> AlignBranchBoundaryStack; + protected: MCStreamer(MCContext &Ctx); @@ -1008,6 +1012,28 @@ /// Ends a bundle-locked group. virtual void EmitBundleUnlock(); + /// Enter a new .align_branch_boundary region which allows alignment for + /// performance of instruction described by the target specific Mask to be + /// aligned so as no to cross or end at an alignment boundary of 2^Align. + virtual void EmitPushAlignBranchBoundary(unsigned Align, unsigned Mask) { + AlignBranchBoundaryStack.push_back(std::make_pair(Align, Mask)); + } + + /// Ends a .align_branch_boundary region. + virtual void EmitPopAlignBranchBoundary() { + assert(!AlignBranchBoundaryStack.empty()); + AlignBranchBoundaryStack.pop_back(); + } + + /// Return information on the current .align_branch_boundary region if any, + /// or None. + Optional> + getCurrentAlignBranchBoundary() const { + if (AlignBranchBoundaryStack.empty()) + return None; + return AlignBranchBoundaryStack.back(); + } + /// If this file is backed by a assembly streamer, this dumps the /// specified string in the output .s file. This capability is indicated by /// the hasRawTextSupport() predicate. By default this aborts. Index: llvm/lib/MC/MCAsmStreamer.cpp =================================================================== --- llvm/lib/MC/MCAsmStreamer.cpp +++ llvm/lib/MC/MCAsmStreamer.cpp @@ -337,6 +337,9 @@ void EmitBundleLock(bool AlignToEnd) override; void EmitBundleUnlock() override; + void EmitPushAlignBranchBoundary(unsigned Align, unsigned Mask) override; + void EmitPopAlignBranchBoundary() override; + bool EmitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc Loc, const MCSubtargetInfo &STI) override; @@ -1972,6 +1975,22 @@ EmitEOL(); } +void MCAsmStreamer::EmitPushAlignBranchBoundary(unsigned Align, + unsigned Mask) { + MCStreamer::EmitPushAlignBranchBoundary(Align, Mask); + OS << "\t.push_align_branch_boundary "; + OS << Align << " "; + OS << getAssembler().getBackend().getAlignBranchBoundaryMaskStr(Mask); + EmitEOL(); +} + +void MCAsmStreamer::EmitPopAlignBranchBoundary() { + MCStreamer::EmitPopAlignBranchBoundary(); + OS << "\t.pop_align_branch_boundary"; + EmitEOL(); +} + + bool MCAsmStreamer::EmitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc, const MCSubtargetInfo &STI) { Index: llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp =================================================================== --- llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp +++ llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp @@ -878,6 +878,9 @@ bool parseDirectiveSEHSaveXMM(SMLoc); bool parseDirectiveSEHPushFrame(SMLoc); + /// .push_align_branch_boundary + bool parseDirectivePushAlignBranchBoundary(SMLoc); + unsigned checkTargetMatchPredicate(MCInst &Inst) override; bool validateInstruction(MCInst &Inst, const OperandVector &Ops); @@ -3610,6 +3613,19 @@ return parseDirectiveSEHSaveXMM(DirectiveID.getLoc()); else if (IDVal == ".seh_pushframe") return parseDirectiveSEHPushFrame(DirectiveID.getLoc()); + else if (IDVal == ".push_align_branch_boundary") + return parseDirectivePushAlignBranchBoundary(DirectiveID.getLoc()); + else if (IDVal == ".pop_align_branch_boundary") { + if (None == getStreamer().getCurrentAlignBranchBoundary()) + report_fatal_error("Mismatched .pop_align_branch_boundary"); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token in directive"); + getParser().Lex(); + + getStreamer().EmitPopAlignBranchBoundary(); + return false; + } return true; } @@ -3880,6 +3896,37 @@ return false; } +bool X86AsmParser::parseDirectivePushAlignBranchBoundary(SMLoc Loc) { + int64_t BoundaryLog2; + if (getParser().parseAbsoluteExpression(BoundaryLog2)) + return TokError("expected power-of-2 aligment expression"); + + unsigned Mask = 0; + if (getLexer().isNot(AsmToken::EndOfStatement)) { + do { + StringRef Name; + if (getParser().parseIdentifier(Name)) + return TokError("Missing instruction type"); + unsigned MaskBit = StringSwitch(Name) + .Case("fused", X86::AlignBranchFused) + .Case("jcc", X86::AlignBranchJcc) + .Case("jmp", X86::AlignBranchJmp) + .Case("call", X86::AlignBranchCall) + .Case("ret", X86::AlignBranchRet) + .Case("indirect", X86::AlignBranchIndirect) + .Default(-1); + if (MaskBit == -1U) + return TokError("unrecognized instruction type in directive"); + Mask |= MaskBit; + } while (parseOptionalToken(AsmToken::Comma)); + } + if (getLexer().isNot(AsmToken::EndOfStatement)) + return TokError("unexpected token in directive"); + getParser().Lex(); + getStreamer().EmitPushAlignBranchBoundary(BoundaryLog2, Mask); + return false; +} + // Force static initialization. extern "C" void LLVMInitializeX86AsmParser() { RegisterMCAsmParser X(getTheX86_32Target()); Index: llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp +++ llvm/lib/Target/X86/MCTargetDesc/X86AsmBackend.cpp @@ -8,6 +8,7 @@ #include "MCTargetDesc/X86BaseInfo.h" #include "MCTargetDesc/X86FixupKinds.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" @@ -151,6 +152,8 @@ MCInst &Res) const override; bool writeNopData(raw_ostream &OS, uint64_t Count) const override; + + std::string getAlignBranchBoundaryMaskStr(unsigned Mask) const override; }; } // end anonymous namespace @@ -387,6 +390,24 @@ return true; } +std::string X86AsmBackend::getAlignBranchBoundaryMaskStr(unsigned Mask) const { + SmallVector Strings; + if (Mask & X86::AlignBranchFused) + Strings.push_back("fused"); + if (Mask & X86::AlignBranchJcc) + Strings.push_back("jcc"); + if (Mask & X86::AlignBranchJmp) + Strings.push_back("jmp"); + if (Mask & X86::AlignBranchCall) + Strings.push_back("call"); + if (Mask & X86::AlignBranchRet) + Strings.push_back("ret"); + if (Mask & X86::AlignBranchIndirect) + Strings.push_back("indirect"); + + return join(Strings, ", "); +} + /* *** */ namespace { Index: llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h +++ llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h @@ -343,6 +343,19 @@ } llvm_unreachable("unknown fusion type"); } + + /// Defines the value used for the target specific mask in a scope for + /// align_branch_boundary + enum AlignBranchBoundaryKinds : uint8_t { + AlignBranchNone = 0, + AlignBranchFused = 1U << 0, + AlignBranchJcc = 1U << 1, + AlignBranchJmp = 1U << 2, + AlignBranchCall = 1U << 3, + AlignBranchRet = 1U << 4, + AlignBranchIndirect = 1U << 5 + }; + } // end namespace X86; /// X86II - This namespace holds all of the target specific flags that Index: llvm/test/MC/X86/align-branch-boundary.s =================================================================== --- /dev/null +++ llvm/test/MC/X86/align-branch-boundary.s @@ -0,0 +1,32 @@ + # RUN: llvm-mc -triple x86_64-pc-linux-gnu %s | FileCheck %s + + # basic sanity check for round trip serialization of the proposed + # assembler extension. Many more tests needed of course + + # CHECK: test1: + # CHECK-NEXT: .push_align_branch_boundary 7 + # CHECK-NEXT: .push_align_branch_boundary 5 jmp, ret, indirect + # CHECK-NEXT: .push_align_branch_boundary 5 fused, jcc, call + # CHECK-NEXT: callq bar + # CHECK-NEXT: .pop_align_branch_boundary + # CHECK-NEXT: .pop_align_branch_boundary + # CHECK-NEXT: .pop_align_branch_boundary + .text + .globl test1 + .p2align 5 +test1: + .push_align_branch_boundary 7 #no instruction type + .push_align_branch_boundary 5 jmp, indirect, ret + .push_align_branch_boundary 5 jcc, fused, call + callq bar + .pop_align_branch_boundary + .pop_align_branch_boundary + .pop_align_branch_boundary +bar: + retq + + # a far target (4 byte imm) + .section "unknown" + .type baz,@function +baz: + retq