Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1515,6 +1515,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/CodeGen/AsmPrinter.h =================================================================== --- include/llvm/CodeGen/AsmPrinter.h +++ include/llvm/CodeGen/AsmPrinter.h @@ -313,6 +313,8 @@ void emitStackSizeSection(const MachineFunction &MF); + void emitMCACodeRegionsSection(); + enum CFIMoveType { CFI_M_None, CFI_M_EH, CFI_M_Debug }; CFIMoveType needsCFIMoves() const; Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -1050,6 +1050,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 @@ -64,6 +64,16 @@ public: using SymbolTable = StringMap; + /// MCACodeRegions are two markers, one that identifies the start of a code + /// region, and the second which identifies the end of a code region. + struct MCACodeRegion { + MCACodeRegion(uint64_t ID, const MCSymbol *S, const MCSymbol *E) + : Identifier(ID), StartMarker(S), EndMarker(E) {} + uint64_t Identifier; + const MCSymbol *StartMarker; + const MCSymbol *EndMarker; + }; + private: /// The SourceMgr for this object, if any. const SourceMgr *SrcMgr; @@ -260,6 +270,12 @@ bool HadError = false; + /// A collection of llvm-mca code region markers. These markers + /// represent a sequence of instructions (region of code) that llvm-mca + /// can analyze. The key is the user-defined region number. That number + /// is an arbitrary value used to uniquely identify the region. + SmallVector MCACodeRegions; + MCSymbol *createSymbolImpl(const StringMapEntry *Name, bool CanBeUnnamed); MCSymbol *createSymbol(StringRef Name, bool AlwaysAddSuffix, @@ -663,6 +679,25 @@ } void undefineMacro(StringRef Name) { MacroMap.erase(Name); } + + /// Returns true if this module has llvm-mca code regions. + bool hasMCACodeRegionMarkers() const { + return !MCACodeRegions.empty(); + } + + /// Returns a container of llvm-mca code regions that llvm-mca can analyze. + const ArrayRef getMCACodeRegions() const { + return MCACodeRegions; + } + + /// Returns the number of llvm-mca code regions. + size_t getMCACodeRegionsCount() const { return MCACodeRegions.size(); } + + /// Add a marker to the container of llvm-mca code markers. + void addMCACodeRegionMarker(uint64_t ID, const MCSymbol *StartLabel, + const MCSymbol *EndLabel) { + MCACodeRegions.emplace_back(ID, StartLabel, EndLabel); + } }; } // end namespace llvm Index: include/llvm/Support/TargetOpcodes.def =================================================================== --- include/llvm/Support/TargetOpcodes.def +++ include/llvm/Support/TargetOpcodes.def @@ -510,6 +510,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 @@ -1178,6 +1178,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 = 0; +} +def MCA_CODE_REGION_END : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins); + let AsmString = "# LLVM-MCA-END"; + let hasSideEffects = 0; +} // 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 @@ -837,6 +837,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-START" + StringRef(" ID: ") + + std::to_string(ID)); + MCSymbol *Symbol = AP.OutContext.getOrCreateSymbol( + ".Lmca_code_region_start_" + + std::to_string(AP.OutContext.getMCACodeRegionsCount())); + AP.OutStreamer->EmitLabel(Symbol); + return Symbol; +} + +/// This routine annotates the assembly with a comment and stores +/// the marker positions for use when the llvm-mca region MCSection +/// is emitted. +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)); + MCSymbol *EndSymbol = AP.OutContext.getOrCreateSymbol( + ".Lmca_code_region_end_" + + std::to_string(AP.OutContext.getMCACodeRegionsCount())); + AP.OutStreamer->EmitLabel(EndSymbol); + + // Store this data so it can be emitted into an object file section. + AP.OutContext.addMCACodeRegionMarker(ID, StartSymbol, EndSymbol); +} + /// 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. @@ -1026,6 +1063,35 @@ OutStreamer->PopSection(); } +void AsmPrinter::emitMCACodeRegionsSection() { + if (TM.getTargetTriple().isOSBinFormatELF()) { + MCSection *Sect = + OutContext.getELFSection(".mca_code_regions", ELF::SHT_PROGBITS, 0); + assert(Sect && "Failed to generate a .mca_code_regions section."); + OutStreamer->PushSection(); + OutStreamer->SwitchSection(Sect); + const ArrayRef &Regions = + OutContext.getMCACodeRegions(); + + // Emit each region. + for (const MCContext::MCACodeRegion &CR : Regions) { + assert(CR.StartMarker && CR.EndMarker && + "Missing start or end llvm-mca region symbol."); + // Emit the region ID. This was the user's specified number to the + // llvm.mca.code.region.start intrinisic. + emitInt64(CR.Identifier); + + // Emit the start address of the region (start label). + const MCExpr *Expr = MCSymbolRefExpr::create(CR.StartMarker, OutContext); + OutStreamer->EmitValue(Expr, sizeof(uint64_t)); + + // Emit the number of bytes of the region. + EmitLabelDifference(CR.EndMarker, CR.StartMarker, sizeof(uint64_t)); + } + OutStreamer->PopSection(); + } +} + static bool needFuncLabelsForEHOrDebugInfo(const MachineFunction &MF, MachineModuleInfo *MMI) { if (!MF.getLandingPads().empty() || MF.hasEHFunclets() || MMI->hasDebugInfo()) @@ -1070,6 +1136,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); @@ -1131,6 +1199,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; @@ -1153,6 +1235,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(), @@ -1590,6 +1678,9 @@ OutStreamer->EmitAddrsigSym(getSymbol(&GV)); } + if (OutContext.hasMCACodeRegionMarkers()) + emitMCACodeRegionsSection(); + // Allow the target to emit any magic that it wants at the end of the file, // after everything else has gone out. EmitEndOfAsmFile(M); Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -3837,6 +3837,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 @@ -6337,6 +6337,29 @@ // MachineFunction in SelectionDAGISel::PrepareEHLandingPad. We can safely // delete it now. return nullptr; + + case Intrinsic::mca_code_region_start: { + const TargetInstrInfo *TII = DAG.getSubtarget().getInstrInfo(); + 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; + } + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, dl, + TII->get(TargetOpcode::MCA_CODE_REGION_START)) + .addImm(ID->getLimitedValue()); + return nullptr; + } + + case Intrinsic::mca_code_region_end: { + const TargetInstrInfo *TII = DAG.getSubtarget().getInstrInfo(); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, dl, + TII->get(TargetOpcode::MCA_CODE_REGION_END)); + return nullptr; + } } } Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -304,6 +304,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); @@ -348,6 +352,7 @@ // FIXME: We strip const here because the inst visitor strips const. visit(const_cast(F)); verifySiblingFuncletUnwinds(); + verifyMCACodeRegions(); InstsInThisBlock.clear(); DebugFnArgs.clear(); LandingPadResultTy = nullptr; @@ -519,6 +524,9 @@ /// Module-level verification that all @llvm.experimental.deoptimize /// declarations share the same calling convention. void verifyDeoptimizeCallingConvs(); + + /// Verify that the MCA code regions collected are well-formed. + void verifyMCACodeRegions(); }; } // end anonymous namespace @@ -4531,6 +4539,35 @@ "of ints"); 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(CS.getInstruction())); + 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); + auto I = cast(CS.getInstruction()); + + // If we have an end, then we must have a start. Check that the region + // belongs to a single basic block. + auto Start = MCACodeRegions.back(); + if (I->getParent() != Start->getParent()) { + report_fatal_error("An llvm-mca code region cannot " + "span multiple basic blocks."); + } + + MCACodeRegions.push_back(I); + break; + } }; } @@ -4744,6 +4781,17 @@ } } +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/X86/mca-code-region-error-multi-block.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/mca-code-region-error-multi-block.ll @@ -0,0 +1,18 @@ +; RUN: not llc -O0 -mtriple=x86_64-- %s 2>&1 | FileCheck %s + +; Test that regions spanning multiple blocks are rejected. +; CHECK: An llvm-mca code region cannot span multiple basic blocks. +define dso_local void @test(i1 %val) { + br i1 %val, label %if.then, label %if.end + if.then: + call void @llvm.mca.code.region.start(metadata i32 1) + br label %if.end + + if.end: + 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/X86/mca-code-region-error-nest.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/mca-code-region-error-nest.ll @@ -0,0 +1,19 @@ +; RUN: not llc -O0 -mtriple=x86_64-- %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/X86/mca-code-region-error-no-start.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/mca-code-region-error-no-start.ll @@ -0,0 +1,13 @@ +; RUN: not llc -O0 -mtriple=x86_64-- < %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/X86/mca-code-region-error-unterminated.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/mca-code-region-error-unterminated.ll @@ -0,0 +1,13 @@ +; RUN: not llc -O0 -mtriple=x86_64-- %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: .Lmca_code_region_start_0: +; CHECK-NEXT: movl $1, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_0: +; CHECK-LABEL: .Lmca_code_region_start_1: +; CHECK-NEXT: movl $2, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_1: +; CHECK-LABEL: .Lmca_code_region_start_2: +; CHECK-NEXT: movl $3, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_2: +; CHECK-LABEL: .Lmca_code_region_start_3: +; CHECK-NEXT: movl $4, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_3: +; CHECK-LABEL: .Lmca_code_region_start_4: +; CHECK-NEXT: movl $4, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_4: +; CHECK-LABEL: .Lmca_code_region_start_5: +; CHECK-NEXT: movl $5, %edi +; CHECK-NEXT: callq doStuff +; CHECK-LABEL: .Lmca_code_region_end_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.test =================================================================== --- /dev/null +++ test/tools/llvm-mca/Binary/multiple-code-regions.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.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: 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 @@ -17,16 +17,19 @@ #include "CodeRegionGenerator.h" #include "llvm/ADT/ArrayRef.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/SMLoc.h" #include namespace llvm { namespace mca { +using namespace llvm::object; + // This virtual dtor serves as the anchor for the CodeRegionGenerator class. CodeRegionGenerator::~CodeRegionGenerator() {} @@ -133,5 +136,146 @@ return Regions; } +Error ObjFileCodeRegionGenerator::readMCACodeRegionsSection( + const ObjectFile &O) { + // Locate the .mca_code_regions section. + StringRef CurrentName; + section_iterator SItr = O.section_end(); + for (auto SB = O.section_begin(), SE = O.section_end(); SB != SE; ++SB) + if (!SB->getName(CurrentName) && CurrentName == ".mca_code_regions") { + SItr = SB; + break; + } + + // Not finding a section is ok. + if (SItr == O.section_end()) + return Error::success(); + + // Get the contents of the section and convert into a stream reader. + const SectionRef Section = *SItr; + StringRef SData; + if (Section.getContents(SData)) + return make_error("Failed to read the contents of .mca_code_regions.", + inconvertibleErrorCode()); + BinaryStreamReader BS(SData, O.isLittleEndian() ? support::endianness::little + : support::endianness::big); + // Read in each triple. + uint32_t NumZeroRegions = 0; + while (!BS.empty()) { + uint64_t ID, Addr, Size; + if (BS.readInteger(ID) || BS.readInteger(Addr) || BS.readInteger(Size)) + return make_error( + "Failed to read a .mca_code_regions entry.", + inconvertibleErrorCode()); + // Ignore zero-sized regions. + if (Size > 0) + ObjRegions.addRegion(ID, Addr, Size); + else + ++NumZeroRegions; + } + 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) { + // Get the section's data. + StringRef SData; + if (Section.getContents(SData)) + return; + auto Data = arrayRefFromStringRef(SData); + uint64_t BaseAddr = Section.getAddress(); + for (uint64_t Offset = Region.getStartAddress() - BaseAddr, Size = 0; + Offset < Data.size() && Size < 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; + } +} + +// 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 @@ -37,11 +37,16 @@ #include "include/Context.h" #include "include/Pipeline.h" #include "include/Support.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/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(""), @@ -178,6 +184,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) { @@ -254,11 +265,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, @@ -286,9 +298,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)); @@ -304,7 +315,6 @@ std::unique_ptr BOS; std::unique_ptr MCII(TheTarget->createMCInstrInfo()); - std::unique_ptr MCIA( TheTarget->createMCInstrAnalysis(MCII.get())); @@ -335,9 +345,29 @@ return 1; } - // Parse the input and create CodeRegions that llvm-mca can analyze. - mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); - Expected RegionsOrErr = CRG.parseCodeRegions(); + // 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(); if (auto Err = RegionsOrErr.takeError()) { WithColor::error() << Err << "\n"; return 1; @@ -348,16 +378,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) {