Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1519,6 +1519,10 @@ BUILTIN(__builtin_ms_va_end, "vc*&", "n") BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n") +// Builtins for llvm-mca +BUILTIN(__mca_code_region_start, "vIi", "") +BUILTIN(__mca_code_region_end, "v", "") + #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -1132,6 +1132,13 @@ def int_ssa_copy : Intrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrNoMem, Returned<0>]>; + + +//===---------------------- Tool Specific Intrinsics ----------------------===// +// +def int_mca_code_region_start : Intrinsic<[], [llvm_metadata_ty]>; +def int_mca_code_region_end : Intrinsic<[]>; + //===----------------------------------------------------------------------===// // Target-specific intrinsics //===----------------------------------------------------------------------===// Index: include/llvm/MC/MCContext.h =================================================================== --- include/llvm/MC/MCContext.h +++ include/llvm/MC/MCContext.h @@ -260,6 +260,10 @@ bool HadError = false; + /// A counter for the number of MCACodeRegions we expect to generate. + /// This value is used to make the region names unique per module. + size_t MCACodeRegionCount = 0; + MCSymbol *createSymbolImpl(const StringMapEntry *Name, bool CanBeUnnamed); MCSymbol *createSymbol(StringRef Name, bool AlwaysAddSuffix, @@ -663,6 +667,12 @@ } void undefineMacro(StringRef Name) { MacroMap.erase(Name); } + + /// Returns the number of llvm-mca code regions. + size_t getMCACodeRegionCount() const { return MCACodeRegionCount; } + + /// Add a marker to the container of llvm-mca code markers. + void incrementMCACodeRegionCount() { ++MCACodeRegionCount; } }; } // end namespace llvm Index: include/llvm/Support/TargetOpcodes.def =================================================================== --- include/llvm/Support/TargetOpcodes.def +++ include/llvm/Support/TargetOpcodes.def @@ -527,6 +527,10 @@ /// Generic block address HANDLE_TARGET_OPCODE(G_BLOCK_ADDR) +/// llvm-mca code markers. +HANDLE_TARGET_OPCODE(MCA_CODE_REGION_START) +HANDLE_TARGET_OPCODE(MCA_CODE_REGION_END) + // TODO: Add more generic opcodes as we move along. /// Marker for the end of the generic opcode. Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -1179,6 +1179,18 @@ let AsmString = ""; let hasSideEffects = 1; } +def MCA_CODE_REGION_START : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$id); + let AsmString = "# LLVM-MCA-BEGIN"; + let hasSideEffects = 1; +} +def MCA_CODE_REGION_END : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins); + let AsmString = "# LLVM-MCA-END"; + let hasSideEffects = 1; +} // Generic opcodes used in GlobalISel. include "llvm/Target/GenericOpcodes.td" Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -836,6 +836,43 @@ AP.OutStreamer->AddBlankLine(); } +/// This routine annotates the assembly with a comment and inserts the unique +/// llvm-mca code region start label. 'ID' represents the user's defined +/// region number, the argument specified in llvm.mca.code.region.start(). +static MCSymbol *emitMCACodeRegionStart(const MachineInstr *StartMI, + uint64_t ID, AsmPrinter &AP) { + assert(StartMI->getOpcode() == TargetOpcode::MCA_CODE_REGION_START && + "Invalid llvm-mca start code region instruction."); + AP.OutStreamer->AddComment("LLVM-MCA-BEGIN" + StringRef(" ID: ") + + std::to_string(ID)); + // The symbol name format: .mca_code_region_start.. + MCSymbol *Symbol = AP.OutContext.getOrCreateSymbol( + ".mca_code_region_start." + std::to_string(ID) + "." + + std::to_string(AP.OutContext.getMCACodeRegionCount())); + AP.OutStreamer->EmitLabel(Symbol); + return Symbol; +} + +/// This routine annotates the assembly with a comment and creates a label to +/// mark the end of a llvm-mca code region. +static void emitMCACodeRegionEnd(const MachineInstr *EndMI, uint64_t ID, + const MCSymbol *StartSymbol, AsmPrinter &AP) { + assert(EndMI->getOpcode() == TargetOpcode::MCA_CODE_REGION_END && + "Invalid llvm-mca 'end' code region instruction."); + // Ensure that we have a start symbol for this region. + assert(StartSymbol && "Found an 'end' llvm-mca code region marker without a " + "preceding start marker."); + AP.OutStreamer->AddComment("LLVM-MCA-END" + StringRef(" ID: ") + + std::to_string(ID)); + + // The symbol name format: .mca_code_region_end.. + MCSymbol *EndSymbol = AP.OutContext.getOrCreateSymbol( + ".mca_code_region_end." + std::to_string(ID) + "." + + std::to_string(AP.OutContext.getMCACodeRegionCount())); + AP.OutStreamer->EmitLabel(EndSymbol); + AP.OutContext.incrementMCACodeRegionCount(); +} + /// emitDebugValueComment - This method handles the target-independent form /// of DBG_VALUE, returning true if it was able to do so. A false return /// means the target will need to handle MI in EmitInstruction. @@ -1069,6 +1106,8 @@ // Print out code for the function. bool HasAnyRealCode = false; int NumInstsInFunction = 0; + uint64_t MCARegionID = 0; + const MCSymbol *MCARegionStartSym = nullptr; for (auto &MBB : *MF) { // Print a label for the basic block. EmitBasicBlockStart(MBB); @@ -1130,6 +1169,20 @@ case TargetOpcode::KILL: if (isVerbose()) emitKill(&MI, *this); break; + case TargetOpcode::MCA_CODE_REGION_START: { + assert(!MCARegionStartSym && "Nested llvm-mca markers are not allowed"); + // Get the user's defined region ID. + assert(MI.getOperand(0).isImm() && + "Invalid operand for MCA_CODE_REGION_START."); + MCARegionID = MI.getOperand(0).getImm(); + MCARegionStartSym = emitMCACodeRegionStart(&MI, MCARegionID, *this); + break; + } + case TargetOpcode::MCA_CODE_REGION_END: { + emitMCACodeRegionEnd(&MI, MCARegionID, MCARegionStartSym, *this); + MCARegionStartSym = nullptr; + break; + } default: EmitInstruction(&MI); break; @@ -1152,6 +1205,12 @@ EmitBasicBlockEnd(MBB); } + // Ensure that if MCA markers were used, that there are no + // dangling/unterminated regions. MCARegionStartSym is set to nullptr when an + // end-region marker is discovered. + assert(!MCARegionStartSym && + "Found an unterminated llvm-mca code region."); + EmittedInsts += NumInstsInFunction; MachineOptimizationRemarkAnalysis R(DEBUG_TYPE, "InstructionCount", MF->getFunction().getSubprogram(), Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -3907,6 +3907,25 @@ Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val"); return RValue::get(Builder.CreateStore(ArgPtr, DestAddr)); } + + case Builtin::BI__mca_code_region_start: { + // Get the user-defined identifier as a ConstantInt*, the frontend should + // guarantee that arg(0) is an ICE. + APSInt ID; + E->getArg(0)->isIntegerConstantExpr(ID, getContext()); + ConstantInt *IDValue = llvm::ConstantInt::get(getLLVMContext(), ID); + + // Create the call to llvm.mca.code.region.start(metadata). + Metadata *MD = llvm::ConstantAsMetadata::get(IDValue); + Function *F = CGM.getIntrinsic(Intrinsic::mca_code_region_start); + return RValue::get(Builder.CreateCall( + F, llvm::MetadataAsValue::get(getLLVMContext(), MD))); + } + + case Builtin::BI__mca_code_region_end: { + Function *F = CGM.getIntrinsic(Intrinsic::mca_code_region_end); + return RValue::get(Builder.CreateCall(F)); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6373,6 +6373,35 @@ // MachineFunction in SelectionDAGISel::PrepareEHLandingPad. We can safely // delete it now. return nullptr; + + case Intrinsic::mca_code_region_start: { + MetadataAsValue *RegionMD = dyn_cast(I.getArgOperand(0)); + Metadata *MD = RegionMD->getMetadata(); + ConstantInt *ID = mdconst::extract_or_null(MD); + if (!ID) { + DAG.getContext()->emitError( + &I, "Expected constant integer argument to mca_code_region_start."); + return nullptr; + } + SDLoc DL = getCurSDLoc(); + SDValue IDVal = DAG.getTargetConstant(ID->getLimitedValue(), sdl, MVT::i64); + SDValue Chain = getRoot(); + SDValue N(DAG.getMachineNode(TargetOpcode::MCA_CODE_REGION_START, sdl, + MVT::Other, IDVal, Chain), + 0); + DAG.setRoot(N); + setValue(&I, N); + return nullptr; + } + + case Intrinsic::mca_code_region_end: { + SDValue N(DAG.getMachineNode(TargetOpcode::MCA_CODE_REGION_END, sdl, + MVT::Other, getRoot()), + 0); + DAG.setRoot(N); + setValue(&I, N); + return nullptr; + } } } Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -305,6 +305,10 @@ // Keeps track of duplicate function argument debug info. SmallVector DebugFnArgs; + // Keeps track of the start/end mca code region intrinsics to ensure + // they do not nest. + SmallVector MCACodeRegions; + TBAAVerifier TBAAVerifyHelper; void checkAtomicMemAccessSize(Type *Ty, const Instruction *I); @@ -349,6 +353,7 @@ // FIXME: We strip const here because the inst visitor strips const. visit(const_cast(F)); verifySiblingFuncletUnwinds(); + verifyMCACodeRegions(); InstsInThisBlock.clear(); DebugFnArgs.clear(); LandingPadResultTy = nullptr; @@ -525,6 +530,9 @@ /// Verify all-or-nothing property of DIFile source attribute within a CU. void verifySourceDebugInfo(const DICompileUnit &U, const DIFile &F); + + /// Verify that the MCA code regions collected are well-formed. + void verifyMCACodeRegions(); }; } // end anonymous namespace @@ -4579,6 +4587,26 @@ "the scale of smul_fix must be less than the width of the operands"); break; } + case Intrinsic::mca_code_region_start: { + if (!MCACodeRegions.empty() && + MCACodeRegions.back()->getIntrinsicID() == + Intrinsic::mca_code_region_start) + report_fatal_error("Nested llvm-mca code region markers are not allowed.", + false); + MCACodeRegions.push_back(cast(&Call)); + break; + } + case Intrinsic::mca_code_region_end: { + if (MCACodeRegions.empty() || + MCACodeRegions.back()->getIntrinsicID() == + Intrinsic::mca_code_region_end) + report_fatal_error("Found an 'end' llvm-mca code region marker without a " + "preceding start marker.", + false); + const auto I = cast(&Call); + MCACodeRegions.push_back(I); + break; + } }; } @@ -4800,6 +4828,17 @@ "inconsistent use of embedded source"); } +void Verifier::verifyMCACodeRegions() { + if (!MCACodeRegions.empty()) { + // We have already verified the order of the start/end markers via + // visitIntrinsicCallSite. Verify that the last marker is an end marker. + if (MCACodeRegions.back()->getIntrinsicID() != + Intrinsic::mca_code_region_end) + report_fatal_error("Found an unterminated llvm-mca code region."); + MCACodeRegions.clear(); + } +} + //===----------------------------------------------------------------------===// // Implement the public interfaces to this file... //===----------------------------------------------------------------------===// Index: test/CodeGen/Generic/mca-code-region-error-nest.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/mca-code-region-error-nest.ll @@ -0,0 +1,19 @@ +; RUN: not llc -O0 %s 2>&1 | FileCheck %s + +; Test that nests are rejected. +; CHECK: Nested llvm-mca code region markers are not allowed. +define dso_local void @test() { + call void @llvm.mca.code.region.start(metadata i32 1) + call void @doStuff() + call void @llvm.mca.code.region.end() + + call void @llvm.mca.code.region.start(metadata i32 2) + call void @llvm.mca.code.region.start(metadata i32 3) + call void @doStuff() + call void @llvm.mca.code.region.end() + ret void +} + +declare dso_local void @doStuff() +declare void @llvm.mca.code.region.start(metadata) +declare void @llvm.mca.code.region.end() Index: test/CodeGen/Generic/mca-code-region-error-no-start.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/mca-code-region-error-no-start.ll @@ -0,0 +1,13 @@ +; RUN: not llc -O0 < %s 2>&1 | FileCheck %s + +; Test no start intrinsic. +; CHECK: Found an 'end' llvm-mca code region marker without a preceding start marker. +define dso_local void @test() { + call void @doStuff() + call void @llvm.mca.code.region.end() + ret void +} + +declare dso_local void @doStuff() +declare void @llvm.mca.code.region.start(metadata) +declare void @llvm.mca.code.region.end() Index: test/CodeGen/Generic/mca-code-region-error-unterminated.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/mca-code-region-error-unterminated.ll @@ -0,0 +1,13 @@ +; RUN: not llc -O0 %s 2>&1 | FileCheck %s + +; Test no end intrinsic. +; CHECK: Found an unterminated llvm-mca code region. +define dso_local void @test() { + call void @llvm.mca.code.region.start(metadata i32 1) + call void @doStuff() + ret void +} + +declare dso_local void @doStuff() +declare void @llvm.mca.code.region.start(metadata) +declare void @llvm.mca.code.region.end() Index: test/CodeGen/X86/mca-code-regions.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/mca-code-regions.ll @@ -0,0 +1,59 @@ +; RUN: llc -O0 -mtriple=x86_64-- < %s | FileCheck %s + +define dso_local void @test() { + call void @llvm.mca.code.region.start(metadata i32 1) + call void @doStuff(i32 1) + call void @llvm.mca.code.region.end() + + call void @llvm.mca.code.region.start(metadata i32 2) + call void @doStuff(i32 2) + call void @llvm.mca.code.region.end() + + call void @llvm.mca.code.region.start(metadata i32 3) + call void @doStuff(i32 3) + call void @llvm.mca.code.region.end() + + call void @llvm.mca.code.region.start(metadata i32 4) + call void @doStuff(i32 4) + call void @llvm.mca.code.region.end() + + ; Duplicate user-defined region identifiers (number 4 here) are allowed. + call void @llvm.mca.code.region.start(metadata i32 4) + call void @doStuff(i32 4) + call void @llvm.mca.code.region.end() + + call void @llvm.mca.code.region.start(metadata i32 5) + call void @doStuff(i32 5) + call void @llvm.mca.code.region.end() + + ret void +} + +; CHECK-LABEL: .mca_code_region_start.1.0: +; CHECK-NEXT: movl $1, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.1.0: +; CHECK-LABEL: .mca_code_region_start.2.1: +; CHECK-NEXT: movl $2, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.2.1: +; CHECK-LABEL: .mca_code_region_start.3.2: +; CHECK-NEXT: movl $3, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.3.2: +; CHECK-LABEL: .mca_code_region_start.4.3: +; CHECK-NEXT: movl $4, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.4.3: +; CHECK-LABEL: .mca_code_region_start.4.4: +; CHECK-NEXT: movl $4, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.4.4: +; CHECK-LABEL: .mca_code_region_start.5.5: +; CHECK-NEXT: movl $5, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .mca_code_region_end.5.5: + +declare dso_local void @doStuff(i32) +declare void @llvm.mca.code.region.start(metadata) +declare void @llvm.mca.code.region.end() Index: test/CodeGen/builtins-mca.c =================================================================== --- /dev/null +++ test/CodeGen/builtins-mca.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O0 -o - %s | FileCheck %s +extern void sendUniverse(void); + +void addUniverse(){ + __mca_code_region_start(42); +// CHECK: call void @llvm.mca.code.region.start(metadata i32 42) +// CHECK: call void @sendUniverse() + sendUniverse(); +// CHECK: call void @llvm.mca.code.region.end() + __mca_code_region_end(); +} + +// CHECK: declare void @llvm.mca.code.region.start(metadata) +// CHECK: declare void @llvm.mca.code.region.end() Index: test/tools/llvm-mca/Binary/multiple-code-regions-exec.test =================================================================== --- /dev/null +++ test/tools/llvm-mca/Binary/multiple-code-regions-exec.test @@ -0,0 +1,13 @@ +# Note that the original source can be extracted from multiple-regions.elf via +# objcopy --dump-section .test_source=test.c ./multiple-regions-exec.elf + +# RUN: llvm-mca %p/Inputs/multiple-regions-exec.elf -dump-binary-regions | FileCheck %s + +# CHECK: .mca_code_regions: +# CHECK-NEXT: (1) <1, 0x4004fe, 7 bytes> +# CHECK-NEXT: (2) <2, 0x400505, 7 bytes> +# CHECK-NEXT: (3) <3, 0x40050c, 7 bytes> +# CHECK-NEXT: (4) <42, 0x400513, 14 bytes> +# CHECK-NEXT: (5) <5, 0x400521, 7 bytes> +# CHECK-NEXT: (6) <5, 0x400528, 7 bytes> +# CHECK-NEXT: (7) <6, 0x40052f, 7 bytes> Index: test/tools/llvm-mca/Binary/multiple-code-regions-obj.test =================================================================== --- /dev/null +++ test/tools/llvm-mca/Binary/multiple-code-regions-obj.test @@ -0,0 +1,13 @@ +# Note that the original source can be extracted from multiple-regions.elf via +# objcopy --dump-section .test_source=test.c ./multiple-regions.elf + +# RUN: llvm-mca %p/Inputs/multiple-regions-obj.elf -dump-binary-regions | FileCheck %s + +# CHECK: .mca_code_regions: +# CHECK-NEXT: (1) <1, 0x3e, 7 bytes> +# CHECK-NEXT: (2) <2, 0x45, 7 bytes> +# CHECK-NEXT: (3) <3, 0x4c, 7 bytes> +# CHECK-NEXT: (4) <42, 0x53, 14 bytes> +# CHECK-NEXT: (5) <5, 0x61, 7 bytes> +# CHECK-NEXT: (6) <5, 0x68, 7 bytes> +# CHECK-NEXT: (7) <6, 0x6f, 7 bytes> Index: tools/llvm-mca/CodeRegion.h =================================================================== --- tools/llvm-mca/CodeRegion.h +++ tools/llvm-mca/CodeRegion.h @@ -60,6 +60,7 @@ CodeRegion &operator=(const CodeRegion &) = delete; public: + CodeRegion() {} CodeRegion(llvm::StringRef Desc, llvm::SMLoc Start) : Description(Desc), RangeStart(Start), RangeEnd() {} @@ -108,6 +109,13 @@ void addInstruction(const llvm::MCInst &Instruction); llvm::SourceMgr &getSourceMgr() const { return SM; } + CodeRegion *createEmptyRegion() { + auto CR = llvm::make_unique(); + auto CRPtr = CR.get(); + Regions.push_back(std::move(CR)); + return CRPtr; + } + CodeRegions(llvm::SourceMgr &S) : SM(S) { // Create a default region for the input code sequence. addRegion("Default", llvm::SMLoc()); Index: tools/llvm-mca/CodeRegionGenerator.h =================================================================== --- tools/llvm-mca/CodeRegionGenerator.h +++ tools/llvm-mca/CodeRegionGenerator.h @@ -17,14 +17,18 @@ #ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H #define LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H +#include "BinaryCodeRegion.h" #include "CodeRegion.h" +#include "llvm/ADT/StringRef.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" -#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/Error.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include +#include namespace llvm { namespace mca { @@ -64,6 +68,30 @@ Expected parseCodeRegions() override; }; +/// This class is responsible for parsing input object files and generating +/// a CodeRegions instance. +class ObjFileCodeRegionGenerator final : public CodeRegionGenerator { + const Target &TheTarget; + MCContext &Ctx; + const MCSubtargetInfo &STI; + const MCObjectFileInfo &MOFI; + std::string InputFilename; + BinaryCodeRegions ObjRegions; + + Error readMCACodeRegionsSection(const object::ObjectFile &O); + Error disassembleIntoRegions(const object::Binary &Bin); + +public: + ObjFileCodeRegionGenerator(const Target &T, SourceMgr &SM, MCContext &C, + const MCSubtargetInfo &S, + const MCObjectFileInfo &M, StringRef FileName) + : CodeRegionGenerator(SM), TheTarget(T), Ctx(C), STI(S), MOFI(M), + InputFilename(FileName) {} + + Expected parseCodeRegions() override; + void dumpObjFileRegions() const { ObjRegions.dump(); } +}; + } // namespace mca } // namespace llvm Index: tools/llvm-mca/CodeRegionGenerator.cpp =================================================================== --- tools/llvm-mca/CodeRegionGenerator.cpp +++ tools/llvm-mca/CodeRegionGenerator.cpp @@ -16,17 +16,24 @@ #include "CodeRegionGenerator.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCTargetOptions.h" -#include "llvm/Support/Error.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/SMLoc.h" #include namespace llvm { namespace mca { +#define DEBUG_TYPE "llvm-mca" +using namespace llvm::object; + // This virtual dtor serves as the anchor for the CodeRegionGenerator class. CodeRegionGenerator::~CodeRegionGenerator() {} @@ -133,5 +140,211 @@ return Regions; } +// Struct to sort mca symbols. +struct MCASym { + bool IsStartOfRegion; + uint64_t Address; + uint64_t ID; // User defined value. + uint64_t Sequence; // Compiler generated value. +}; + +// Parse the symbol name in 'Sym' and create a MCASym instance in Syms. +// This routine will return early if the symbol is not a llvm-mca region symbol. +static void parseMCASym(const SymbolRef Sym, SmallVectorImpl &Syms) { + // Region markers are treated as unknown symbol types, skip + // any symbol type that is not an unknown. + auto SymType = Sym.getType(); + if (!SymType || SymType.get() != SymbolRef::ST_Unknown) + return; + + // Check the name and reject this symbol if it is not for llvm-mca. + auto MaybeName = Sym.getName(); + if (!MaybeName) + return; + StringRef Name = MaybeName.get(); + bool IsStart = Name.startswith(".mca_code_region_start."); + if (!IsStart && !Name.startswith(".mca_code_region_end.")) + return; + + // If we are this far we have a start or end llvm-mca symbol. + // Get the address from the SymbolRef. + auto MaybeAddr = Sym.getAddress(); + if (!MaybeAddr) + return; + uint64_t Addr = MaybeAddr.get(); + + // Skip past the leading '.' in: .mca_code_region_{start,end}.. + auto NameStr = Name.str(); + size_t StrOffset = 1; + + // Parse the user-defined ID integer. This is the first number following + // the next '.' This ID is only used for report/comestic purposes. + StrOffset = NameStr.find('.', StrOffset); + if (StrOffset == std::string::npos) + return; + ++StrOffset; + uint64_t ID = atoi(NameStr.c_str() + StrOffset); + + // Parse the compiler-defined Sequence integer. This is the first number + // following the next '.' This value is used for sorting regions. + if ((StrOffset = NameStr.find('.', StrOffset)) == std::string::npos) + return; + ++StrOffset; + uint64_t Seq = atoi(NameStr.c_str() + StrOffset); + + // Add the parsed symbol to the list of discovered llvm-mca symbols. + Syms.push_back({IsStart, Addr, ID, Seq}); +} + +Error ObjFileCodeRegionGenerator::readMCACodeRegionsSection( + const ObjectFile &O) { + // Parse the symbol table looking for .mca_code_region_{start,end}. + SmallVector MCASyms; + for (const auto Sym : O.symbols()) + parseMCASym(Sym, MCASyms); + if (MCASyms.size() % 2 != 0) + return make_error("Odd number of .mca_code_regions.", + inconvertibleErrorCode()); + + // Sort symbols based on address and sequence number. A region can end at + // one address and a new region can start at that same address. This + // predicate takes that into account, by sorting on the compiler-generated + // sequence numbers for the region if the addresses have the same value. + llvm::sort(MCASyms, [](const MCASym &A, const MCASym &B) { + return (A.Address == B.Address) ? A.Sequence < B.Sequence + : A.Address < B.Address; + }); + +#ifndef NDEBUG + LLVM_DEBUG(llvm::dbgs() << "Found " << MCASyms.size() << " MCA Symbols:\n"); + for (auto M : MCASyms) + LLVM_DEBUG(llvm::dbgs() + << " " << format_hex(M.Address, 2) << ", " + << "Seq:" << M.Sequence << ", " + << "ID: " << M.ID << ", " + << (M.IsStartOfRegion ? "StartOfRegion" : "EndOfRegion") + << '\n'); +#endif + + // Turn the symbols into code regions. + for (size_t I = 0; I < MCASyms.size(); I += 2) { + auto StartSym = MCASyms[I]; + auto EndSym = MCASyms[I + 1]; + if (!StartSym.IsStartOfRegion || EndSym.IsStartOfRegion || + StartSym.ID != EndSym.ID) + return make_error("Invalid start/end region marker. " + "Nested or empty regions are not allowed.", + inconvertibleErrorCode()); + if (EndSym.Address - StartSym.Address) + ObjRegions.addRegion(StartSym.ID, StartSym.Address, + EndSym.Address - StartSym.Address); + } + + return Error::success(); +} + +// This utility class is used to sort the .text sections by increasing +// address. +struct SectionRange { + SectionRef Section; + size_t StartAddress; + size_t EndAddress; + bool operator<(const SectionRange &RHS) const { + return StartAddress < RHS.StartAddress; + } +}; + +// Populate Sections with the .text sections from O. +static void createSectionList(const ObjectFile &O, + SmallVectorImpl &Sections) { + for (auto S = O.section_begin(), SE = O.section_end(); S != SE; ++S) + if (S->isText()) + Sections.push_back({*S, S->getAddress(), S->getAddress() + S->getSize()}); + sort(Sections, std::less()); +} + +// Populate Region with instructions from Section. +static void addInstructionsInRange(MCDisassembler &MCD, + const SectionRef Section, + BinaryCodeRegion &Region) { + StringRef SData; + if (Section.getContents(SData)) + return; + auto Data = arrayRefFromStringRef(SData); + uint64_t NBytesRead = 0; + uint64_t BaseAddr = Section.getAddress(); + for (uint64_t Offset = Region.getStartAddress() - BaseAddr, Size = 0; + Offset < Data.size() && NBytesRead < Region.getRegionLength(); + Offset += Size) { + // Read in data at offset, it could be trash or it could be an inst. + MCInst I; + const ArrayRef Slice = Data.slice(Offset); + MCDisassembler::DecodeStatus Status = + MCD.getInstruction(I, Size, Slice, Offset, nulls(), nulls()); + if (Status == MCDisassembler::DecodeStatus::Success) + Region.addInstruction(I); + + // Ensure progress by advancing 1 byte if we did not advance any. + if (Size == 0) + Size = 1; + NBytesRead += Size; + } +} + +// Given a binary, scan the .mca_code_regions section to obtain a set of triples. These triples are used here as ranges of +// instructions. The instructions within the ranges of that set are +// disassembled into CodeRegions. CodeRegions are what llvm-mca analyzes. +Expected ObjFileCodeRegionGenerator::parseCodeRegions() { + // Create a disassembler. We need to convert raw object code to MCInst. + std::unique_ptr MCD = + std::unique_ptr(TheTarget.createMCDisassembler(STI, Ctx)); + if (!MCD) + return make_error("Failed to initialize a disassembler.", + inconvertibleErrorCode()); + + // Get the input file as a binary object. + Expected> OB = createBinary(InputFilename); + if (!OB) + return std::move(OB.takeError()); + Binary *Bin = OB->getBinary(); + if (!Bin->isObject()) + return make_error("Expected object file.", + inconvertibleErrorCode()); + + // Cast the input binary into an object file representation. + const ObjectFile *O = cast(Bin); + + // Read the code regions that the user wants to analyze. These regions + // are specified in the llvm-mca code region section of the binary. + if (auto Err = readMCACodeRegionsSection(*O)) + return Err; + + if (ObjRegions.empty()) + return make_error( + "No llvm-mca code regions are defined in this binary.", + inconvertibleErrorCode()); + + // Create a table of sections sorted by the sections start address. + SmallVector SL; + createSectionList(*O, SL); + + // Get the instructions that fall within each region. + for (auto &Region : ObjRegions) { + // Find the Section that contains the Region. + auto SItr = find_if(SL, [&Region](const SectionRange &S) { + return ((Region->getStartAddress() >= S.StartAddress) && + ((Region->getStartAddress() + Region->getRegionLength()) < + S.EndAddress)); + }); + if (SItr != SL.end()) + addInstructionsInRange(*MCD, SItr->Section, *Region); + } + + // Get the CodeRegion from each ObjRegion. + ObjRegions.toCodeRegions(Regions); + return Regions; +} + } // namespace mca } // namespace llvm Index: tools/llvm-mca/llvm-mca.cpp =================================================================== --- tools/llvm-mca/llvm-mca.cpp +++ tools/llvm-mca/llvm-mca.cpp @@ -32,16 +32,21 @@ #include "Views/SchedulerStatistics.h" #include "Views/SummaryView.h" #include "Views/TimelineView.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" #include "llvm/MCA/Context.h" #include "llvm/MCA/Pipeline.h" #include "llvm/MCA/Stages/EntryStage.h" #include "llvm/MCA/Stages/InstructionTables.h" #include "llvm/MCA/Support.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" @@ -58,6 +63,7 @@ static cl::OptionCategory ToolOptions("Tool Options"); static cl::OptionCategory ViewOptions("View Options"); +static cl::OptionCategory DebugOptions("Debug Options"); static cl::opt InputFilename(cl::Positional, cl::desc(""), @@ -176,6 +182,11 @@ cl::desc("Print all views including hardware statistics"), cl::cat(ViewOptions), cl::init(false)); +static cl::opt DumpBinaryRegions( + "dump-binary-regions", + cl::desc("Dump the contents of the .mca_code_regions section"), + cl::cat(DebugOptions), cl::init(false)); + namespace { const Target *getTarget(const char *ProgName) { @@ -253,11 +264,12 @@ InitializeAllTargetInfos(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); + InitializeAllDisassemblers(); // Enable printing of available targets when flag --version is specified. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions}); + cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions, &DebugOptions}); // Parse flags and initialize target options. cl::ParseCommandLineOptions(argc, argv, @@ -285,9 +297,8 @@ // Apply overrides to llvm-mca specific options. processViewOptions(); - SourceMgr SrcMgr; - // Tell SrcMgr about this buffer, which is what the parser will pick up. + SourceMgr SrcMgr; SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); std::unique_ptr MRI(TheTarget->createMCRegInfo(TripleName)); @@ -303,7 +314,6 @@ std::unique_ptr BOS; std::unique_ptr MCII(TheTarget->createMCInstrInfo()); - std::unique_ptr MCIA( TheTarget->createMCInstrAnalysis(MCII.get())); @@ -334,9 +344,31 @@ return 1; } + // Automatically detect object files if the input file is not STDIN. + bool InputIsBinary = false; + if (InputFilename != "-") { + file_magic Presto; + if (auto EC = identify_magic(InputFilename, Presto)) { + WithColor::error() << "Failed to identify input file's magic number.\n"; + return 1; + } else + InputIsBinary = Presto.is_object(); + } + + // Create a generator for the input passed to llvm-mca. + std::unique_ptr CRG; + if (InputIsBinary) + CRG = llvm::make_unique( + *TheTarget, SrcMgr, Ctx, *STI, MOFI, InputFilename); + else + CRG = llvm::make_unique( + *TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); + + // Populate the code regions using the generator. A region is a collection + // of instructions. + Expected RegionsOrErr = CRG->parseCodeRegions(); + // Parse the input and create CodeRegions that llvm-mca can analyze. - mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); - Expected RegionsOrErr = CRG.parseCodeRegions(); if (!RegionsOrErr) { if (auto Err = handleErrors(RegionsOrErr.takeError(), [](const StringError &E) { @@ -353,16 +385,30 @@ return 1; } - // Now initialize the output file. + // Optionally dump the code regions if the input file was an object file + // (this is primarily for debugging and testing). + if (InputIsBinary && DumpBinaryRegions) { + auto G = static_cast(CRG.get()); + G->dumpObjFileRegions(); + return 0; + } + + // If the input is ASM, then obtain the dialect used in that file. + unsigned AssemblerDialect = 0; + if (!InputIsBinary) { + auto G = static_cast(CRG.get()); + AssemblerDialect = G->getAssemblerDialect(); + } + if (OutputAsmVariant >= 0) + AssemblerDialect = static_cast(OutputAsmVariant); + + // Initialize the output file. auto OF = getOutputStream(); if (std::error_code EC = OF.getError()) { WithColor::error() << EC.message() << '\n'; return 1; } - unsigned AssemblerDialect = CRG.getAssemblerDialect(); - if (OutputAsmVariant >= 0) - AssemblerDialect = static_cast(OutputAsmVariant); std::unique_ptr IP(TheTarget->createMCInstPrinter( Triple(TripleName), AssemblerDialect, *MAI, *MCII, *MRI)); if (!IP) {