diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h index c4bf370fa3dd..1a7595d75404 100644 --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -1,279 +1,279 @@ //===-Config.h - LLVM Link Time Optimizer Configuration ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the lto::Config data structure, which allows clients to // configure LTO. // //===----------------------------------------------------------------------===// #ifndef LLVM_LTO_CONFIG_H #define LLVM_LTO_CONFIG_H #include "llvm/ADT/DenseSet.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/CodeGen.h" #include "llvm/Target/TargetOptions.h" #include namespace llvm { class Error; class Module; class ModuleSummaryIndex; class raw_pwrite_stream; namespace lto { /// LTO configuration. A linker can configure LTO by setting fields in this data /// structure and passing it to the lto::LTO constructor. struct Config { // Note: when adding fields here, consider whether they need to be added to // computeCacheKey in LTO.cpp. std::string CPU; TargetOptions Options; std::vector MAttrs; std::vector PassPlugins; Optional RelocModel = Reloc::PIC_; Optional CodeModel = None; CodeGenOpt::Level CGOptLevel = CodeGenOpt::Default; CodeGenFileType CGFileType = CGFT_ObjectFile; unsigned OptLevel = 2; bool DisableVerify = false; /// Use the new pass manager bool UseNewPM = LLVM_ENABLE_NEW_PASS_MANAGER; /// Flag to indicate that the optimizer should not assume builtins are present /// on the target. bool Freestanding = false; /// Disable entirely the optimizer, including importing for ThinLTO bool CodeGenOnly = false; /// Run PGO context sensitive IR instrumentation. bool RunCSIRInstr = false; /// Asserts whether we can assume whole program visibility during the LTO /// link. bool HasWholeProgramVisibility = false; /// Always emit a Regular LTO object even when it is empty because no Regular /// LTO modules were linked. This option is useful for some build system which /// want to know a priori all possible output files. bool AlwaysEmitRegularLTOObj = false; /// If this field is set, the set of passes run in the middle-end optimizer /// will be the one specified by the string. Only works with the new pass /// manager as the old one doesn't have this ability. std::string OptPipeline; // If this field is set, it has the same effect of specifying an AA pipeline // identified by the string. Only works with the new pass manager, in // conjunction OptPipeline. std::string AAPipeline; /// Setting this field will replace target triples in input files with this /// triple. std::string OverrideTriple; /// Setting this field will replace unspecified target triples in input files /// with this triple. std::string DefaultTriple; /// Context Sensitive PGO profile path. std::string CSIRProfile; /// Sample PGO profile path. std::string SampleProfile; /// Name remapping file for profile data. std::string ProfileRemapping; /// The directory to store .dwo files. std::string DwoDir; /// The name for the split debug info file used for the DW_AT_[GNU_]dwo_name /// attribute in the skeleton CU. This should generally only be used when /// running an individual backend directly via thinBackend(), as otherwise /// all objects would use the same .dwo file. Not used as output path. std::string SplitDwarfFile; /// The path to write a .dwo file to. This should generally only be used when /// running an individual backend directly via thinBackend(), as otherwise /// all .dwo files will be written to the same path. Not used in skeleton CU. std::string SplitDwarfOutput; /// Optimization remarks file path. - std::string RemarksFilename = ""; + std::string RemarksFilename; /// Optimization remarks pass filter. - std::string RemarksPasses = ""; + std::string RemarksPasses; /// Whether to emit optimization remarks with hotness informations. bool RemarksWithHotness = false; /// The minimum hotness value a diagnostic needs in order to be included in /// optimization diagnostics. /// /// The threshold is an Optional value, which maps to one of the 3 states: /// 1. 0 => threshold disabled. All emarks will be printed. /// 2. positive int => manual threshold by user. Remarks with hotness exceed /// threshold will be printed. /// 3. None => 'auto' threshold by user. The actual value is not /// available at command line, but will be synced with /// hotness threhold from profile summary during /// compilation. /// /// If threshold option is not specified, it is disabled by default. llvm::Optional RemarksHotnessThreshold = 0; /// The format used for serializing remarks (default: YAML). - std::string RemarksFormat = ""; + std::string RemarksFormat; /// Whether to emit the pass manager debuggging informations. bool DebugPassManager = false; /// Statistics output file path. std::string StatsFile; /// Specific thinLTO modules to compile. std::vector ThinLTOModulesToCompile; /// Time trace enabled. bool TimeTraceEnabled = false; /// Time trace granularity. unsigned TimeTraceGranularity = 500; bool ShouldDiscardValueNames = true; DiagnosticHandlerFunction DiagHandler; /// If this field is set, LTO will write input file paths and symbol /// resolutions here in llvm-lto2 command line flag format. This can be /// used for testing and for running the LTO pipeline outside of the linker /// with llvm-lto2. std::unique_ptr ResolutionFile; /// Tunable parameters for passes in the default pipelines. PipelineTuningOptions PTO; /// The following callbacks deal with tasks, which normally represent the /// entire optimization and code generation pipeline for what will become a /// single native object file. Each task has a unique identifier between 0 and /// getMaxTasks()-1, which is supplied to the callback via the Task parameter. /// A task represents the entire pipeline for ThinLTO and regular /// (non-parallel) LTO, but a parallel code generation task will be split into /// N tasks before code generation, where N is the parallelism level. /// /// LTO may decide to stop processing a task at any time, for example if the /// module is empty or if a module hook (see below) returns false. For this /// reason, the client should not expect to receive exactly getMaxTasks() /// native object files. /// A module hook may be used by a linker to perform actions during the LTO /// pipeline. For example, a linker may use this function to implement /// -save-temps. If this function returns false, any further processing for /// that task is aborted. /// /// Module hooks must be thread safe with respect to the linker's internal /// data structures. A module hook will never be called concurrently from /// multiple threads with the same task ID, or the same module. /// /// Note that in out-of-process backend scenarios, none of the hooks will be /// called for ThinLTO tasks. using ModuleHookFn = std::function; /// This module hook is called after linking (regular LTO) or loading /// (ThinLTO) the module, before modifying it. ModuleHookFn PreOptModuleHook; /// This hook is called after promoting any internal functions /// (ThinLTO-specific). ModuleHookFn PostPromoteModuleHook; /// This hook is called after internalizing the module. ModuleHookFn PostInternalizeModuleHook; /// This hook is called after importing from other modules (ThinLTO-specific). ModuleHookFn PostImportModuleHook; /// This module hook is called after optimization is complete. ModuleHookFn PostOptModuleHook; /// This module hook is called before code generation. It is similar to the /// PostOptModuleHook, but for parallel code generation it is called after /// splitting the module. ModuleHookFn PreCodeGenModuleHook; /// A combined index hook is called after all per-module indexes have been /// combined (ThinLTO-specific). It can be used to implement -save-temps for /// the combined index. /// /// If this function returns false, any further processing for ThinLTO tasks /// is aborted. /// /// It is called regardless of whether the backend is in-process, although it /// is not called from individual backend processes. using CombinedIndexHookFn = std::function &GUIDPreservedSymbols)>; CombinedIndexHookFn CombinedIndexHook; /// This is a convenience function that configures this Config object to write /// temporary files named after the given OutputFileName for each of the LTO /// phases to disk. A client can use this function to implement -save-temps. /// /// FIXME: Temporary files derived from ThinLTO backends are currently named /// after the input file name, rather than the output file name, when /// UseInputModulePath is set to true. /// /// Specifically, it (1) sets each of the above module hooks and the combined /// index hook to a function that calls the hook function (if any) that was /// present in the appropriate field when the addSaveTemps function was /// called, and writes the module to a bitcode file with a name prefixed by /// the given output file name, and (2) creates a resolution file whose name /// is prefixed by the given output file name and sets ResolutionFile to its /// file handle. Error addSaveTemps(std::string OutputFileName, bool UseInputModulePath = false); }; struct LTOLLVMDiagnosticHandler : public DiagnosticHandler { DiagnosticHandlerFunction *Fn; LTOLLVMDiagnosticHandler(DiagnosticHandlerFunction *DiagHandlerFn) : Fn(DiagHandlerFn) {} bool handleDiagnostics(const DiagnosticInfo &DI) override { (*Fn)(DI); return true; } }; /// A derived class of LLVMContext that initializes itself according to a given /// Config object. The purpose of this class is to tie ownership of the /// diagnostic handler to the context, as opposed to the Config object (which /// may be ephemeral). // FIXME: This should not be required as diagnostic handler is not callback. struct LTOLLVMContext : LLVMContext { LTOLLVMContext(const Config &C) : DiagHandler(C.DiagHandler) { setDiscardValueNames(C.ShouldDiscardValueNames); enableDebugTypeODRUniquing(); setDiagnosticHandler( std::make_unique(&DiagHandler), true); } DiagnosticHandlerFunction DiagHandler; }; } } #endif diff --git a/llvm/lib/Analysis/CallPrinter.cpp b/llvm/lib/Analysis/CallPrinter.cpp index c3922d556023..872a91ad7cbf 100644 --- a/llvm/lib/Analysis/CallPrinter.cpp +++ b/llvm/lib/Analysis/CallPrinter.cpp @@ -1,309 +1,309 @@ //===- CallPrinter.cpp - DOT printer for call graph -----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines '-dot-callgraph', which emit a callgraph..dot // containing the call graph of a module. // // There is also a pass available to directly call dotty ('-view-callgraph'). // //===----------------------------------------------------------------------===// #include "llvm/Analysis/CallPrinter.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/DOTGraphTraitsPass.h" #include "llvm/Analysis/HeatUtils.h" #include "llvm/Support/CommandLine.h" #include "llvm/InitializePasses.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" using namespace llvm; // This option shows static (relative) call counts. // FIXME: // Need to show real counts when profile data is available static cl::opt ShowHeatColors("callgraph-heat-colors", cl::init(false), cl::Hidden, cl::desc("Show heat colors in call-graph")); static cl::opt ShowEdgeWeight("callgraph-show-weights", cl::init(false), cl::Hidden, cl::desc("Show edges labeled with weights")); static cl::opt CallMultiGraph("callgraph-multigraph", cl::init(false), cl::Hidden, cl::desc("Show call-multigraph (do not remove parallel edges)")); static cl::opt CallGraphDotFilenamePrefix( "callgraph-dot-filename-prefix", cl::Hidden, cl::desc("The prefix used for the CallGraph dot file names.")); namespace llvm { class CallGraphDOTInfo { private: Module *M; CallGraph *CG; DenseMap Freq; uint64_t MaxFreq; public: std::function LookupBFI; CallGraphDOTInfo(Module *M, CallGraph *CG, function_ref LookupBFI) : M(M), CG(CG), LookupBFI(LookupBFI) { MaxFreq = 0; for (auto F = M->getFunctionList().begin(); F != M->getFunctionList().end(); ++F) { uint64_t localSumFreq = 0; SmallSet Callers; for (User *U : (*F).users()) if (isa(U)) Callers.insert(cast(U)->getFunction()); for (auto iter = Callers.begin() ; iter != Callers.end() ; ++iter) localSumFreq += getNumOfCalls((**iter), *F); if (localSumFreq >= MaxFreq) MaxFreq = localSumFreq; Freq[&*F] = localSumFreq; } if (!CallMultiGraph) removeParallelEdges(); } Module *getModule() const { return M; } CallGraph *getCallGraph() const { return CG; } uint64_t getFreq(const Function *F) { return Freq[F]; } uint64_t getMaxFreq() { return MaxFreq; } private: void removeParallelEdges() { for (auto &I : (*CG)) { CallGraphNode *Node = I.second.get(); bool FoundParallelEdge = true; while (FoundParallelEdge) { SmallSet Visited; FoundParallelEdge = false; for (auto CI = Node->begin(), CE = Node->end(); CI != CE; CI++) { if (!(Visited.insert(CI->second->getFunction())).second) { FoundParallelEdge = true; Node->removeCallEdge(CI); break; } } } } } }; template <> struct GraphTraits : public GraphTraits { static NodeRef getEntryNode(CallGraphDOTInfo *CGInfo) { // Start at the external node! return CGInfo->getCallGraph()->getExternalCallingNode(); } typedef std::pair> PairTy; static const CallGraphNode *CGGetValuePtr(const PairTy &P) { return P.second.get(); } // nodes_iterator/begin/end - Allow iteration over all nodes in the graph typedef mapped_iterator nodes_iterator; static nodes_iterator nodes_begin(CallGraphDOTInfo *CGInfo) { return nodes_iterator(CGInfo->getCallGraph()->begin(), &CGGetValuePtr); } static nodes_iterator nodes_end(CallGraphDOTInfo *CGInfo) { return nodes_iterator(CGInfo->getCallGraph()->end(), &CGGetValuePtr); } }; template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} static std::string getGraphName(CallGraphDOTInfo *CGInfo) { return "Call graph: " + std::string(CGInfo->getModule()->getModuleIdentifier()); } static bool isNodeHidden(const CallGraphNode *Node, const CallGraphDOTInfo *CGInfo) { if (CallMultiGraph || Node->getFunction()) return false; return true; } std::string getNodeLabel(const CallGraphNode *Node, CallGraphDOTInfo *CGInfo) { if (Node == CGInfo->getCallGraph()->getExternalCallingNode()) return "external caller"; if (Node == CGInfo->getCallGraph()->getCallsExternalNode()) return "external callee"; if (Function *Func = Node->getFunction()) return std::string(Func->getName()); return "external node"; } static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) { return P.second; } // nodes_iterator/begin/end - Allow iteration over all nodes in the graph typedef mapped_iterator nodes_iterator; std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I, CallGraphDOTInfo *CGInfo) { if (!ShowEdgeWeight) return ""; Function *Caller = Node->getFunction(); if (Caller == nullptr || Caller->isDeclaration()) return ""; Function *Callee = (*I)->getFunction(); if (Callee == nullptr) return ""; uint64_t Counter = getNumOfCalls(*Caller, *Callee); double Width = 1 + 2 * (double(Counter) / CGInfo->getMaxFreq()); std::string Attrs = "label=\"" + std::to_string(Counter) + "\" penwidth=" + std::to_string(Width); return Attrs; } std::string getNodeAttributes(const CallGraphNode *Node, CallGraphDOTInfo *CGInfo) { Function *F = Node->getFunction(); if (F == nullptr) return ""; - std::string attrs = ""; + std::string attrs; if (ShowHeatColors) { uint64_t freq = CGInfo->getFreq(F); std::string color = getHeatColor(freq, CGInfo->getMaxFreq()); std::string edgeColor = (freq <= (CGInfo->getMaxFreq() / 2)) ? getHeatColor(0) : getHeatColor(1); attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" + color + "80\""; } return attrs; } }; } // end llvm namespace namespace { // Viewer class CallGraphViewer : public ModulePass { public: static char ID; CallGraphViewer() : ModulePass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnModule(Module &M) override; }; void CallGraphViewer::getAnalysisUsage(AnalysisUsage &AU) const { ModulePass::getAnalysisUsage(AU); AU.addRequired(); AU.setPreservesAll(); } bool CallGraphViewer::runOnModule(Module &M) { auto LookupBFI = [this](Function &F) { return &this->getAnalysis(F).getBFI(); }; CallGraph CG(M); CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); std::string Title = DOTGraphTraits::getGraphName(&CFGInfo); ViewGraph(&CFGInfo, "callgraph", true, Title); return false; } // DOT Printer class CallGraphDOTPrinter : public ModulePass { public: static char ID; CallGraphDOTPrinter() : ModulePass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override; bool runOnModule(Module &M) override; }; void CallGraphDOTPrinter::getAnalysisUsage(AnalysisUsage &AU) const { ModulePass::getAnalysisUsage(AU); AU.addRequired(); AU.setPreservesAll(); } bool CallGraphDOTPrinter::runOnModule(Module &M) { auto LookupBFI = [this](Function &F) { return &this->getAnalysis(F).getBFI(); }; std::string Filename; if (!CallGraphDotFilenamePrefix.empty()) Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot"); else Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); errs() << "Writing '" << Filename << "'..."; std::error_code EC; raw_fd_ostream File(Filename, EC, sys::fs::F_Text); CallGraph CG(M); CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); if (!EC) WriteGraph(File, &CFGInfo); else errs() << " error opening file for writing!"; errs() << "\n"; return false; } } // end anonymous namespace char CallGraphViewer::ID = 0; INITIALIZE_PASS(CallGraphViewer, "view-callgraph", "View call graph", false, false) char CallGraphDOTPrinter::ID = 0; INITIALIZE_PASS(CallGraphDOTPrinter, "dot-callgraph", "Print call graph to 'dot' file", false, false) // Create methods available outside of this file, to use them // "include/llvm/LinkAllPasses.h". Otherwise the pass would be deleted by // the link time optimization. ModulePass *llvm::createCallGraphViewerPass() { return new CallGraphViewer(); } ModulePass *llvm::createCallGraphDOTPrinterPass() { return new CallGraphDOTPrinter(); } diff --git a/llvm/lib/Analysis/ConstraintSystem.cpp b/llvm/lib/Analysis/ConstraintSystem.cpp index 1d582802f8e7..9739c6af5769 100644 --- a/llvm/lib/Analysis/ConstraintSystem.cpp +++ b/llvm/lib/Analysis/ConstraintSystem.cpp @@ -1,158 +1,158 @@ //===- ConstraintSytem.cpp - A system of linear constraints. ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Analysis/ConstraintSystem.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/MathExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Debug.h" #include #include using namespace llvm; #define DEBUG_TYPE "constraint-system" bool ConstraintSystem::eliminateUsingFM() { // Implementation of Fourier–Motzkin elimination, with some tricks from the // paper Pugh, William. "The Omega test: a fast and practical integer // programming algorithm for dependence // analysis." // Supercomputing'91: Proceedings of the 1991 ACM/ // IEEE conference on Supercomputing. IEEE, 1991. assert(!Constraints.empty() && "should only be called for non-empty constraint systems"); unsigned NumVariables = Constraints[0].size(); SmallVector, 4> NewSystem; unsigned NumConstraints = Constraints.size(); uint32_t NewGCD = 1; // FIXME do not use copy for (unsigned R1 = 0; R1 < NumConstraints; R1++) { if (Constraints[R1][1] == 0) { SmallVector NR; NR.push_back(Constraints[R1][0]); for (unsigned i = 2; i < NumVariables; i++) { NR.push_back(Constraints[R1][i]); } NewSystem.push_back(std::move(NR)); continue; } // FIXME do not use copy for (unsigned R2 = R1 + 1; R2 < NumConstraints; R2++) { if (R1 == R2) continue; // FIXME: can we do better than just dropping things here? if (Constraints[R2][1] == 0) continue; if ((Constraints[R1][1] < 0 && Constraints[R2][1] < 0) || (Constraints[R1][1] > 0 && Constraints[R2][1] > 0)) continue; unsigned LowerR = R1; unsigned UpperR = R2; if (Constraints[UpperR][1] < 0) std::swap(LowerR, UpperR); SmallVector NR; for (unsigned I = 0; I < NumVariables; I++) { if (I == 1) continue; int64_t M1, M2, N; if (MulOverflow(Constraints[UpperR][I], ((-1) * Constraints[LowerR][1] / GCD), M1)) return false; if (MulOverflow(Constraints[LowerR][I], (Constraints[UpperR][1] / GCD), M2)) return false; if (AddOverflow(M1, M2, N)) return false; NR.push_back(N); NewGCD = APIntOps::GreatestCommonDivisor({32, (uint32_t)NR.back()}, {32, NewGCD}) .getZExtValue(); } NewSystem.push_back(std::move(NR)); // Give up if the new system gets too big. if (NewSystem.size() > 500) return false; } } Constraints = std::move(NewSystem); GCD = NewGCD; return true; } bool ConstraintSystem::mayHaveSolutionImpl() { while (!Constraints.empty() && Constraints[0].size() > 1) { if (!eliminateUsingFM()) return true; } if (Constraints.empty() || Constraints[0].size() > 1) return true; return all_of(Constraints, [](auto &R) { return R[0] >= 0; }); } void ConstraintSystem::dump(ArrayRef Names) const { if (Constraints.empty()) return; for (auto &Row : Constraints) { SmallVector Parts; for (unsigned I = 1, S = Row.size(); I < S; ++I) { if (Row[I] == 0) continue; - std::string Coefficient = ""; + std::string Coefficient; if (Row[I] != 1) Coefficient = std::to_string(Row[I]) + " * "; Parts.push_back(Coefficient + Names[I - 1]); } assert(!Parts.empty() && "need to have at least some parts"); LLVM_DEBUG(dbgs() << join(Parts, std::string(" + ")) << " <= " << std::to_string(Row[0]) << "\n"); } } void ConstraintSystem::dump() const { SmallVector Names; for (unsigned i = 1; i < Constraints.back().size(); ++i) Names.push_back("x" + std::to_string(i)); LLVM_DEBUG(dbgs() << "---\n"); dump(Names); } bool ConstraintSystem::mayHaveSolution() { LLVM_DEBUG(dump()); bool HasSolution = mayHaveSolutionImpl(); LLVM_DEBUG(dbgs() << (HasSolution ? "sat" : "unsat") << "\n"); return HasSolution; } bool ConstraintSystem::isConditionImplied(SmallVector R) { // If all variable coefficients are 0, we have 'C >= 0'. If the constant is >= // 0, R is always true, regardless of the system. if (all_of(makeArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; })) return R[0] >= 0; // If there is no solution with the negation of R added to the system, the // condition must hold based on the existing constraints. R = ConstraintSystem::negate(R); auto NewSystem = *this; NewSystem.addVariableRow(R); return !NewSystem.mayHaveSolution(); } diff --git a/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp b/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp index f32278d07052..25fae5487187 100644 --- a/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp +++ b/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp @@ -1,261 +1,261 @@ //===- llvm/CodeGen/GlobalISel/InstructionSelect.cpp - InstructionSelect ---==// // // 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 // //===----------------------------------------------------------------------===// /// \file /// This file implements the InstructionSelect class. //===----------------------------------------------------------------------===// #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/Twine.h" #include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/Config/config.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Target/TargetMachine.h" #define DEBUG_TYPE "instruction-select" using namespace llvm; #ifdef LLVM_GISEL_COV_PREFIX static cl::opt CoveragePrefix("gisel-coverage-prefix", cl::init(LLVM_GISEL_COV_PREFIX), cl::desc("Record GlobalISel rule coverage files of this " "prefix if instrumentation was generated")); #else -static const std::string CoveragePrefix = ""; +static const std::string CoveragePrefix; #endif char InstructionSelect::ID = 0; INITIALIZE_PASS_BEGIN(InstructionSelect, DEBUG_TYPE, "Select target instructions out of generic instructions", false, false) INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis) INITIALIZE_PASS_END(InstructionSelect, DEBUG_TYPE, "Select target instructions out of generic instructions", false, false) InstructionSelect::InstructionSelect() : MachineFunctionPass(ID) { } void InstructionSelect::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.addPreserved(); getSelectionDAGFallbackAnalysisUsage(AU); MachineFunctionPass::getAnalysisUsage(AU); } bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) { // If the ISel pipeline failed, do not bother running that pass. if (MF.getProperties().hasProperty( MachineFunctionProperties::Property::FailedISel)) return false; LLVM_DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n'); GISelKnownBits &KB = getAnalysis().get(MF); const TargetPassConfig &TPC = getAnalysis(); InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector(); CodeGenCoverage CoverageInfo; assert(ISel && "Cannot work without InstructionSelector"); ISel->setupMF(MF, KB, CoverageInfo); // An optimization remark emitter. Used to report failures. MachineOptimizationRemarkEmitter MORE(MF, /*MBFI=*/nullptr); // FIXME: There are many other MF/MFI fields we need to initialize. MachineRegisterInfo &MRI = MF.getRegInfo(); #ifndef NDEBUG // Check that our input is fully legal: we require the function to have the // Legalized property, so it should be. // FIXME: This should be in the MachineVerifier, as the RegBankSelected // property check already is. if (!DisableGISelLegalityCheck) if (const MachineInstr *MI = machineFunctionIsIllegal(MF)) { reportGISelFailure(MF, TPC, MORE, "gisel-select", "instruction is not legal", *MI); return false; } // FIXME: We could introduce new blocks and will need to fix the outer loop. // Until then, keep track of the number of blocks to assert that we don't. const size_t NumBlocks = MF.size(); #endif for (MachineBasicBlock *MBB : post_order(&MF)) { if (MBB->empty()) continue; // Select instructions in reverse block order. We permit erasing so have // to resort to manually iterating and recognizing the begin (rend) case. bool ReachedBegin = false; for (auto MII = std::prev(MBB->end()), Begin = MBB->begin(); !ReachedBegin;) { #ifndef NDEBUG // Keep track of the insertion range for debug printing. const auto AfterIt = std::next(MII); #endif // Select this instruction. MachineInstr &MI = *MII; // And have our iterator point to the next instruction, if there is one. if (MII == Begin) ReachedBegin = true; else --MII; LLVM_DEBUG(dbgs() << "Selecting: \n " << MI); // We could have folded this instruction away already, making it dead. // If so, erase it. if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << "Is dead; erasing.\n"); MI.eraseFromParentAndMarkDBGValuesForRemoval(); continue; } if (!ISel->select(MI)) { // FIXME: It would be nice to dump all inserted instructions. It's // not obvious how, esp. considering select() can insert after MI. reportGISelFailure(MF, TPC, MORE, "gisel-select", "cannot select", MI); return false; } // Dump the range of instructions that MI expanded into. LLVM_DEBUG({ auto InsertedBegin = ReachedBegin ? MBB->begin() : std::next(MII); dbgs() << "Into:\n"; for (auto &InsertedMI : make_range(InsertedBegin, AfterIt)) dbgs() << " " << InsertedMI; dbgs() << '\n'; }); } } for (MachineBasicBlock &MBB : MF) { if (MBB.empty()) continue; // Try to find redundant copies b/w vregs of the same register class. bool ReachedBegin = false; for (auto MII = std::prev(MBB.end()), Begin = MBB.begin(); !ReachedBegin;) { // Select this instruction. MachineInstr &MI = *MII; // And have our iterator point to the next instruction, if there is one. if (MII == Begin) ReachedBegin = true; else --MII; if (MI.getOpcode() != TargetOpcode::COPY) continue; Register SrcReg = MI.getOperand(1).getReg(); Register DstReg = MI.getOperand(0).getReg(); if (Register::isVirtualRegister(SrcReg) && Register::isVirtualRegister(DstReg)) { auto SrcRC = MRI.getRegClass(SrcReg); auto DstRC = MRI.getRegClass(DstReg); if (SrcRC == DstRC) { MRI.replaceRegWith(DstReg, SrcReg); MI.eraseFromParent(); } } } } #ifndef NDEBUG const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); // Now that selection is complete, there are no more generic vregs. Verify // that the size of the now-constrained vreg is unchanged and that it has a // register class. for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) { unsigned VReg = Register::index2VirtReg(I); MachineInstr *MI = nullptr; if (!MRI.def_empty(VReg)) MI = &*MRI.def_instr_begin(VReg); else if (!MRI.use_empty(VReg)) MI = &*MRI.use_instr_begin(VReg); if (!MI) continue; const TargetRegisterClass *RC = MRI.getRegClassOrNull(VReg); if (!RC) { reportGISelFailure(MF, TPC, MORE, "gisel-select", "VReg has no regclass after selection", *MI); return false; } const LLT Ty = MRI.getType(VReg); if (Ty.isValid() && Ty.getSizeInBits() > TRI.getRegSizeInBits(*RC)) { reportGISelFailure( MF, TPC, MORE, "gisel-select", "VReg's low-level type and register class have different sizes", *MI); return false; } } if (MF.size() != NumBlocks) { MachineOptimizationRemarkMissed R("gisel-select", "GISelFailure", MF.getFunction().getSubprogram(), /*MBB=*/nullptr); R << "inserting blocks is not supported yet"; reportGISelFailure(MF, TPC, MORE, R); return false; } #endif // Determine if there are any calls in this machine function. Ported from // SelectionDAG. MachineFrameInfo &MFI = MF.getFrameInfo(); for (const auto &MBB : MF) { if (MFI.hasCalls() && MF.hasInlineAsm()) break; for (const auto &MI : MBB) { if ((MI.isCall() && !MI.isReturn()) || MI.isStackAligningInlineAsm()) MFI.setHasCalls(true); if (MI.isInlineAsm()) MF.setHasInlineAsm(true); } } // FIXME: FinalizeISel pass calls finalizeLowering, so it's called twice. auto &TLI = *MF.getSubtarget().getTargetLowering(); TLI.finalizeLowering(MF); LLVM_DEBUG({ dbgs() << "Rules covered by selecting function: " << MF.getName() << ":"; for (auto RuleID : CoverageInfo.covered()) dbgs() << " id" << RuleID; dbgs() << "\n\n"; }); CoverageInfo.emit(CoveragePrefix, TLI.getTargetMachine().getTarget().getBackendName()); // If we successfully selected the function nothing is going to use the vreg // types after us (otherwise MIRPrinter would need them). Make sure the types // disappear. MRI.clearVirtRegTypes(); // FIXME: Should we accurately track changes? return true; } diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 7bc5c98b89bc..32e8393a88e5 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1,2341 +1,2341 @@ //===- llvm/CodeGen/TargetLoweringObjectFileImpl.cpp - Object File Info ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements classes used to handle lowerings specific to common // object file formats. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/CodeGen/BasicBlockSectionUtils.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/IR/Comdat.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalObject.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Mangler.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/PseudoProbe.h" #include "llvm/IR/Type.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCValue.h" #include "llvm/MC/SectionKind.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include #include using namespace llvm; using namespace dwarf; static void GetObjCImageInfo(Module &M, unsigned &Version, unsigned &Flags, StringRef &Section) { SmallVector ModuleFlags; M.getModuleFlagsMetadata(ModuleFlags); for (const auto &MFE: ModuleFlags) { // Ignore flags with 'Require' behaviour. if (MFE.Behavior == Module::Require) continue; StringRef Key = MFE.Key->getString(); if (Key == "Objective-C Image Info Version") { Version = mdconst::extract(MFE.Val)->getZExtValue(); } else if (Key == "Objective-C Garbage Collection" || Key == "Objective-C GC Only" || Key == "Objective-C Is Simulated" || Key == "Objective-C Class Properties" || Key == "Objective-C Image Swift Version") { Flags |= mdconst::extract(MFE.Val)->getZExtValue(); } else if (Key == "Objective-C Image Info Section") { Section = cast(MFE.Val)->getString(); } // Backend generates L_OBJC_IMAGE_INFO from Swift ABI version + major + minor + // "Objective-C Garbage Collection". else if (Key == "Swift ABI Version") { Flags |= (mdconst::extract(MFE.Val)->getZExtValue()) << 8; } else if (Key == "Swift Major Version") { Flags |= (mdconst::extract(MFE.Val)->getZExtValue()) << 24; } else if (Key == "Swift Minor Version") { Flags |= (mdconst::extract(MFE.Val)->getZExtValue()) << 16; } } } //===----------------------------------------------------------------------===// // ELF //===----------------------------------------------------------------------===// TargetLoweringObjectFileELF::TargetLoweringObjectFileELF() : TargetLoweringObjectFile() { SupportDSOLocalEquivalentLowering = true; } void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx, const TargetMachine &TgtM) { TargetLoweringObjectFile::Initialize(Ctx, TgtM); CodeModel::Model CM = TgtM.getCodeModel(); InitializeELF(TgtM.Options.UseInitArray); switch (TgtM.getTargetTriple().getArch()) { case Triple::arm: case Triple::armeb: case Triple::thumb: case Triple::thumbeb: if (Ctx.getAsmInfo()->getExceptionHandlingType() == ExceptionHandling::ARM) break; // Fallthrough if not using EHABI LLVM_FALLTHROUGH; case Triple::ppc: case Triple::ppcle: case Triple::x86: PersonalityEncoding = isPositionIndependent() ? dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_absptr; LSDAEncoding = isPositionIndependent() ? dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_absptr; TTypeEncoding = isPositionIndependent() ? dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_absptr; break; case Triple::x86_64: if (isPositionIndependent()) { PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | ((CM == CodeModel::Small || CM == CodeModel::Medium) ? dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_sdata8); LSDAEncoding = dwarf::DW_EH_PE_pcrel | (CM == CodeModel::Small ? dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_sdata8); TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | ((CM == CodeModel::Small || CM == CodeModel::Medium) ? dwarf::DW_EH_PE_sdata8 : dwarf::DW_EH_PE_sdata4); } else { PersonalityEncoding = (CM == CodeModel::Small || CM == CodeModel::Medium) ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr; LSDAEncoding = (CM == CodeModel::Small) ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr; TTypeEncoding = (CM == CodeModel::Small) ? dwarf::DW_EH_PE_udata4 : dwarf::DW_EH_PE_absptr; } break; case Triple::hexagon: PersonalityEncoding = dwarf::DW_EH_PE_absptr; LSDAEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; if (isPositionIndependent()) { PersonalityEncoding |= dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel; LSDAEncoding |= dwarf::DW_EH_PE_pcrel; TTypeEncoding |= dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel; } break; case Triple::aarch64: case Triple::aarch64_be: case Triple::aarch64_32: // The small model guarantees static code/data size < 4GB, but not where it // will be in memory. Most of these could end up >2GB away so even a signed // pc-relative 32-bit address is insufficient, theoretically. if (isPositionIndependent()) { PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata8; LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata8; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata8; } else { PersonalityEncoding = dwarf::DW_EH_PE_absptr; LSDAEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; } break; case Triple::lanai: LSDAEncoding = dwarf::DW_EH_PE_absptr; PersonalityEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; break; case Triple::mips: case Triple::mipsel: case Triple::mips64: case Triple::mips64el: // MIPS uses indirect pointer to refer personality functions and types, so // that the eh_frame section can be read-only. DW.ref.personality will be // generated for relocation. PersonalityEncoding = dwarf::DW_EH_PE_indirect; // FIXME: The N64 ABI probably ought to use DW_EH_PE_sdata8 but we can't // identify N64 from just a triple. TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; // We don't support PC-relative LSDA references in GAS so we use the default // DW_EH_PE_absptr for those. // FreeBSD must be explicit about the data size and using pcrel since it's // assembler/linker won't do the automatic conversion that the Linux tools // do. if (TgtM.getTargetTriple().isOSFreeBSD()) { PersonalityEncoding |= dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } break; case Triple::ppc64: case Triple::ppc64le: PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_udata8; LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_udata8; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_udata8; break; case Triple::sparcel: case Triple::sparc: if (isPositionIndependent()) { LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } else { LSDAEncoding = dwarf::DW_EH_PE_absptr; PersonalityEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; } CallSiteEncoding = dwarf::DW_EH_PE_udata4; break; case Triple::riscv32: case Triple::riscv64: LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; CallSiteEncoding = dwarf::DW_EH_PE_udata4; break; case Triple::sparcv9: LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; if (isPositionIndependent()) { PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } else { PersonalityEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; } break; case Triple::systemz: // All currently-defined code models guarantee that 4-byte PC-relative // values will be in range. if (isPositionIndependent()) { PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; LSDAEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } else { PersonalityEncoding = dwarf::DW_EH_PE_absptr; LSDAEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; } break; default: break; } } void TargetLoweringObjectFileELF::emitModuleMetadata(MCStreamer &Streamer, Module &M) const { auto &C = getContext(); if (NamedMDNode *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) { auto *S = C.getELFSection(".linker-options", ELF::SHT_LLVM_LINKER_OPTIONS, ELF::SHF_EXCLUDE); Streamer.SwitchSection(S); for (const auto *Operand : LinkerOptions->operands()) { if (cast(Operand)->getNumOperands() != 2) report_fatal_error("invalid llvm.linker.options"); for (const auto &Option : cast(Operand)->operands()) { Streamer.emitBytes(cast(Option)->getString()); Streamer.emitInt8(0); } } } if (NamedMDNode *DependentLibraries = M.getNamedMetadata("llvm.dependent-libraries")) { auto *S = C.getELFSection(".deplibs", ELF::SHT_LLVM_DEPENDENT_LIBRARIES, ELF::SHF_MERGE | ELF::SHF_STRINGS, 1, ""); Streamer.SwitchSection(S); for (const auto *Operand : DependentLibraries->operands()) { Streamer.emitBytes( cast(cast(Operand)->getOperand(0))->getString()); Streamer.emitInt8(0); } } if (NamedMDNode *FuncInfo = M.getNamedMetadata(PseudoProbeDescMetadataName)) { // Emit a descriptor for every function including functions that have an // available external linkage. We may not want this for imported functions // that has code in another thinLTO module but we don't have a good way to // tell them apart from inline functions defined in header files. Therefore // we put each descriptor in a separate comdat section and rely on the // linker to deduplicate. for (const auto *Operand : FuncInfo->operands()) { const auto *MD = cast(Operand); auto *GUID = mdconst::dyn_extract(MD->getOperand(0)); auto *Hash = mdconst::dyn_extract(MD->getOperand(1)); auto *Name = cast(MD->getOperand(2)); auto *S = C.getObjectFileInfo()->getPseudoProbeDescSection( TM->getFunctionSections() ? Name->getString() : StringRef()); Streamer.SwitchSection(S); Streamer.emitInt64(GUID->getZExtValue()); Streamer.emitInt64(Hash->getZExtValue()); Streamer.emitULEB128IntValue(Name->getString().size()); Streamer.emitBytes(Name->getString()); } } unsigned Version = 0; unsigned Flags = 0; StringRef Section; GetObjCImageInfo(M, Version, Flags, Section); if (!Section.empty()) { auto *S = C.getELFSection(Section, ELF::SHT_PROGBITS, ELF::SHF_ALLOC); Streamer.SwitchSection(S); Streamer.emitLabel(C.getOrCreateSymbol(StringRef("OBJC_IMAGE_INFO"))); Streamer.emitInt32(Version); Streamer.emitInt32(Flags); Streamer.AddBlankLine(); } emitCGProfileMetadata(Streamer, M); } MCSymbol *TargetLoweringObjectFileELF::getCFIPersonalitySymbol( const GlobalValue *GV, const TargetMachine &TM, MachineModuleInfo *MMI) const { unsigned Encoding = getPersonalityEncoding(); if ((Encoding & 0x80) == DW_EH_PE_indirect) return getContext().getOrCreateSymbol(StringRef("DW.ref.") + TM.getSymbol(GV)->getName()); if ((Encoding & 0x70) == DW_EH_PE_absptr) return TM.getSymbol(GV); report_fatal_error("We do not support this DWARF encoding yet!"); } void TargetLoweringObjectFileELF::emitPersonalityValue( MCStreamer &Streamer, const DataLayout &DL, const MCSymbol *Sym) const { SmallString<64> NameData("DW.ref."); NameData += Sym->getName(); MCSymbolELF *Label = cast(getContext().getOrCreateSymbol(NameData)); Streamer.emitSymbolAttribute(Label, MCSA_Hidden); Streamer.emitSymbolAttribute(Label, MCSA_Weak); unsigned Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE | ELF::SHF_GROUP; MCSection *Sec = getContext().getELFNamedSection(".data", Label->getName(), ELF::SHT_PROGBITS, Flags, 0); unsigned Size = DL.getPointerSize(); Streamer.SwitchSection(Sec); Streamer.emitValueToAlignment(DL.getPointerABIAlignment(0).value()); Streamer.emitSymbolAttribute(Label, MCSA_ELF_TypeObject); const MCExpr *E = MCConstantExpr::create(Size, getContext()); Streamer.emitELFSize(Label, E); Streamer.emitLabel(Label); Streamer.emitSymbolValue(Sym, Size); } const MCExpr *TargetLoweringObjectFileELF::getTTypeGlobalReference( const GlobalValue *GV, unsigned Encoding, const TargetMachine &TM, MachineModuleInfo *MMI, MCStreamer &Streamer) const { if (Encoding & DW_EH_PE_indirect) { MachineModuleInfoELF &ELFMMI = MMI->getObjFileInfo(); MCSymbol *SSym = getSymbolWithGlobalValueBase(GV, ".DW.stub", TM); // Add information about the stub reference to ELFMMI so that the stub // gets emitted by the asmprinter. MachineModuleInfoImpl::StubValueTy &StubSym = ELFMMI.getGVStubEntry(SSym); if (!StubSym.getPointer()) { MCSymbol *Sym = TM.getSymbol(GV); StubSym = MachineModuleInfoImpl::StubValueTy(Sym, !GV->hasLocalLinkage()); } return TargetLoweringObjectFile:: getTTypeReference(MCSymbolRefExpr::create(SSym, getContext()), Encoding & ~DW_EH_PE_indirect, Streamer); } return TargetLoweringObjectFile::getTTypeGlobalReference(GV, Encoding, TM, MMI, Streamer); } static SectionKind getELFKindForNamedSection(StringRef Name, SectionKind K) { // N.B.: The defaults used in here are not the same ones used in MC. // We follow gcc, MC follows gas. For example, given ".section .eh_frame", // both gas and MC will produce a section with no flags. Given // section(".eh_frame") gcc will produce: // // .section .eh_frame,"a",@progbits if (Name == getInstrProfSectionName(IPSK_covmap, Triple::ELF, /*AddSegmentInfo=*/false) || Name == getInstrProfSectionName(IPSK_covfun, Triple::ELF, /*AddSegmentInfo=*/false) || Name == ".llvmbc" || Name == ".llvmcmd") return SectionKind::getMetadata(); if (Name.empty() || Name[0] != '.') return K; // Default implementation based on some magic section names. if (Name == ".bss" || Name.startswith(".bss.") || Name.startswith(".gnu.linkonce.b.") || Name.startswith(".llvm.linkonce.b.") || Name == ".sbss" || Name.startswith(".sbss.") || Name.startswith(".gnu.linkonce.sb.") || Name.startswith(".llvm.linkonce.sb.")) return SectionKind::getBSS(); if (Name == ".tdata" || Name.startswith(".tdata.") || Name.startswith(".gnu.linkonce.td.") || Name.startswith(".llvm.linkonce.td.")) return SectionKind::getThreadData(); if (Name == ".tbss" || Name.startswith(".tbss.") || Name.startswith(".gnu.linkonce.tb.") || Name.startswith(".llvm.linkonce.tb.")) return SectionKind::getThreadBSS(); return K; } static unsigned getELFSectionType(StringRef Name, SectionKind K) { // Use SHT_NOTE for section whose name starts with ".note" to allow // emitting ELF notes from C variable declaration. // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77609 if (Name.startswith(".note")) return ELF::SHT_NOTE; if (Name == ".init_array") return ELF::SHT_INIT_ARRAY; if (Name == ".fini_array") return ELF::SHT_FINI_ARRAY; if (Name == ".preinit_array") return ELF::SHT_PREINIT_ARRAY; if (K.isBSS() || K.isThreadBSS()) return ELF::SHT_NOBITS; return ELF::SHT_PROGBITS; } static unsigned getELFSectionFlags(SectionKind K) { unsigned Flags = 0; if (!K.isMetadata()) Flags |= ELF::SHF_ALLOC; if (K.isText()) Flags |= ELF::SHF_EXECINSTR; if (K.isExecuteOnly()) Flags |= ELF::SHF_ARM_PURECODE; if (K.isWriteable()) Flags |= ELF::SHF_WRITE; if (K.isThreadLocal()) Flags |= ELF::SHF_TLS; if (K.isMergeableCString() || K.isMergeableConst()) Flags |= ELF::SHF_MERGE; if (K.isMergeableCString()) Flags |= ELF::SHF_STRINGS; return Flags; } static const Comdat *getELFComdat(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); if (!C) return nullptr; if (C->getSelectionKind() != Comdat::Any) report_fatal_error("ELF COMDATs only support SelectionKind::Any, '" + C->getName() + "' cannot be lowered."); return C; } static const MCSymbolELF *getLinkedToSymbol(const GlobalObject *GO, const TargetMachine &TM) { MDNode *MD = GO->getMetadata(LLVMContext::MD_associated); if (!MD) return nullptr; const MDOperand &Op = MD->getOperand(0); if (!Op.get()) return nullptr; auto *VM = dyn_cast(Op); if (!VM) report_fatal_error("MD_associated operand is not ValueAsMetadata"); auto *OtherGV = dyn_cast(VM->getValue()); return OtherGV ? dyn_cast(TM.getSymbol(OtherGV)) : nullptr; } static unsigned getEntrySizeForKind(SectionKind Kind) { if (Kind.isMergeable1ByteCString()) return 1; else if (Kind.isMergeable2ByteCString()) return 2; else if (Kind.isMergeable4ByteCString()) return 4; else if (Kind.isMergeableConst4()) return 4; else if (Kind.isMergeableConst8()) return 8; else if (Kind.isMergeableConst16()) return 16; else if (Kind.isMergeableConst32()) return 32; else { // We shouldn't have mergeable C strings or mergeable constants that we // didn't handle above. assert(!Kind.isMergeableCString() && "unknown string width"); assert(!Kind.isMergeableConst() && "unknown data width"); return 0; } } /// Return the section prefix name used by options FunctionsSections and /// DataSections. static StringRef getSectionPrefixForGlobal(SectionKind Kind) { if (Kind.isText()) return ".text"; if (Kind.isReadOnly()) return ".rodata"; if (Kind.isBSS()) return ".bss"; if (Kind.isThreadData()) return ".tdata"; if (Kind.isThreadBSS()) return ".tbss"; if (Kind.isData()) return ".data"; if (Kind.isReadOnlyWithRel()) return ".data.rel.ro"; llvm_unreachable("Unknown section kind"); } static SmallString<128> getELFSectionNameForGlobal(const GlobalObject *GO, SectionKind Kind, Mangler &Mang, const TargetMachine &TM, unsigned EntrySize, bool UniqueSectionName) { SmallString<128> Name; if (Kind.isMergeableCString()) { // We also need alignment here. // FIXME: this is getting the alignment of the character, not the // alignment of the global! Align Alignment = GO->getParent()->getDataLayout().getPreferredAlign( cast(GO)); std::string SizeSpec = ".rodata.str" + utostr(EntrySize) + "."; Name = SizeSpec + utostr(Alignment.value()); } else if (Kind.isMergeableConst()) { Name = ".rodata.cst"; Name += utostr(EntrySize); } else { Name = getSectionPrefixForGlobal(Kind); } bool HasPrefix = false; if (const auto *F = dyn_cast(GO)) { if (Optional Prefix = F->getSectionPrefix()) { raw_svector_ostream(Name) << '.' << *Prefix; HasPrefix = true; } } if (UniqueSectionName) { Name.push_back('.'); TM.getNameWithPrefix(Name, GO, Mang, /*MayAlwaysUsePrivate*/true); } else if (HasPrefix) Name.push_back('.'); return Name; } namespace { class LoweringDiagnosticInfo : public DiagnosticInfo { const Twine &Msg; public: LoweringDiagnosticInfo(const Twine &DiagMsg, DiagnosticSeverity Severity = DS_Error) : DiagnosticInfo(DK_Lowering, Severity), Msg(DiagMsg) {} void print(DiagnosticPrinter &DP) const override { DP << Msg; } }; } MCSection *TargetLoweringObjectFileELF::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { StringRef SectionName = GO->getSection(); // Check if '#pragma clang section' name is applicable. // Note that pragma directive overrides -ffunction-section, -fdata-section // and so section name is exactly as user specified and not uniqued. const GlobalVariable *GV = dyn_cast(GO); if (GV && GV->hasImplicitSection()) { auto Attrs = GV->getAttributes(); if (Attrs.hasAttribute("bss-section") && Kind.isBSS()) { SectionName = Attrs.getAttribute("bss-section").getValueAsString(); } else if (Attrs.hasAttribute("rodata-section") && Kind.isReadOnly()) { SectionName = Attrs.getAttribute("rodata-section").getValueAsString(); } else if (Attrs.hasAttribute("relro-section") && Kind.isReadOnlyWithRel()) { SectionName = Attrs.getAttribute("relro-section").getValueAsString(); } else if (Attrs.hasAttribute("data-section") && Kind.isData()) { SectionName = Attrs.getAttribute("data-section").getValueAsString(); } } const Function *F = dyn_cast(GO); if (F && F->hasFnAttribute("implicit-section-name")) { SectionName = F->getFnAttribute("implicit-section-name").getValueAsString(); } // Infer section flags from the section name if we can. Kind = getELFKindForNamedSection(SectionName, Kind); StringRef Group = ""; unsigned Flags = getELFSectionFlags(Kind); if (const Comdat *C = getELFComdat(GO)) { Group = C->getName(); Flags |= ELF::SHF_GROUP; } unsigned EntrySize = getEntrySizeForKind(Kind); // A section can have at most one associated section. Put each global with // MD_associated in a unique section. unsigned UniqueID = MCContext::GenericSectionID; const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM); if (GO->getMetadata(LLVMContext::MD_associated)) { UniqueID = NextUniqueID++; Flags |= ELF::SHF_LINK_ORDER; } else { if (getContext().getAsmInfo()->useIntegratedAssembler()) { // Symbols must be placed into sections with compatible entry // sizes. Generate unique sections for symbols that have not // been assigned to compatible sections. if (Flags & ELF::SHF_MERGE) { auto maybeID = getContext().getELFUniqueIDForEntsize(SectionName, Flags, EntrySize); if (maybeID) UniqueID = *maybeID; else { // If the user has specified the same section name as would be created // implicitly for this symbol e.g. .rodata.str1.1, then we don't need // to unique the section as the entry size for this symbol will be // compatible with implicitly created sections. SmallString<128> ImplicitSectionNameStem = getELFSectionNameForGlobal( GO, Kind, getMangler(), TM, EntrySize, false); if (!(getContext().isELFImplicitMergeableSectionNamePrefix( SectionName) && SectionName.startswith(ImplicitSectionNameStem))) UniqueID = NextUniqueID++; } } else { // We need to unique the section if the user has explicity // assigned a non-mergeable symbol to a section name for // a generic mergeable section. if (getContext().isELFGenericMergeableSection(SectionName)) { auto maybeID = getContext().getELFUniqueIDForEntsize( SectionName, Flags, EntrySize); UniqueID = maybeID ? *maybeID : NextUniqueID++; } } } else { // If two symbols with differing sizes end up in the same mergeable // section that section can be assigned an incorrect entry size. To avoid // this we usually put symbols of the same size into distinct mergeable // sections with the same name. Doing so relies on the ",unique ," // assembly feature. This feature is not avalible until bintuils // version 2.35 (https://sourceware.org/bugzilla/show_bug.cgi?id=25380). Flags &= ~ELF::SHF_MERGE; EntrySize = 0; } } MCSectionELF *Section = getContext().getELFSection( SectionName, getELFSectionType(SectionName, Kind), Flags, EntrySize, Group, UniqueID, LinkedToSym); // Make sure that we did not get some other section with incompatible sh_link. // This should not be possible due to UniqueID code above. assert(Section->getLinkedToSymbol() == LinkedToSym && "Associated symbol mismatch between sections"); if (!getContext().getAsmInfo()->useIntegratedAssembler()) { // If we are not using the integrated assembler then this symbol might have // been placed in an incompatible mergeable section. Emit an error if this // is the case to avoid creating broken output. if ((Section->getFlags() & ELF::SHF_MERGE) && (Section->getEntrySize() != getEntrySizeForKind(Kind))) GO->getContext().diagnose(LoweringDiagnosticInfo( "Symbol '" + GO->getName() + "' from module '" + (GO->getParent() ? GO->getParent()->getSourceFileName() : "unknown") + "' required a section with entry-size=" + Twine(getEntrySizeForKind(Kind)) + " but was placed in section '" + SectionName + "' with entry-size=" + Twine(Section->getEntrySize()) + ": Explicit assignment by pragma or attribute of an incompatible " "symbol to this section?")); } return Section; } static MCSectionELF *selectELFSectionForGlobal( MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang, const TargetMachine &TM, bool EmitUniqueSection, unsigned Flags, unsigned *NextUniqueID, const MCSymbolELF *AssociatedSymbol) { StringRef Group = ""; if (const Comdat *C = getELFComdat(GO)) { Flags |= ELF::SHF_GROUP; Group = C->getName(); } // Get the section entry size based on the kind. unsigned EntrySize = getEntrySizeForKind(Kind); bool UniqueSectionName = false; unsigned UniqueID = MCContext::GenericSectionID; if (EmitUniqueSection) { if (TM.getUniqueSectionNames()) { UniqueSectionName = true; } else { UniqueID = *NextUniqueID; (*NextUniqueID)++; } } SmallString<128> Name = getELFSectionNameForGlobal( GO, Kind, Mang, TM, EntrySize, UniqueSectionName); // Use 0 as the unique ID for execute-only text. if (Kind.isExecuteOnly()) UniqueID = 0; return Ctx.getELFSection(Name, getELFSectionType(Name, Kind), Flags, EntrySize, Group, UniqueID, AssociatedSymbol); } MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { unsigned Flags = getELFSectionFlags(Kind); // If we have -ffunction-section or -fdata-section then we should emit the // global value to a uniqued section specifically for it. bool EmitUniqueSection = false; if (!(Flags & ELF::SHF_MERGE) && !Kind.isCommon()) { if (Kind.isText()) EmitUniqueSection = TM.getFunctionSections(); else EmitUniqueSection = TM.getDataSections(); } EmitUniqueSection |= GO->hasComdat(); const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM); if (LinkedToSym) { EmitUniqueSection = true; Flags |= ELF::SHF_LINK_ORDER; } MCSectionELF *Section = selectELFSectionForGlobal( getContext(), GO, Kind, getMangler(), TM, EmitUniqueSection, Flags, &NextUniqueID, LinkedToSym); assert(Section->getLinkedToSymbol() == LinkedToSym); return Section; } MCSection *TargetLoweringObjectFileELF::getSectionForJumpTable( const Function &F, const TargetMachine &TM) const { // If the function can be removed, produce a unique section so that // the table doesn't prevent the removal. const Comdat *C = F.getComdat(); bool EmitUniqueSection = TM.getFunctionSections() || C; if (!EmitUniqueSection) return ReadOnlySection; return selectELFSectionForGlobal(getContext(), &F, SectionKind::getReadOnly(), getMangler(), TM, EmitUniqueSection, ELF::SHF_ALLOC, &NextUniqueID, /* AssociatedSymbol */ nullptr); } MCSection * TargetLoweringObjectFileELF::getSectionForLSDA(const Function &F, const TargetMachine &TM) const { // If neither COMDAT nor function sections, use the monolithic LSDA section. // Re-use this path if LSDASection is null as in the Arm EHABI. if (!LSDASection || (!F.hasComdat() && !TM.getFunctionSections())) return LSDASection; const auto *LSDA = cast(LSDASection); unsigned Flags = LSDA->getFlags(); StringRef Group; if (F.hasComdat()) { Group = F.getComdat()->getName(); Flags |= ELF::SHF_GROUP; } // Append the function name as the suffix like GCC, assuming // -funique-section-names applies to .gcc_except_table sections. if (TM.getUniqueSectionNames()) return getContext().getELFSection(LSDA->getName() + "." + F.getName(), LSDA->getType(), Flags, 0, Group, MCSection::NonUniqueID, nullptr); // Allocate a unique ID if function sections && (integrated assembler or GNU // as>=2.35). Note we could use SHF_LINK_ORDER to facilitate --gc-sections but // that would require that we know the linker is a modern LLD (12.0 or later). // GNU ld as of 2.35 does not support mixed SHF_LINK_ORDER & // non-SHF_LINK_ORDER components in an output section // https://sourceware.org/bugzilla/show_bug.cgi?id=26256 unsigned ID = TM.getFunctionSections() && getContext().getAsmInfo()->useIntegratedAssembler() ? NextUniqueID++ : MCSection::NonUniqueID; return getContext().getELFSection(LSDA->getName(), LSDA->getType(), Flags, 0, Group, ID, nullptr); } bool TargetLoweringObjectFileELF::shouldPutJumpTableInFunctionSection( bool UsesLabelDifference, const Function &F) const { // We can always create relative relocations, so use another section // that can be marked non-executable. return false; } /// Given a mergeable constant with the specified size and relocation /// information, return a section that it should be placed in. MCSection *TargetLoweringObjectFileELF::getSectionForConstant( const DataLayout &DL, SectionKind Kind, const Constant *C, Align &Alignment) const { if (Kind.isMergeableConst4() && MergeableConst4Section) return MergeableConst4Section; if (Kind.isMergeableConst8() && MergeableConst8Section) return MergeableConst8Section; if (Kind.isMergeableConst16() && MergeableConst16Section) return MergeableConst16Section; if (Kind.isMergeableConst32() && MergeableConst32Section) return MergeableConst32Section; if (Kind.isReadOnly()) return ReadOnlySection; assert(Kind.isReadOnlyWithRel() && "Unknown section kind"); return DataRelROSection; } /// Returns a unique section for the given machine basic block. MCSection *TargetLoweringObjectFileELF::getSectionForMachineBasicBlock( const Function &F, const MachineBasicBlock &MBB, const TargetMachine &TM) const { assert(MBB.isBeginSection() && "Basic block does not start a section!"); unsigned UniqueID = MCContext::GenericSectionID; // For cold sections use the .text.split. prefix along with the parent // function name. All cold blocks for the same function go to the same // section. Similarly all exception blocks are grouped by symbol name // under the .text.eh prefix. For regular sections, we either use a unique // name, or a unique ID for the section. SmallString<128> Name; if (MBB.getSectionID() == MBBSectionID::ColdSectionID) { Name += BBSectionsColdTextPrefix; Name += MBB.getParent()->getName(); } else if (MBB.getSectionID() == MBBSectionID::ExceptionSectionID) { Name += ".text.eh."; Name += MBB.getParent()->getName(); } else { Name += MBB.getParent()->getSection()->getName(); if (TM.getUniqueBasicBlockSectionNames()) { Name += "."; Name += MBB.getSymbol()->getName(); } else { UniqueID = NextUniqueID++; } } unsigned Flags = ELF::SHF_ALLOC | ELF::SHF_EXECINSTR; - std::string GroupName = ""; + std::string GroupName; if (F.hasComdat()) { Flags |= ELF::SHF_GROUP; GroupName = F.getComdat()->getName().str(); } return getContext().getELFSection(Name, ELF::SHT_PROGBITS, Flags, 0 /* Entry Size */, GroupName, UniqueID, nullptr); } static MCSectionELF *getStaticStructorSection(MCContext &Ctx, bool UseInitArray, bool IsCtor, unsigned Priority, const MCSymbol *KeySym) { std::string Name; unsigned Type; unsigned Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; StringRef COMDAT = KeySym ? KeySym->getName() : ""; if (KeySym) Flags |= ELF::SHF_GROUP; if (UseInitArray) { if (IsCtor) { Type = ELF::SHT_INIT_ARRAY; Name = ".init_array"; } else { Type = ELF::SHT_FINI_ARRAY; Name = ".fini_array"; } if (Priority != 65535) { Name += '.'; Name += utostr(Priority); } } else { // The default scheme is .ctor / .dtor, so we have to invert the priority // numbering. if (IsCtor) Name = ".ctors"; else Name = ".dtors"; if (Priority != 65535) raw_string_ostream(Name) << format(".%05u", 65535 - Priority); Type = ELF::SHT_PROGBITS; } return Ctx.getELFSection(Name, Type, Flags, 0, COMDAT); } MCSection *TargetLoweringObjectFileELF::getStaticCtorSection( unsigned Priority, const MCSymbol *KeySym) const { return getStaticStructorSection(getContext(), UseInitArray, true, Priority, KeySym); } MCSection *TargetLoweringObjectFileELF::getStaticDtorSection( unsigned Priority, const MCSymbol *KeySym) const { return getStaticStructorSection(getContext(), UseInitArray, false, Priority, KeySym); } const MCExpr *TargetLoweringObjectFileELF::lowerRelativeReference( const GlobalValue *LHS, const GlobalValue *RHS, const TargetMachine &TM) const { // We may only use a PLT-relative relocation to refer to unnamed_addr // functions. if (!LHS->hasGlobalUnnamedAddr() || !LHS->getValueType()->isFunctionTy()) return nullptr; // Basic sanity checks. if (LHS->getType()->getPointerAddressSpace() != 0 || RHS->getType()->getPointerAddressSpace() != 0 || LHS->isThreadLocal() || RHS->isThreadLocal()) return nullptr; return MCBinaryExpr::createSub( MCSymbolRefExpr::create(TM.getSymbol(LHS), PLTRelativeVariantKind, getContext()), MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext()); } const MCExpr *TargetLoweringObjectFileELF::lowerDSOLocalEquivalent( const DSOLocalEquivalent *Equiv, const TargetMachine &TM) const { assert(supportDSOLocalEquivalentLowering()); const auto *GV = Equiv->getGlobalValue(); // A PLT entry is not needed for dso_local globals. if (GV->isDSOLocal() || GV->isImplicitDSOLocal()) return MCSymbolRefExpr::create(TM.getSymbol(GV), getContext()); return MCSymbolRefExpr::create(TM.getSymbol(GV), PLTRelativeVariantKind, getContext()); } MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const { // Use ".GCC.command.line" since this feature is to support clang's // -frecord-gcc-switches which in turn attempts to mimic GCC's switch of the // same name. return getContext().getELFSection(".GCC.command.line", ELF::SHT_PROGBITS, ELF::SHF_MERGE | ELF::SHF_STRINGS, 1, ""); } void TargetLoweringObjectFileELF::InitializeELF(bool UseInitArray_) { UseInitArray = UseInitArray_; MCContext &Ctx = getContext(); if (!UseInitArray) { StaticCtorSection = Ctx.getELFSection(".ctors", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_WRITE); StaticDtorSection = Ctx.getELFSection(".dtors", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_WRITE); return; } StaticCtorSection = Ctx.getELFSection(".init_array", ELF::SHT_INIT_ARRAY, ELF::SHF_WRITE | ELF::SHF_ALLOC); StaticDtorSection = Ctx.getELFSection(".fini_array", ELF::SHT_FINI_ARRAY, ELF::SHF_WRITE | ELF::SHF_ALLOC); } //===----------------------------------------------------------------------===// // MachO //===----------------------------------------------------------------------===// TargetLoweringObjectFileMachO::TargetLoweringObjectFileMachO() : TargetLoweringObjectFile() { SupportIndirectSymViaGOTPCRel = true; } void TargetLoweringObjectFileMachO::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFile::Initialize(Ctx, TM); if (TM.getRelocationModel() == Reloc::Static) { StaticCtorSection = Ctx.getMachOSection("__TEXT", "__constructor", 0, SectionKind::getData()); StaticDtorSection = Ctx.getMachOSection("__TEXT", "__destructor", 0, SectionKind::getData()); } else { StaticCtorSection = Ctx.getMachOSection("__DATA", "__mod_init_func", MachO::S_MOD_INIT_FUNC_POINTERS, SectionKind::getData()); StaticDtorSection = Ctx.getMachOSection("__DATA", "__mod_term_func", MachO::S_MOD_TERM_FUNC_POINTERS, SectionKind::getData()); } PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; LSDAEncoding = dwarf::DW_EH_PE_pcrel; TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } void TargetLoweringObjectFileMachO::emitModuleMetadata(MCStreamer &Streamer, Module &M) const { // Emit the linker options if present. if (auto *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) { for (const auto *Option : LinkerOptions->operands()) { SmallVector StrOptions; for (const auto &Piece : cast(Option)->operands()) StrOptions.push_back(std::string(cast(Piece)->getString())); Streamer.emitLinkerOptions(StrOptions); } } unsigned VersionVal = 0; unsigned ImageInfoFlags = 0; StringRef SectionVal; GetObjCImageInfo(M, VersionVal, ImageInfoFlags, SectionVal); // The section is mandatory. If we don't have it, then we don't have GC info. if (SectionVal.empty()) return; StringRef Segment, Section; unsigned TAA = 0, StubSize = 0; bool TAAParsed; std::string ErrorCode = MCSectionMachO::ParseSectionSpecifier(SectionVal, Segment, Section, TAA, TAAParsed, StubSize); if (!ErrorCode.empty()) // If invalid, report the error with report_fatal_error. report_fatal_error("Invalid section specifier '" + Section + "': " + ErrorCode + "."); // Get the section. MCSectionMachO *S = getContext().getMachOSection( Segment, Section, TAA, StubSize, SectionKind::getData()); Streamer.SwitchSection(S); Streamer.emitLabel(getContext(). getOrCreateSymbol(StringRef("L_OBJC_IMAGE_INFO"))); Streamer.emitInt32(VersionVal); Streamer.emitInt32(ImageInfoFlags); Streamer.AddBlankLine(); } static void checkMachOComdat(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); if (!C) return; report_fatal_error("MachO doesn't support COMDATs, '" + C->getName() + "' cannot be lowered."); } MCSection *TargetLoweringObjectFileMachO::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { // Parse the section specifier and create it if valid. StringRef Segment, Section; unsigned TAA = 0, StubSize = 0; bool TAAParsed; checkMachOComdat(GO); std::string ErrorCode = MCSectionMachO::ParseSectionSpecifier(GO->getSection(), Segment, Section, TAA, TAAParsed, StubSize); if (!ErrorCode.empty()) { // If invalid, report the error with report_fatal_error. report_fatal_error("Global variable '" + GO->getName() + "' has an invalid section specifier '" + GO->getSection() + "': " + ErrorCode + "."); } // Get the section. MCSectionMachO *S = getContext().getMachOSection(Segment, Section, TAA, StubSize, Kind); // If TAA wasn't set by ParseSectionSpecifier() above, // use the value returned by getMachOSection() as a default. if (!TAAParsed) TAA = S->getTypeAndAttributes(); // Okay, now that we got the section, verify that the TAA & StubSize agree. // If the user declared multiple globals with different section flags, we need // to reject it here. if (S->getTypeAndAttributes() != TAA || S->getStubSize() != StubSize) { // If invalid, report the error with report_fatal_error. report_fatal_error("Global variable '" + GO->getName() + "' section type or attributes does not match previous" " section specifier"); } return S; } MCSection *TargetLoweringObjectFileMachO::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { checkMachOComdat(GO); // Handle thread local data. if (Kind.isThreadBSS()) return TLSBSSSection; if (Kind.isThreadData()) return TLSDataSection; if (Kind.isText()) return GO->isWeakForLinker() ? TextCoalSection : TextSection; // If this is weak/linkonce, put this in a coalescable section, either in text // or data depending on if it is writable. if (GO->isWeakForLinker()) { if (Kind.isReadOnly()) return ConstTextCoalSection; if (Kind.isReadOnlyWithRel()) return ConstDataCoalSection; return DataCoalSection; } // FIXME: Alignment check should be handled by section classifier. if (Kind.isMergeable1ByteCString() && GO->getParent()->getDataLayout().getPreferredAlign( cast(GO)) < Align(32)) return CStringSection; // Do not put 16-bit arrays in the UString section if they have an // externally visible label, this runs into issues with certain linker // versions. if (Kind.isMergeable2ByteCString() && !GO->hasExternalLinkage() && GO->getParent()->getDataLayout().getPreferredAlign( cast(GO)) < Align(32)) return UStringSection; // With MachO only variables whose corresponding symbol starts with 'l' or // 'L' can be merged, so we only try merging GVs with private linkage. if (GO->hasPrivateLinkage() && Kind.isMergeableConst()) { if (Kind.isMergeableConst4()) return FourByteConstantSection; if (Kind.isMergeableConst8()) return EightByteConstantSection; if (Kind.isMergeableConst16()) return SixteenByteConstantSection; } // Otherwise, if it is readonly, but not something we can specially optimize, // just drop it in .const. if (Kind.isReadOnly()) return ReadOnlySection; // If this is marked const, put it into a const section. But if the dynamic // linker needs to write to it, put it in the data segment. if (Kind.isReadOnlyWithRel()) return ConstDataSection; // Put zero initialized globals with strong external linkage in the // DATA, __common section with the .zerofill directive. if (Kind.isBSSExtern()) return DataCommonSection; // Put zero initialized globals with local linkage in __DATA,__bss directive // with the .zerofill directive (aka .lcomm). if (Kind.isBSSLocal()) return DataBSSSection; // Otherwise, just drop the variable in the normal data section. return DataSection; } MCSection *TargetLoweringObjectFileMachO::getSectionForConstant( const DataLayout &DL, SectionKind Kind, const Constant *C, Align &Alignment) const { // If this constant requires a relocation, we have to put it in the data // segment, not in the text segment. if (Kind.isData() || Kind.isReadOnlyWithRel()) return ConstDataSection; if (Kind.isMergeableConst4()) return FourByteConstantSection; if (Kind.isMergeableConst8()) return EightByteConstantSection; if (Kind.isMergeableConst16()) return SixteenByteConstantSection; return ReadOnlySection; // .const } const MCExpr *TargetLoweringObjectFileMachO::getTTypeGlobalReference( const GlobalValue *GV, unsigned Encoding, const TargetMachine &TM, MachineModuleInfo *MMI, MCStreamer &Streamer) const { // The mach-o version of this method defaults to returning a stub reference. if (Encoding & DW_EH_PE_indirect) { MachineModuleInfoMachO &MachOMMI = MMI->getObjFileInfo(); MCSymbol *SSym = getSymbolWithGlobalValueBase(GV, "$non_lazy_ptr", TM); // Add information about the stub reference to MachOMMI so that the stub // gets emitted by the asmprinter. MachineModuleInfoImpl::StubValueTy &StubSym = MachOMMI.getGVStubEntry(SSym); if (!StubSym.getPointer()) { MCSymbol *Sym = TM.getSymbol(GV); StubSym = MachineModuleInfoImpl::StubValueTy(Sym, !GV->hasLocalLinkage()); } return TargetLoweringObjectFile:: getTTypeReference(MCSymbolRefExpr::create(SSym, getContext()), Encoding & ~DW_EH_PE_indirect, Streamer); } return TargetLoweringObjectFile::getTTypeGlobalReference(GV, Encoding, TM, MMI, Streamer); } MCSymbol *TargetLoweringObjectFileMachO::getCFIPersonalitySymbol( const GlobalValue *GV, const TargetMachine &TM, MachineModuleInfo *MMI) const { // The mach-o version of this method defaults to returning a stub reference. MachineModuleInfoMachO &MachOMMI = MMI->getObjFileInfo(); MCSymbol *SSym = getSymbolWithGlobalValueBase(GV, "$non_lazy_ptr", TM); // Add information about the stub reference to MachOMMI so that the stub // gets emitted by the asmprinter. MachineModuleInfoImpl::StubValueTy &StubSym = MachOMMI.getGVStubEntry(SSym); if (!StubSym.getPointer()) { MCSymbol *Sym = TM.getSymbol(GV); StubSym = MachineModuleInfoImpl::StubValueTy(Sym, !GV->hasLocalLinkage()); } return SSym; } const MCExpr *TargetLoweringObjectFileMachO::getIndirectSymViaGOTPCRel( const GlobalValue *GV, const MCSymbol *Sym, const MCValue &MV, int64_t Offset, MachineModuleInfo *MMI, MCStreamer &Streamer) const { // Although MachO 32-bit targets do not explicitly have a GOTPCREL relocation // as 64-bit do, we replace the GOT equivalent by accessing the final symbol // through a non_lazy_ptr stub instead. One advantage is that it allows the // computation of deltas to final external symbols. Example: // // _extgotequiv: // .long _extfoo // // _delta: // .long _extgotequiv-_delta // // is transformed to: // // _delta: // .long L_extfoo$non_lazy_ptr-(_delta+0) // // .section __IMPORT,__pointers,non_lazy_symbol_pointers // L_extfoo$non_lazy_ptr: // .indirect_symbol _extfoo // .long 0 // // The indirect symbol table (and sections of non_lazy_symbol_pointers type) // may point to both local (same translation unit) and global (other // translation units) symbols. Example: // // .section __DATA,__pointers,non_lazy_symbol_pointers // L1: // .indirect_symbol _myGlobal // .long 0 // L2: // .indirect_symbol _myLocal // .long _myLocal // // If the symbol is local, instead of the symbol's index, the assembler // places the constant INDIRECT_SYMBOL_LOCAL into the indirect symbol table. // Then the linker will notice the constant in the table and will look at the // content of the symbol. MachineModuleInfoMachO &MachOMMI = MMI->getObjFileInfo(); MCContext &Ctx = getContext(); // The offset must consider the original displacement from the base symbol // since 32-bit targets don't have a GOTPCREL to fold the PC displacement. Offset = -MV.getConstant(); const MCSymbol *BaseSym = &MV.getSymB()->getSymbol(); // Access the final symbol via sym$non_lazy_ptr and generate the appropriated // non_lazy_ptr stubs. SmallString<128> Name; StringRef Suffix = "$non_lazy_ptr"; Name += MMI->getModule()->getDataLayout().getPrivateGlobalPrefix(); Name += Sym->getName(); Name += Suffix; MCSymbol *Stub = Ctx.getOrCreateSymbol(Name); MachineModuleInfoImpl::StubValueTy &StubSym = MachOMMI.getGVStubEntry(Stub); if (!StubSym.getPointer()) StubSym = MachineModuleInfoImpl::StubValueTy(const_cast(Sym), !GV->hasLocalLinkage()); const MCExpr *BSymExpr = MCSymbolRefExpr::create(BaseSym, MCSymbolRefExpr::VK_None, Ctx); const MCExpr *LHS = MCSymbolRefExpr::create(Stub, MCSymbolRefExpr::VK_None, Ctx); if (!Offset) return MCBinaryExpr::createSub(LHS, BSymExpr, Ctx); const MCExpr *RHS = MCBinaryExpr::createAdd(BSymExpr, MCConstantExpr::create(Offset, Ctx), Ctx); return MCBinaryExpr::createSub(LHS, RHS, Ctx); } static bool canUsePrivateLabel(const MCAsmInfo &AsmInfo, const MCSection &Section) { if (!AsmInfo.isSectionAtomizableBySymbols(Section)) return true; // If it is not dead stripped, it is safe to use private labels. const MCSectionMachO &SMO = cast(Section); if (SMO.hasAttribute(MachO::S_ATTR_NO_DEAD_STRIP)) return true; return false; } void TargetLoweringObjectFileMachO::getNameWithPrefix( SmallVectorImpl &OutName, const GlobalValue *GV, const TargetMachine &TM) const { bool CannotUsePrivateLabel = true; if (auto *GO = GV->getBaseObject()) { SectionKind GOKind = TargetLoweringObjectFile::getKindForGlobal(GO, TM); const MCSection *TheSection = SectionForGlobal(GO, GOKind, TM); CannotUsePrivateLabel = !canUsePrivateLabel(*TM.getMCAsmInfo(), *TheSection); } getMangler().getNameWithPrefix(OutName, GV, CannotUsePrivateLabel); } //===----------------------------------------------------------------------===// // COFF //===----------------------------------------------------------------------===// static unsigned getCOFFSectionFlags(SectionKind K, const TargetMachine &TM) { unsigned Flags = 0; bool isThumb = TM.getTargetTriple().getArch() == Triple::thumb; if (K.isMetadata()) Flags |= COFF::IMAGE_SCN_MEM_DISCARDABLE; else if (K.isText()) Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_CNT_CODE | (isThumb ? COFF::IMAGE_SCN_MEM_16BIT : (COFF::SectionCharacteristics)0); else if (K.isBSS()) Flags |= COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; else if (K.isThreadLocal()) Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; else if (K.isReadOnly() || K.isReadOnlyWithRel()) Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ; else if (K.isWriteable()) Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; return Flags; } static const GlobalValue *getComdatGVForCOFF(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); assert(C && "expected GV to have a Comdat!"); StringRef ComdatGVName = C->getName(); const GlobalValue *ComdatGV = GV->getParent()->getNamedValue(ComdatGVName); if (!ComdatGV) report_fatal_error("Associative COMDAT symbol '" + ComdatGVName + "' does not exist."); if (ComdatGV->getComdat() != C) report_fatal_error("Associative COMDAT symbol '" + ComdatGVName + "' is not a key for its COMDAT."); return ComdatGV; } static int getSelectionForCOFF(const GlobalValue *GV) { if (const Comdat *C = GV->getComdat()) { const GlobalValue *ComdatKey = getComdatGVForCOFF(GV); if (const auto *GA = dyn_cast(ComdatKey)) ComdatKey = GA->getBaseObject(); if (ComdatKey == GV) { switch (C->getSelectionKind()) { case Comdat::Any: return COFF::IMAGE_COMDAT_SELECT_ANY; case Comdat::ExactMatch: return COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH; case Comdat::Largest: return COFF::IMAGE_COMDAT_SELECT_LARGEST; case Comdat::NoDuplicates: return COFF::IMAGE_COMDAT_SELECT_NODUPLICATES; case Comdat::SameSize: return COFF::IMAGE_COMDAT_SELECT_SAME_SIZE; } } else { return COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE; } } return 0; } MCSection *TargetLoweringObjectFileCOFF::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { int Selection = 0; unsigned Characteristics = getCOFFSectionFlags(Kind, TM); StringRef Name = GO->getSection(); StringRef COMDATSymName = ""; if (GO->hasComdat()) { Selection = getSelectionForCOFF(GO); const GlobalValue *ComdatGV; if (Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) ComdatGV = getComdatGVForCOFF(GO); else ComdatGV = GO; if (!ComdatGV->hasPrivateLinkage()) { MCSymbol *Sym = TM.getSymbol(ComdatGV); COMDATSymName = Sym->getName(); Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; } else { Selection = 0; } } return getContext().getCOFFSection(Name, Characteristics, Kind, COMDATSymName, Selection); } static StringRef getCOFFSectionNameForUniqueGlobal(SectionKind Kind) { if (Kind.isText()) return ".text"; if (Kind.isBSS()) return ".bss"; if (Kind.isThreadLocal()) return ".tls$"; if (Kind.isReadOnly() || Kind.isReadOnlyWithRel()) return ".rdata"; return ".data"; } MCSection *TargetLoweringObjectFileCOFF::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { // If we have -ffunction-sections then we should emit the global value to a // uniqued section specifically for it. bool EmitUniquedSection; if (Kind.isText()) EmitUniquedSection = TM.getFunctionSections(); else EmitUniquedSection = TM.getDataSections(); if ((EmitUniquedSection && !Kind.isCommon()) || GO->hasComdat()) { SmallString<256> Name = getCOFFSectionNameForUniqueGlobal(Kind); unsigned Characteristics = getCOFFSectionFlags(Kind, TM); Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; int Selection = getSelectionForCOFF(GO); if (!Selection) Selection = COFF::IMAGE_COMDAT_SELECT_NODUPLICATES; const GlobalValue *ComdatGV; if (GO->hasComdat()) ComdatGV = getComdatGVForCOFF(GO); else ComdatGV = GO; unsigned UniqueID = MCContext::GenericSectionID; if (EmitUniquedSection) UniqueID = NextUniqueID++; if (!ComdatGV->hasPrivateLinkage()) { MCSymbol *Sym = TM.getSymbol(ComdatGV); StringRef COMDATSymName = Sym->getName(); if (const auto *F = dyn_cast(GO)) if (Optional Prefix = F->getSectionPrefix()) raw_svector_ostream(Name) << '$' << *Prefix; // Append "$symbol" to the section name *before* IR-level mangling is // applied when targetting mingw. This is what GCC does, and the ld.bfd // COFF linker will not properly handle comdats otherwise. if (getTargetTriple().isWindowsGNUEnvironment()) raw_svector_ostream(Name) << '$' << ComdatGV->getName(); return getContext().getCOFFSection(Name, Characteristics, Kind, COMDATSymName, Selection, UniqueID); } else { SmallString<256> TmpData; getMangler().getNameWithPrefix(TmpData, GO, /*CannotUsePrivateLabel=*/true); return getContext().getCOFFSection(Name, Characteristics, Kind, TmpData, Selection, UniqueID); } } if (Kind.isText()) return TextSection; if (Kind.isThreadLocal()) return TLSDataSection; if (Kind.isReadOnly() || Kind.isReadOnlyWithRel()) return ReadOnlySection; // Note: we claim that common symbols are put in BSSSection, but they are // really emitted with the magic .comm directive, which creates a symbol table // entry but not a section. if (Kind.isBSS() || Kind.isCommon()) return BSSSection; return DataSection; } void TargetLoweringObjectFileCOFF::getNameWithPrefix( SmallVectorImpl &OutName, const GlobalValue *GV, const TargetMachine &TM) const { bool CannotUsePrivateLabel = false; if (GV->hasPrivateLinkage() && ((isa(GV) && TM.getFunctionSections()) || (isa(GV) && TM.getDataSections()))) CannotUsePrivateLabel = true; getMangler().getNameWithPrefix(OutName, GV, CannotUsePrivateLabel); } MCSection *TargetLoweringObjectFileCOFF::getSectionForJumpTable( const Function &F, const TargetMachine &TM) const { // If the function can be removed, produce a unique section so that // the table doesn't prevent the removal. const Comdat *C = F.getComdat(); bool EmitUniqueSection = TM.getFunctionSections() || C; if (!EmitUniqueSection) return ReadOnlySection; // FIXME: we should produce a symbol for F instead. if (F.hasPrivateLinkage()) return ReadOnlySection; MCSymbol *Sym = TM.getSymbol(&F); StringRef COMDATSymName = Sym->getName(); SectionKind Kind = SectionKind::getReadOnly(); StringRef SecName = getCOFFSectionNameForUniqueGlobal(Kind); unsigned Characteristics = getCOFFSectionFlags(Kind, TM); Characteristics |= COFF::IMAGE_SCN_LNK_COMDAT; unsigned UniqueID = NextUniqueID++; return getContext().getCOFFSection( SecName, Characteristics, Kind, COMDATSymName, COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE, UniqueID); } void TargetLoweringObjectFileCOFF::emitModuleMetadata(MCStreamer &Streamer, Module &M) const { emitLinkerDirectives(Streamer, M); unsigned Version = 0; unsigned Flags = 0; StringRef Section; GetObjCImageInfo(M, Version, Flags, Section); if (!Section.empty()) { auto &C = getContext(); auto *S = C.getCOFFSection(Section, COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, SectionKind::getReadOnly()); Streamer.SwitchSection(S); Streamer.emitLabel(C.getOrCreateSymbol(StringRef("OBJC_IMAGE_INFO"))); Streamer.emitInt32(Version); Streamer.emitInt32(Flags); Streamer.AddBlankLine(); } emitCGProfileMetadata(Streamer, M); } void TargetLoweringObjectFileCOFF::emitLinkerDirectives( MCStreamer &Streamer, Module &M) const { if (NamedMDNode *LinkerOptions = M.getNamedMetadata("llvm.linker.options")) { // Emit the linker options to the linker .drectve section. According to the // spec, this section is a space-separated string containing flags for // linker. MCSection *Sec = getDrectveSection(); Streamer.SwitchSection(Sec); for (const auto *Option : LinkerOptions->operands()) { for (const auto &Piece : cast(Option)->operands()) { // Lead with a space for consistency with our dllexport implementation. std::string Directive(" "); Directive.append(std::string(cast(Piece)->getString())); Streamer.emitBytes(Directive); } } } // Emit /EXPORT: flags for each exported global as necessary. std::string Flags; for (const GlobalValue &GV : M.global_values()) { raw_string_ostream OS(Flags); emitLinkerFlagsForGlobalCOFF(OS, &GV, getTargetTriple(), getMangler()); OS.flush(); if (!Flags.empty()) { Streamer.SwitchSection(getDrectveSection()); Streamer.emitBytes(Flags); } Flags.clear(); } // Emit /INCLUDE: flags for each used global as necessary. if (const auto *LU = M.getNamedGlobal("llvm.used")) { assert(LU->hasInitializer() && "expected llvm.used to have an initializer"); assert(isa(LU->getValueType()) && "expected llvm.used to be an array type"); if (const auto *A = cast(LU->getInitializer())) { for (const Value *Op : A->operands()) { const auto *GV = cast(Op->stripPointerCasts()); // Global symbols with internal or private linkage are not visible to // the linker, and thus would cause an error when the linker tried to // preserve the symbol due to the `/include:` directive. if (GV->hasLocalLinkage()) continue; raw_string_ostream OS(Flags); emitLinkerFlagsForUsedCOFF(OS, GV, getTargetTriple(), getMangler()); OS.flush(); if (!Flags.empty()) { Streamer.SwitchSection(getDrectveSection()); Streamer.emitBytes(Flags); } Flags.clear(); } } } } void TargetLoweringObjectFileCOFF::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFile::Initialize(Ctx, TM); this->TM = &TM; const Triple &T = TM.getTargetTriple(); if (T.isWindowsMSVCEnvironment() || T.isWindowsItaniumEnvironment()) { StaticCtorSection = Ctx.getCOFFSection(".CRT$XCU", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, SectionKind::getReadOnly()); StaticDtorSection = Ctx.getCOFFSection(".CRT$XTX", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, SectionKind::getReadOnly()); } else { StaticCtorSection = Ctx.getCOFFSection( ".ctors", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()); StaticDtorSection = Ctx.getCOFFSection( ".dtors", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()); } } static MCSectionCOFF *getCOFFStaticStructorSection(MCContext &Ctx, const Triple &T, bool IsCtor, unsigned Priority, const MCSymbol *KeySym, MCSectionCOFF *Default) { if (T.isWindowsMSVCEnvironment() || T.isWindowsItaniumEnvironment()) { // If the priority is the default, use .CRT$XCU, possibly associative. if (Priority == 65535) return Ctx.getAssociativeCOFFSection(Default, KeySym, 0); // Otherwise, we need to compute a new section name. Low priorities should // run earlier. The linker will sort sections ASCII-betically, and we need a // string that sorts between .CRT$XCA and .CRT$XCU. In the general case, we // make a name like ".CRT$XCT12345", since that runs before .CRT$XCU. Really // low priorities need to sort before 'L', since the CRT uses that // internally, so we use ".CRT$XCA00001" for them. SmallString<24> Name; raw_svector_ostream OS(Name); OS << ".CRT$X" << (IsCtor ? "C" : "T") << (Priority < 200 ? 'A' : 'T') << format("%05u", Priority); MCSectionCOFF *Sec = Ctx.getCOFFSection( Name, COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, SectionKind::getReadOnly()); return Ctx.getAssociativeCOFFSection(Sec, KeySym, 0); } std::string Name = IsCtor ? ".ctors" : ".dtors"; if (Priority != 65535) raw_string_ostream(Name) << format(".%05u", 65535 - Priority); return Ctx.getAssociativeCOFFSection( Ctx.getCOFFSection(Name, COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()), KeySym, 0); } MCSection *TargetLoweringObjectFileCOFF::getStaticCtorSection( unsigned Priority, const MCSymbol *KeySym) const { return getCOFFStaticStructorSection(getContext(), getTargetTriple(), true, Priority, KeySym, cast(StaticCtorSection)); } MCSection *TargetLoweringObjectFileCOFF::getStaticDtorSection( unsigned Priority, const MCSymbol *KeySym) const { return getCOFFStaticStructorSection(getContext(), getTargetTriple(), false, Priority, KeySym, cast(StaticDtorSection)); } const MCExpr *TargetLoweringObjectFileCOFF::lowerRelativeReference( const GlobalValue *LHS, const GlobalValue *RHS, const TargetMachine &TM) const { const Triple &T = TM.getTargetTriple(); if (T.isOSCygMing()) return nullptr; // Our symbols should exist in address space zero, cowardly no-op if // otherwise. if (LHS->getType()->getPointerAddressSpace() != 0 || RHS->getType()->getPointerAddressSpace() != 0) return nullptr; // Both ptrtoint instructions must wrap global objects: // - Only global variables are eligible for image relative relocations. // - The subtrahend refers to the special symbol __ImageBase, a GlobalVariable. // We expect __ImageBase to be a global variable without a section, externally // defined. // // It should look something like this: @__ImageBase = external constant i8 if (!isa(LHS) || !isa(RHS) || LHS->isThreadLocal() || RHS->isThreadLocal() || RHS->getName() != "__ImageBase" || !RHS->hasExternalLinkage() || cast(RHS)->hasInitializer() || RHS->hasSection()) return nullptr; return MCSymbolRefExpr::create(TM.getSymbol(LHS), MCSymbolRefExpr::VK_COFF_IMGREL32, getContext()); } static std::string APIntToHexString(const APInt &AI) { unsigned Width = (AI.getBitWidth() / 8) * 2; std::string HexString = AI.toString(16, /*Signed=*/false); llvm::transform(HexString, HexString.begin(), tolower); unsigned Size = HexString.size(); assert(Width >= Size && "hex string is too large!"); HexString.insert(HexString.begin(), Width - Size, '0'); return HexString; } static std::string scalarConstantToHexString(const Constant *C) { Type *Ty = C->getType(); if (isa(C)) { return APIntToHexString(APInt::getNullValue(Ty->getPrimitiveSizeInBits())); } else if (const auto *CFP = dyn_cast(C)) { return APIntToHexString(CFP->getValueAPF().bitcastToAPInt()); } else if (const auto *CI = dyn_cast(C)) { return APIntToHexString(CI->getValue()); } else { unsigned NumElements; if (auto *VTy = dyn_cast(Ty)) NumElements = cast(VTy)->getNumElements(); else NumElements = Ty->getArrayNumElements(); std::string HexString; for (int I = NumElements - 1, E = -1; I != E; --I) HexString += scalarConstantToHexString(C->getAggregateElement(I)); return HexString; } } MCSection *TargetLoweringObjectFileCOFF::getSectionForConstant( const DataLayout &DL, SectionKind Kind, const Constant *C, Align &Alignment) const { if (Kind.isMergeableConst() && C && getContext().getAsmInfo()->hasCOFFComdatConstants()) { // This creates comdat sections with the given symbol name, but unless // AsmPrinter::GetCPISymbol actually makes the symbol global, the symbol // will be created with a null storage class, which makes GNU binutils // error out. const unsigned Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_LNK_COMDAT; std::string COMDATSymName; if (Kind.isMergeableConst4()) { if (Alignment <= 4) { COMDATSymName = "__real@" + scalarConstantToHexString(C); Alignment = Align(4); } } else if (Kind.isMergeableConst8()) { if (Alignment <= 8) { COMDATSymName = "__real@" + scalarConstantToHexString(C); Alignment = Align(8); } } else if (Kind.isMergeableConst16()) { // FIXME: These may not be appropriate for non-x86 architectures. if (Alignment <= 16) { COMDATSymName = "__xmm@" + scalarConstantToHexString(C); Alignment = Align(16); } } else if (Kind.isMergeableConst32()) { if (Alignment <= 32) { COMDATSymName = "__ymm@" + scalarConstantToHexString(C); Alignment = Align(32); } } if (!COMDATSymName.empty()) return getContext().getCOFFSection(".rdata", Characteristics, Kind, COMDATSymName, COFF::IMAGE_COMDAT_SELECT_ANY); } return TargetLoweringObjectFile::getSectionForConstant(DL, Kind, C, Alignment); } //===----------------------------------------------------------------------===// // Wasm //===----------------------------------------------------------------------===// static const Comdat *getWasmComdat(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); if (!C) return nullptr; if (C->getSelectionKind() != Comdat::Any) report_fatal_error("WebAssembly COMDATs only support " "SelectionKind::Any, '" + C->getName() + "' cannot be " "lowered."); return C; } MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { // We don't support explict section names for functions in the wasm object // format. Each function has to be in its own unique section. if (isa(GO)) { return SelectSectionForGlobal(GO, Kind, TM); } StringRef Name = GO->getSection(); // Certain data sections we treat as named custom sections rather than // segments within the data section. // This could be avoided if all data segements (the wasm sense) were // represented as their own sections (in the llvm sense). // TODO(sbc): https://github.com/WebAssembly/tool-conventions/issues/138 if (Name == ".llvmcmd" || Name == ".llvmbc") Kind = SectionKind::getMetadata(); StringRef Group = ""; if (const Comdat *C = getWasmComdat(GO)) { Group = C->getName(); } MCSectionWasm* Section = getContext().getWasmSection(Name, Kind, Group, MCContext::GenericSectionID); return Section; } static MCSectionWasm *selectWasmSectionForGlobal( MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang, const TargetMachine &TM, bool EmitUniqueSection, unsigned *NextUniqueID) { StringRef Group = ""; if (const Comdat *C = getWasmComdat(GO)) { Group = C->getName(); } bool UniqueSectionNames = TM.getUniqueSectionNames(); SmallString<128> Name = getSectionPrefixForGlobal(Kind); if (const auto *F = dyn_cast(GO)) { const auto &OptionalPrefix = F->getSectionPrefix(); if (OptionalPrefix) raw_svector_ostream(Name) << '.' << *OptionalPrefix; } if (EmitUniqueSection && UniqueSectionNames) { Name.push_back('.'); TM.getNameWithPrefix(Name, GO, Mang, true); } unsigned UniqueID = MCContext::GenericSectionID; if (EmitUniqueSection && !UniqueSectionNames) { UniqueID = *NextUniqueID; (*NextUniqueID)++; } return Ctx.getWasmSection(Name, Kind, Group, UniqueID); } MCSection *TargetLoweringObjectFileWasm::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { if (Kind.isCommon()) report_fatal_error("mergable sections not supported yet on wasm"); // If we have -ffunction-section or -fdata-section then we should emit the // global value to a uniqued section specifically for it. bool EmitUniqueSection = false; if (Kind.isText()) EmitUniqueSection = TM.getFunctionSections(); else EmitUniqueSection = TM.getDataSections(); EmitUniqueSection |= GO->hasComdat(); return selectWasmSectionForGlobal(getContext(), GO, Kind, getMangler(), TM, EmitUniqueSection, &NextUniqueID); } bool TargetLoweringObjectFileWasm::shouldPutJumpTableInFunctionSection( bool UsesLabelDifference, const Function &F) const { // We can always create relative relocations, so use another section // that can be marked non-executable. return false; } const MCExpr *TargetLoweringObjectFileWasm::lowerRelativeReference( const GlobalValue *LHS, const GlobalValue *RHS, const TargetMachine &TM) const { // We may only use a PLT-relative relocation to refer to unnamed_addr // functions. if (!LHS->hasGlobalUnnamedAddr() || !LHS->getValueType()->isFunctionTy()) return nullptr; // Basic sanity checks. if (LHS->getType()->getPointerAddressSpace() != 0 || RHS->getType()->getPointerAddressSpace() != 0 || LHS->isThreadLocal() || RHS->isThreadLocal()) return nullptr; return MCBinaryExpr::createSub( MCSymbolRefExpr::create(TM.getSymbol(LHS), MCSymbolRefExpr::VK_None, getContext()), MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext()); } void TargetLoweringObjectFileWasm::InitializeWasm() { StaticCtorSection = getContext().getWasmSection(".init_array", SectionKind::getData()); // We don't use PersonalityEncoding and LSDAEncoding because we don't emit // .cfi directives. We use TTypeEncoding to encode typeinfo global variables. TTypeEncoding = dwarf::DW_EH_PE_absptr; } MCSection *TargetLoweringObjectFileWasm::getStaticCtorSection( unsigned Priority, const MCSymbol *KeySym) const { return Priority == UINT16_MAX ? StaticCtorSection : getContext().getWasmSection(".init_array." + utostr(Priority), SectionKind::getData()); } MCSection *TargetLoweringObjectFileWasm::getStaticDtorSection( unsigned Priority, const MCSymbol *KeySym) const { llvm_unreachable("@llvm.global_dtors should have been lowered already"); return nullptr; } //===----------------------------------------------------------------------===// // XCOFF //===----------------------------------------------------------------------===// bool TargetLoweringObjectFileXCOFF::ShouldEmitEHBlock( const MachineFunction *MF) { if (!MF->getLandingPads().empty()) return true; const Function &F = MF->getFunction(); if (!F.hasPersonalityFn() || !F.needsUnwindTableEntry()) return false; const Function *Per = dyn_cast(F.getPersonalityFn()->stripPointerCasts()); if (isNoOpWithoutInvoke(classifyEHPersonality(Per))) return false; return true; } MCSymbol * TargetLoweringObjectFileXCOFF::getEHInfoTableSymbol(const MachineFunction *MF) { return MF->getMMI().getContext().getOrCreateSymbol( "__ehinfo." + Twine(MF->getFunctionNumber())); } MCSymbol * TargetLoweringObjectFileXCOFF::getTargetSymbol(const GlobalValue *GV, const TargetMachine &TM) const { // We always use a qualname symbol for a GV that represents // a declaration, a function descriptor, or a common symbol. // If a GV represents a GlobalVariable and -fdata-sections is enabled, we // also return a qualname so that a label symbol could be avoided. // It is inherently ambiguous when the GO represents the address of a // function, as the GO could either represent a function descriptor or a // function entry point. We choose to always return a function descriptor // here. if (const GlobalObject *GO = dyn_cast(GV)) { if (GO->isDeclarationForLinker()) return cast(getSectionForExternalReference(GO, TM)) ->getQualNameSymbol(); SectionKind GOKind = getKindForGlobal(GO, TM); if (GOKind.isText()) return cast( getSectionForFunctionDescriptor(cast(GO), TM)) ->getQualNameSymbol(); if ((TM.getDataSections() && !GO->hasSection()) || GOKind.isCommon() || GOKind.isBSSLocal()) return cast(SectionForGlobal(GO, GOKind, TM)) ->getQualNameSymbol(); } // For all other cases, fall back to getSymbol to return the unqualified name. return nullptr; } MCSection *TargetLoweringObjectFileXCOFF::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { if (!GO->hasSection()) report_fatal_error("#pragma clang section is not yet supported"); StringRef SectionName = GO->getSection(); XCOFF::StorageMappingClass MappingClass; if (Kind.isText()) MappingClass = XCOFF::XMC_PR; else if (Kind.isData() || Kind.isReadOnlyWithRel() || Kind.isBSS()) MappingClass = XCOFF::XMC_RW; else if (Kind.isReadOnly()) MappingClass = XCOFF::XMC_RO; else report_fatal_error("XCOFF other section types not yet implemented."); return getContext().getXCOFFSection(SectionName, MappingClass, XCOFF::XTY_SD, Kind, /* MultiSymbolsAllowed*/ true); } MCSection *TargetLoweringObjectFileXCOFF::getSectionForExternalReference( const GlobalObject *GO, const TargetMachine &TM) const { assert(GO->isDeclarationForLinker() && "Tried to get ER section for a defined global."); SmallString<128> Name; getNameWithPrefix(Name, GO, TM); // Externals go into a csect of type ER. return getContext().getXCOFFSection( Name, isa(GO) ? XCOFF::XMC_DS : XCOFF::XMC_UA, XCOFF::XTY_ER, SectionKind::getMetadata()); } MCSection *TargetLoweringObjectFileXCOFF::SelectSectionForGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { // Common symbols go into a csect with matching name which will get mapped // into the .bss section. if (Kind.isBSSLocal() || Kind.isCommon()) { SmallString<128> Name; getNameWithPrefix(Name, GO, TM); return getContext().getXCOFFSection( Name, Kind.isBSSLocal() ? XCOFF::XMC_BS : XCOFF::XMC_RW, XCOFF::XTY_CM, Kind); } if (Kind.isMergeableCString()) { Align Alignment = GO->getParent()->getDataLayout().getPreferredAlign( cast(GO)); unsigned EntrySize = getEntrySizeForKind(Kind); std::string SizeSpec = ".rodata.str" + utostr(EntrySize) + "."; SmallString<128> Name; Name = SizeSpec + utostr(Alignment.value()); if (TM.getDataSections()) getNameWithPrefix(Name, GO, TM); return getContext().getXCOFFSection( Name, XCOFF::XMC_RO, XCOFF::XTY_SD, Kind, /* MultiSymbolsAllowed*/ !TM.getDataSections()); } if (Kind.isText()) { if (TM.getFunctionSections()) { return cast(getFunctionEntryPointSymbol(GO, TM)) ->getRepresentedCsect(); } return TextSection; } // TODO: We may put Kind.isReadOnlyWithRel() under option control, because // user may want to have read-only data with relocations placed into a // read-only section by the compiler. // For BSS kind, zero initialized data must be emitted to the .data section // because external linkage control sections that get mapped to the .bss // section will be linked as tentative defintions, which is only appropriate // for SectionKind::Common. if (Kind.isData() || Kind.isReadOnlyWithRel() || Kind.isBSS()) { if (TM.getDataSections()) { SmallString<128> Name; getNameWithPrefix(Name, GO, TM); return getContext().getXCOFFSection(Name, XCOFF::XMC_RW, XCOFF::XTY_SD, SectionKind::getData()); } return DataSection; } if (Kind.isReadOnly()) { if (TM.getDataSections()) { SmallString<128> Name; getNameWithPrefix(Name, GO, TM); return getContext().getXCOFFSection(Name, XCOFF::XMC_RO, XCOFF::XTY_SD, SectionKind::getReadOnly()); } return ReadOnlySection; } report_fatal_error("XCOFF other section types not yet implemented."); } MCSection *TargetLoweringObjectFileXCOFF::getSectionForJumpTable( const Function &F, const TargetMachine &TM) const { assert (!F.getComdat() && "Comdat not supported on XCOFF."); if (!TM.getFunctionSections()) return ReadOnlySection; // If the function can be removed, produce a unique section so that // the table doesn't prevent the removal. SmallString<128> NameStr(".rodata.jmp.."); getNameWithPrefix(NameStr, &F, TM); return getContext().getXCOFFSection(NameStr, XCOFF::XMC_RO, XCOFF::XTY_SD, SectionKind::getReadOnly()); } bool TargetLoweringObjectFileXCOFF::shouldPutJumpTableInFunctionSection( bool UsesLabelDifference, const Function &F) const { return false; } /// Given a mergeable constant with the specified size and relocation /// information, return a section that it should be placed in. MCSection *TargetLoweringObjectFileXCOFF::getSectionForConstant( const DataLayout &DL, SectionKind Kind, const Constant *C, Align &Alignment) const { //TODO: Enable emiting constant pool to unique sections when we support it. return ReadOnlySection; } void TargetLoweringObjectFileXCOFF::Initialize(MCContext &Ctx, const TargetMachine &TgtM) { TargetLoweringObjectFile::Initialize(Ctx, TgtM); TTypeEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_datarel | (TgtM.getTargetTriple().isArch32Bit() ? dwarf::DW_EH_PE_sdata4 : dwarf::DW_EH_PE_sdata8); PersonalityEncoding = 0; LSDAEncoding = 0; CallSiteEncoding = dwarf::DW_EH_PE_udata4; } MCSection *TargetLoweringObjectFileXCOFF::getStaticCtorSection( unsigned Priority, const MCSymbol *KeySym) const { report_fatal_error("no static constructor section on AIX"); } MCSection *TargetLoweringObjectFileXCOFF::getStaticDtorSection( unsigned Priority, const MCSymbol *KeySym) const { report_fatal_error("no static destructor section on AIX"); } const MCExpr *TargetLoweringObjectFileXCOFF::lowerRelativeReference( const GlobalValue *LHS, const GlobalValue *RHS, const TargetMachine &TM) const { report_fatal_error("XCOFF not yet implemented."); } XCOFF::StorageClass TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(const GlobalValue *GV) { assert(!isa(GV) && "GlobalIFunc is not supported on AIX."); switch (GV->getLinkage()) { case GlobalValue::InternalLinkage: case GlobalValue::PrivateLinkage: return XCOFF::C_HIDEXT; case GlobalValue::ExternalLinkage: case GlobalValue::CommonLinkage: case GlobalValue::AvailableExternallyLinkage: return XCOFF::C_EXT; case GlobalValue::ExternalWeakLinkage: case GlobalValue::LinkOnceAnyLinkage: case GlobalValue::LinkOnceODRLinkage: case GlobalValue::WeakAnyLinkage: case GlobalValue::WeakODRLinkage: return XCOFF::C_WEAKEXT; case GlobalValue::AppendingLinkage: report_fatal_error( "There is no mapping that implements AppendingLinkage for XCOFF."); } llvm_unreachable("Unknown linkage type!"); } MCSymbol *TargetLoweringObjectFileXCOFF::getFunctionEntryPointSymbol( const GlobalValue *Func, const TargetMachine &TM) const { assert( (isa(Func) || (isa(Func) && isa_and_nonnull(cast(Func)->getBaseObject()))) && "Func must be a function or an alias which has a function as base " "object."); SmallString<128> NameStr; NameStr.push_back('.'); getNameWithPrefix(NameStr, Func, TM); // When -function-sections is enabled and explicit section is not specified, // it's not necessary to emit function entry point label any more. We will use // function entry point csect instead. And for function delcarations, the // undefined symbols gets treated as csect with XTY_ER property. if (((TM.getFunctionSections() && !Func->hasSection()) || Func->isDeclaration()) && isa(Func)) { return getContext() .getXCOFFSection(NameStr, XCOFF::XMC_PR, Func->isDeclaration() ? XCOFF::XTY_ER : XCOFF::XTY_SD, SectionKind::getText()) ->getQualNameSymbol(); } return getContext().getOrCreateSymbol(NameStr); } MCSection *TargetLoweringObjectFileXCOFF::getSectionForFunctionDescriptor( const Function *F, const TargetMachine &TM) const { SmallString<128> NameStr; getNameWithPrefix(NameStr, F, TM); return getContext().getXCOFFSection(NameStr, XCOFF::XMC_DS, XCOFF::XTY_SD, SectionKind::getData()); } MCSection *TargetLoweringObjectFileXCOFF::getSectionForTOCEntry( const MCSymbol *Sym, const TargetMachine &TM) const { // Use TE storage-mapping class when large code model is enabled so that // the chance of needing -bbigtoc is decreased. return getContext().getXCOFFSection( cast(Sym)->getSymbolTableName(), TM.getCodeModel() == CodeModel::Large ? XCOFF::XMC_TE : XCOFF::XMC_TC, XCOFF::XTY_SD, SectionKind::getData()); } diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp index e5e512672daa..2fbe707ce8df 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp @@ -1,886 +1,886 @@ //===--- RuntimeDyldChecker.cpp - RuntimeDyld tester framework --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "RuntimeDyldCheckerImpl.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/Endian.h" #include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Path.h" #include #include #include #define DEBUG_TYPE "rtdyld" using namespace llvm; namespace llvm { // Helper class that implements the language evaluated by RuntimeDyldChecker. class RuntimeDyldCheckerExprEval { public: RuntimeDyldCheckerExprEval(const RuntimeDyldCheckerImpl &Checker, raw_ostream &ErrStream) : Checker(Checker) {} bool evaluate(StringRef Expr) const { // Expect equality expression of the form 'LHS = RHS'. Expr = Expr.trim(); size_t EQIdx = Expr.find('='); ParseContext OutsideLoad(false); // Evaluate LHS. StringRef LHSExpr = Expr.substr(0, EQIdx).rtrim(); StringRef RemainingExpr; EvalResult LHSResult; std::tie(LHSResult, RemainingExpr) = evalComplexExpr(evalSimpleExpr(LHSExpr, OutsideLoad), OutsideLoad); if (LHSResult.hasError()) return handleError(Expr, LHSResult); if (RemainingExpr != "") return handleError(Expr, unexpectedToken(RemainingExpr, LHSExpr, "")); // Evaluate RHS. StringRef RHSExpr = Expr.substr(EQIdx + 1).ltrim(); EvalResult RHSResult; std::tie(RHSResult, RemainingExpr) = evalComplexExpr(evalSimpleExpr(RHSExpr, OutsideLoad), OutsideLoad); if (RHSResult.hasError()) return handleError(Expr, RHSResult); if (RemainingExpr != "") return handleError(Expr, unexpectedToken(RemainingExpr, RHSExpr, "")); if (LHSResult.getValue() != RHSResult.getValue()) { Checker.ErrStream << "Expression '" << Expr << "' is false: " << format("0x%" PRIx64, LHSResult.getValue()) << " != " << format("0x%" PRIx64, RHSResult.getValue()) << "\n"; return false; } return true; } private: // RuntimeDyldCheckerExprEval requires some context when parsing exprs. In // particular, it needs to know whether a symbol is being evaluated in the // context of a load, in which case we want the linker's local address for // the symbol, or outside of a load, in which case we want the symbol's // address in the remote target. struct ParseContext { bool IsInsideLoad; ParseContext(bool IsInsideLoad) : IsInsideLoad(IsInsideLoad) {} }; const RuntimeDyldCheckerImpl &Checker; enum class BinOpToken : unsigned { Invalid, Add, Sub, BitwiseAnd, BitwiseOr, ShiftLeft, ShiftRight }; class EvalResult { public: EvalResult() : Value(0), ErrorMsg("") {} EvalResult(uint64_t Value) : Value(Value), ErrorMsg("") {} EvalResult(std::string ErrorMsg) : Value(0), ErrorMsg(std::move(ErrorMsg)) {} uint64_t getValue() const { return Value; } bool hasError() const { return ErrorMsg != ""; } const std::string &getErrorMsg() const { return ErrorMsg; } private: uint64_t Value; std::string ErrorMsg; }; StringRef getTokenForError(StringRef Expr) const { if (Expr.empty()) return ""; StringRef Token, Remaining; if (isalpha(Expr[0])) std::tie(Token, Remaining) = parseSymbol(Expr); else if (isdigit(Expr[0])) std::tie(Token, Remaining) = parseNumberString(Expr); else { unsigned TokLen = 1; if (Expr.startswith("<<") || Expr.startswith(">>")) TokLen = 2; Token = Expr.substr(0, TokLen); } return Token; } EvalResult unexpectedToken(StringRef TokenStart, StringRef SubExpr, StringRef ErrText) const { std::string ErrorMsg("Encountered unexpected token '"); ErrorMsg += getTokenForError(TokenStart); if (SubExpr != "") { ErrorMsg += "' while parsing subexpression '"; ErrorMsg += SubExpr; } ErrorMsg += "'"; if (ErrText != "") { ErrorMsg += " "; ErrorMsg += ErrText; } return EvalResult(std::move(ErrorMsg)); } bool handleError(StringRef Expr, const EvalResult &R) const { assert(R.hasError() && "Not an error result."); Checker.ErrStream << "Error evaluating expression '" << Expr << "': " << R.getErrorMsg() << "\n"; return false; } std::pair parseBinOpToken(StringRef Expr) const { if (Expr.empty()) return std::make_pair(BinOpToken::Invalid, ""); // Handle the two 2-character tokens. if (Expr.startswith("<<")) return std::make_pair(BinOpToken::ShiftLeft, Expr.substr(2).ltrim()); if (Expr.startswith(">>")) return std::make_pair(BinOpToken::ShiftRight, Expr.substr(2).ltrim()); // Handle one-character tokens. BinOpToken Op; switch (Expr[0]) { default: return std::make_pair(BinOpToken::Invalid, Expr); case '+': Op = BinOpToken::Add; break; case '-': Op = BinOpToken::Sub; break; case '&': Op = BinOpToken::BitwiseAnd; break; case '|': Op = BinOpToken::BitwiseOr; break; } return std::make_pair(Op, Expr.substr(1).ltrim()); } EvalResult computeBinOpResult(BinOpToken Op, const EvalResult &LHSResult, const EvalResult &RHSResult) const { switch (Op) { default: llvm_unreachable("Tried to evaluate unrecognized operation."); case BinOpToken::Add: return EvalResult(LHSResult.getValue() + RHSResult.getValue()); case BinOpToken::Sub: return EvalResult(LHSResult.getValue() - RHSResult.getValue()); case BinOpToken::BitwiseAnd: return EvalResult(LHSResult.getValue() & RHSResult.getValue()); case BinOpToken::BitwiseOr: return EvalResult(LHSResult.getValue() | RHSResult.getValue()); case BinOpToken::ShiftLeft: return EvalResult(LHSResult.getValue() << RHSResult.getValue()); case BinOpToken::ShiftRight: return EvalResult(LHSResult.getValue() >> RHSResult.getValue()); } } // Parse a symbol and return a (string, string) pair representing the symbol // name and expression remaining to be parsed. std::pair parseSymbol(StringRef Expr) const { size_t FirstNonSymbol = Expr.find_first_not_of("0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ":_.$"); return std::make_pair(Expr.substr(0, FirstNonSymbol), Expr.substr(FirstNonSymbol).ltrim()); } // Evaluate a call to decode_operand. Decode the instruction operand at the // given symbol and get the value of the requested operand. // Returns an error if the instruction cannot be decoded, or the requested // operand is not an immediate. // On success, returns a pair containing the value of the operand, plus // the expression remaining to be evaluated. std::pair evalDecodeOperand(StringRef Expr) const { if (!Expr.startswith("(")) return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); StringRef RemainingExpr = Expr.substr(1).ltrim(); StringRef Symbol; std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); if (!Checker.isSymbolValid(Symbol)) return std::make_pair( EvalResult(("Cannot decode unknown symbol '" + Symbol + "'").str()), ""); if (!RemainingExpr.startswith(",")) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected ','"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); EvalResult OpIdxExpr; std::tie(OpIdxExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); if (OpIdxExpr.hasError()) return std::make_pair(OpIdxExpr, ""); if (!RemainingExpr.startswith(")")) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected ')'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); MCInst Inst; uint64_t Size; if (!decodeInst(Symbol, Inst, Size)) return std::make_pair( EvalResult(("Couldn't decode instruction at '" + Symbol + "'").str()), ""); unsigned OpIdx = OpIdxExpr.getValue(); if (OpIdx >= Inst.getNumOperands()) { std::string ErrMsg; raw_string_ostream ErrMsgStream(ErrMsg); ErrMsgStream << "Invalid operand index '" << format("%i", OpIdx) << "' for instruction '" << Symbol << "'. Instruction has only " << format("%i", Inst.getNumOperands()) << " operands.\nInstruction is:\n "; Inst.dump_pretty(ErrMsgStream, Checker.InstPrinter); return std::make_pair(EvalResult(ErrMsgStream.str()), ""); } const MCOperand &Op = Inst.getOperand(OpIdx); if (!Op.isImm()) { std::string ErrMsg; raw_string_ostream ErrMsgStream(ErrMsg); ErrMsgStream << "Operand '" << format("%i", OpIdx) << "' of instruction '" << Symbol << "' is not an immediate.\nInstruction is:\n "; Inst.dump_pretty(ErrMsgStream, Checker.InstPrinter); return std::make_pair(EvalResult(ErrMsgStream.str()), ""); } return std::make_pair(EvalResult(Op.getImm()), RemainingExpr); } // Evaluate a call to next_pc. // Decode the instruction at the given symbol and return the following program // counter. // Returns an error if the instruction cannot be decoded. // On success, returns a pair containing the next PC, plus of the // expression remaining to be evaluated. std::pair evalNextPC(StringRef Expr, ParseContext PCtx) const { if (!Expr.startswith("(")) return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); StringRef RemainingExpr = Expr.substr(1).ltrim(); StringRef Symbol; std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); if (!Checker.isSymbolValid(Symbol)) return std::make_pair( EvalResult(("Cannot decode unknown symbol '" + Symbol + "'").str()), ""); if (!RemainingExpr.startswith(")")) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected ')'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); MCInst Inst; uint64_t InstSize; if (!decodeInst(Symbol, Inst, InstSize)) return std::make_pair( EvalResult(("Couldn't decode instruction at '" + Symbol + "'").str()), ""); uint64_t SymbolAddr = PCtx.IsInsideLoad ? Checker.getSymbolLocalAddr(Symbol) : Checker.getSymbolRemoteAddr(Symbol); uint64_t NextPC = SymbolAddr + InstSize; return std::make_pair(EvalResult(NextPC), RemainingExpr); } // Evaluate a call to stub_addr/got_addr. // Look up and return the address of the stub for the given // (,
, ) tuple. // On success, returns a pair containing the stub address, plus the expression // remaining to be evaluated. std::pair evalStubOrGOTAddr(StringRef Expr, ParseContext PCtx, bool IsStubAddr) const { if (!Expr.startswith("(")) return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); StringRef RemainingExpr = Expr.substr(1).ltrim(); // Handle file-name specially, as it may contain characters that aren't // legal for symbols. StringRef StubContainerName; size_t ComaIdx = RemainingExpr.find(','); StubContainerName = RemainingExpr.substr(0, ComaIdx).rtrim(); RemainingExpr = RemainingExpr.substr(ComaIdx).ltrim(); if (!RemainingExpr.startswith(",")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ','"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); StringRef Symbol; std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); if (!RemainingExpr.startswith(")")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); uint64_t StubAddr; - std::string ErrorMsg = ""; + std::string ErrorMsg; std::tie(StubAddr, ErrorMsg) = Checker.getStubOrGOTAddrFor( StubContainerName, Symbol, PCtx.IsInsideLoad, IsStubAddr); if (ErrorMsg != "") return std::make_pair(EvalResult(ErrorMsg), ""); return std::make_pair(EvalResult(StubAddr), RemainingExpr); } std::pair evalSectionAddr(StringRef Expr, ParseContext PCtx) const { if (!Expr.startswith("(")) return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); StringRef RemainingExpr = Expr.substr(1).ltrim(); // Handle file-name specially, as it may contain characters that aren't // legal for symbols. StringRef FileName; size_t ComaIdx = RemainingExpr.find(','); FileName = RemainingExpr.substr(0, ComaIdx).rtrim(); RemainingExpr = RemainingExpr.substr(ComaIdx).ltrim(); if (!RemainingExpr.startswith(",")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ','"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); StringRef SectionName; std::tie(SectionName, RemainingExpr) = parseSymbol(RemainingExpr); if (!RemainingExpr.startswith(")")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); uint64_t StubAddr; - std::string ErrorMsg = ""; + std::string ErrorMsg; std::tie(StubAddr, ErrorMsg) = Checker.getSectionAddr( FileName, SectionName, PCtx.IsInsideLoad); if (ErrorMsg != "") return std::make_pair(EvalResult(ErrorMsg), ""); return std::make_pair(EvalResult(StubAddr), RemainingExpr); } // Evaluate an identiefer expr, which may be a symbol, or a call to // one of the builtin functions: get_insn_opcode or get_insn_length. // Return the result, plus the expression remaining to be parsed. std::pair evalIdentifierExpr(StringRef Expr, ParseContext PCtx) const { StringRef Symbol; StringRef RemainingExpr; std::tie(Symbol, RemainingExpr) = parseSymbol(Expr); // Check for builtin function calls. if (Symbol == "decode_operand") return evalDecodeOperand(RemainingExpr); else if (Symbol == "next_pc") return evalNextPC(RemainingExpr, PCtx); else if (Symbol == "stub_addr") return evalStubOrGOTAddr(RemainingExpr, PCtx, true); else if (Symbol == "got_addr") return evalStubOrGOTAddr(RemainingExpr, PCtx, false); else if (Symbol == "section_addr") return evalSectionAddr(RemainingExpr, PCtx); if (!Checker.isSymbolValid(Symbol)) { std::string ErrMsg("No known address for symbol '"); ErrMsg += Symbol; ErrMsg += "'"; if (Symbol.startswith("L")) ErrMsg += " (this appears to be an assembler local label - " " perhaps drop the 'L'?)"; return std::make_pair(EvalResult(ErrMsg), ""); } // The value for the symbol depends on the context we're evaluating in: // Inside a load this is the address in the linker's memory, outside a // load it's the address in the target processes memory. uint64_t Value = PCtx.IsInsideLoad ? Checker.getSymbolLocalAddr(Symbol) : Checker.getSymbolRemoteAddr(Symbol); // Looks like a plain symbol reference. return std::make_pair(EvalResult(Value), RemainingExpr); } // Parse a number (hexadecimal or decimal) and return a (string, string) // pair representing the number and the expression remaining to be parsed. std::pair parseNumberString(StringRef Expr) const { size_t FirstNonDigit = StringRef::npos; if (Expr.startswith("0x")) { FirstNonDigit = Expr.find_first_not_of("0123456789abcdefABCDEF", 2); if (FirstNonDigit == StringRef::npos) FirstNonDigit = Expr.size(); } else { FirstNonDigit = Expr.find_first_not_of("0123456789"); if (FirstNonDigit == StringRef::npos) FirstNonDigit = Expr.size(); } return std::make_pair(Expr.substr(0, FirstNonDigit), Expr.substr(FirstNonDigit)); } // Evaluate a constant numeric expression (hexadecimal or decimal) and // return a pair containing the result, and the expression remaining to be // evaluated. std::pair evalNumberExpr(StringRef Expr) const { StringRef ValueStr; StringRef RemainingExpr; std::tie(ValueStr, RemainingExpr) = parseNumberString(Expr); if (ValueStr.empty() || !isdigit(ValueStr[0])) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected number"), ""); uint64_t Value; ValueStr.getAsInteger(0, Value); return std::make_pair(EvalResult(Value), RemainingExpr); } // Evaluate an expression of the form "()" and return a pair // containing the result of evaluating , plus the expression // remaining to be parsed. std::pair evalParensExpr(StringRef Expr, ParseContext PCtx) const { assert(Expr.startswith("(") && "Not a parenthesized expression"); EvalResult SubExprResult; StringRef RemainingExpr; std::tie(SubExprResult, RemainingExpr) = evalComplexExpr(evalSimpleExpr(Expr.substr(1).ltrim(), PCtx), PCtx); if (SubExprResult.hasError()) return std::make_pair(SubExprResult, ""); if (!RemainingExpr.startswith(")")) return std::make_pair( unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); return std::make_pair(SubExprResult, RemainingExpr); } // Evaluate an expression in one of the following forms: // *{} // Return a pair containing the result, plus the expression remaining to be // parsed. std::pair evalLoadExpr(StringRef Expr) const { assert(Expr.startswith("*") && "Not a load expression"); StringRef RemainingExpr = Expr.substr(1).ltrim(); // Parse read size. if (!RemainingExpr.startswith("{")) return std::make_pair(EvalResult("Expected '{' following '*'."), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); EvalResult ReadSizeExpr; std::tie(ReadSizeExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); if (ReadSizeExpr.hasError()) return std::make_pair(ReadSizeExpr, RemainingExpr); uint64_t ReadSize = ReadSizeExpr.getValue(); if (ReadSize < 1 || ReadSize > 8) return std::make_pair(EvalResult("Invalid size for dereference."), ""); if (!RemainingExpr.startswith("}")) return std::make_pair(EvalResult("Missing '}' for dereference."), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); // Evaluate the expression representing the load address. ParseContext LoadCtx(true); EvalResult LoadAddrExprResult; std::tie(LoadAddrExprResult, RemainingExpr) = evalComplexExpr(evalSimpleExpr(RemainingExpr, LoadCtx), LoadCtx); if (LoadAddrExprResult.hasError()) return std::make_pair(LoadAddrExprResult, ""); uint64_t LoadAddr = LoadAddrExprResult.getValue(); // If there is no error but the content pointer is null then this is a // zero-fill symbol/section. if (LoadAddr == 0) return std::make_pair(0, RemainingExpr); return std::make_pair( EvalResult(Checker.readMemoryAtAddr(LoadAddr, ReadSize)), RemainingExpr); } // Evaluate a "simple" expression. This is any expression that _isn't_ an // un-parenthesized binary expression. // // "Simple" expressions can be optionally bit-sliced. See evalSlicedExpr. // // Returns a pair containing the result of the evaluation, plus the // expression remaining to be parsed. std::pair evalSimpleExpr(StringRef Expr, ParseContext PCtx) const { EvalResult SubExprResult; StringRef RemainingExpr; if (Expr.empty()) return std::make_pair(EvalResult("Unexpected end of expression"), ""); if (Expr[0] == '(') std::tie(SubExprResult, RemainingExpr) = evalParensExpr(Expr, PCtx); else if (Expr[0] == '*') std::tie(SubExprResult, RemainingExpr) = evalLoadExpr(Expr); else if (isalpha(Expr[0]) || Expr[0] == '_') std::tie(SubExprResult, RemainingExpr) = evalIdentifierExpr(Expr, PCtx); else if (isdigit(Expr[0])) std::tie(SubExprResult, RemainingExpr) = evalNumberExpr(Expr); else return std::make_pair( unexpectedToken(Expr, Expr, "expected '(', '*', identifier, or number"), ""); if (SubExprResult.hasError()) return std::make_pair(SubExprResult, RemainingExpr); // Evaluate bit-slice if present. if (RemainingExpr.startswith("[")) std::tie(SubExprResult, RemainingExpr) = evalSliceExpr(std::make_pair(SubExprResult, RemainingExpr)); return std::make_pair(SubExprResult, RemainingExpr); } // Evaluate a bit-slice of an expression. // A bit-slice has the form "[high:low]". The result of evaluating a // slice is the bits between high and low (inclusive) in the original // expression, right shifted so that the "low" bit is in position 0 in the // result. // Returns a pair containing the result of the slice operation, plus the // expression remaining to be parsed. std::pair evalSliceExpr(const std::pair &Ctx) const { EvalResult SubExprResult; StringRef RemainingExpr; std::tie(SubExprResult, RemainingExpr) = Ctx; assert(RemainingExpr.startswith("[") && "Not a slice expr."); RemainingExpr = RemainingExpr.substr(1).ltrim(); EvalResult HighBitExpr; std::tie(HighBitExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); if (HighBitExpr.hasError()) return std::make_pair(HighBitExpr, RemainingExpr); if (!RemainingExpr.startswith(":")) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected ':'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); EvalResult LowBitExpr; std::tie(LowBitExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); if (LowBitExpr.hasError()) return std::make_pair(LowBitExpr, RemainingExpr); if (!RemainingExpr.startswith("]")) return std::make_pair( unexpectedToken(RemainingExpr, RemainingExpr, "expected ']'"), ""); RemainingExpr = RemainingExpr.substr(1).ltrim(); unsigned HighBit = HighBitExpr.getValue(); unsigned LowBit = LowBitExpr.getValue(); uint64_t Mask = ((uint64_t)1 << (HighBit - LowBit + 1)) - 1; uint64_t SlicedValue = (SubExprResult.getValue() >> LowBit) & Mask; return std::make_pair(EvalResult(SlicedValue), RemainingExpr); } // Evaluate a "complex" expression. // Takes an already evaluated subexpression and checks for the presence of a // binary operator, computing the result of the binary operation if one is // found. Used to make arithmetic expressions left-associative. // Returns a pair containing the ultimate result of evaluating the // expression, plus the expression remaining to be evaluated. std::pair evalComplexExpr(const std::pair &LHSAndRemaining, ParseContext PCtx) const { EvalResult LHSResult; StringRef RemainingExpr; std::tie(LHSResult, RemainingExpr) = LHSAndRemaining; // If there was an error, or there's nothing left to evaluate, return the // result. if (LHSResult.hasError() || RemainingExpr == "") return std::make_pair(LHSResult, RemainingExpr); // Otherwise check if this is a binary expressioan. BinOpToken BinOp; std::tie(BinOp, RemainingExpr) = parseBinOpToken(RemainingExpr); // If this isn't a recognized expression just return. if (BinOp == BinOpToken::Invalid) return std::make_pair(LHSResult, RemainingExpr); // This is a recognized bin-op. Evaluate the RHS, then evaluate the binop. EvalResult RHSResult; std::tie(RHSResult, RemainingExpr) = evalSimpleExpr(RemainingExpr, PCtx); // If there was an error evaluating the RHS, return it. if (RHSResult.hasError()) return std::make_pair(RHSResult, RemainingExpr); // This is a binary expression - evaluate and try to continue as a // complex expr. EvalResult ThisResult(computeBinOpResult(BinOp, LHSResult, RHSResult)); return evalComplexExpr(std::make_pair(ThisResult, RemainingExpr), PCtx); } bool decodeInst(StringRef Symbol, MCInst &Inst, uint64_t &Size) const { MCDisassembler *Dis = Checker.Disassembler; StringRef SymbolMem = Checker.getSymbolContent(Symbol); ArrayRef SymbolBytes(SymbolMem.bytes_begin(), SymbolMem.size()); MCDisassembler::DecodeStatus S = Dis->getInstruction(Inst, Size, SymbolBytes, 0, nulls()); return (S == MCDisassembler::Success); } }; } RuntimeDyldCheckerImpl::RuntimeDyldCheckerImpl( IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo, GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo, GetGOTInfoFunction GetGOTInfo, support::endianness Endianness, MCDisassembler *Disassembler, MCInstPrinter *InstPrinter, raw_ostream &ErrStream) : IsSymbolValid(std::move(IsSymbolValid)), GetSymbolInfo(std::move(GetSymbolInfo)), GetSectionInfo(std::move(GetSectionInfo)), GetStubInfo(std::move(GetStubInfo)), GetGOTInfo(std::move(GetGOTInfo)), Endianness(Endianness), Disassembler(Disassembler), InstPrinter(InstPrinter), ErrStream(ErrStream) {} bool RuntimeDyldCheckerImpl::check(StringRef CheckExpr) const { CheckExpr = CheckExpr.trim(); LLVM_DEBUG(dbgs() << "RuntimeDyldChecker: Checking '" << CheckExpr << "'...\n"); RuntimeDyldCheckerExprEval P(*this, ErrStream); bool Result = P.evaluate(CheckExpr); (void)Result; LLVM_DEBUG(dbgs() << "RuntimeDyldChecker: '" << CheckExpr << "' " << (Result ? "passed" : "FAILED") << ".\n"); return Result; } bool RuntimeDyldCheckerImpl::checkAllRulesInBuffer(StringRef RulePrefix, MemoryBuffer *MemBuf) const { bool DidAllTestsPass = true; unsigned NumRules = 0; std::string CheckExpr; const char *LineStart = MemBuf->getBufferStart(); // Eat whitespace. while (LineStart != MemBuf->getBufferEnd() && isSpace(*LineStart)) ++LineStart; while (LineStart != MemBuf->getBufferEnd() && *LineStart != '\0') { const char *LineEnd = LineStart; while (LineEnd != MemBuf->getBufferEnd() && *LineEnd != '\r' && *LineEnd != '\n') ++LineEnd; StringRef Line(LineStart, LineEnd - LineStart); if (Line.startswith(RulePrefix)) CheckExpr += Line.substr(RulePrefix.size()).str(); // If there's a check expr string... if (!CheckExpr.empty()) { // ... and it's complete then run it, otherwise remove the trailer '\'. if (CheckExpr.back() != '\\') { DidAllTestsPass &= check(CheckExpr); CheckExpr.clear(); ++NumRules; } else CheckExpr.pop_back(); } // Eat whitespace. LineStart = LineEnd; while (LineStart != MemBuf->getBufferEnd() && isSpace(*LineStart)) ++LineStart; } return DidAllTestsPass && (NumRules != 0); } bool RuntimeDyldCheckerImpl::isSymbolValid(StringRef Symbol) const { return IsSymbolValid(Symbol); } uint64_t RuntimeDyldCheckerImpl::getSymbolLocalAddr(StringRef Symbol) const { auto SymInfo = GetSymbolInfo(Symbol); if (!SymInfo) { logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); return 0; } if (SymInfo->isZeroFill()) return 0; return static_cast( reinterpret_cast(SymInfo->getContent().data())); } uint64_t RuntimeDyldCheckerImpl::getSymbolRemoteAddr(StringRef Symbol) const { auto SymInfo = GetSymbolInfo(Symbol); if (!SymInfo) { logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); return 0; } return SymInfo->getTargetAddress(); } uint64_t RuntimeDyldCheckerImpl::readMemoryAtAddr(uint64_t SrcAddr, unsigned Size) const { uintptr_t PtrSizedAddr = static_cast(SrcAddr); assert(PtrSizedAddr == SrcAddr && "Linker memory pointer out-of-range."); void *Ptr = reinterpret_cast(PtrSizedAddr); switch (Size) { case 1: return support::endian::read(Ptr, Endianness); case 2: return support::endian::read(Ptr, Endianness); case 4: return support::endian::read(Ptr, Endianness); case 8: return support::endian::read(Ptr, Endianness); } llvm_unreachable("Unsupported read size"); } StringRef RuntimeDyldCheckerImpl::getSymbolContent(StringRef Symbol) const { auto SymInfo = GetSymbolInfo(Symbol); if (!SymInfo) { logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); return StringRef(); } return SymInfo->getContent(); } std::pair RuntimeDyldCheckerImpl::getSectionAddr( StringRef FileName, StringRef SectionName, bool IsInsideLoad) const { auto SecInfo = GetSectionInfo(FileName, SectionName); if (!SecInfo) { std::string ErrMsg; { raw_string_ostream ErrMsgStream(ErrMsg); logAllUnhandledErrors(SecInfo.takeError(), ErrMsgStream, "RTDyldChecker: "); } return std::make_pair(0, std::move(ErrMsg)); } // If this address is being looked up in "load" mode, return the content // pointer, otherwise return the target address. uint64_t Addr = 0; if (IsInsideLoad) { if (SecInfo->isZeroFill()) Addr = 0; else Addr = pointerToJITTargetAddress(SecInfo->getContent().data()); } else Addr = SecInfo->getTargetAddress(); return std::make_pair(Addr, ""); } std::pair RuntimeDyldCheckerImpl::getStubOrGOTAddrFor( StringRef StubContainerName, StringRef SymbolName, bool IsInsideLoad, bool IsStubAddr) const { auto StubInfo = IsStubAddr ? GetStubInfo(StubContainerName, SymbolName) : GetGOTInfo(StubContainerName, SymbolName); if (!StubInfo) { std::string ErrMsg; { raw_string_ostream ErrMsgStream(ErrMsg); logAllUnhandledErrors(StubInfo.takeError(), ErrMsgStream, "RTDyldChecker: "); } return std::make_pair((uint64_t)0, std::move(ErrMsg)); } uint64_t Addr = 0; if (IsInsideLoad) { if (StubInfo->isZeroFill()) return std::make_pair((uint64_t)0, "Detected zero-filled stub/GOT entry"); Addr = pointerToJITTargetAddress(StubInfo->getContent().data()); } else Addr = StubInfo->getTargetAddress(); return std::make_pair(Addr, ""); } RuntimeDyldChecker::RuntimeDyldChecker( IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo, GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo, GetGOTInfoFunction GetGOTInfo, support::endianness Endianness, MCDisassembler *Disassembler, MCInstPrinter *InstPrinter, raw_ostream &ErrStream) : Impl(::std::make_unique( std::move(IsSymbolValid), std::move(GetSymbolInfo), std::move(GetSectionInfo), std::move(GetStubInfo), std::move(GetGOTInfo), Endianness, Disassembler, InstPrinter, ErrStream)) {} RuntimeDyldChecker::~RuntimeDyldChecker() {} bool RuntimeDyldChecker::check(StringRef CheckExpr) const { return Impl->check(CheckExpr); } bool RuntimeDyldChecker::checkAllRulesInBuffer(StringRef RulePrefix, MemoryBuffer *MemBuf) const { return Impl->checkAllRulesInBuffer(RulePrefix, MemBuf); } std::pair RuntimeDyldChecker::getSectionAddr(StringRef FileName, StringRef SectionName, bool LocalAddress) { return Impl->getSectionAddr(FileName, SectionName, LocalAddress); } diff --git a/llvm/lib/Target/Mips/MipsRegisterBankInfo.h b/llvm/lib/Target/Mips/MipsRegisterBankInfo.h index 55eeaf096b14..df51606e1e8a 100644 --- a/llvm/lib/Target/Mips/MipsRegisterBankInfo.h +++ b/llvm/lib/Target/Mips/MipsRegisterBankInfo.h @@ -1,230 +1,230 @@ //===- MipsRegisterBankInfo.h -----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This file declares the targeting of the RegisterBankInfo class for Mips. /// \todo This should be generated by TableGen. //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_MIPS_MIPSREGISTERBANKINFO_H #define LLVM_LIB_TARGET_MIPS_MIPSREGISTERBANKINFO_H #include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #define GET_REGBANK_DECLARATIONS #include "MipsGenRegisterBank.inc" namespace llvm { class TargetRegisterInfo; class MipsGenRegisterBankInfo : public RegisterBankInfo { #define GET_TARGET_REGBANK_CLASS #include "MipsGenRegisterBank.inc" }; /// This class provides the information for the target register banks. class MipsRegisterBankInfo final : public MipsGenRegisterBankInfo { public: MipsRegisterBankInfo(const TargetRegisterInfo &TRI); const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC, LLT) const override; const InstructionMapping & getInstrMapping(const MachineInstr &MI) const override; /// Here we have to narrowScalar s64 operands to s32, combine away G_MERGE or /// G_UNMERGE and erase instructions that became dead in the process. We /// manually assign bank to def operand of all new instructions that were /// created in the process since they will not end up in RegBankSelect loop. void applyMappingImpl(const OperandsMapper &OpdMapper) const override; /// RegBankSelect determined that s64 operand is better to be split into two /// s32 operands in gprb. Here we manually set register banks of def operands /// of newly created instructions since they will not get regbankselected. void setRegBank(MachineInstr &MI, MachineRegisterInfo &MRI) const; private: /// Some instructions are used with both floating point and integer operands. /// We assign InstType to such instructions as it helps us to avoid cross bank /// copies. InstType deppends on context. enum InstType { /// Temporary type, when visit(..., nullptr) finishes will convert to one of /// the remaining types: Integer, FloatingPoint or Ambiguous. NotDetermined, /// Connected with instruction that interprets 'bags of bits' as integers. /// Select gprb to avoid cross bank copies. Integer, /// Connected with instruction that interprets 'bags of bits' as floating /// point numbers. Select fprb to avoid cross bank copies. FloatingPoint, /// Represents moving 'bags of bits' around. Select same bank for entire /// chain to avoid cross bank copies. Currently we select fprb for s64 and /// gprb for s32 Ambiguous operands. Ambiguous, /// Only used for s64. Unlike Ambiguous s64, AmbiguousWithMergeOrUnmerge s64 /// is mapped to gprb (legalized using narrow scalar to s32). AmbiguousWithMergeOrUnmerge }; bool isAmbiguous_64(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::Ambiguous && OpSize == 64) return true; return false; } bool isAmbiguous_32(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::Ambiguous && OpSize == 32) return true; return false; } bool isAmbiguous_32or64(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::Ambiguous && (OpSize == 32 || OpSize == 64)) return true; return false; } bool isAmbiguousWithMergeOrUnmerge_64(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::AmbiguousWithMergeOrUnmerge && OpSize == 64) return true; return false; } bool isFloatingPoint_32or64(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::FloatingPoint && (OpSize == 32 || OpSize == 64)) return true; return false; } bool isFloatingPoint_64(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::FloatingPoint && OpSize == 64) return true; return false; } bool isInteger_32(InstType InstTy, unsigned OpSize) const { if (InstTy == InstType::Integer && OpSize == 32) return true; return false; } /// Some generic instructions have operands that can be mapped to either fprb /// or gprb e.g. for G_LOAD we consider only operand 0 as ambiguous, operand 1 /// is always gprb since it is a pointer. /// This class provides containers for MI's ambiguous: /// DefUses : MachineInstrs that use one of MI's ambiguous def operands. /// UseDefs : MachineInstrs that define MI's ambiguous use operands. class AmbiguousRegDefUseContainer { SmallVector DefUses; SmallVector UseDefs; void addDefUses(Register Reg, const MachineRegisterInfo &MRI); void addUseDef(Register Reg, const MachineRegisterInfo &MRI); /// Skip copy instructions until we get to a non-copy instruction or to a /// copy with phys register as def. Used during search for DefUses. /// MI : %5 = COPY %4 /// %6 = COPY %5 /// $v0 = COPY %6 <- we want this one. MachineInstr *skipCopiesOutgoing(MachineInstr *MI) const; /// Skip copy instructions until we get to a non-copy instruction or to a /// copy with phys register as use. Used during search for UseDefs. /// %1 = COPY $a1 <- we want this one. /// %2 = COPY %1 /// MI = %3 = COPY %2 MachineInstr *skipCopiesIncoming(MachineInstr *MI) const; public: AmbiguousRegDefUseContainer(const MachineInstr *MI); SmallVectorImpl &getDefUses() { return DefUses; } SmallVectorImpl &getUseDefs() { return UseDefs; } }; class TypeInfoForMF { /// MachineFunction name is used to recognise when MF changes. - std::string MFName = ""; + std::string MFName; /// : value is vector of all MachineInstrs that are waiting for /// key to figure out type of some of its ambiguous operands. DenseMap> WaitingQueues; /// Recorded InstTypes for visited instructions. DenseMap Types; /// Recursively visit MI's adjacent instructions and find MI's InstType. bool visit(const MachineInstr *MI, const MachineInstr *WaitingForTypeOfMI, InstType &AmbiguousTy); /// Visit MI's adjacent UseDefs or DefUses. bool visitAdjacentInstrs(const MachineInstr *MI, SmallVectorImpl &AdjacentInstrs, bool isDefUse, InstType &AmbiguousTy); /// Set type for MI, and recursively for all instructions that are /// waiting for MI's type. void setTypes(const MachineInstr *MI, InstType ITy); /// InstType for MI is determined, set it to InstType that corresponds to /// physical regisiter that is operand number Op in CopyInst. void setTypesAccordingToPhysicalRegister(const MachineInstr *MI, const MachineInstr *CopyInst, unsigned Op); /// Set default values for MI in order to start visit. void startVisit(const MachineInstr *MI) { Types.try_emplace(MI, InstType::NotDetermined); WaitingQueues.try_emplace(MI); } /// Returns true if instruction was already visited. Type might not be /// determined at this point but will be when visit(..., nullptr) finishes. bool wasVisited(const MachineInstr *MI) const { return Types.count(MI); }; /// Returns recorded type for instruction. const InstType &getRecordedTypeForInstr(const MachineInstr *MI) const { assert(wasVisited(MI) && "Instruction was not visited!"); return Types.find(MI)->getSecond(); }; /// Change recorded type for instruction. void changeRecordedTypeForInstr(const MachineInstr *MI, InstType InstTy) { assert(wasVisited(MI) && "Instruction was not visited!"); Types.find(MI)->getSecond() = InstTy; }; /// Returns WaitingQueue for instruction. const SmallVectorImpl & getWaitingQueueFor(const MachineInstr *MI) const { assert(WaitingQueues.count(MI) && "Instruction was not visited!"); return WaitingQueues.find(MI)->getSecond(); }; /// Add WaitingForMI to MI's WaitingQueue. void addToWaitingQueue(const MachineInstr *MI, const MachineInstr *WaitingForMI) { assert(WaitingQueues.count(MI) && "Instruction was not visited!"); WaitingQueues.find(MI)->getSecond().push_back(WaitingForMI); }; public: InstType determineInstType(const MachineInstr *MI); void cleanupIfNewFunction(llvm::StringRef FunctionName); /// MI is about to get destroyed (using narrow scalar). Internal data is /// saved based on MI's address, clear it since it is no longer valid. void clearTypeInfoData(const MachineInstr *MI) { Types.erase(MI); WaitingQueues.erase(MI); }; }; }; } // end namespace llvm #endif diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index ab4ad7c784a4..03ad45135001 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -1,2560 +1,2560 @@ //===- Attributor.cpp - Module-wide attribute deduction -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements an interprocedural pass that deduces and/or propagates // attributes. This is done in an abstract interpretation style fixpoint // iteration. See the Attributor.h file comment and the class descriptions in // that file for more information. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/IPO/Attributor.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/InlineCost.h" #include "llvm/Analysis/LazyValueInfo.h" #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/MustExecute.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/Verifier.h" #include "llvm/InitializePasses.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/DebugCounter.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" #include #include using namespace llvm; #define DEBUG_TYPE "attributor" DEBUG_COUNTER(ManifestDBGCounter, "attributor-manifest", "Determine what attributes are manifested in the IR"); STATISTIC(NumFnDeleted, "Number of function deleted"); STATISTIC(NumFnWithExactDefinition, "Number of functions with exact definitions"); STATISTIC(NumFnWithoutExactDefinition, "Number of functions without exact definitions"); STATISTIC(NumFnShallowWrappersCreated, "Number of shallow wrappers created"); STATISTIC(NumAttributesTimedOut, "Number of abstract attributes timed out before fixpoint"); STATISTIC(NumAttributesValidFixpoint, "Number of abstract attributes in a valid fixpoint state"); STATISTIC(NumAttributesManifested, "Number of abstract attributes manifested in IR"); STATISTIC(NumAttributesFixedDueToRequiredDependences, "Number of abstract attributes fixed due to required dependences"); // TODO: Determine a good default value. // // In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads // (when run with the first 5 abstract attributes). The results also indicate // that we never reach 32 iterations but always find a fixpoint sooner. // // This will become more evolved once we perform two interleaved fixpoint // iterations: bottom-up and top-down. static cl::opt MaxFixpointIterations("attributor-max-iterations", cl::Hidden, cl::desc("Maximal number of fixpoint iterations."), cl::init(32)); static cl::opt MaxInitializationChainLengthX( "attributor-max-initialization-chain-length", cl::Hidden, cl::desc( "Maximal number of chained initializations (to avoid stack overflows)"), cl::location(MaxInitializationChainLength), cl::init(1024)); unsigned llvm::MaxInitializationChainLength; static cl::opt VerifyMaxFixpointIterations( "attributor-max-iterations-verify", cl::Hidden, cl::desc("Verify that max-iterations is a tight bound for a fixpoint"), cl::init(false)); static cl::opt AnnotateDeclarationCallSites( "attributor-annotate-decl-cs", cl::Hidden, cl::desc("Annotate call sites of function declarations."), cl::init(false)); static cl::opt EnableHeapToStack("enable-heap-to-stack-conversion", cl::init(true), cl::Hidden); static cl::opt AllowShallowWrappers("attributor-allow-shallow-wrappers", cl::Hidden, cl::desc("Allow the Attributor to create shallow " "wrappers for non-exact definitions."), cl::init(false)); static cl::opt AllowDeepWrapper("attributor-allow-deep-wrappers", cl::Hidden, cl::desc("Allow the Attributor to use IP information " "derived from non-exact functions via cloning"), cl::init(false)); // These options can only used for debug builds. #ifndef NDEBUG static cl::list SeedAllowList("attributor-seed-allow-list", cl::Hidden, cl::desc("Comma seperated list of attribute names that are " "allowed to be seeded."), cl::ZeroOrMore, cl::CommaSeparated); static cl::list FunctionSeedAllowList( "attributor-function-seed-allow-list", cl::Hidden, cl::desc("Comma seperated list of function names that are " "allowed to be seeded."), cl::ZeroOrMore, cl::CommaSeparated); #endif static cl::opt DumpDepGraph("attributor-dump-dep-graph", cl::Hidden, cl::desc("Dump the dependency graph to dot files."), cl::init(false)); static cl::opt DepGraphDotFileNamePrefix( "attributor-depgraph-dot-filename-prefix", cl::Hidden, cl::desc("The prefix used for the CallGraph dot file names.")); static cl::opt ViewDepGraph("attributor-view-dep-graph", cl::Hidden, cl::desc("View the dependency graph."), cl::init(false)); static cl::opt PrintDependencies("attributor-print-dep", cl::Hidden, cl::desc("Print attribute dependencies"), cl::init(false)); /// Logic operators for the change status enum class. /// ///{ ChangeStatus llvm::operator|(ChangeStatus L, ChangeStatus R) { return L == ChangeStatus::CHANGED ? L : R; } ChangeStatus llvm::operator&(ChangeStatus L, ChangeStatus R) { return L == ChangeStatus::UNCHANGED ? L : R; } ///} /// Return true if \p New is equal or worse than \p Old. static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) { if (!Old.isIntAttribute()) return true; return Old.getValueAsInt() >= New.getValueAsInt(); } /// Return true if the information provided by \p Attr was added to the /// attribute list \p Attrs. This is only the case if it was not already present /// in \p Attrs at the position describe by \p PK and \p AttrIdx. static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr, AttributeList &Attrs, int AttrIdx) { if (Attr.isEnumAttribute()) { Attribute::AttrKind Kind = Attr.getKindAsEnum(); if (Attrs.hasAttribute(AttrIdx, Kind)) if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind))) return false; Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr); return true; } if (Attr.isStringAttribute()) { StringRef Kind = Attr.getKindAsString(); if (Attrs.hasAttribute(AttrIdx, Kind)) if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind))) return false; Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr); return true; } if (Attr.isIntAttribute()) { Attribute::AttrKind Kind = Attr.getKindAsEnum(); if (Attrs.hasAttribute(AttrIdx, Kind)) if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind))) return false; Attrs = Attrs.removeAttribute(Ctx, AttrIdx, Kind); Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr); return true; } llvm_unreachable("Expected enum or string attribute!"); } Argument *IRPosition::getAssociatedArgument() const { if (getPositionKind() == IRP_ARGUMENT) return cast(&getAnchorValue()); // Not an Argument and no argument number means this is not a call site // argument, thus we cannot find a callback argument to return. int ArgNo = getCallSiteArgNo(); if (ArgNo < 0) return nullptr; // Use abstract call sites to make the connection between the call site // values and the ones in callbacks. If a callback was found that makes use // of the underlying call site operand, we want the corresponding callback // callee argument and not the direct callee argument. Optional CBCandidateArg; SmallVector CallbackUses; const auto &CB = cast(getAnchorValue()); AbstractCallSite::getCallbackUses(CB, CallbackUses); for (const Use *U : CallbackUses) { AbstractCallSite ACS(U); assert(ACS && ACS.isCallbackCall()); if (!ACS.getCalledFunction()) continue; for (unsigned u = 0, e = ACS.getNumArgOperands(); u < e; u++) { // Test if the underlying call site operand is argument number u of the // callback callee. if (ACS.getCallArgOperandNo(u) != ArgNo) continue; assert(ACS.getCalledFunction()->arg_size() > u && "ACS mapped into var-args arguments!"); if (CBCandidateArg.hasValue()) { CBCandidateArg = nullptr; break; } CBCandidateArg = ACS.getCalledFunction()->getArg(u); } } // If we found a unique callback candidate argument, return it. if (CBCandidateArg.hasValue() && CBCandidateArg.getValue()) return CBCandidateArg.getValue(); // If no callbacks were found, or none used the underlying call site operand // exclusively, use the direct callee argument if available. const Function *Callee = CB.getCalledFunction(); if (Callee && Callee->arg_size() > unsigned(ArgNo)) return Callee->getArg(ArgNo); return nullptr; } ChangeStatus AbstractAttribute::update(Attributor &A) { ChangeStatus HasChanged = ChangeStatus::UNCHANGED; if (getState().isAtFixpoint()) return HasChanged; LLVM_DEBUG(dbgs() << "[Attributor] Update: " << *this << "\n"); HasChanged = updateImpl(A); LLVM_DEBUG(dbgs() << "[Attributor] Update " << HasChanged << " " << *this << "\n"); return HasChanged; } ChangeStatus IRAttributeManifest::manifestAttrs(Attributor &A, const IRPosition &IRP, const ArrayRef &DeducedAttrs) { Function *ScopeFn = IRP.getAnchorScope(); IRPosition::Kind PK = IRP.getPositionKind(); // In the following some generic code that will manifest attributes in // DeducedAttrs if they improve the current IR. Due to the different // annotation positions we use the underlying AttributeList interface. AttributeList Attrs; switch (PK) { case IRPosition::IRP_INVALID: case IRPosition::IRP_FLOAT: return ChangeStatus::UNCHANGED; case IRPosition::IRP_ARGUMENT: case IRPosition::IRP_FUNCTION: case IRPosition::IRP_RETURNED: Attrs = ScopeFn->getAttributes(); break; case IRPosition::IRP_CALL_SITE: case IRPosition::IRP_CALL_SITE_RETURNED: case IRPosition::IRP_CALL_SITE_ARGUMENT: Attrs = cast(IRP.getAnchorValue()).getAttributes(); break; } ChangeStatus HasChanged = ChangeStatus::UNCHANGED; LLVMContext &Ctx = IRP.getAnchorValue().getContext(); for (const Attribute &Attr : DeducedAttrs) { if (!addIfNotExistent(Ctx, Attr, Attrs, IRP.getAttrIdx())) continue; HasChanged = ChangeStatus::CHANGED; } if (HasChanged == ChangeStatus::UNCHANGED) return HasChanged; switch (PK) { case IRPosition::IRP_ARGUMENT: case IRPosition::IRP_FUNCTION: case IRPosition::IRP_RETURNED: ScopeFn->setAttributes(Attrs); break; case IRPosition::IRP_CALL_SITE: case IRPosition::IRP_CALL_SITE_RETURNED: case IRPosition::IRP_CALL_SITE_ARGUMENT: cast(IRP.getAnchorValue()).setAttributes(Attrs); break; case IRPosition::IRP_INVALID: case IRPosition::IRP_FLOAT: break; } return HasChanged; } const IRPosition IRPosition::EmptyKey(DenseMapInfo::getEmptyKey()); const IRPosition IRPosition::TombstoneKey(DenseMapInfo::getTombstoneKey()); SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) { IRPositions.emplace_back(IRP); // Helper to determine if operand bundles on a call site are benin or // potentially problematic. We handle only llvm.assume for now. auto CanIgnoreOperandBundles = [](const CallBase &CB) { return (isa(CB) && cast(CB).getIntrinsicID() == Intrinsic ::assume); }; const auto *CB = dyn_cast(&IRP.getAnchorValue()); switch (IRP.getPositionKind()) { case IRPosition::IRP_INVALID: case IRPosition::IRP_FLOAT: case IRPosition::IRP_FUNCTION: return; case IRPosition::IRP_ARGUMENT: case IRPosition::IRP_RETURNED: IRPositions.emplace_back(IRPosition::function(*IRP.getAnchorScope())); return; case IRPosition::IRP_CALL_SITE: assert(CB && "Expected call site!"); // TODO: We need to look at the operand bundles similar to the redirection // in CallBase. if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB)) if (const Function *Callee = CB->getCalledFunction()) IRPositions.emplace_back(IRPosition::function(*Callee)); return; case IRPosition::IRP_CALL_SITE_RETURNED: assert(CB && "Expected call site!"); // TODO: We need to look at the operand bundles similar to the redirection // in CallBase. if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB)) { if (const Function *Callee = CB->getCalledFunction()) { IRPositions.emplace_back(IRPosition::returned(*Callee)); IRPositions.emplace_back(IRPosition::function(*Callee)); for (const Argument &Arg : Callee->args()) if (Arg.hasReturnedAttr()) { IRPositions.emplace_back( IRPosition::callsite_argument(*CB, Arg.getArgNo())); IRPositions.emplace_back( IRPosition::value(*CB->getArgOperand(Arg.getArgNo()))); IRPositions.emplace_back(IRPosition::argument(Arg)); } } } IRPositions.emplace_back(IRPosition::callsite_function(*CB)); return; case IRPosition::IRP_CALL_SITE_ARGUMENT: { assert(CB && "Expected call site!"); // TODO: We need to look at the operand bundles similar to the redirection // in CallBase. if (!CB->hasOperandBundles() || CanIgnoreOperandBundles(*CB)) { const Function *Callee = CB->getCalledFunction(); if (Callee) { if (Argument *Arg = IRP.getAssociatedArgument()) IRPositions.emplace_back(IRPosition::argument(*Arg)); IRPositions.emplace_back(IRPosition::function(*Callee)); } } IRPositions.emplace_back(IRPosition::value(IRP.getAssociatedValue())); return; } } } bool IRPosition::hasAttr(ArrayRef AKs, bool IgnoreSubsumingPositions, Attributor *A) const { SmallVector Attrs; for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) { for (Attribute::AttrKind AK : AKs) if (EquivIRP.getAttrsFromIRAttr(AK, Attrs)) return true; // The first position returned by the SubsumingPositionIterator is // always the position itself. If we ignore subsuming positions we // are done after the first iteration. if (IgnoreSubsumingPositions) break; } if (A) for (Attribute::AttrKind AK : AKs) if (getAttrsFromAssumes(AK, Attrs, *A)) return true; return false; } void IRPosition::getAttrs(ArrayRef AKs, SmallVectorImpl &Attrs, bool IgnoreSubsumingPositions, Attributor *A) const { for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) { for (Attribute::AttrKind AK : AKs) EquivIRP.getAttrsFromIRAttr(AK, Attrs); // The first position returned by the SubsumingPositionIterator is // always the position itself. If we ignore subsuming positions we // are done after the first iteration. if (IgnoreSubsumingPositions) break; } if (A) for (Attribute::AttrKind AK : AKs) getAttrsFromAssumes(AK, Attrs, *A); } bool IRPosition::getAttrsFromIRAttr(Attribute::AttrKind AK, SmallVectorImpl &Attrs) const { if (getPositionKind() == IRP_INVALID || getPositionKind() == IRP_FLOAT) return false; AttributeList AttrList; if (const auto *CB = dyn_cast(&getAnchorValue())) AttrList = CB->getAttributes(); else AttrList = getAssociatedFunction()->getAttributes(); bool HasAttr = AttrList.hasAttribute(getAttrIdx(), AK); if (HasAttr) Attrs.push_back(AttrList.getAttribute(getAttrIdx(), AK)); return HasAttr; } bool IRPosition::getAttrsFromAssumes(Attribute::AttrKind AK, SmallVectorImpl &Attrs, Attributor &A) const { assert(getPositionKind() != IRP_INVALID && "Did expect a valid position!"); Value &AssociatedValue = getAssociatedValue(); const Assume2KnowledgeMap &A2K = A.getInfoCache().getKnowledgeMap().lookup({&AssociatedValue, AK}); // Check if we found any potential assume use, if not we don't need to create // explorer iterators. if (A2K.empty()) return false; LLVMContext &Ctx = AssociatedValue.getContext(); unsigned AttrsSize = Attrs.size(); MustBeExecutedContextExplorer &Explorer = A.getInfoCache().getMustBeExecutedContextExplorer(); auto EIt = Explorer.begin(getCtxI()), EEnd = Explorer.end(getCtxI()); for (auto &It : A2K) if (Explorer.findInContextOf(It.first, EIt, EEnd)) Attrs.push_back(Attribute::get(Ctx, AK, It.second.Max)); return AttrsSize != Attrs.size(); } void IRPosition::verify() { #ifdef EXPENSIVE_CHECKS switch (getPositionKind()) { case IRP_INVALID: assert(!Enc.getOpaqueValue() && "Expected a nullptr for an invalid position!"); return; case IRP_FLOAT: assert((!isa(&getAssociatedValue()) && !isa(&getAssociatedValue())) && "Expected specialized kind for call base and argument values!"); return; case IRP_RETURNED: assert(isa(getAsValuePtr()) && "Expected function for a 'returned' position!"); assert(getAsValuePtr() == &getAssociatedValue() && "Associated value mismatch!"); return; case IRP_CALL_SITE_RETURNED: assert((isa(getAsValuePtr())) && "Expected call base for 'call site returned' position!"); assert(getAsValuePtr() == &getAssociatedValue() && "Associated value mismatch!"); return; case IRP_CALL_SITE: assert((isa(getAsValuePtr())) && "Expected call base for 'call site function' position!"); assert(getAsValuePtr() == &getAssociatedValue() && "Associated value mismatch!"); return; case IRP_FUNCTION: assert(isa(getAsValuePtr()) && "Expected function for a 'function' position!"); assert(getAsValuePtr() == &getAssociatedValue() && "Associated value mismatch!"); return; case IRP_ARGUMENT: assert(isa(getAsValuePtr()) && "Expected argument for a 'argument' position!"); assert(getAsValuePtr() == &getAssociatedValue() && "Associated value mismatch!"); return; case IRP_CALL_SITE_ARGUMENT: { Use *U = getAsUsePtr(); assert(U && "Expected use for a 'call site argument' position!"); assert(isa(U->getUser()) && "Expected call base user for a 'call site argument' position!"); assert(cast(U->getUser())->isArgOperand(U) && "Expected call base argument operand for a 'call site argument' " "position"); assert(cast(U->getUser())->getArgOperandNo(U) == unsigned(getCallSiteArgNo()) && "Argument number mismatch!"); assert(U->get() == &getAssociatedValue() && "Associated value mismatch!"); return; } } #endif } Optional Attributor::getAssumedConstant(const Value &V, const AbstractAttribute &AA, bool &UsedAssumedInformation) { const auto &ValueSimplifyAA = getAAFor( AA, IRPosition::value(V), /* TrackDependence */ false); Optional SimplifiedV = ValueSimplifyAA.getAssumedSimplifiedValue(*this); bool IsKnown = ValueSimplifyAA.isKnown(); UsedAssumedInformation |= !IsKnown; if (!SimplifiedV.hasValue()) { recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL); return llvm::None; } if (isa_and_nonnull(SimplifiedV.getValue())) { recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL); return llvm::None; } Constant *CI = dyn_cast_or_null(SimplifiedV.getValue()); if (CI && CI->getType() != V.getType()) { // TODO: Check for a save conversion. return nullptr; } if (CI) recordDependence(ValueSimplifyAA, AA, DepClassTy::OPTIONAL); return CI; } Attributor::~Attributor() { // The abstract attributes are allocated via the BumpPtrAllocator Allocator, // thus we cannot delete them. We can, and want to, destruct them though. for (auto &DepAA : DG.SyntheticRoot.Deps) { AbstractAttribute *AA = cast(DepAA.getPointer()); AA->~AbstractAttribute(); } } bool Attributor::isAssumedDead(const AbstractAttribute &AA, const AAIsDead *FnLivenessAA, bool CheckBBLivenessOnly, DepClassTy DepClass) { const IRPosition &IRP = AA.getIRPosition(); if (!Functions.count(IRP.getAnchorScope())) return false; return isAssumedDead(IRP, &AA, FnLivenessAA, CheckBBLivenessOnly, DepClass); } bool Attributor::isAssumedDead(const Use &U, const AbstractAttribute *QueryingAA, const AAIsDead *FnLivenessAA, bool CheckBBLivenessOnly, DepClassTy DepClass) { Instruction *UserI = dyn_cast(U.getUser()); if (!UserI) return isAssumedDead(IRPosition::value(*U.get()), QueryingAA, FnLivenessAA, CheckBBLivenessOnly, DepClass); if (auto *CB = dyn_cast(UserI)) { // For call site argument uses we can check if the argument is // unused/dead. if (CB->isArgOperand(&U)) { const IRPosition &CSArgPos = IRPosition::callsite_argument(*CB, CB->getArgOperandNo(&U)); return isAssumedDead(CSArgPos, QueryingAA, FnLivenessAA, CheckBBLivenessOnly, DepClass); } } else if (ReturnInst *RI = dyn_cast(UserI)) { const IRPosition &RetPos = IRPosition::returned(*RI->getFunction()); return isAssumedDead(RetPos, QueryingAA, FnLivenessAA, CheckBBLivenessOnly, DepClass); } else if (PHINode *PHI = dyn_cast(UserI)) { BasicBlock *IncomingBB = PHI->getIncomingBlock(U); return isAssumedDead(*IncomingBB->getTerminator(), QueryingAA, FnLivenessAA, CheckBBLivenessOnly, DepClass); } return isAssumedDead(IRPosition::value(*UserI), QueryingAA, FnLivenessAA, CheckBBLivenessOnly, DepClass); } bool Attributor::isAssumedDead(const Instruction &I, const AbstractAttribute *QueryingAA, const AAIsDead *FnLivenessAA, bool CheckBBLivenessOnly, DepClassTy DepClass) { if (!FnLivenessAA) FnLivenessAA = lookupAAFor(IRPosition::function(*I.getFunction()), QueryingAA, /* TrackDependence */ false); // If we have a context instruction and a liveness AA we use it. if (FnLivenessAA && FnLivenessAA->getIRPosition().getAnchorScope() == I.getFunction() && FnLivenessAA->isAssumedDead(&I)) { if (QueryingAA) recordDependence(*FnLivenessAA, *QueryingAA, DepClass); return true; } if (CheckBBLivenessOnly) return false; const AAIsDead &IsDeadAA = getOrCreateAAFor( IRPosition::value(I), QueryingAA, /* TrackDependence */ false); // Don't check liveness for AAIsDead. if (QueryingAA == &IsDeadAA) return false; if (IsDeadAA.isAssumedDead()) { if (QueryingAA) recordDependence(IsDeadAA, *QueryingAA, DepClass); return true; } return false; } bool Attributor::isAssumedDead(const IRPosition &IRP, const AbstractAttribute *QueryingAA, const AAIsDead *FnLivenessAA, bool CheckBBLivenessOnly, DepClassTy DepClass) { Instruction *CtxI = IRP.getCtxI(); if (CtxI && isAssumedDead(*CtxI, QueryingAA, FnLivenessAA, /* CheckBBLivenessOnly */ true, CheckBBLivenessOnly ? DepClass : DepClassTy::OPTIONAL)) return true; if (CheckBBLivenessOnly) return false; // If we haven't succeeded we query the specific liveness info for the IRP. const AAIsDead *IsDeadAA; if (IRP.getPositionKind() == IRPosition::IRP_CALL_SITE) IsDeadAA = &getOrCreateAAFor( IRPosition::callsite_returned(cast(IRP.getAssociatedValue())), QueryingAA, /* TrackDependence */ false); else IsDeadAA = &getOrCreateAAFor(IRP, QueryingAA, /* TrackDependence */ false); // Don't check liveness for AAIsDead. if (QueryingAA == IsDeadAA) return false; if (IsDeadAA->isAssumedDead()) { if (QueryingAA) recordDependence(*IsDeadAA, *QueryingAA, DepClass); return true; } return false; } bool Attributor::checkForAllUses(function_ref Pred, const AbstractAttribute &QueryingAA, const Value &V, DepClassTy LivenessDepClass) { // Check the trivial case first as it catches void values. if (V.use_empty()) return true; // If the value is replaced by another one, for now a constant, we do not have // uses. Note that this requires users of `checkForAllUses` to not recurse but // instead use the `follow` callback argument to look at transitive users, // however, that should be clear from the presence of the argument. bool UsedAssumedInformation = false; Optional C = getAssumedConstant(V, QueryingAA, UsedAssumedInformation); if (C.hasValue() && C.getValue()) { LLVM_DEBUG(dbgs() << "[Attributor] Value is simplified, uses skipped: " << V << " -> " << *C.getValue() << "\n"); return true; } const IRPosition &IRP = QueryingAA.getIRPosition(); SmallVector Worklist; SmallPtrSet Visited; for (const Use &U : V.uses()) Worklist.push_back(&U); LLVM_DEBUG(dbgs() << "[Attributor] Got " << Worklist.size() << " initial uses to check\n"); const Function *ScopeFn = IRP.getAnchorScope(); const auto *LivenessAA = ScopeFn ? &getAAFor(QueryingAA, IRPosition::function(*ScopeFn), /* TrackDependence */ false) : nullptr; while (!Worklist.empty()) { const Use *U = Worklist.pop_back_val(); if (!Visited.insert(U).second) continue; LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << " in " << *U->getUser() << "\n"); if (isAssumedDead(*U, &QueryingAA, LivenessAA, /* CheckBBLivenessOnly */ false, LivenessDepClass)) { LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); continue; } if (U->getUser()->isDroppable()) { LLVM_DEBUG(dbgs() << "[Attributor] Droppable user, skip!\n"); continue; } bool Follow = false; if (!Pred(*U, Follow)) return false; if (!Follow) continue; for (const Use &UU : U->getUser()->uses()) Worklist.push_back(&UU); } return true; } bool Attributor::checkForAllCallSites(function_ref Pred, const AbstractAttribute &QueryingAA, bool RequireAllCallSites, bool &AllCallSitesKnown) { // We can try to determine information from // the call sites. However, this is only possible all call sites are known, // hence the function has internal linkage. const IRPosition &IRP = QueryingAA.getIRPosition(); const Function *AssociatedFunction = IRP.getAssociatedFunction(); if (!AssociatedFunction) { LLVM_DEBUG(dbgs() << "[Attributor] No function associated with " << IRP << "\n"); AllCallSitesKnown = false; return false; } return checkForAllCallSites(Pred, *AssociatedFunction, RequireAllCallSites, &QueryingAA, AllCallSitesKnown); } bool Attributor::checkForAllCallSites(function_ref Pred, const Function &Fn, bool RequireAllCallSites, const AbstractAttribute *QueryingAA, bool &AllCallSitesKnown) { if (RequireAllCallSites && !Fn.hasLocalLinkage()) { LLVM_DEBUG( dbgs() << "[Attributor] Function " << Fn.getName() << " has no internal linkage, hence not all call sites are known\n"); AllCallSitesKnown = false; return false; } // If we do not require all call sites we might not see all. AllCallSitesKnown = RequireAllCallSites; SmallVector Uses(make_pointer_range(Fn.uses())); for (unsigned u = 0; u < Uses.size(); ++u) { const Use &U = *Uses[u]; LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << *U << " in " << *U.getUser() << "\n"); if (isAssumedDead(U, QueryingAA, nullptr, /* CheckBBLivenessOnly */ true)) { LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n"); continue; } if (ConstantExpr *CE = dyn_cast(U.getUser())) { if (CE->isCast() && CE->getType()->isPointerTy() && CE->getType()->getPointerElementType()->isFunctionTy()) { for (const Use &CEU : CE->uses()) Uses.push_back(&CEU); continue; } } AbstractCallSite ACS(&U); if (!ACS) { LLVM_DEBUG(dbgs() << "[Attributor] Function " << Fn.getName() << " has non call site use " << *U.get() << " in " << *U.getUser() << "\n"); // BlockAddress users are allowed. if (isa(U.getUser())) continue; return false; } const Use *EffectiveUse = ACS.isCallbackCall() ? &ACS.getCalleeUseForCallback() : &U; if (!ACS.isCallee(EffectiveUse)) { if (!RequireAllCallSites) continue; LLVM_DEBUG(dbgs() << "[Attributor] User " << EffectiveUse->getUser() << " is an invalid use of " << Fn.getName() << "\n"); return false; } // Make sure the arguments that can be matched between the call site and the // callee argee on their type. It is unlikely they do not and it doesn't // make sense for all attributes to know/care about this. assert(&Fn == ACS.getCalledFunction() && "Expected known callee"); unsigned MinArgsParams = std::min(size_t(ACS.getNumArgOperands()), Fn.arg_size()); for (unsigned u = 0; u < MinArgsParams; ++u) { Value *CSArgOp = ACS.getCallArgOperand(u); if (CSArgOp && Fn.getArg(u)->getType() != CSArgOp->getType()) { LLVM_DEBUG( dbgs() << "[Attributor] Call site / callee argument type mismatch [" << u << "@" << Fn.getName() << ": " << *Fn.getArg(u)->getType() << " vs. " << *ACS.getCallArgOperand(u)->getType() << "\n"); return false; } } if (Pred(ACS)) continue; LLVM_DEBUG(dbgs() << "[Attributor] Call site callback failed for " << *ACS.getInstruction() << "\n"); return false; } return true; } bool Attributor::checkForAllReturnedValuesAndReturnInsts( function_ref &)> Pred, const AbstractAttribute &QueryingAA) { const IRPosition &IRP = QueryingAA.getIRPosition(); // Since we need to provide return instructions we have to have an exact // definition. const Function *AssociatedFunction = IRP.getAssociatedFunction(); if (!AssociatedFunction) return false; // If this is a call site query we use the call site specific return values // and liveness information. // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto &AARetVal = getAAFor(QueryingAA, QueryIRP); if (!AARetVal.getState().isValidState()) return false; return AARetVal.checkForAllReturnedValuesAndReturnInsts(Pred); } bool Attributor::checkForAllReturnedValues( function_ref Pred, const AbstractAttribute &QueryingAA) { const IRPosition &IRP = QueryingAA.getIRPosition(); const Function *AssociatedFunction = IRP.getAssociatedFunction(); if (!AssociatedFunction) return false; // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto &AARetVal = getAAFor(QueryingAA, QueryIRP); if (!AARetVal.getState().isValidState()) return false; return AARetVal.checkForAllReturnedValuesAndReturnInsts( [&](Value &RV, const SmallSetVector &) { return Pred(RV); }); } static bool checkForAllInstructionsImpl( Attributor *A, InformationCache::OpcodeInstMapTy &OpcodeInstMap, function_ref Pred, const AbstractAttribute *QueryingAA, const AAIsDead *LivenessAA, const ArrayRef &Opcodes, bool CheckBBLivenessOnly = false) { for (unsigned Opcode : Opcodes) { // Check if we have instructions with this opcode at all first. auto *Insts = OpcodeInstMap.lookup(Opcode); if (!Insts) continue; for (Instruction *I : *Insts) { // Skip dead instructions. if (A && A->isAssumedDead(IRPosition::value(*I), QueryingAA, LivenessAA, CheckBBLivenessOnly)) continue; if (!Pred(*I)) return false; } } return true; } bool Attributor::checkForAllInstructions(function_ref Pred, const AbstractAttribute &QueryingAA, const ArrayRef &Opcodes, bool CheckBBLivenessOnly) { const IRPosition &IRP = QueryingAA.getIRPosition(); // Since we need to provide instructions we have to have an exact definition. const Function *AssociatedFunction = IRP.getAssociatedFunction(); if (!AssociatedFunction) return false; // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto *LivenessAA = CheckBBLivenessOnly ? nullptr : &(getAAFor(QueryingAA, QueryIRP, /* TrackDependence */ false)); auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction); if (!checkForAllInstructionsImpl(this, OpcodeInstMap, Pred, &QueryingAA, LivenessAA, Opcodes, CheckBBLivenessOnly)) return false; return true; } bool Attributor::checkForAllReadWriteInstructions( function_ref Pred, AbstractAttribute &QueryingAA) { const Function *AssociatedFunction = QueryingAA.getIRPosition().getAssociatedFunction(); if (!AssociatedFunction) return false; // TODO: use the function scope once we have call site AAReturnedValues. const IRPosition &QueryIRP = IRPosition::function(*AssociatedFunction); const auto &LivenessAA = getAAFor(QueryingAA, QueryIRP, /* TrackDependence */ false); for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) { // Skip dead instructions. if (isAssumedDead(IRPosition::value(*I), &QueryingAA, &LivenessAA)) continue; if (!Pred(*I)) return false; } return true; } void Attributor::runTillFixpoint() { TimeTraceScope TimeScope("Attributor::runTillFixpoint"); LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized " << DG.SyntheticRoot.Deps.size() << " abstract attributes.\n"); // Now that all abstract attributes are collected and initialized we start // the abstract analysis. unsigned IterationCounter = 1; SmallVector ChangedAAs; SetVector Worklist, InvalidAAs; Worklist.insert(DG.SyntheticRoot.begin(), DG.SyntheticRoot.end()); do { // Remember the size to determine new attributes. size_t NumAAs = DG.SyntheticRoot.Deps.size(); LLVM_DEBUG(dbgs() << "\n\n[Attributor] #Iteration: " << IterationCounter << ", Worklist size: " << Worklist.size() << "\n"); // For invalid AAs we can fix dependent AAs that have a required dependence, // thereby folding long dependence chains in a single step without the need // to run updates. for (unsigned u = 0; u < InvalidAAs.size(); ++u) { AbstractAttribute *InvalidAA = InvalidAAs[u]; // Check the dependences to fast track invalidation. LLVM_DEBUG(dbgs() << "[Attributor] InvalidAA: " << *InvalidAA << " has " << InvalidAA->Deps.size() << " required & optional dependences\n"); while (!InvalidAA->Deps.empty()) { const auto &Dep = InvalidAA->Deps.back(); InvalidAA->Deps.pop_back(); AbstractAttribute *DepAA = cast(Dep.getPointer()); if (Dep.getInt() == unsigned(DepClassTy::OPTIONAL)) { Worklist.insert(DepAA); continue; } DepAA->getState().indicatePessimisticFixpoint(); assert(DepAA->getState().isAtFixpoint() && "Expected fixpoint state!"); if (!DepAA->getState().isValidState()) InvalidAAs.insert(DepAA); else ChangedAAs.push_back(DepAA); } } // Add all abstract attributes that are potentially dependent on one that // changed to the work list. for (AbstractAttribute *ChangedAA : ChangedAAs) while (!ChangedAA->Deps.empty()) { Worklist.insert( cast(ChangedAA->Deps.back().getPointer())); ChangedAA->Deps.pop_back(); } LLVM_DEBUG(dbgs() << "[Attributor] #Iteration: " << IterationCounter << ", Worklist+Dependent size: " << Worklist.size() << "\n"); // Reset the changed and invalid set. ChangedAAs.clear(); InvalidAAs.clear(); // Update all abstract attribute in the work list and record the ones that // changed. for (AbstractAttribute *AA : Worklist) { const auto &AAState = AA->getState(); if (!AAState.isAtFixpoint()) if (updateAA(*AA) == ChangeStatus::CHANGED) ChangedAAs.push_back(AA); // Use the InvalidAAs vector to propagate invalid states fast transitively // without requiring updates. if (!AAState.isValidState()) InvalidAAs.insert(AA); } // Add attributes to the changed set if they have been created in the last // iteration. ChangedAAs.append(DG.SyntheticRoot.begin() + NumAAs, DG.SyntheticRoot.end()); // Reset the work list and repopulate with the changed abstract attributes. // Note that dependent ones are added above. Worklist.clear(); Worklist.insert(ChangedAAs.begin(), ChangedAAs.end()); } while (!Worklist.empty() && (IterationCounter++ < MaxFixpointIterations || VerifyMaxFixpointIterations)); LLVM_DEBUG(dbgs() << "\n[Attributor] Fixpoint iteration done after: " << IterationCounter << "/" << MaxFixpointIterations << " iterations\n"); // Reset abstract arguments not settled in a sound fixpoint by now. This // happens when we stopped the fixpoint iteration early. Note that only the // ones marked as "changed" *and* the ones transitively depending on them // need to be reverted to a pessimistic state. Others might not be in a // fixpoint state but we can use the optimistic results for them anyway. SmallPtrSet Visited; for (unsigned u = 0; u < ChangedAAs.size(); u++) { AbstractAttribute *ChangedAA = ChangedAAs[u]; if (!Visited.insert(ChangedAA).second) continue; AbstractState &State = ChangedAA->getState(); if (!State.isAtFixpoint()) { State.indicatePessimisticFixpoint(); NumAttributesTimedOut++; } while (!ChangedAA->Deps.empty()) { ChangedAAs.push_back( cast(ChangedAA->Deps.back().getPointer())); ChangedAA->Deps.pop_back(); } } LLVM_DEBUG({ if (!Visited.empty()) dbgs() << "\n[Attributor] Finalized " << Visited.size() << " abstract attributes.\n"; }); if (VerifyMaxFixpointIterations && IterationCounter != MaxFixpointIterations) { errs() << "\n[Attributor] Fixpoint iteration done after: " << IterationCounter << "/" << MaxFixpointIterations << " iterations\n"; llvm_unreachable("The fixpoint was not reached with exactly the number of " "specified iterations!"); } } ChangeStatus Attributor::manifestAttributes() { TimeTraceScope TimeScope("Attributor::manifestAttributes"); size_t NumFinalAAs = DG.SyntheticRoot.Deps.size(); unsigned NumManifested = 0; unsigned NumAtFixpoint = 0; ChangeStatus ManifestChange = ChangeStatus::UNCHANGED; for (auto &DepAA : DG.SyntheticRoot.Deps) { AbstractAttribute *AA = cast(DepAA.getPointer()); AbstractState &State = AA->getState(); // If there is not already a fixpoint reached, we can now take the // optimistic state. This is correct because we enforced a pessimistic one // on abstract attributes that were transitively dependent on a changed one // already above. if (!State.isAtFixpoint()) State.indicateOptimisticFixpoint(); // If the state is invalid, we do not try to manifest it. if (!State.isValidState()) continue; // Skip dead code. if (isAssumedDead(*AA, nullptr, /* CheckBBLivenessOnly */ true)) continue; // Check if the manifest debug counter that allows skipping manifestation of // AAs if (!DebugCounter::shouldExecute(ManifestDBGCounter)) continue; // Manifest the state and record if we changed the IR. ChangeStatus LocalChange = AA->manifest(*this); if (LocalChange == ChangeStatus::CHANGED && AreStatisticsEnabled()) AA->trackStatistics(); LLVM_DEBUG(dbgs() << "[Attributor] Manifest " << LocalChange << " : " << *AA << "\n"); ManifestChange = ManifestChange | LocalChange; NumAtFixpoint++; NumManifested += (LocalChange == ChangeStatus::CHANGED); } (void)NumManifested; (void)NumAtFixpoint; LLVM_DEBUG(dbgs() << "\n[Attributor] Manifested " << NumManifested << " arguments while " << NumAtFixpoint << " were in a valid fixpoint state\n"); NumAttributesManifested += NumManifested; NumAttributesValidFixpoint += NumAtFixpoint; (void)NumFinalAAs; if (NumFinalAAs != DG.SyntheticRoot.Deps.size()) { for (unsigned u = NumFinalAAs; u < DG.SyntheticRoot.Deps.size(); ++u) errs() << "Unexpected abstract attribute: " << cast(DG.SyntheticRoot.Deps[u].getPointer()) << " :: " << cast(DG.SyntheticRoot.Deps[u].getPointer()) ->getIRPosition() .getAssociatedValue() << "\n"; llvm_unreachable("Expected the final number of abstract attributes to " "remain unchanged!"); } return ManifestChange; } void Attributor::identifyDeadInternalFunctions() { // Identify dead internal functions and delete them. This happens outside // the other fixpoint analysis as we might treat potentially dead functions // as live to lower the number of iterations. If they happen to be dead, the // below fixpoint loop will identify and eliminate them. SmallVector InternalFns; for (Function *F : Functions) if (F->hasLocalLinkage()) InternalFns.push_back(F); SmallPtrSet LiveInternalFns; bool FoundLiveInternal = true; while (FoundLiveInternal) { FoundLiveInternal = false; for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) { Function *F = InternalFns[u]; if (!F) continue; bool AllCallSitesKnown; if (checkForAllCallSites( [&](AbstractCallSite ACS) { Function *Callee = ACS.getInstruction()->getFunction(); return ToBeDeletedFunctions.count(Callee) || (Functions.count(Callee) && Callee->hasLocalLinkage() && !LiveInternalFns.count(Callee)); }, *F, true, nullptr, AllCallSitesKnown)) { continue; } LiveInternalFns.insert(F); InternalFns[u] = nullptr; FoundLiveInternal = true; } } for (unsigned u = 0, e = InternalFns.size(); u < e; ++u) if (Function *F = InternalFns[u]) ToBeDeletedFunctions.insert(F); } ChangeStatus Attributor::cleanupIR() { TimeTraceScope TimeScope("Attributor::cleanupIR"); // Delete stuff at the end to avoid invalid references and a nice order. LLVM_DEBUG(dbgs() << "\n[Attributor] Delete at least " << ToBeDeletedFunctions.size() << " functions and " << ToBeDeletedBlocks.size() << " blocks and " << ToBeDeletedInsts.size() << " instructions and " << ToBeChangedUses.size() << " uses\n"); SmallVector DeadInsts; SmallVector TerminatorsToFold; for (auto &It : ToBeChangedUses) { Use *U = It.first; Value *NewV = It.second; Value *OldV = U->get(); // Do not replace uses in returns if the value is a must-tail call we will // not delete. if (isa(U->getUser())) if (auto *CI = dyn_cast(OldV->stripPointerCasts())) if (CI->isMustTailCall() && !ToBeDeletedInsts.count(CI)) continue; LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser() << " instead of " << *OldV << "\n"); U->set(NewV); // Do not modify call instructions outside the SCC. if (auto *CB = dyn_cast(OldV)) if (!Functions.count(CB->getCaller())) continue; if (Instruction *I = dyn_cast(OldV)) { CGModifiedFunctions.insert(I->getFunction()); if (!isa(I) && !ToBeDeletedInsts.count(I) && isInstructionTriviallyDead(I)) DeadInsts.push_back(I); } if (isa(NewV) && isa(U->getUser())) { Instruction *UserI = cast(U->getUser()); if (isa(NewV)) { ToBeChangedToUnreachableInsts.insert(UserI); } else { TerminatorsToFold.push_back(UserI); } } } for (auto &V : InvokeWithDeadSuccessor) if (InvokeInst *II = dyn_cast_or_null(V)) { bool UnwindBBIsDead = II->hasFnAttr(Attribute::NoUnwind); bool NormalBBIsDead = II->hasFnAttr(Attribute::NoReturn); bool Invoke2CallAllowed = !AAIsDead::mayCatchAsynchronousExceptions(*II->getFunction()); assert((UnwindBBIsDead || NormalBBIsDead) && "Invoke does not have dead successors!"); BasicBlock *BB = II->getParent(); BasicBlock *NormalDestBB = II->getNormalDest(); if (UnwindBBIsDead) { Instruction *NormalNextIP = &NormalDestBB->front(); if (Invoke2CallAllowed) { changeToCall(II); NormalNextIP = BB->getTerminator(); } if (NormalBBIsDead) ToBeChangedToUnreachableInsts.insert(NormalNextIP); } else { assert(NormalBBIsDead && "Broken invariant!"); if (!NormalDestBB->getUniquePredecessor()) NormalDestBB = SplitBlockPredecessors(NormalDestBB, {BB}, ".dead"); ToBeChangedToUnreachableInsts.insert(&NormalDestBB->front()); } } for (Instruction *I : TerminatorsToFold) { CGModifiedFunctions.insert(I->getFunction()); ConstantFoldTerminator(I->getParent()); } for (auto &V : ToBeChangedToUnreachableInsts) if (Instruction *I = dyn_cast_or_null(V)) { CGModifiedFunctions.insert(I->getFunction()); changeToUnreachable(I, /* UseLLVMTrap */ false); } for (auto &V : ToBeDeletedInsts) { if (Instruction *I = dyn_cast_or_null(V)) { I->dropDroppableUses(); CGModifiedFunctions.insert(I->getFunction()); if (!I->getType()->isVoidTy()) I->replaceAllUsesWith(UndefValue::get(I->getType())); if (!isa(I) && isInstructionTriviallyDead(I)) DeadInsts.push_back(I); else I->eraseFromParent(); } } LLVM_DEBUG(dbgs() << "[Attributor] DeadInsts size: " << DeadInsts.size() << "\n"); RecursivelyDeleteTriviallyDeadInstructions(DeadInsts); if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) { SmallVector ToBeDeletedBBs; ToBeDeletedBBs.reserve(NumDeadBlocks); for (BasicBlock *BB : ToBeDeletedBlocks) { CGModifiedFunctions.insert(BB->getParent()); ToBeDeletedBBs.push_back(BB); } // Actually we do not delete the blocks but squash them into a single // unreachable but untangling branches that jump here is something we need // to do in a more generic way. DetatchDeadBlocks(ToBeDeletedBBs, nullptr); } identifyDeadInternalFunctions(); // Rewrite the functions as requested during manifest. ChangeStatus ManifestChange = rewriteFunctionSignatures(CGModifiedFunctions); for (Function *Fn : CGModifiedFunctions) if (!ToBeDeletedFunctions.count(Fn)) CGUpdater.reanalyzeFunction(*Fn); for (Function *Fn : ToBeDeletedFunctions) { if (!Functions.count(Fn)) continue; CGUpdater.removeFunction(*Fn); } if (!ToBeChangedUses.empty()) ManifestChange = ChangeStatus::CHANGED; if (!ToBeChangedToUnreachableInsts.empty()) ManifestChange = ChangeStatus::CHANGED; if (!ToBeDeletedFunctions.empty()) ManifestChange = ChangeStatus::CHANGED; if (!ToBeDeletedBlocks.empty()) ManifestChange = ChangeStatus::CHANGED; if (!ToBeDeletedInsts.empty()) ManifestChange = ChangeStatus::CHANGED; if (!InvokeWithDeadSuccessor.empty()) ManifestChange = ChangeStatus::CHANGED; if (!DeadInsts.empty()) ManifestChange = ChangeStatus::CHANGED; NumFnDeleted += ToBeDeletedFunctions.size(); LLVM_DEBUG(dbgs() << "[Attributor] Deleted " << ToBeDeletedFunctions.size() << " functions after manifest.\n"); #ifdef EXPENSIVE_CHECKS for (Function *F : Functions) { if (ToBeDeletedFunctions.count(F)) continue; assert(!verifyFunction(*F, &errs()) && "Module verification failed!"); } #endif return ManifestChange; } ChangeStatus Attributor::run() { TimeTraceScope TimeScope("Attributor::run"); Phase = AttributorPhase::UPDATE; runTillFixpoint(); // dump graphs on demand if (DumpDepGraph) DG.dumpGraph(); if (ViewDepGraph) DG.viewGraph(); if (PrintDependencies) DG.print(); Phase = AttributorPhase::MANIFEST; ChangeStatus ManifestChange = manifestAttributes(); Phase = AttributorPhase::CLEANUP; ChangeStatus CleanupChange = cleanupIR(); return ManifestChange | CleanupChange; } ChangeStatus Attributor::updateAA(AbstractAttribute &AA) { TimeTraceScope TimeScope( AA.getName() + std::to_string(AA.getIRPosition().getPositionKind()) + "::updateAA"); assert(Phase == AttributorPhase::UPDATE && "We can update AA only in the update stage!"); // Use a new dependence vector for this update. DependenceVector DV; DependenceStack.push_back(&DV); auto &AAState = AA.getState(); ChangeStatus CS = ChangeStatus::UNCHANGED; if (!isAssumedDead(AA, nullptr, /* CheckBBLivenessOnly */ true)) CS = AA.update(*this); if (DV.empty()) { // If the attribute did not query any non-fix information, the state // will not change and we can indicate that right away. AAState.indicateOptimisticFixpoint(); } if (!AAState.isAtFixpoint()) rememberDependences(); // Verify the stack was used properly, that is we pop the dependence vector we // put there earlier. DependenceVector *PoppedDV = DependenceStack.pop_back_val(); (void)PoppedDV; assert(PoppedDV == &DV && "Inconsistent usage of the dependence stack!"); return CS; } void Attributor::createShallowWrapper(Function &F) { assert(!F.isDeclaration() && "Cannot create a wrapper around a declaration!"); Module &M = *F.getParent(); LLVMContext &Ctx = M.getContext(); FunctionType *FnTy = F.getFunctionType(); Function *Wrapper = Function::Create(FnTy, F.getLinkage(), F.getAddressSpace(), F.getName()); F.setName(""); // set the inside function anonymous M.getFunctionList().insert(F.getIterator(), Wrapper); F.setLinkage(GlobalValue::InternalLinkage); F.replaceAllUsesWith(Wrapper); assert(F.use_empty() && "Uses remained after wrapper was created!"); // Move the COMDAT section to the wrapper. // TODO: Check if we need to keep it for F as well. Wrapper->setComdat(F.getComdat()); F.setComdat(nullptr); // Copy all metadata and attributes but keep them on F as well. SmallVector, 1> MDs; F.getAllMetadata(MDs); for (auto MDIt : MDs) Wrapper->addMetadata(MDIt.first, *MDIt.second); Wrapper->setAttributes(F.getAttributes()); // Create the call in the wrapper. BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", Wrapper); SmallVector Args; Argument *FArgIt = F.arg_begin(); for (Argument &Arg : Wrapper->args()) { Args.push_back(&Arg); Arg.setName((FArgIt++)->getName()); } CallInst *CI = CallInst::Create(&F, Args, "", EntryBB); CI->setTailCall(true); CI->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline); ReturnInst::Create(Ctx, CI->getType()->isVoidTy() ? nullptr : CI, EntryBB); NumFnShallowWrappersCreated++; } /// Make another copy of the function \p F such that the copied version has /// internal linkage afterwards and can be analysed. Then we replace all uses /// of the original function to the copied one /// /// Only non-exactly defined functions that have `linkonce_odr` or `weak_odr` /// linkage can be internalized because these linkages guarantee that other /// definitions with the same name have the same semantics as this one /// static Function *internalizeFunction(Function &F) { assert(AllowDeepWrapper && "Cannot create a copy if not allowed."); assert(!F.isDeclaration() && !F.hasExactDefinition() && !GlobalValue::isInterposableLinkage(F.getLinkage()) && "Trying to internalize function which cannot be internalized."); Module &M = *F.getParent(); FunctionType *FnTy = F.getFunctionType(); // create a copy of the current function Function *Copied = Function::Create(FnTy, F.getLinkage(), F.getAddressSpace(), F.getName() + ".internalized"); ValueToValueMapTy VMap; auto *NewFArgIt = Copied->arg_begin(); for (auto &Arg : F.args()) { auto ArgName = Arg.getName(); NewFArgIt->setName(ArgName); VMap[&Arg] = &(*NewFArgIt++); } SmallVector Returns; // Copy the body of the original function to the new one CloneFunctionInto(Copied, &F, VMap, /* ModuleLevelChanges */ false, Returns); // Set the linakage and visibility late as CloneFunctionInto has some implicit // requirements. Copied->setVisibility(GlobalValue::DefaultVisibility); Copied->setLinkage(GlobalValue::PrivateLinkage); // Copy metadata SmallVector, 1> MDs; F.getAllMetadata(MDs); for (auto MDIt : MDs) Copied->addMetadata(MDIt.first, *MDIt.second); M.getFunctionList().insert(F.getIterator(), Copied); F.replaceAllUsesWith(Copied); Copied->setDSOLocal(true); return Copied; } bool Attributor::isValidFunctionSignatureRewrite( Argument &Arg, ArrayRef ReplacementTypes) { auto CallSiteCanBeChanged = [](AbstractCallSite ACS) { // Forbid the call site to cast the function return type. If we need to // rewrite these functions we need to re-create a cast for the new call site // (if the old had uses). if (!ACS.getCalledFunction() || ACS.getInstruction()->getType() != ACS.getCalledFunction()->getReturnType()) return false; // Forbid must-tail calls for now. return !ACS.isCallbackCall() && !ACS.getInstruction()->isMustTailCall(); }; Function *Fn = Arg.getParent(); // Avoid var-arg functions for now. if (Fn->isVarArg()) { LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite var-args functions\n"); return false; } // Avoid functions with complicated argument passing semantics. AttributeList FnAttributeList = Fn->getAttributes(); if (FnAttributeList.hasAttrSomewhere(Attribute::Nest) || FnAttributeList.hasAttrSomewhere(Attribute::StructRet) || FnAttributeList.hasAttrSomewhere(Attribute::InAlloca) || FnAttributeList.hasAttrSomewhere(Attribute::Preallocated)) { LLVM_DEBUG( dbgs() << "[Attributor] Cannot rewrite due to complex attribute\n"); return false; } // Avoid callbacks for now. bool AllCallSitesKnown; if (!checkForAllCallSites(CallSiteCanBeChanged, *Fn, true, nullptr, AllCallSitesKnown)) { LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite all call sites\n"); return false; } auto InstPred = [](Instruction &I) { if (auto *CI = dyn_cast(&I)) return !CI->isMustTailCall(); return true; }; // Forbid must-tail calls for now. // TODO: auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn); if (!checkForAllInstructionsImpl(nullptr, OpcodeInstMap, InstPred, nullptr, nullptr, {Instruction::Call})) { LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite due to instructions\n"); return false; } return true; } bool Attributor::registerFunctionSignatureRewrite( Argument &Arg, ArrayRef ReplacementTypes, ArgumentReplacementInfo::CalleeRepairCBTy &&CalleeRepairCB, ArgumentReplacementInfo::ACSRepairCBTy &&ACSRepairCB) { LLVM_DEBUG(dbgs() << "[Attributor] Register new rewrite of " << Arg << " in " << Arg.getParent()->getName() << " with " << ReplacementTypes.size() << " replacements\n"); assert(isValidFunctionSignatureRewrite(Arg, ReplacementTypes) && "Cannot register an invalid rewrite"); Function *Fn = Arg.getParent(); SmallVectorImpl> &ARIs = ArgumentReplacementMap[Fn]; if (ARIs.empty()) ARIs.resize(Fn->arg_size()); // If we have a replacement already with less than or equal new arguments, // ignore this request. std::unique_ptr &ARI = ARIs[Arg.getArgNo()]; if (ARI && ARI->getNumReplacementArgs() <= ReplacementTypes.size()) { LLVM_DEBUG(dbgs() << "[Attributor] Existing rewrite is preferred\n"); return false; } // If we have a replacement already but we like the new one better, delete // the old. ARI.reset(); LLVM_DEBUG(dbgs() << "[Attributor] Register new rewrite of " << Arg << " in " << Arg.getParent()->getName() << " with " << ReplacementTypes.size() << " replacements\n"); // Remember the replacement. ARI.reset(new ArgumentReplacementInfo(*this, Arg, ReplacementTypes, std::move(CalleeRepairCB), std::move(ACSRepairCB))); return true; } bool Attributor::shouldSeedAttribute(AbstractAttribute &AA) { bool Result = true; #ifndef NDEBUG if (SeedAllowList.size() != 0) Result = std::count(SeedAllowList.begin(), SeedAllowList.end(), AA.getName()); Function *Fn = AA.getAnchorScope(); if (FunctionSeedAllowList.size() != 0 && Fn) Result &= std::count(FunctionSeedAllowList.begin(), FunctionSeedAllowList.end(), Fn->getName()); #endif return Result; } ChangeStatus Attributor::rewriteFunctionSignatures( SmallPtrSetImpl &ModifiedFns) { ChangeStatus Changed = ChangeStatus::UNCHANGED; for (auto &It : ArgumentReplacementMap) { Function *OldFn = It.getFirst(); // Deleted functions do not require rewrites. if (!Functions.count(OldFn) || ToBeDeletedFunctions.count(OldFn)) continue; const SmallVectorImpl> &ARIs = It.getSecond(); assert(ARIs.size() == OldFn->arg_size() && "Inconsistent state!"); SmallVector NewArgumentTypes; SmallVector NewArgumentAttributes; // Collect replacement argument types and copy over existing attributes. AttributeList OldFnAttributeList = OldFn->getAttributes(); for (Argument &Arg : OldFn->args()) { if (const std::unique_ptr &ARI = ARIs[Arg.getArgNo()]) { NewArgumentTypes.append(ARI->ReplacementTypes.begin(), ARI->ReplacementTypes.end()); NewArgumentAttributes.append(ARI->getNumReplacementArgs(), AttributeSet()); } else { NewArgumentTypes.push_back(Arg.getType()); NewArgumentAttributes.push_back( OldFnAttributeList.getParamAttributes(Arg.getArgNo())); } } FunctionType *OldFnTy = OldFn->getFunctionType(); Type *RetTy = OldFnTy->getReturnType(); // Construct the new function type using the new arguments types. FunctionType *NewFnTy = FunctionType::get(RetTy, NewArgumentTypes, OldFnTy->isVarArg()); LLVM_DEBUG(dbgs() << "[Attributor] Function rewrite '" << OldFn->getName() << "' from " << *OldFn->getFunctionType() << " to " << *NewFnTy << "\n"); // Create the new function body and insert it into the module. Function *NewFn = Function::Create(NewFnTy, OldFn->getLinkage(), OldFn->getAddressSpace(), ""); OldFn->getParent()->getFunctionList().insert(OldFn->getIterator(), NewFn); NewFn->takeName(OldFn); NewFn->copyAttributesFrom(OldFn); // Patch the pointer to LLVM function in debug info descriptor. NewFn->setSubprogram(OldFn->getSubprogram()); OldFn->setSubprogram(nullptr); // Recompute the parameter attributes list based on the new arguments for // the function. LLVMContext &Ctx = OldFn->getContext(); NewFn->setAttributes(AttributeList::get( Ctx, OldFnAttributeList.getFnAttributes(), OldFnAttributeList.getRetAttributes(), NewArgumentAttributes)); // Since we have now created the new function, splice the body of the old // function right into the new function, leaving the old rotting hulk of the // function empty. NewFn->getBasicBlockList().splice(NewFn->begin(), OldFn->getBasicBlockList()); // Fixup block addresses to reference new function. SmallVector BlockAddresses; for (User *U : OldFn->users()) if (auto *BA = dyn_cast(U)) BlockAddresses.push_back(BA); for (auto *BA : BlockAddresses) BA->replaceAllUsesWith(BlockAddress::get(NewFn, BA->getBasicBlock())); // Set of all "call-like" instructions that invoke the old function mapped // to their new replacements. SmallVector, 8> CallSitePairs; // Callback to create a new "call-like" instruction for a given one. auto CallSiteReplacementCreator = [&](AbstractCallSite ACS) { CallBase *OldCB = cast(ACS.getInstruction()); const AttributeList &OldCallAttributeList = OldCB->getAttributes(); // Collect the new argument operands for the replacement call site. SmallVector NewArgOperands; SmallVector NewArgOperandAttributes; for (unsigned OldArgNum = 0; OldArgNum < ARIs.size(); ++OldArgNum) { unsigned NewFirstArgNum = NewArgOperands.size(); (void)NewFirstArgNum; // only used inside assert. if (const std::unique_ptr &ARI = ARIs[OldArgNum]) { if (ARI->ACSRepairCB) ARI->ACSRepairCB(*ARI, ACS, NewArgOperands); assert(ARI->getNumReplacementArgs() + NewFirstArgNum == NewArgOperands.size() && "ACS repair callback did not provide as many operand as new " "types were registered!"); // TODO: Exose the attribute set to the ACS repair callback NewArgOperandAttributes.append(ARI->ReplacementTypes.size(), AttributeSet()); } else { NewArgOperands.push_back(ACS.getCallArgOperand(OldArgNum)); NewArgOperandAttributes.push_back( OldCallAttributeList.getParamAttributes(OldArgNum)); } } assert(NewArgOperands.size() == NewArgOperandAttributes.size() && "Mismatch # argument operands vs. # argument operand attributes!"); assert(NewArgOperands.size() == NewFn->arg_size() && "Mismatch # argument operands vs. # function arguments!"); SmallVector OperandBundleDefs; OldCB->getOperandBundlesAsDefs(OperandBundleDefs); // Create a new call or invoke instruction to replace the old one. CallBase *NewCB; if (InvokeInst *II = dyn_cast(OldCB)) { NewCB = InvokeInst::Create(NewFn, II->getNormalDest(), II->getUnwindDest(), NewArgOperands, OperandBundleDefs, "", OldCB); } else { auto *NewCI = CallInst::Create(NewFn, NewArgOperands, OperandBundleDefs, "", OldCB); NewCI->setTailCallKind(cast(OldCB)->getTailCallKind()); NewCB = NewCI; } // Copy over various properties and the new attributes. NewCB->copyMetadata(*OldCB, {LLVMContext::MD_prof, LLVMContext::MD_dbg}); NewCB->setCallingConv(OldCB->getCallingConv()); NewCB->takeName(OldCB); NewCB->setAttributes(AttributeList::get( Ctx, OldCallAttributeList.getFnAttributes(), OldCallAttributeList.getRetAttributes(), NewArgOperandAttributes)); CallSitePairs.push_back({OldCB, NewCB}); return true; }; // Use the CallSiteReplacementCreator to create replacement call sites. bool AllCallSitesKnown; bool Success = checkForAllCallSites(CallSiteReplacementCreator, *OldFn, true, nullptr, AllCallSitesKnown); (void)Success; assert(Success && "Assumed call site replacement to succeed!"); // Rewire the arguments. Argument *OldFnArgIt = OldFn->arg_begin(); Argument *NewFnArgIt = NewFn->arg_begin(); for (unsigned OldArgNum = 0; OldArgNum < ARIs.size(); ++OldArgNum, ++OldFnArgIt) { if (const std::unique_ptr &ARI = ARIs[OldArgNum]) { if (ARI->CalleeRepairCB) ARI->CalleeRepairCB(*ARI, *NewFn, NewFnArgIt); NewFnArgIt += ARI->ReplacementTypes.size(); } else { NewFnArgIt->takeName(&*OldFnArgIt); OldFnArgIt->replaceAllUsesWith(&*NewFnArgIt); ++NewFnArgIt; } } // Eliminate the instructions *after* we visited all of them. for (auto &CallSitePair : CallSitePairs) { CallBase &OldCB = *CallSitePair.first; CallBase &NewCB = *CallSitePair.second; assert(OldCB.getType() == NewCB.getType() && "Cannot handle call sites with different types!"); ModifiedFns.insert(OldCB.getFunction()); CGUpdater.replaceCallSite(OldCB, NewCB); OldCB.replaceAllUsesWith(&NewCB); OldCB.eraseFromParent(); } // Replace the function in the call graph (if any). CGUpdater.replaceFunctionWith(*OldFn, *NewFn); // If the old function was modified and needed to be reanalyzed, the new one // does now. if (ModifiedFns.erase(OldFn)) ModifiedFns.insert(NewFn); Changed = ChangeStatus::CHANGED; } return Changed; } void InformationCache::initializeInformationCache(const Function &CF, FunctionInfo &FI) { // As we do not modify the function here we can remove the const // withouth breaking implicit assumptions. At the end of the day, we could // initialize the cache eagerly which would look the same to the users. Function &F = const_cast(CF); // Walk all instructions to find interesting instructions that might be // queried by abstract attributes during their initialization or update. // This has to happen before we create attributes. for (Instruction &I : instructions(&F)) { bool IsInterestingOpcode = false; // To allow easy access to all instructions in a function with a given // opcode we store them in the InfoCache. As not all opcodes are interesting // to concrete attributes we only cache the ones that are as identified in // the following switch. // Note: There are no concrete attributes now so this is initially empty. switch (I.getOpcode()) { default: assert(!isa(&I) && "New call base instruction type needs to be known in the " "Attributor."); break; case Instruction::Call: // Calls are interesting on their own, additionally: // For `llvm.assume` calls we also fill the KnowledgeMap as we find them. // For `must-tail` calls we remember the caller and callee. if (IntrinsicInst *Assume = dyn_cast(&I)) { if (Assume->getIntrinsicID() == Intrinsic::assume) fillMapFromAssume(*Assume, KnowledgeMap); } else if (cast(I).isMustTailCall()) { FI.ContainsMustTailCall = true; if (const Function *Callee = cast(I).getCalledFunction()) getFunctionInfo(*Callee).CalledViaMustTail = true; } LLVM_FALLTHROUGH; case Instruction::CallBr: case Instruction::Invoke: case Instruction::CleanupRet: case Instruction::CatchSwitch: case Instruction::AtomicRMW: case Instruction::AtomicCmpXchg: case Instruction::Br: case Instruction::Resume: case Instruction::Ret: case Instruction::Load: // The alignment of a pointer is interesting for loads. case Instruction::Store: // The alignment of a pointer is interesting for stores. IsInterestingOpcode = true; } if (IsInterestingOpcode) { auto *&Insts = FI.OpcodeInstMap[I.getOpcode()]; if (!Insts) Insts = new (Allocator) InstructionVectorTy(); Insts->push_back(&I); } if (I.mayReadOrWriteMemory()) FI.RWInsts.push_back(&I); } if (F.hasFnAttribute(Attribute::AlwaysInline) && isInlineViable(F).isSuccess()) InlineableFunctions.insert(&F); } AAResults *InformationCache::getAAResultsForFunction(const Function &F) { return AG.getAnalysis(F); } InformationCache::FunctionInfo::~FunctionInfo() { // The instruction vectors are allocated using a BumpPtrAllocator, we need to // manually destroy them. for (auto &It : OpcodeInstMap) It.getSecond()->~InstructionVectorTy(); } void Attributor::recordDependence(const AbstractAttribute &FromAA, const AbstractAttribute &ToAA, DepClassTy DepClass) { // If we are outside of an update, thus before the actual fixpoint iteration // started (= when we create AAs), we do not track dependences because we will // put all AAs into the initial worklist anyway. if (DependenceStack.empty()) return; if (FromAA.getState().isAtFixpoint()) return; DependenceStack.back()->push_back({&FromAA, &ToAA, DepClass}); } void Attributor::rememberDependences() { assert(!DependenceStack.empty() && "No dependences to remember!"); for (DepInfo &DI : *DependenceStack.back()) { auto &DepAAs = const_cast(*DI.FromAA).Deps; DepAAs.push_back(AbstractAttribute::DepTy( const_cast(DI.ToAA), unsigned(DI.DepClass))); } } void Attributor::identifyDefaultAbstractAttributes(Function &F) { if (!VisitedFunctions.insert(&F).second) return; if (F.isDeclaration()) return; // In non-module runs we need to look at the call sites of a function to // determine if it is part of a must-tail call edge. This will influence what // attributes we can derive. InformationCache::FunctionInfo &FI = InfoCache.getFunctionInfo(F); if (!isModulePass() && !FI.CalledViaMustTail) { for (const Use &U : F.uses()) if (const auto *CB = dyn_cast(U.getUser())) if (CB->isCallee(&U) && CB->isMustTailCall()) FI.CalledViaMustTail = true; } IRPosition FPos = IRPosition::function(F); // Check for dead BasicBlocks in every function. // We need dead instruction detection because we do not want to deal with // broken IR in which SSA rules do not apply. getOrCreateAAFor(FPos); // Every function might be "will-return". getOrCreateAAFor(FPos); // Every function might contain instructions that cause "undefined behavior". getOrCreateAAFor(FPos); // Every function can be nounwind. getOrCreateAAFor(FPos); // Every function might be marked "nosync" getOrCreateAAFor(FPos); // Every function might be "no-free". getOrCreateAAFor(FPos); // Every function might be "no-return". getOrCreateAAFor(FPos); // Every function might be "no-recurse". getOrCreateAAFor(FPos); // Every function might be "readnone/readonly/writeonly/...". getOrCreateAAFor(FPos); // Every function can be "readnone/argmemonly/inaccessiblememonly/...". getOrCreateAAFor(FPos); // Every function might be applicable for Heap-To-Stack conversion. if (EnableHeapToStack) getOrCreateAAFor(FPos); // Return attributes are only appropriate if the return type is non void. Type *ReturnType = F.getReturnType(); if (!ReturnType->isVoidTy()) { // Argument attribute "returned" --- Create only one per function even // though it is an argument attribute. getOrCreateAAFor(FPos); IRPosition RetPos = IRPosition::returned(F); // Every returned value might be dead. getOrCreateAAFor(RetPos); // Every function might be simplified. getOrCreateAAFor(RetPos); // Every returned value might be marked noundef. getOrCreateAAFor(RetPos); if (ReturnType->isPointerTy()) { // Every function with pointer return type might be marked align. getOrCreateAAFor(RetPos); // Every function with pointer return type might be marked nonnull. getOrCreateAAFor(RetPos); // Every function with pointer return type might be marked noalias. getOrCreateAAFor(RetPos); // Every function with pointer return type might be marked // dereferenceable. getOrCreateAAFor(RetPos); } } for (Argument &Arg : F.args()) { IRPosition ArgPos = IRPosition::argument(Arg); // Every argument might be simplified. getOrCreateAAFor(ArgPos); // Every argument might be dead. getOrCreateAAFor(ArgPos); // Every argument might be marked noundef. getOrCreateAAFor(ArgPos); if (Arg.getType()->isPointerTy()) { // Every argument with pointer type might be marked nonnull. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked noalias. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked dereferenceable. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked align. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked nocapture. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked // "readnone/readonly/writeonly/..." getOrCreateAAFor(ArgPos); // Every argument with pointer type might be marked nofree. getOrCreateAAFor(ArgPos); // Every argument with pointer type might be privatizable (or promotable) getOrCreateAAFor(ArgPos); } } auto CallSitePred = [&](Instruction &I) -> bool { auto &CB = cast(I); IRPosition CBRetPos = IRPosition::callsite_returned(CB); // Call sites might be dead if they do not have side effects and no live // users. The return value might be dead if there are no live users. getOrCreateAAFor(CBRetPos); Function *Callee = CB.getCalledFunction(); // TODO: Even if the callee is not known now we might be able to simplify // the call/callee. if (!Callee) return true; // Skip declarations except if annotations on their call sites were // explicitly requested. if (!AnnotateDeclarationCallSites && Callee->isDeclaration() && !Callee->hasMetadata(LLVMContext::MD_callback)) return true; if (!Callee->getReturnType()->isVoidTy() && !CB.use_empty()) { IRPosition CBRetPos = IRPosition::callsite_returned(CB); // Call site return integer values might be limited by a constant range. if (Callee->getReturnType()->isIntegerTy()) getOrCreateAAFor(CBRetPos); } for (int I = 0, E = CB.getNumArgOperands(); I < E; ++I) { IRPosition CBArgPos = IRPosition::callsite_argument(CB, I); // Every call site argument might be dead. getOrCreateAAFor(CBArgPos); // Call site argument might be simplified. getOrCreateAAFor(CBArgPos); // Every call site argument might be marked "noundef". getOrCreateAAFor(CBArgPos); if (!CB.getArgOperand(I)->getType()->isPointerTy()) continue; // Call site argument attribute "non-null". getOrCreateAAFor(CBArgPos); // Call site argument attribute "nocapture". getOrCreateAAFor(CBArgPos); // Call site argument attribute "no-alias". getOrCreateAAFor(CBArgPos); // Call site argument attribute "dereferenceable". getOrCreateAAFor(CBArgPos); // Call site argument attribute "align". getOrCreateAAFor(CBArgPos); // Call site argument attribute // "readnone/readonly/writeonly/..." getOrCreateAAFor(CBArgPos); // Call site argument attribute "nofree". getOrCreateAAFor(CBArgPos); } return true; }; auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); bool Success; Success = checkForAllInstructionsImpl( nullptr, OpcodeInstMap, CallSitePred, nullptr, nullptr, {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, (unsigned)Instruction::Call}); (void)Success; assert(Success && "Expected the check call to be successful!"); auto LoadStorePred = [&](Instruction &I) -> bool { if (isa(I)) getOrCreateAAFor( IRPosition::value(*cast(I).getPointerOperand())); else getOrCreateAAFor( IRPosition::value(*cast(I).getPointerOperand())); return true; }; Success = checkForAllInstructionsImpl( nullptr, OpcodeInstMap, LoadStorePred, nullptr, nullptr, {(unsigned)Instruction::Load, (unsigned)Instruction::Store}); (void)Success; assert(Success && "Expected the check call to be successful!"); } /// Helpers to ease debugging through output streams and print calls. /// ///{ raw_ostream &llvm::operator<<(raw_ostream &OS, ChangeStatus S) { return OS << (S == ChangeStatus::CHANGED ? "changed" : "unchanged"); } raw_ostream &llvm::operator<<(raw_ostream &OS, IRPosition::Kind AP) { switch (AP) { case IRPosition::IRP_INVALID: return OS << "inv"; case IRPosition::IRP_FLOAT: return OS << "flt"; case IRPosition::IRP_RETURNED: return OS << "fn_ret"; case IRPosition::IRP_CALL_SITE_RETURNED: return OS << "cs_ret"; case IRPosition::IRP_FUNCTION: return OS << "fn"; case IRPosition::IRP_CALL_SITE: return OS << "cs"; case IRPosition::IRP_ARGUMENT: return OS << "arg"; case IRPosition::IRP_CALL_SITE_ARGUMENT: return OS << "cs_arg"; } llvm_unreachable("Unknown attribute position!"); } raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) { const Value &AV = Pos.getAssociatedValue(); return OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " [" << Pos.getAnchorValue().getName() << "@" << Pos.getCallSiteArgNo() << "]}"; } raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerRangeState &S) { OS << "range-state(" << S.getBitWidth() << ")<"; S.getKnown().print(OS); OS << " / "; S.getAssumed().print(OS); OS << ">"; return OS << static_cast(S); } raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractState &S) { return OS << (!S.isValidState() ? "top" : (S.isAtFixpoint() ? "fix" : "")); } raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractAttribute &AA) { AA.print(OS); return OS; } raw_ostream &llvm::operator<<(raw_ostream &OS, const PotentialConstantIntValuesState &S) { OS << "set-state(< {"; if (!S.isValidState()) OS << "full-set"; else { for (auto &it : S.getAssumedSet()) OS << it << ", "; if (S.undefIsContained()) OS << "undef "; } OS << "} >)"; return OS; } void AbstractAttribute::print(raw_ostream &OS) const { OS << "["; OS << getName(); OS << "] for CtxI "; if (auto *I = getCtxI()) { OS << "'"; I->print(OS); OS << "'"; } else OS << "<>"; OS << " at position " << getIRPosition() << " with state " << getAsStr() << '\n'; } void AbstractAttribute::printWithDeps(raw_ostream &OS) const { print(OS); for (const auto &DepAA : Deps) { auto *AA = DepAA.getPointer(); OS << " updates "; AA->print(OS); } OS << '\n'; } ///} /// ---------------------------------------------------------------------------- /// Pass (Manager) Boilerplate /// ---------------------------------------------------------------------------- static bool runAttributorOnFunctions(InformationCache &InfoCache, SetVector &Functions, AnalysisGetter &AG, CallGraphUpdater &CGUpdater) { if (Functions.empty()) return false; LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << Functions.size() << " functions.\n"); // Create an Attributor and initially empty information cache that is filled // while we identify default attribute opportunities. Attributor A(Functions, InfoCache, CGUpdater); // Create shallow wrappers for all functions that are not IPO amendable if (AllowShallowWrappers) for (Function *F : Functions) if (!A.isFunctionIPOAmendable(*F)) Attributor::createShallowWrapper(*F); // Internalize non-exact functions // TODO: for now we eagerly internalize functions without calculating the // cost, we need a cost interface to determine whether internalizing // a function is "benefitial" if (AllowDeepWrapper) { unsigned FunSize = Functions.size(); for (unsigned u = 0; u < FunSize; u++) { Function *F = Functions[u]; if (!F->isDeclaration() && !F->isDefinitionExact() && F->getNumUses() && !GlobalValue::isInterposableLinkage(F->getLinkage())) { Function *NewF = internalizeFunction(*F); Functions.insert(NewF); // Update call graph CGUpdater.replaceFunctionWith(*F, *NewF); for (const Use &U : NewF->uses()) if (CallBase *CB = dyn_cast(U.getUser())) { auto *CallerF = CB->getCaller(); CGUpdater.reanalyzeFunction(*CallerF); } } } } for (Function *F : Functions) { if (F->hasExactDefinition()) NumFnWithExactDefinition++; else NumFnWithoutExactDefinition++; // We look at internal functions only on-demand but if any use is not a // direct call or outside the current set of analyzed functions, we have // to do it eagerly. if (F->hasLocalLinkage()) { if (llvm::all_of(F->uses(), [&Functions](const Use &U) { const auto *CB = dyn_cast(U.getUser()); return CB && CB->isCallee(&U) && Functions.count(const_cast(CB->getCaller())); })) continue; } // Populate the Attributor with abstract attribute opportunities in the // function and the information cache with IR information. A.identifyDefaultAbstractAttributes(*F); } ChangeStatus Changed = A.run(); LLVM_DEBUG(dbgs() << "[Attributor] Done with " << Functions.size() << " functions, result: " << Changed << ".\n"); return Changed == ChangeStatus::CHANGED; } void AADepGraph::viewGraph() { llvm::ViewGraph(this, "Dependency Graph"); } void AADepGraph::dumpGraph() { static std::atomic CallTimes; std::string Prefix; if (!DepGraphDotFileNamePrefix.empty()) Prefix = DepGraphDotFileNamePrefix; else Prefix = "dep_graph"; std::string Filename = Prefix + "_" + std::to_string(CallTimes.load()) + ".dot"; outs() << "Dependency graph dump to " << Filename << ".\n"; std::error_code EC; raw_fd_ostream File(Filename, EC, sys::fs::OF_Text); if (!EC) llvm::WriteGraph(File, this); CallTimes++; } void AADepGraph::print() { for (auto DepAA : SyntheticRoot.Deps) cast(DepAA.getPointer())->printWithDeps(outs()); } PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) { FunctionAnalysisManager &FAM = AM.getResult(M).getManager(); AnalysisGetter AG(FAM); SetVector Functions; for (Function &F : M) Functions.insert(&F); CallGraphUpdater CGUpdater; BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr); if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { // FIXME: Think about passes we will preserve and add them here. return PreservedAnalyses::none(); } return PreservedAnalyses::all(); } PreservedAnalyses AttributorCGSCCPass::run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR) { FunctionAnalysisManager &FAM = AM.getResult(C, CG).getManager(); AnalysisGetter AG(FAM); SetVector Functions; for (LazyCallGraph::Node &N : C) Functions.insert(&N.getFunction()); if (Functions.empty()) return PreservedAnalyses::all(); Module &M = *Functions.back()->getParent(); CallGraphUpdater CGUpdater; CGUpdater.initialize(CG, C, AM, UR); BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions); if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) { // FIXME: Think about passes we will preserve and add them here. PreservedAnalyses PA; PA.preserve(); return PA; } return PreservedAnalyses::all(); } namespace llvm { template <> struct GraphTraits { using NodeRef = AADepGraphNode *; using DepTy = PointerIntPair; using EdgeRef = PointerIntPair; static NodeRef getEntryNode(AADepGraphNode *DGN) { return DGN; } static NodeRef DepGetVal(DepTy &DT) { return DT.getPointer(); } using ChildIteratorType = mapped_iterator::iterator, decltype(&DepGetVal)>; using ChildEdgeIteratorType = TinyPtrVector::iterator; static ChildIteratorType child_begin(NodeRef N) { return N->child_begin(); } static ChildIteratorType child_end(NodeRef N) { return N->child_end(); } }; template <> struct GraphTraits : public GraphTraits { static NodeRef getEntryNode(AADepGraph *DG) { return DG->GetEntryNode(); } using nodes_iterator = mapped_iterator::iterator, decltype(&DepGetVal)>; static nodes_iterator nodes_begin(AADepGraph *DG) { return DG->begin(); } static nodes_iterator nodes_end(AADepGraph *DG) { return DG->end(); } }; template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} static std::string getNodeLabel(const AADepGraphNode *Node, const AADepGraph *DG) { - std::string AAString = ""; + std::string AAString; raw_string_ostream O(AAString); Node->print(O); return AAString; } }; } // end namespace llvm namespace { struct AttributorLegacyPass : public ModulePass { static char ID; AttributorLegacyPass() : ModulePass(ID) { initializeAttributorLegacyPassPass(*PassRegistry::getPassRegistry()); } bool runOnModule(Module &M) override { if (skipModule(M)) return false; AnalysisGetter AG; SetVector Functions; for (Function &F : M) Functions.insert(&F); CallGraphUpdater CGUpdater; BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ nullptr); return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); } void getAnalysisUsage(AnalysisUsage &AU) const override { // FIXME: Think about passes we will preserve and add them here. AU.addRequired(); } }; struct AttributorCGSCCLegacyPass : public CallGraphSCCPass { static char ID; AttributorCGSCCLegacyPass() : CallGraphSCCPass(ID) { initializeAttributorCGSCCLegacyPassPass(*PassRegistry::getPassRegistry()); } bool runOnSCC(CallGraphSCC &SCC) override { if (skipSCC(SCC)) return false; SetVector Functions; for (CallGraphNode *CGN : SCC) if (Function *Fn = CGN->getFunction()) if (!Fn->isDeclaration()) Functions.insert(Fn); if (Functions.empty()) return false; AnalysisGetter AG; CallGraph &CG = const_cast(SCC.getCallGraph()); CallGraphUpdater CGUpdater; CGUpdater.initialize(CG, SCC); Module &M = *Functions.back()->getParent(); BumpPtrAllocator Allocator; InformationCache InfoCache(M, AG, Allocator, /* CGSCC */ &Functions); return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater); } void getAnalysisUsage(AnalysisUsage &AU) const override { // FIXME: Think about passes we will preserve and add them here. AU.addRequired(); CallGraphSCCPass::getAnalysisUsage(AU); } }; } // end anonymous namespace Pass *llvm::createAttributorLegacyPass() { return new AttributorLegacyPass(); } Pass *llvm::createAttributorCGSCCLegacyPass() { return new AttributorCGSCCLegacyPass(); } char AttributorLegacyPass::ID = 0; char AttributorCGSCCLegacyPass::ID = 0; INITIALIZE_PASS_BEGIN(AttributorLegacyPass, "attributor", "Deduce and propagate attributes", false, false) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_END(AttributorLegacyPass, "attributor", "Deduce and propagate attributes", false, false) INITIALIZE_PASS_BEGIN(AttributorCGSCCLegacyPass, "attributor-cgscc", "Deduce and propagate attributes (CGSCC pass)", false, false) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) INITIALIZE_PASS_END(AttributorCGSCCLegacyPass, "attributor-cgscc", "Deduce and propagate attributes (CGSCC pass)", false, false) diff --git a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp index 191739728798..9d856c85c3b4 100644 --- a/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp +++ b/llvm/lib/Transforms/Scalar/LowerMatrixIntrinsics.cpp @@ -1,2051 +1,2051 @@ //===- LowerMatrixIntrinsics.cpp - Lower matrix intrinsics -----*- 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 // //===----------------------------------------------------------------------===// // // Lower matrix intrinsics to vector operations. // // TODO: // * Improve fusion: // * Support more cases, e.g. multiply-add, multiply-sub, operands/results // transposed. // * Improve cost-modeling, e.g. choose different number of rows/columns // columns for tiles, consider cost of copies on alias. // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/DomTreeUpdater.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/VectorUtils.h" #include "llvm/IR/CFG.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PatternMatch.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/LoopUtils.h" #include "llvm/Transforms/Utils/MatrixUtils.h" using namespace llvm; using namespace PatternMatch; #define DEBUG_TYPE "lower-matrix-intrinsics" static cl::opt EnableShapePropagation( "matrix-propagate-shape", cl::init(true), cl::Hidden, cl::desc("Enable/disable shape propagation from matrix intrinsics to other " "instructions.")); static cl::opt FuseMatrix("fuse-matrix", cl::init(true), cl::Hidden, cl::desc("Enable/disable fusing matrix instructions.")); // TODO: Allow and use non-square tiles. static cl::opt TileSize( "fuse-matrix-tile-size", cl::init(4), cl::Hidden, cl::desc( "Tile size for matrix instruction fusion using square-shaped tiles.")); static cl::opt TileUseLoops("fuse-matrix-use-loops", cl::init(false), cl::Hidden, cl::desc("Generate loop nest for tiling.")); static cl::opt ForceFusion( "force-fuse-matrix", cl::init(false), cl::Hidden, cl::desc("Force matrix instruction fusion even if not profitable.")); static cl::opt AllowContractEnabled( "matrix-allow-contract", cl::init(false), cl::Hidden, cl::desc("Allow the use of FMAs if available and profitable. This may " "result in different results, due to less rounding error.")); enum class MatrixLayoutTy { ColumnMajor, RowMajor }; static cl::opt MatrixLayout( "matrix-default-layout", cl::init(MatrixLayoutTy::ColumnMajor), cl::desc("Sets the default matrix layout"), cl::values(clEnumValN(MatrixLayoutTy::ColumnMajor, "column-major", "Use column-major layout"), clEnumValN(MatrixLayoutTy::RowMajor, "row-major", "Use row-major layout"))); /// Helper function to either return Scope, if it is a subprogram or the /// attached subprogram for a local scope. static DISubprogram *getSubprogram(DIScope *Scope) { if (auto *Subprogram = dyn_cast(Scope)) return Subprogram; return cast(Scope)->getSubprogram(); } namespace { // Given an element pointer \p BasePtr to the start of a (sub) matrix, compute // the start address of vector \p VecIdx with type (\p EltType x \p NumElements) // assuming \p Stride elements between start two consecutive vectors. // \p Stride must be >= \p NumElements. // For column-major matrixes, the function computes the address of a column // vectors and \p NumElements must be set to the number of elements in a column // (= number of rows of the matrix). For row-major matrixes, the function // computes the address of a row vector and \p NumElements must be set to the // number of elements in a column (= number of columns of the matrix). // // Consider a 4x4 matrix in column-mjaor layout like below // // 0 1 2 3 // 0 v_0_0 v_0_1 v_0_2 v_0_3 // 1 v_1_0 v_1_1 v_1_2 v_1_3 // 2 v_2_0 v_2_1 v_2_2 v_2_3 // 3 v_3_0 v_3_1 v_3_2 v_3_3 // To compute the column addresses for a 2x3 sub-matrix at row 1 and column 1, // we need a pointer to the first element of the submatrix as base pointer. // Then we can use computeVectorAddr to compute the addresses for the columns // of the sub-matrix. // // Column 0: computeVectorAddr(Base, 0 (column), 4 (stride), 2 (num rows), ..) // -> just returns Base // Column 1: computeVectorAddr(Base, 1 (column), 4 (stride), 2 (num rows), ..) // -> returns Base + (1 * 4) // Column 2: computeVectorAddr(Base, 2 (column), 4 (stride), 2 (num rows), ..) // -> returns Base + (2 * 4) // // The graphic below illustrates the number of elements in a column (marked // with |) and the number of skipped elements (marked with }). // // v_0_0 v_0_1 {v_0_2 {v_0_3 // Base Col 1 Col 2 // | | | // v_1_0 |v_1_1 |v_1_2 |v_1_3 // v_2_0 |v_2_1 |v_2_2 |v_2_3 // v_3_0 {v_3_1 {v_3_2 v_3_3 // Value *computeVectorAddr(Value *BasePtr, Value *VecIdx, Value *Stride, unsigned NumElements, Type *EltType, IRBuilder<> &Builder) { assert((!isa(Stride) || cast(Stride)->getZExtValue() >= NumElements) && "Stride must be >= the number of elements in the result vector."); unsigned AS = cast(BasePtr->getType())->getAddressSpace(); // Compute the start of the vector with index VecIdx as VecIdx * Stride. Value *VecStart = Builder.CreateMul(VecIdx, Stride, "vec.start"); // Get pointer to the start of the selected vector. Skip GEP creation, // if we select vector 0. if (isa(VecStart) && cast(VecStart)->isZero()) VecStart = BasePtr; else VecStart = Builder.CreateGEP(EltType, BasePtr, VecStart, "vec.gep"); // Cast elementwise vector start pointer to a pointer to a vector // (EltType x NumElements)*. auto *VecType = FixedVectorType::get(EltType, NumElements); Type *VecPtrType = PointerType::get(VecType, AS); return Builder.CreatePointerCast(VecStart, VecPtrType, "vec.cast"); } /// LowerMatrixIntrinsics contains the methods used to lower matrix intrinsics. /// /// Currently, the lowering for each matrix intrinsic is done as follows: /// 1. Propagate the shape information from intrinsics to connected /// instructions. /// 2. Lower instructions with shape information (assuming column-major layout). /// The lowering works similarly using row-major layout. /// 2.1. Get column vectors for each argument. If we already lowered the /// definition of an argument, use the produced column vectors directly. /// If not, split the operand vector containing an embedded matrix into /// a set of column vectors, /// 2.2. Lower the instruction in terms of column major operations, which /// yields a set of column vectors containing result matrix. Note that we /// lower all instructions that have shape information. Besides the /// intrinsics, this includes stores for example. /// 2.3. Update uses of the lowered instruction. If we have shape information /// for a user, there is nothing to do, as we will look up the result /// column matrix when lowering the user. For other uses, we embed the /// result matrix in a flat vector and update the use. /// 2.4. Cache the result column matrix for the instruction we lowered /// 3. After we lowered all instructions in a function, remove the now /// obsolete instructions. /// class LowerMatrixIntrinsics { Function &Func; const DataLayout &DL; const TargetTransformInfo &TTI; AliasAnalysis *AA; DominatorTree *DT; LoopInfo *LI; OptimizationRemarkEmitter *ORE; /// Contains estimates of the number of operations (loads, stores, compute) required to lower a matrix operation. struct OpInfoTy { /// Number of stores emitted to generate this matrix. unsigned NumStores = 0; /// Number of loads emitted to generate this matrix. unsigned NumLoads = 0; /// Number of compute operations emitted to generate this matrix. unsigned NumComputeOps = 0; OpInfoTy &operator+=(const OpInfoTy &RHS) { NumStores += RHS.NumStores; NumLoads += RHS.NumLoads; NumComputeOps += RHS.NumComputeOps; return *this; } }; /// Wrapper class representing a matrix as a set of vectors, either in row or /// column major layout. All vectors must have the same vector type. class MatrixTy { SmallVector Vectors; OpInfoTy OpInfo; bool IsColumnMajor = true; public: MatrixTy() : Vectors(), IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} MatrixTy(ArrayRef Vectors) : Vectors(Vectors.begin(), Vectors.end()), IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} MatrixTy(unsigned NumRows, unsigned NumColumns, Type *EltTy) : IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) { unsigned D = isColumnMajor() ? NumColumns : NumRows; for (unsigned J = 0; J < D; ++J) addVector(UndefValue::get(FixedVectorType::get( EltTy, isColumnMajor() ? NumRows : NumColumns))); } Value *getVector(unsigned i) const { return Vectors[i]; } Value *getColumn(unsigned i) const { assert(isColumnMajor() && "only supported for column-major matrixes"); return Vectors[i]; } Value *getRow(unsigned i) const { assert(!isColumnMajor() && "only supported for row-major matrixes"); return Vectors[i]; } void setVector(unsigned i, Value *V) { Vectors[i] = V; } Type *getElementType() const { return getVectorTy()->getElementType(); } unsigned getNumVectors() const { if (isColumnMajor()) return getNumColumns(); return getNumRows(); } unsigned getNumColumns() const { if (isColumnMajor()) return Vectors.size(); else { assert(Vectors.size() > 0 && "Cannot call getNumRows without columns"); return cast(Vectors[0]->getType())->getNumElements(); } } unsigned getNumRows() const { if (isColumnMajor()) { assert(Vectors.size() > 0 && "Cannot call getNumRows without columns"); return cast(Vectors[0]->getType())->getNumElements(); } else return Vectors.size(); } void addVector(Value *V) { Vectors.push_back(V); } VectorType *getColumnTy() { assert(isColumnMajor() && "only supported for column-major matrixes"); return getVectorTy(); } VectorType *getVectorTy() const { return cast(Vectors[0]->getType()); } iterator_range::iterator> columns() { assert(isColumnMajor() && "columns() only supported for column-major matrixes"); return make_range(Vectors.begin(), Vectors.end()); } iterator_range::iterator> vectors() { return make_range(Vectors.begin(), Vectors.end()); } /// Embed the vectors of the matrix into a flat vector by concatenating /// them. Value *embedInVector(IRBuilder<> &Builder) const { return Vectors.size() == 1 ? Vectors[0] : concatenateVectors(Builder, Vectors); } MatrixTy &addNumLoads(unsigned N) { OpInfo.NumLoads += N; return *this; } void setNumLoads(unsigned N) { OpInfo.NumLoads = N; } MatrixTy &addNumStores(unsigned N) { OpInfo.NumStores += N; return *this; } MatrixTy &addNumComputeOps(unsigned N) { OpInfo.NumComputeOps += N; return *this; } unsigned getNumStores() const { return OpInfo.NumStores; } unsigned getNumLoads() const { return OpInfo.NumLoads; } unsigned getNumComputeOps() const { return OpInfo.NumComputeOps; } const OpInfoTy &getOpInfo() const { return OpInfo; } bool isColumnMajor() const { return IsColumnMajor; } unsigned getStride() const { if (isColumnMajor()) return getNumRows(); return getNumColumns(); } /// Extract a vector of \p NumElts starting at index (\p I, \p J). If the /// matrix is column-major, the result vector is extracted from a column /// vector, otherwise from a row vector. Value *extractVector(unsigned I, unsigned J, unsigned NumElts, IRBuilder<> &Builder) const { Value *Vec = isColumnMajor() ? getColumn(J) : getRow(I); return Builder.CreateShuffleVector( Vec, createSequentialMask(isColumnMajor() ? I : J, NumElts, 0), "block"); } }; struct ShapeInfo { unsigned NumRows; unsigned NumColumns; bool IsColumnMajor; ShapeInfo(unsigned NumRows = 0, unsigned NumColumns = 0) : NumRows(NumRows), NumColumns(NumColumns), IsColumnMajor(MatrixLayout == MatrixLayoutTy::ColumnMajor) {} ShapeInfo(Value *NumRows, Value *NumColumns) : ShapeInfo(cast(NumRows)->getZExtValue(), cast(NumColumns)->getZExtValue()) {} bool operator==(const ShapeInfo &other) { return NumRows == other.NumRows && NumColumns == other.NumColumns; } bool operator!=(const ShapeInfo &other) { return !(*this == other); } /// Returns true if shape-information is defined, meaning both dimensions /// are != 0. operator bool() const { assert(NumRows == 0 || NumColumns != 0); return NumRows != 0; } unsigned getStride() const { if (IsColumnMajor) return NumRows; return NumColumns; } unsigned getNumVectors() const { if (IsColumnMajor) return NumColumns; return NumRows; } }; /// Maps instructions to their shape information. The shape information /// describes the shape to be used while lowering. This matches the shape of /// the result value of the instruction, with the only exceptions being store /// instructions and the matrix_column_major_store intrinsics. For those, the /// shape information indicates that those instructions should be lowered /// using shape information as well. DenseMap ShapeMap; /// List of instructions to remove. While lowering, we are not replacing all /// users of a lowered instruction, if shape information is available and /// those need to be removed after we finished lowering. SmallVector ToRemove; /// Map from instructions to their produced column matrix. MapVector Inst2ColumnMatrix; public: LowerMatrixIntrinsics(Function &F, TargetTransformInfo &TTI, AliasAnalysis *AA, DominatorTree *DT, LoopInfo *LI, OptimizationRemarkEmitter *ORE) : Func(F), DL(F.getParent()->getDataLayout()), TTI(TTI), AA(AA), DT(DT), LI(LI), ORE(ORE) {} unsigned getNumOps(Type *VT) { assert(isa(VT) && "Expected vector type"); return getNumOps(VT->getScalarType(), cast(VT)->getNumElements()); } // /// Return the estimated number of vector ops required for an operation on /// \p VT * N. unsigned getNumOps(Type *ST, unsigned N) { return std::ceil((ST->getPrimitiveSizeInBits() * N).getFixedSize() / double(TTI.getRegisterBitWidth(true))); } /// Return the set of vectors that a matrix value is lowered to. /// /// If we lowered \p MatrixVal, just return the cache result matrix. Otherwise /// split the flat vector \p MatrixVal containing a matrix with shape \p SI /// into vectors. MatrixTy getMatrix(Value *MatrixVal, const ShapeInfo &SI, IRBuilder<> &Builder) { VectorType *VType = dyn_cast(MatrixVal->getType()); assert(VType && "MatrixVal must be a vector type"); assert(cast(VType)->getNumElements() == SI.NumRows * SI.NumColumns && "The vector size must match the number of matrix elements"); // Check if we lowered MatrixVal using shape information. In that case, // return the existing matrix, if it matches the requested shape // information. If there is a mis-match, embed the result in a flat // vector and split it later. auto Found = Inst2ColumnMatrix.find(MatrixVal); if (Found != Inst2ColumnMatrix.end()) { MatrixTy &M = Found->second; // Return the found matrix, if its shape matches the requested shape // information if (SI.NumRows == M.getNumRows() && SI.NumColumns == M.getNumColumns()) return M; MatrixVal = M.embedInVector(Builder); } // Otherwise split MatrixVal. SmallVector SplitVecs; for (unsigned MaskStart = 0; MaskStart < cast(VType)->getNumElements(); MaskStart += SI.getStride()) { Value *V = Builder.CreateShuffleVector( MatrixVal, createSequentialMask(MaskStart, SI.getStride(), 0), "split"); SplitVecs.push_back(V); } return {SplitVecs}; } /// If \p V already has a known shape return false. Otherwise set the shape /// for instructions that support it. bool setShapeInfo(Value *V, ShapeInfo Shape) { assert(Shape && "Shape not set"); if (isa(V) || !supportsShapeInfo(V)) return false; auto SIter = ShapeMap.find(V); if (SIter != ShapeMap.end()) { LLVM_DEBUG(dbgs() << " not overriding existing shape: " << SIter->second.NumRows << " " << SIter->second.NumColumns << " for " << *V << "\n"); return false; } ShapeMap.insert({V, Shape}); LLVM_DEBUG(dbgs() << " " << Shape.NumRows << " x " << Shape.NumColumns << " for " << *V << "\n"); return true; } bool isUniformShape(Value *V) { Instruction *I = dyn_cast(V); if (!I) return true; switch (I->getOpcode()) { case Instruction::FAdd: case Instruction::FSub: case Instruction::FMul: // Scalar multiply. case Instruction::Add: case Instruction::Mul: case Instruction::Sub: return true; default: return false; } } /// Returns true if shape information can be used for \p V. The supported /// instructions must match the instructions that can be lowered by this pass. bool supportsShapeInfo(Value *V) { Instruction *Inst = dyn_cast(V); if (!Inst) return false; IntrinsicInst *II = dyn_cast(Inst); if (II) switch (II->getIntrinsicID()) { case Intrinsic::matrix_multiply: case Intrinsic::matrix_transpose: case Intrinsic::matrix_column_major_load: case Intrinsic::matrix_column_major_store: return true; default: return false; } return isUniformShape(V) || isa(V) || isa(V); } /// Propagate the shape information of instructions to their users. /// The work list contains instructions for which we can compute the shape, /// either based on the information provided by matrix intrinsics or known /// shapes of operands. SmallVector propagateShapeForward(SmallVectorImpl &WorkList) { SmallVector NewWorkList; // Pop an element for which we guaranteed to have at least one of the // operand shapes. Add the shape for this and then add users to the work // list. LLVM_DEBUG(dbgs() << "Forward-propagate shapes:\n"); while (!WorkList.empty()) { Instruction *Inst = WorkList.back(); WorkList.pop_back(); // New entry, set the value and insert operands bool Propagate = false; Value *MatrixA; Value *MatrixB; Value *M; Value *N; Value *K; if (match(Inst, m_Intrinsic( m_Value(MatrixA), m_Value(MatrixB), m_Value(M), m_Value(N), m_Value(K)))) { Propagate = setShapeInfo(Inst, {M, K}); } else if (match(Inst, m_Intrinsic( m_Value(MatrixA), m_Value(M), m_Value(N)))) { // Flip dimensions. Propagate = setShapeInfo(Inst, {N, M}); } else if (match(Inst, m_Intrinsic( m_Value(MatrixA), m_Value(), m_Value(), m_Value(), m_Value(M), m_Value(N)))) { Propagate = setShapeInfo(Inst, {N, M}); } else if (match(Inst, m_Intrinsic( m_Value(), m_Value(), m_Value(), m_Value(M), m_Value(N)))) { Propagate = setShapeInfo(Inst, {M, N}); } else if (match(Inst, m_Store(m_Value(MatrixA), m_Value()))) { auto OpShape = ShapeMap.find(MatrixA); if (OpShape != ShapeMap.end()) setShapeInfo(Inst, OpShape->second); continue; } else if (isUniformShape(Inst)) { // Find the first operand that has a known shape and use that. for (auto &Op : Inst->operands()) { auto OpShape = ShapeMap.find(Op.get()); if (OpShape != ShapeMap.end()) { Propagate |= setShapeInfo(Inst, OpShape->second); break; } } } if (Propagate) { NewWorkList.push_back(Inst); for (auto *User : Inst->users()) if (ShapeMap.count(User) == 0) WorkList.push_back(cast(User)); } } return NewWorkList; } /// Propagate the shape to operands of instructions with shape information. /// \p Worklist contains the instruction for which we already know the shape. SmallVector propagateShapeBackward(SmallVectorImpl &WorkList) { SmallVector NewWorkList; auto pushInstruction = [](Value *V, SmallVectorImpl &WorkList) { Instruction *I = dyn_cast(V); if (I) WorkList.push_back(I); }; // Pop an element with known shape. Traverse the operands, if their shape // derives from the result shape and is unknown, add it and add them to the // worklist. LLVM_DEBUG(dbgs() << "Backward-propagate shapes:\n"); while (!WorkList.empty()) { Value *V = WorkList.back(); WorkList.pop_back(); size_t BeforeProcessingV = WorkList.size(); if (!isa(V)) continue; Value *MatrixA; Value *MatrixB; Value *M; Value *N; Value *K; if (match(V, m_Intrinsic( m_Value(MatrixA), m_Value(MatrixB), m_Value(M), m_Value(N), m_Value(K)))) { if (setShapeInfo(MatrixA, {M, N})) pushInstruction(MatrixA, WorkList); if (setShapeInfo(MatrixB, {N, K})) pushInstruction(MatrixB, WorkList); } else if (match(V, m_Intrinsic( m_Value(MatrixA), m_Value(M), m_Value(N)))) { // Flip dimensions. if (setShapeInfo(MatrixA, {M, N})) pushInstruction(MatrixA, WorkList); } else if (match(V, m_Intrinsic( m_Value(MatrixA), m_Value(), m_Value(), m_Value(), m_Value(M), m_Value(N)))) { if (setShapeInfo(MatrixA, {M, N})) { pushInstruction(MatrixA, WorkList); } } else if (isa(V) || match(V, m_Intrinsic())) { // Nothing to do, no matrix input. } else if (isa(V)) { // Nothing to do. We forward-propagated to this so we would just // backward propagate to an instruction with an already known shape. } else if (isUniformShape(V)) { // Propagate to all operands. ShapeInfo Shape = ShapeMap[V]; for (Use &U : cast(V)->operands()) { if (setShapeInfo(U.get(), Shape)) pushInstruction(U.get(), WorkList); } } // After we discovered new shape info for new instructions in the // worklist, we use their users as seeds for the next round of forward // propagation. for (size_t I = BeforeProcessingV; I != WorkList.size(); I++) for (User *U : WorkList[I]->users()) if (isa(U) && V != U) NewWorkList.push_back(cast(U)); } return NewWorkList; } bool Visit() { if (EnableShapePropagation) { SmallVector WorkList; // Initially only the shape of matrix intrinsics is known. // Initialize the work list with ops carrying shape information. for (BasicBlock &BB : Func) for (Instruction &Inst : BB) { IntrinsicInst *II = dyn_cast(&Inst); if (!II) continue; switch (II->getIntrinsicID()) { case Intrinsic::matrix_multiply: case Intrinsic::matrix_transpose: case Intrinsic::matrix_column_major_load: case Intrinsic::matrix_column_major_store: WorkList.push_back(&Inst); break; default: break; } } // Propagate shapes until nothing changes any longer. while (!WorkList.empty()) { WorkList = propagateShapeForward(WorkList); WorkList = propagateShapeBackward(WorkList); } } bool Changed = false; SmallVector MaybeFusableInsts; SmallVector MatrixInsts; // First, collect all instructions with shape information and candidates for // fusion (currently only matrix multiplies). ReversePostOrderTraversal RPOT(&Func); for (auto *BB : RPOT) for (Instruction &I : *BB) { if (ShapeMap.find(&I) == ShapeMap.end()) continue; if (match(&I, m_Intrinsic())) MaybeFusableInsts.push_back(cast(&I)); MatrixInsts.push_back(&I); } // Second, try to fuse candidates. SmallPtrSet FusedInsts; for (CallInst *CI : MaybeFusableInsts) LowerMatrixMultiplyFused(CI, FusedInsts); Changed = !FusedInsts.empty(); // Third, lower remaining instructions with shape information. for (Instruction *Inst : MatrixInsts) { if (FusedInsts.count(Inst)) continue; IRBuilder<> Builder(Inst); if (CallInst *CInst = dyn_cast(Inst)) Changed |= VisitCallInst(CInst); Value *Op1; Value *Op2; if (auto *BinOp = dyn_cast(Inst)) Changed |= VisitBinaryOperator(BinOp); if (match(Inst, m_Load(m_Value(Op1)))) Changed |= VisitLoad(cast(Inst), Op1, Builder); else if (match(Inst, m_Store(m_Value(Op1), m_Value(Op2)))) Changed |= VisitStore(cast(Inst), Op1, Op2, Builder); } if (ORE) { RemarkGenerator RemarkGen(Inst2ColumnMatrix, *ORE, Func); RemarkGen.emitRemarks(); } for (Instruction *Inst : reverse(ToRemove)) Inst->eraseFromParent(); return Changed; } /// Turns \p BasePtr into an elementwise pointer to \p EltType. Value *createElementPtr(Value *BasePtr, Type *EltType, IRBuilder<> &Builder) { unsigned AS = cast(BasePtr->getType())->getAddressSpace(); Type *EltPtrType = PointerType::get(EltType, AS); return Builder.CreatePointerCast(BasePtr, EltPtrType); } /// Replace intrinsic calls bool VisitCallInst(CallInst *Inst) { if (!Inst->getCalledFunction() || !Inst->getCalledFunction()->isIntrinsic()) return false; switch (Inst->getCalledFunction()->getIntrinsicID()) { case Intrinsic::matrix_multiply: LowerMultiply(Inst); break; case Intrinsic::matrix_transpose: LowerTranspose(Inst); break; case Intrinsic::matrix_column_major_load: LowerColumnMajorLoad(Inst); break; case Intrinsic::matrix_column_major_store: LowerColumnMajorStore(Inst); break; default: return false; } return true; } /// Compute the alignment for a column/row \p Idx with \p Stride between them. /// The address at \p Idx == 0 has alignment \p A. If \p Stride is a /// ConstantInt, reduce the initial alignment based on the byte offset. For /// non-ConstantInt strides, return the common alignment of the initial /// alignment and the element size in bytes. Align getAlignForIndex(unsigned Idx, Value *Stride, Type *ElementTy, MaybeAlign A) const { Align InitialAlign = DL.getValueOrABITypeAlignment(A, ElementTy); if (Idx == 0) return InitialAlign; TypeSize ElementSizeInBits = DL.getTypeSizeInBits(ElementTy); if (auto *ConstStride = dyn_cast(Stride)) { uint64_t StrideInBytes = ConstStride->getZExtValue() * ElementSizeInBits / 8; return commonAlignment(InitialAlign, Idx * StrideInBytes); } return commonAlignment(InitialAlign, ElementSizeInBits / 8); } /// Load a matrix with \p Shape starting at \p Ptr and using \p Stride between /// vectors. MatrixTy loadMatrix(Type *Ty, Value *Ptr, MaybeAlign MAlign, Value *Stride, bool IsVolatile, ShapeInfo Shape, IRBuilder<> &Builder) { auto VType = cast(Ty); Value *EltPtr = createElementPtr(Ptr, VType->getElementType(), Builder); MatrixTy Result; for (unsigned I = 0, E = Shape.getNumVectors(); I < E; ++I) { Value *GEP = computeVectorAddr(EltPtr, Builder.getInt64(I), Stride, Shape.getStride(), VType->getElementType(), Builder); Value *Vector = Builder.CreateAlignedLoad( GEP, getAlignForIndex(I, Stride, VType->getElementType(), MAlign), IsVolatile, "col.load"); Result.addVector(Vector); } return Result.addNumLoads(getNumOps(Result.getVectorTy()) * Result.getNumVectors()); } /// Loads a sub-matrix with shape \p ResultShape from a \p R x \p C matrix, /// starting at \p MatrixPtr[I][J]. MatrixTy loadMatrix(Value *MatrixPtr, MaybeAlign Align, bool IsVolatile, ShapeInfo MatrixShape, Value *I, Value *J, ShapeInfo ResultShape, Type *EltTy, IRBuilder<> &Builder) { Value *Offset = Builder.CreateAdd( Builder.CreateMul(J, Builder.getInt64(MatrixShape.getStride())), I); unsigned AS = cast(MatrixPtr->getType())->getAddressSpace(); Value *EltPtr = Builder.CreatePointerCast(MatrixPtr, PointerType::get(EltTy, AS)); Value *TileStart = Builder.CreateGEP(EltTy, EltPtr, Offset); auto *TileTy = FixedVectorType::get(EltTy, ResultShape.NumRows * ResultShape.NumColumns); Type *TilePtrTy = PointerType::get(TileTy, AS); Value *TilePtr = Builder.CreatePointerCast(TileStart, TilePtrTy, "col.cast"); return loadMatrix(TileTy, TilePtr, Align, Builder.getInt64(MatrixShape.getStride()), IsVolatile, ResultShape, Builder); } /// Lower a load instruction with shape information. void LowerLoad(Instruction *Inst, Value *Ptr, MaybeAlign Align, Value *Stride, bool IsVolatile, ShapeInfo Shape) { IRBuilder<> Builder(Inst); finalizeLowering(Inst, loadMatrix(Inst->getType(), Ptr, Align, Stride, IsVolatile, Shape, Builder), Builder); } /// Lowers llvm.matrix.column.major.load. /// /// The intrinsic loads a matrix from memory using a stride between columns. void LowerColumnMajorLoad(CallInst *Inst) { assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && "Intrinsic only supports column-major layout!"); Value *Ptr = Inst->getArgOperand(0); Value *Stride = Inst->getArgOperand(1); LowerLoad(Inst, Ptr, Inst->getParamAlign(0), Stride, cast(Inst->getArgOperand(2))->isOne(), {Inst->getArgOperand(3), Inst->getArgOperand(4)}); } /// Stores a sub-matrix \p StoreVal into the \p R x \p C matrix starting at \p /// MatrixPtr[I][J]. void storeMatrix(const MatrixTy &StoreVal, Value *MatrixPtr, MaybeAlign MAlign, bool IsVolatile, ShapeInfo MatrixShape, Value *I, Value *J, Type *EltTy, IRBuilder<> &Builder) { Value *Offset = Builder.CreateAdd( Builder.CreateMul(J, Builder.getInt64(MatrixShape.getStride())), I); unsigned AS = cast(MatrixPtr->getType())->getAddressSpace(); Value *EltPtr = Builder.CreatePointerCast(MatrixPtr, PointerType::get(EltTy, AS)); Value *TileStart = Builder.CreateGEP(EltTy, EltPtr, Offset); auto *TileTy = FixedVectorType::get(EltTy, StoreVal.getNumRows() * StoreVal.getNumColumns()); Type *TilePtrTy = PointerType::get(TileTy, AS); Value *TilePtr = Builder.CreatePointerCast(TileStart, TilePtrTy, "col.cast"); storeMatrix(TileTy, StoreVal, TilePtr, MAlign, Builder.getInt64(MatrixShape.getStride()), IsVolatile, Builder); } /// Store matrix \p StoreVal starting at \p Ptr and using \p Stride between /// vectors. MatrixTy storeMatrix(Type *Ty, MatrixTy StoreVal, Value *Ptr, MaybeAlign MAlign, Value *Stride, bool IsVolatile, IRBuilder<> &Builder) { auto VType = cast(Ty); Value *EltPtr = createElementPtr(Ptr, VType->getElementType(), Builder); for (auto Vec : enumerate(StoreVal.vectors())) { Value *GEP = computeVectorAddr(EltPtr, Builder.getInt64(Vec.index()), Stride, StoreVal.getStride(), VType->getElementType(), Builder); Builder.CreateAlignedStore(Vec.value(), GEP, getAlignForIndex(Vec.index(), Stride, VType->getElementType(), MAlign), IsVolatile); } return MatrixTy().addNumStores(getNumOps(StoreVal.getVectorTy()) * StoreVal.getNumVectors()); } /// Lower a store instruction with shape information. void LowerStore(Instruction *Inst, Value *Matrix, Value *Ptr, MaybeAlign A, Value *Stride, bool IsVolatile, ShapeInfo Shape) { IRBuilder<> Builder(Inst); auto StoreVal = getMatrix(Matrix, Shape, Builder); finalizeLowering(Inst, storeMatrix(Matrix->getType(), StoreVal, Ptr, A, Stride, IsVolatile, Builder), Builder); } /// Lowers llvm.matrix.column.major.store. /// /// The intrinsic store a matrix back memory using a stride between columns. void LowerColumnMajorStore(CallInst *Inst) { assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && "Intrinsic only supports column-major layout!"); Value *Matrix = Inst->getArgOperand(0); Value *Ptr = Inst->getArgOperand(1); Value *Stride = Inst->getArgOperand(2); LowerStore(Inst, Matrix, Ptr, Inst->getParamAlign(1), Stride, cast(Inst->getArgOperand(3))->isOne(), {Inst->getArgOperand(4), Inst->getArgOperand(5)}); } // Set elements I..I+NumElts-1 to Block Value *insertVector(Value *Col, unsigned I, Value *Block, IRBuilder<> &Builder) { // First, bring Block to the same size as Col unsigned BlockNumElts = cast(Block->getType())->getNumElements(); unsigned NumElts = cast(Col->getType())->getNumElements(); assert(NumElts >= BlockNumElts && "Too few elements for current block"); Block = Builder.CreateShuffleVector( Block, createSequentialMask(0, BlockNumElts, NumElts - BlockNumElts)); // If Col is 7 long and I is 2 and BlockNumElts is 2 the mask is: 0, 1, 7, // 8, 4, 5, 6 SmallVector Mask; unsigned i; for (i = 0; i < I; i++) Mask.push_back(i); unsigned VecNumElts = cast(Col->getType())->getNumElements(); for (; i < I + BlockNumElts; i++) Mask.push_back(i - I + VecNumElts); for (; i < VecNumElts; i++) Mask.push_back(i); return Builder.CreateShuffleVector(Col, Block, Mask); } Value *createMulAdd(Value *Sum, Value *A, Value *B, bool UseFPOp, IRBuilder<> &Builder, bool AllowContraction, unsigned &NumComputeOps) { NumComputeOps += getNumOps(A->getType()); if (!Sum) return UseFPOp ? Builder.CreateFMul(A, B) : Builder.CreateMul(A, B); if (UseFPOp) { if (AllowContraction) { // Use fmuladd for floating point operations and let the backend decide // if that's profitable. Function *FMulAdd = Intrinsic::getDeclaration( Func.getParent(), Intrinsic::fmuladd, A->getType()); return Builder.CreateCall(FMulAdd, {A, B, Sum}); } NumComputeOps += getNumOps(A->getType()); Value *Mul = Builder.CreateFMul(A, B); return Builder.CreateFAdd(Sum, Mul); } NumComputeOps += getNumOps(A->getType()); Value *Mul = Builder.CreateMul(A, B); return Builder.CreateAdd(Sum, Mul); } /// Cache \p Matrix as result of \p Inst and update the uses of \p Inst. For /// users with shape information, there's nothing to do: the will use the /// cached value when they are lowered. For other users, \p Matrix is /// flattened and the uses are updated to use it. Also marks \p Inst for /// deletion. void finalizeLowering(Instruction *Inst, MatrixTy Matrix, IRBuilder<> &Builder) { Inst2ColumnMatrix.insert(std::make_pair(Inst, Matrix)); ToRemove.push_back(Inst); Value *Flattened = nullptr; for (auto I = Inst->use_begin(), E = Inst->use_end(); I != E;) { Use &U = *I++; if (ShapeMap.find(U.getUser()) == ShapeMap.end()) { if (!Flattened) Flattened = Matrix.embedInVector(Builder); U.set(Flattened); } } } /// Compute \p Result += \p A * \p B for input matrices with left-associating /// addition. void emitMatrixMultiply(MatrixTy &Result, const MatrixTy &A, const MatrixTy &B, bool AllowContraction, IRBuilder<> &Builder, bool isTiled) { const unsigned VF = std::max( TTI.getRegisterBitWidth(true) / Result.getElementType()->getPrimitiveSizeInBits().getFixedSize(), 1U); unsigned R = Result.getNumRows(); unsigned C = Result.getNumColumns(); unsigned M = A.getNumColumns(); bool IsFP = Result.getElementType()->isFloatingPointTy(); assert(A.isColumnMajor() == B.isColumnMajor() && Result.isColumnMajor() == A.isColumnMajor() && "operands must agree on matrix layout"); unsigned NumComputeOps = 0; if (A.isColumnMajor()) { // Multiply columns from the first operand with scalars from the second // operand. Then move along the K axes and accumulate the columns. With // this the adds can be vectorized without reassociation. for (unsigned J = 0; J < C; ++J) { unsigned BlockSize = VF; // If Result is zero, we don't need to accumulate in the K==0 iteration. bool isSumZero = isa(Result.getColumn(J)); for (unsigned I = 0; I < R; I += BlockSize) { // Gradually lower the vectorization factor to cover the remainder. while (I + BlockSize > R) BlockSize /= 2; Value *Sum = isTiled ? Result.extractVector(I, J, BlockSize, Builder) : nullptr; for (unsigned K = 0; K < M; ++K) { Value *L = A.extractVector(I, K, BlockSize, Builder); Value *RH = Builder.CreateExtractElement(B.getColumn(J), K); Value *Splat = Builder.CreateVectorSplat(BlockSize, RH, "splat"); Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, L, Splat, Result.getElementType()->isFloatingPointTy(), Builder, AllowContraction, NumComputeOps); } Result.setVector(J, insertVector(Result.getVector(J), I, Sum, Builder)); } } } else { // Multiply rows from the second operand with scalars from the first // operand. Then move along the K axes and accumulate the rows. With this // the adds can be vectorized without reassociation. for (unsigned I = 0; I < R; ++I) { unsigned BlockSize = VF; bool isSumZero = isa(Result.getRow(I)); for (unsigned J = 0; J < C; J += BlockSize) { // Gradually lower the vectorization factor to cover the remainder. while (J + BlockSize > C) BlockSize /= 2; Value *Sum = nullptr; for (unsigned K = 0; K < M; ++K) { Value *R = B.extractVector(K, J, BlockSize, Builder); Value *LH = Builder.CreateExtractElement(A.getVector(I), K); Value *Splat = Builder.CreateVectorSplat(BlockSize, LH, "splat"); Sum = createMulAdd(isSumZero && K == 0 ? nullptr : Sum, Splat, R, IsFP, Builder, AllowContraction, NumComputeOps); } Result.setVector(I, insertVector(Result.getVector(I), J, Sum, Builder)); } } } Result.addNumComputeOps(NumComputeOps); } /// Ensure that the memory in \p Load does not alias \p Store by potentially /// copying it to a new location. This new or otherwise the original location /// is returned. Value *getNonAliasingPointer(LoadInst *Load, StoreInst *Store, CallInst *MatMul) { MemoryLocation StoreLoc = MemoryLocation::get(Store); MemoryLocation LoadLoc = MemoryLocation::get(Load); AliasResult LdAliased = AA->alias(LoadLoc, StoreLoc); // If we can statically determine noalias we're good. if (!LdAliased) return Load->getPointerOperand(); // Create code to check if the memory locations of the Load and Store // overlap and if they do, copy Load's operand to a new buffer. // First, create new blocks for 2n part of the check and the copy. BasicBlock *Check0 = MatMul->getParent(); // FIXME: Use lazy DTU and update SplitBlock to accept a DTU instead of a // DT. Manually collect dominator tree updates, to avoid unnecessary work, // as we adjust Check0 and Check1's branches. SmallVector DTUpdates; for (BasicBlock *Succ : successors(Check0)) DTUpdates.push_back({DT->Delete, Check0, Succ}); BasicBlock *Check1 = SplitBlock(MatMul->getParent(), MatMul, nullptr, LI, nullptr, "alias_cont"); BasicBlock *Copy = SplitBlock(MatMul->getParent(), MatMul, nullptr, LI, nullptr, "copy"); BasicBlock *Fusion = SplitBlock(MatMul->getParent(), MatMul, nullptr, LI, nullptr, "no_alias"); // Check if the loaded memory location begins before the end of the store // location. If the condition holds, they might overlap, otherwise they are // guaranteed to not overlap. IRBuilder<> Builder(MatMul); Check0->getTerminator()->eraseFromParent(); Builder.SetInsertPoint(Check0); Type *IntPtrTy = Builder.getIntPtrTy(Load->getModule()->getDataLayout()); Value *StoreBegin = Builder.CreatePtrToInt( const_cast(StoreLoc.Ptr), IntPtrTy, "store.begin"); Value *StoreEnd = Builder.CreateAdd( StoreBegin, ConstantInt::get(IntPtrTy, StoreLoc.Size.getValue()), "store.end", true, true); Value *LoadBegin = Builder.CreatePtrToInt(const_cast(LoadLoc.Ptr), IntPtrTy, "load.begin"); Builder.CreateCondBr(Builder.CreateICmpULT(LoadBegin, StoreEnd), Check1, Fusion); // Check if the store begins before the end of the load location. If the // condition holds, they alias, otherwise they are guaranteed to not // overlap. Check1->getTerminator()->eraseFromParent(); Builder.SetInsertPoint(Check1, Check1->begin()); Value *LoadEnd = Builder.CreateAdd( LoadBegin, ConstantInt::get(IntPtrTy, LoadLoc.Size.getValue()), "load.end", true, true); Builder.CreateCondBr(Builder.CreateICmpULT(StoreBegin, LoadEnd), Copy, Fusion); // Copy load operand to new alloca. Builder.SetInsertPoint(Copy, Copy->begin()); AllocaInst *NewLd = Builder.CreateAlloca(Load->getType(), Load->getPointerAddressSpace()); Builder.CreateMemCpy(NewLd, NewLd->getAlign(), Load->getPointerOperand(), Load->getAlign(), LoadLoc.Size.getValue()); Builder.SetInsertPoint(Fusion, Fusion->begin()); PHINode *PHI = Builder.CreatePHI(Load->getPointerOperandType(), 3); PHI->addIncoming(Load->getPointerOperand(), Check0); PHI->addIncoming(Load->getPointerOperand(), Check1); PHI->addIncoming(NewLd, Copy); // Adjust DT. DTUpdates.push_back({DT->Insert, Check0, Check1}); DTUpdates.push_back({DT->Insert, Check0, Fusion}); DTUpdates.push_back({DT->Insert, Check1, Copy}); DTUpdates.push_back({DT->Insert, Check1, Fusion}); DT->applyUpdates(DTUpdates); return PHI; } bool isFusionProfitable(CallInst *MatMul) { if (ForceFusion) return true; ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); const unsigned R = LShape.NumRows; const unsigned C = RShape.NumColumns; const unsigned M = LShape.NumColumns; auto *EltType = cast(MatMul->getType())->getElementType(); const unsigned VF = std::max(TTI.getRegisterBitWidth(true) / EltType->getPrimitiveSizeInBits().getFixedSize(), 1U); // Cost model for tiling // // For tiling to be beneficial, we need reuse either along the R or // the C axis. We vectorize along the R axis so that means at least // 3 elements. // TODO: Also consider cost of copying if operands alias. if (R <= VF && C == 1) return false; // Then we need enough elements to exceed the number of vector // registers we have. Note that this is an oversimplification since // fusing also takes some extra loads which may exceed the number of // reloads necessary. unsigned Op0Regs = (R + VF - 1) / VF * M; unsigned Op1Regs = (M + VF - 1) / VF * C; return Op0Regs + Op1Regs > TTI.getNumberOfRegisters(true); } MatrixTy getZeroMatrix(Type *EltType, unsigned R, unsigned C) { MatrixTy Res; auto *ColumType = FixedVectorType::get(EltType, R); for (unsigned I = 0; I < C; ++I) Res.addVector(ConstantAggregateZero::get(ColumType)); return Res; } void createTiledLoops(CallInst *MatMul, Value *LPtr, ShapeInfo LShape, Value *RPtr, ShapeInfo RShape, StoreInst *Store, bool AllowContract) { auto *EltType = cast(MatMul->getType())->getElementType(); // Create the main tiling loop nest. TileInfo TI(LShape.NumRows, RShape.NumColumns, LShape.NumColumns, TileSize); DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy); Instruction *InsertI = cast(MatMul); BasicBlock *Start = InsertI->getParent(); BasicBlock *End = SplitBlock(InsertI->getParent(), InsertI, DT, LI, nullptr, "continue"); IRBuilder<> Builder(MatMul); BasicBlock *InnerBody = TI.CreateTiledLoops(Start, End, Builder, DTU, *LI); Type *TileVecTy = FixedVectorType::get(MatMul->getType()->getScalarType(), TileSize); MatrixTy TileResult; // Insert in the inner loop header. Builder.SetInsertPoint(TI.InnerLoopHeader->getTerminator()); // Create PHI nodes for the result columns to accumulate across iterations. SmallVector ColumnPhis; for (unsigned I = 0; I < TileSize; I++) { auto *Phi = Builder.CreatePHI(TileVecTy, 2, "result.vec." + Twine(I)); Phi->addIncoming(ConstantAggregateZero::get(TileVecTy), TI.RowLoopHeader->getSingleSuccessor()); TileResult.addVector(Phi); ColumnPhis.push_back(Phi); } // Insert in the inner loop body, which computes // Res += Load(CurrentRow, K) * Load(K, CurrentColumn) Builder.SetInsertPoint(InnerBody->getTerminator()); // Load tiles of the operands. MatrixTy A = loadMatrix(LPtr, {}, false, LShape, TI.CurrentRow, TI.CurrentK, {TileSize, TileSize}, EltType, Builder); MatrixTy B = loadMatrix(RPtr, {}, false, RShape, TI.CurrentK, TI.CurrentCol, {TileSize, TileSize}, EltType, Builder); emitMatrixMultiply(TileResult, A, B, AllowContract, Builder, true); // Store result after the inner loop is done. Builder.SetInsertPoint(TI.RowLoopLatch->getTerminator()); storeMatrix(TileResult, Store->getPointerOperand(), Store->getAlign(), Store->isVolatile(), {LShape.NumRows, RShape.NumColumns}, TI.CurrentRow, TI.CurrentCol, EltType, Builder); for (unsigned I = 0; I < TileResult.getNumVectors(); I++) ColumnPhis[I]->addIncoming(TileResult.getVector(I), TI.InnerLoopLatch); // Force unrolling of a few iterations of the inner loop, to make sure there // is enough work per iteration. // FIXME: The unroller should make this decision directly instead, but // currently the cost-model is not up to the task. unsigned InnerLoopUnrollCount = std::min(10u, LShape.NumColumns / TileSize); addStringMetadataToLoop(LI->getLoopFor(TI.InnerLoopHeader), "llvm.loop.unroll.count", InnerLoopUnrollCount); } void emitSIMDTiling(CallInst *MatMul, LoadInst *LoadOp0, LoadInst *LoadOp1, StoreInst *Store, SmallPtrSetImpl &FusedInsts) { assert(MatrixLayout == MatrixLayoutTy::ColumnMajor && "Tiling only supported for column-major matrixes at the moment!"); if (!isFusionProfitable(MatMul)) return; ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); const unsigned R = LShape.NumRows; const unsigned C = RShape.NumColumns; const unsigned M = LShape.NumColumns; auto *EltType = cast(MatMul->getType())->getElementType(); Value *APtr = getNonAliasingPointer(LoadOp0, Store, MatMul); Value *BPtr = getNonAliasingPointer(LoadOp1, Store, MatMul); Value *CPtr = Store->getPointerOperand(); bool AllowContract = AllowContractEnabled || (isa(MatMul) && MatMul->hasAllowContract()); if (TileUseLoops && (R % TileSize == 0 && C % TileSize == 0)) createTiledLoops(MatMul, APtr, LShape, BPtr, RShape, Store, AllowContract); else { IRBuilder<> Builder(Store); for (unsigned J = 0; J < C; J += TileSize) for (unsigned I = 0; I < R; I += TileSize) { const unsigned TileR = std::min(R - I, unsigned(TileSize)); const unsigned TileC = std::min(C - J, unsigned(TileSize)); MatrixTy Res = getZeroMatrix(EltType, TileR, TileC); for (unsigned K = 0; K < M; K += TileSize) { const unsigned TileM = std::min(M - K, unsigned(TileSize)); MatrixTy A = loadMatrix(APtr, LoadOp0->getAlign(), LoadOp0->isVolatile(), LShape, Builder.getInt64(I), Builder.getInt64(K), {TileR, TileM}, EltType, Builder); MatrixTy B = loadMatrix(BPtr, LoadOp1->getAlign(), LoadOp1->isVolatile(), RShape, Builder.getInt64(K), Builder.getInt64(J), {TileM, TileC}, EltType, Builder); emitMatrixMultiply(Res, A, B, AllowContract, Builder, true); } storeMatrix(Res, CPtr, Store->getAlign(), Store->isVolatile(), {R, M}, Builder.getInt64(I), Builder.getInt64(J), EltType, Builder); } } // Mark eliminated instructions as fused and remove them. FusedInsts.insert(Store); FusedInsts.insert(MatMul); Store->eraseFromParent(); MatMul->eraseFromParent(); if (LoadOp0->hasNUses(0)) { FusedInsts.insert(LoadOp0); LoadOp0->eraseFromParent(); } if (LoadOp1->hasNUses(0)) { FusedInsts.insert(LoadOp1); LoadOp1->eraseFromParent(); } } /// Try to lower matrix multiply chains by fusing operations. /// /// Currently we only lower {ld, ld} -> matmul -> st chains. // /// No need to return a MatrixTy object for the result of the operation, since /// the single store user will be lowered as part of this. Instructions that /// are completely eliminated by fusion are added to \p FusedInsts. void LowerMatrixMultiplyFused(CallInst *MatMul, SmallPtrSetImpl &FusedInsts) { if (!FuseMatrix || !MatMul->hasOneUse() || MatrixLayout != MatrixLayoutTy::ColumnMajor || !DT) return; assert(AA && LI && "Analyses should be available"); auto *LoadOp0 = dyn_cast(MatMul->getOperand(0)); auto *LoadOp1 = dyn_cast(MatMul->getOperand(1)); auto *Store = dyn_cast(*MatMul->user_begin()); if (LoadOp0 && LoadOp1 && Store) { // The store address must dominate the MatMul instruction, otherwise // we create invalid IR. // FIXME: See if we can hoist the store address computation. auto *AddrI = dyn_cast(Store->getOperand(1)); if (AddrI && (!DT->dominates(AddrI, MatMul))) return; emitSIMDTiling(MatMul, LoadOp0, LoadOp1, Store, FusedInsts); return; } } /// Lowers llvm.matrix.multiply. void LowerMultiply(CallInst *MatMul) { IRBuilder<> Builder(MatMul); auto *EltType = cast(MatMul->getType())->getElementType(); ShapeInfo LShape(MatMul->getArgOperand(2), MatMul->getArgOperand(3)); ShapeInfo RShape(MatMul->getArgOperand(3), MatMul->getArgOperand(4)); const MatrixTy &Lhs = getMatrix(MatMul->getArgOperand(0), LShape, Builder); const MatrixTy &Rhs = getMatrix(MatMul->getArgOperand(1), RShape, Builder); assert(Lhs.getElementType() == Rhs.getElementType() && "Matrix multiply argument element types do not match."); const unsigned R = LShape.NumRows; const unsigned C = RShape.NumColumns; assert(LShape.NumColumns == RShape.NumRows); // Initialize the output MatrixTy Result(R, C, EltType); assert(Lhs.getElementType() == Result.getElementType() && "Matrix multiply result element type does not match arguments."); bool AllowContract = AllowContractEnabled || (isa(MatMul) && MatMul->hasAllowContract()); emitMatrixMultiply(Result, Lhs, Rhs, AllowContract, Builder, false); finalizeLowering(MatMul, Result, Builder); } /// Lowers llvm.matrix.transpose. void LowerTranspose(CallInst *Inst) { MatrixTy Result; IRBuilder<> Builder(Inst); Value *InputVal = Inst->getArgOperand(0); VectorType *VectorTy = cast(InputVal->getType()); ShapeInfo ArgShape(Inst->getArgOperand(1), Inst->getArgOperand(2)); MatrixTy InputMatrix = getMatrix(InputVal, ArgShape, Builder); const unsigned NewNumVecs = InputMatrix.isColumnMajor() ? ArgShape.NumRows : ArgShape.NumColumns; const unsigned NewNumElts = InputMatrix.isColumnMajor() ? ArgShape.NumColumns : ArgShape.NumRows; for (unsigned I = 0; I < NewNumVecs; ++I) { // Build a single result vector. First initialize it. Value *ResultVector = UndefValue::get( FixedVectorType::get(VectorTy->getElementType(), NewNumElts)); // Go through the old elements and insert it into the resulting vector. for (auto J : enumerate(InputMatrix.vectors())) { Value *Elt = Builder.CreateExtractElement(J.value(), I); // Row and column indices are transposed. ResultVector = Builder.CreateInsertElement(ResultVector, Elt, J.index()); } Result.addVector(ResultVector); } // TODO: Improve estimate of operations needed for transposes. Currently we // just count the insertelement/extractelement instructions, but do not // account for later simplifications/combines. finalizeLowering( Inst, Result.addNumComputeOps(2 * ArgShape.NumRows * ArgShape.NumColumns), Builder); } /// Lower load instructions, if shape information is available. bool VisitLoad(LoadInst *Inst, Value *Ptr, IRBuilder<> &Builder) { auto I = ShapeMap.find(Inst); if (I == ShapeMap.end()) return false; LowerLoad(Inst, Ptr, Inst->getAlign(), Builder.getInt64(I->second.getStride()), Inst->isVolatile(), I->second); return true; } bool VisitStore(StoreInst *Inst, Value *StoredVal, Value *Ptr, IRBuilder<> &Builder) { auto I = ShapeMap.find(StoredVal); if (I == ShapeMap.end()) return false; LowerStore(Inst, StoredVal, Ptr, Inst->getAlign(), Builder.getInt64(I->second.getStride()), Inst->isVolatile(), I->second); return true; } /// Lower binary operators, if shape information is available. bool VisitBinaryOperator(BinaryOperator *Inst) { auto I = ShapeMap.find(Inst); if (I == ShapeMap.end()) return false; Value *Lhs = Inst->getOperand(0); Value *Rhs = Inst->getOperand(1); IRBuilder<> Builder(Inst); ShapeInfo &Shape = I->second; MatrixTy Result; MatrixTy A = getMatrix(Lhs, Shape, Builder); MatrixTy B = getMatrix(Rhs, Shape, Builder); assert(A.isColumnMajor() == B.isColumnMajor() && Result.isColumnMajor() == A.isColumnMajor() && "operands must agree on matrix layout"); // Helper to perform binary op on vectors. auto BuildVectorOp = [&Builder, Inst](Value *LHS, Value *RHS) { switch (Inst->getOpcode()) { case Instruction::Add: return Builder.CreateAdd(LHS, RHS); case Instruction::Mul: return Builder.CreateMul(LHS, RHS); case Instruction::Sub: return Builder.CreateSub(LHS, RHS); case Instruction::FAdd: return Builder.CreateFAdd(LHS, RHS); case Instruction::FMul: return Builder.CreateFMul(LHS, RHS); case Instruction::FSub: return Builder.CreateFSub(LHS, RHS); default: llvm_unreachable("Unsupported binary operator for matrix"); } }; for (unsigned I = 0; I < Shape.getNumVectors(); ++I) Result.addVector(BuildVectorOp(A.getVector(I), B.getVector(I))); finalizeLowering(Inst, Result.addNumComputeOps(getNumOps(Result.getVectorTy()) * Result.getNumVectors()), Builder); return true; } /// Helper to linearize a matrix expression tree into a string. Currently /// matrix expressions are linarized by starting at an expression leaf and /// linearizing bottom up. struct ExprLinearizer { unsigned LengthToBreak = 100; std::string Str; raw_string_ostream Stream; unsigned LineLength = 0; const DataLayout &DL; /// Mapping from instructions to matrixes. It is used to identify /// matrix instructions. const MapVector &Inst2Matrix; /// Mapping from values to the leaves of all expressions that the value is /// part of. const DenseMap> &Shared; /// Set of matrix expressions in the scope of a given DISubprogram. const SmallSetVector &ExprsInSubprogram; /// Leaf node of the expression to linearize. Value *Leaf; /// Used to keep track of sub-expressions that get reused while linearizing /// the expression. Re-used sub-expressions are marked as (reused). SmallPtrSet ReusedExprs; ExprLinearizer(const DataLayout &DL, const MapVector &Inst2Matrix, const DenseMap> &Shared, const SmallSetVector &ExprsInSubprogram, Value *Leaf) : Str(), Stream(Str), DL(DL), Inst2Matrix(Inst2Matrix), Shared(Shared), ExprsInSubprogram(ExprsInSubprogram), Leaf(Leaf) {} void indent(unsigned N) { LineLength += N; for (unsigned i = 0; i < N; i++) Stream << " "; } void lineBreak() { Stream << "\n"; LineLength = 0; } void maybeIndent(unsigned Indent) { if (LineLength >= LengthToBreak) lineBreak(); if (LineLength == 0) indent(Indent); } void write(StringRef S) { LineLength += S.size(); Stream << S; } Value *getUnderlyingObjectThroughLoads(Value *V) { if (Value *Ptr = getPointerOperand(V)) return getUnderlyingObjectThroughLoads(Ptr); else if (V->getType()->isPointerTy()) return getUnderlyingObject(V); return V; } /// Returns true if \p V is a matrix value in the given subprogram. bool isMatrix(Value *V) const { return ExprsInSubprogram.count(V); } /// If \p V is a matrix value, print its shape as as NumRows x NumColumns to /// \p SS. void prettyPrintMatrixType(Value *V, raw_string_ostream &SS) { auto M = Inst2Matrix.find(V); if (M == Inst2Matrix.end()) SS << "unknown"; else { SS << M->second.getNumRows(); SS << "x"; SS << M->second.getNumColumns(); } } /// Write the called function name. Handles calls to llvm.matrix.* /// specially: we write the name, followed by the dimensions of the input /// matrixes, followed by the scalar type name. void writeFnName(CallInst *CI) { if (!CI->getCalledFunction()) write(""); else { StringRef Name = CI->getCalledFunction()->getName(); if (!Name.startswith("llvm.matrix")) { write(Name); return; } IntrinsicInst *II = dyn_cast(CI); write(StringRef(Intrinsic::getName(II->getIntrinsicID(), {})) .drop_front(StringRef("llvm.matrix.").size())); write("."); - std::string Tmp = ""; + std::string Tmp; raw_string_ostream SS(Tmp); switch (II->getIntrinsicID()) { case Intrinsic::matrix_multiply: prettyPrintMatrixType(II->getOperand(0), SS); SS << "."; prettyPrintMatrixType(II->getOperand(1), SS); SS << "." << *II->getType()->getScalarType(); break; case Intrinsic::matrix_transpose: prettyPrintMatrixType(II->getOperand(0), SS); SS << "." << *II->getType()->getScalarType(); break; case Intrinsic::matrix_column_major_load: prettyPrintMatrixType(II, SS); SS << "." << *II->getType()->getScalarType(); break; case Intrinsic::matrix_column_major_store: prettyPrintMatrixType(II->getOperand(0), SS); SS << "." << *II->getOperand(0)->getType()->getScalarType(); break; default: llvm_unreachable("Unhandled case"); } SS.flush(); write(Tmp); } } unsigned getNumShapeArgs(CallInst *CI) const { if (IntrinsicInst *II = dyn_cast(CI)) { switch (II->getIntrinsicID()) { case Intrinsic::matrix_multiply: return 3; case Intrinsic::matrix_transpose: return 2; case Intrinsic::matrix_column_major_load: case Intrinsic::matrix_column_major_store: return 3; default: return 0; } } return 0; } /// Special printing for values: for pointers, we print if they refer to an /// (function) external address or a stack address, for other values we /// either print the constant or "scalar"/"matrix" for other values. void write(Value *V) { V = getUnderlyingObjectThroughLoads(V); if (V->getType()->isPointerTy()) { if (isa(V)) { Stream << "stack addr"; LineLength += StringRef("stack addr").size(); } else { Stream << "addr"; LineLength += StringRef("addr").size(); } if (!V->getName().empty()) { Stream << " %" << V->getName() << ""; LineLength += V->getName().size() + 2; } return; } std::string Tmp; raw_string_ostream TmpStream(Tmp); if (auto *CI = dyn_cast(V)) TmpStream << CI->getValue(); else if (isa(V)) TmpStream << "constant"; else { if (isMatrix(V)) TmpStream << "matrix"; else TmpStream << "scalar"; } TmpStream.flush(); Tmp = std::string(StringRef(Tmp).trim()); LineLength += Tmp.size(); Stream << Tmp; } /// Linearize expression \p Expr starting at an indentation of \p Indent. /// Expressions that are re-used multiple times are prefixed with (reused) /// at the re-used root instruction. void linearizeExpr(Value *Expr, unsigned Indent, bool ParentReused, bool ParentShared) { auto *I = cast(Expr); maybeIndent(Indent); SmallVector Ops; // Is Expr shared with other expression leaves? bool ExprShared = false; // Deal with shared subtrees. Mark them as shared, if required. if (!ParentShared) { auto SI = Shared.find(Expr); assert(SI != Shared.end() && SI->second.count(Leaf)); for (Value *S : SI->second) { if (S == Leaf) continue; DebugLoc DL = cast(S)->getDebugLoc(); write("shared with remark at line " + std::to_string(DL.getLine()) + " column " + std::to_string(DL.getCol()) + " ("); } ExprShared = SI->second.size() > 1; } bool Reused = !ReusedExprs.insert(Expr).second; if (Reused && !ParentReused) write("(reused) "); if (auto *CI = dyn_cast(I)) { writeFnName(CI); Ops.append(CI->arg_begin(), CI->arg_end() - getNumShapeArgs(CI)); } else if (isa(Expr)) { // Special case bitcasts, which are used to materialize matrixes from // non-matrix ops. write("matrix"); return; } else { Ops.append(I->value_op_begin(), I->value_op_end()); write(std::string(I->getOpcodeName())); } write(std::string("(")); unsigned NumOpsToBreak = 1; if (match(Expr, m_Intrinsic())) NumOpsToBreak = 2; for (Value *Op : Ops) { if (Ops.size() > NumOpsToBreak) lineBreak(); maybeIndent(Indent + 1); if (isMatrix(Op)) linearizeExpr(Op, Indent + 1, Reused, ExprShared); else write(Op); if (Op != Ops.back()) write(", "); } write(")"); } const std::string &getResult() { Stream.flush(); return Str; } }; /// Generate remarks for matrix operations in a function. To generate remarks /// for matrix expressions, the following approach is used: /// 1. Use the inlined-at debug information to group matrix operations to the /// DISubprograms they are contained in. /// 2. Collect leaves of matrix expressions (done in /// RemarkGenerator::getExpressionLeaves) for each subprogram - expression // mapping. Leaves are lowered matrix instructions without other matrix // users (like stores) in the current subprogram. /// 3. For each leaf, create a remark containing a linearizied version of the /// matrix expression. The expression is linearized by a recursive /// bottom-up traversal of the matrix operands, starting at a leaf. Note /// that multiple leaves can share sub-expressions. Shared subexpressions /// are explicitly marked as shared(). struct RemarkGenerator { const MapVector &Inst2Matrix; OptimizationRemarkEmitter &ORE; Function &Func; const DataLayout &DL; RemarkGenerator(const MapVector &Inst2Matrix, OptimizationRemarkEmitter &ORE, Function &Func) : Inst2Matrix(Inst2Matrix), ORE(ORE), Func(Func), DL(Func.getParent()->getDataLayout()) {} /// Return all leaves of the expressions in \p ExprsInSubprogram. Those are /// instructions in Inst2Matrix returning void or without any users in /// \p ExprsInSubprogram. Currently that should only include stores. SmallVector getExpressionLeaves(const SmallSetVector &ExprsInSubprogram) { SmallVector Leaves; for (auto *Expr : ExprsInSubprogram) if (Expr->getType()->isVoidTy() || !any_of(Expr->users(), [&ExprsInSubprogram](User *U) { return ExprsInSubprogram.count(U); })) Leaves.push_back(Expr); return Leaves; } /// Recursively traverse expression \p V starting at \p Leaf and add \p Leaf /// to all visited expressions in \p Shared. Limit the matrix operations to /// the ones in \p ExprsInSubprogram. void collectSharedInfo(Value *Leaf, Value *V, const SmallSetVector &ExprsInSubprogram, DenseMap> &Shared) { if (!ExprsInSubprogram.count(V)) return; auto I = Shared.insert({V, {}}); I.first->second.insert(Leaf); for (Value *Op : cast(V)->operand_values()) collectSharedInfo(Leaf, Op, ExprsInSubprogram, Shared); return; } /// Calculate the number of exclusive and shared op counts for expression /// starting at \p V. Expressions used multiple times are counted once. /// Limit the matrix operations to the ones in \p ExprsInSubprogram. std::pair sumOpInfos(Value *Root, SmallPtrSetImpl &ReusedExprs, const SmallSetVector &ExprsInSubprogram, DenseMap> &Shared) const { if (!ExprsInSubprogram.count(Root)) return {}; // Already counted this expression. Stop. if (!ReusedExprs.insert(Root).second) return {}; OpInfoTy SharedCount; OpInfoTy Count; auto I = Shared.find(Root); auto CM = Inst2Matrix.find(Root); if (I->second.size() == 1) Count = CM->second.getOpInfo(); else SharedCount = CM->second.getOpInfo(); for (Value *Op : cast(Root)->operand_values()) { auto C = sumOpInfos(Op, ReusedExprs, ExprsInSubprogram, Shared); Count += C.first; SharedCount += C.second; } return {Count, SharedCount}; } void emitRemarks() { if (!ORE.allowExtraAnalysis(DEBUG_TYPE)) return; // Map matrix operations to their containting subprograms, by traversing // the inlinedAt chain. If the function does not have a DISubprogram, we // only map them to the containing function. MapVector> Subprog2Exprs; for (auto &KV : Inst2Matrix) { if (Func.getSubprogram()) { auto *I = cast(KV.first); DILocation *Context = I->getDebugLoc(); while (Context) { auto I = Subprog2Exprs.insert({getSubprogram(Context->getScope()), {}}); I.first->second.push_back(KV.first); Context = DebugLoc(Context).getInlinedAt(); } } else { auto I = Subprog2Exprs.insert({nullptr, {}}); I.first->second.push_back(KV.first); } } for (auto &KV : Subprog2Exprs) { SmallSetVector ExprsInSubprogram(KV.second.begin(), KV.second.end()); auto Leaves = getExpressionLeaves(ExprsInSubprogram); DenseMap> Shared; for (Value *Leaf : Leaves) collectSharedInfo(Leaf, Leaf, ExprsInSubprogram, Shared); // Generate remarks for each leaf. for (auto *L : Leaves) { DebugLoc Loc = cast(L)->getDebugLoc(); DILocation *Context = cast(L)->getDebugLoc(); while (Context) { if (getSubprogram(Context->getScope()) == KV.first) { Loc = Context; break; } Context = DebugLoc(Context).getInlinedAt(); } SmallPtrSet ReusedExprs; OpInfoTy Counts, SharedCounts; std::tie(Counts, SharedCounts) = sumOpInfos(L, ReusedExprs, ExprsInSubprogram, Shared); OptimizationRemark Rem(DEBUG_TYPE, "matrix-lowered", Loc, cast(L)->getParent()); Rem << "Lowered with "; Rem << ore::NV("NumStores", Counts.NumStores) << " stores, " << ore::NV("NumLoads", Counts.NumLoads) << " loads, " << ore::NV("NumComputeOps", Counts.NumComputeOps) << " compute ops"; if (SharedCounts.NumStores > 0 || SharedCounts.NumLoads > 0 || SharedCounts.NumComputeOps > 0) { Rem << ",\nadditionally " << ore::NV("NumStores", SharedCounts.NumStores) << " stores, " << ore::NV("NumLoads", SharedCounts.NumLoads) << " loads, " << ore::NV("NumFPOps", SharedCounts.NumComputeOps) << " compute ops" << " are shared with other expressions"; } Rem << ("\n" + linearize(L, Shared, ExprsInSubprogram, DL)); ORE.emit(Rem); } } } std::string linearize(Value *L, const DenseMap> &Shared, const SmallSetVector &ExprsInSubprogram, const DataLayout &DL) { ExprLinearizer Lin(DL, Inst2Matrix, Shared, ExprsInSubprogram, L); Lin.linearizeExpr(L, 0, false, false); return Lin.getResult(); } }; }; } // namespace PreservedAnalyses LowerMatrixIntrinsicsPass::run(Function &F, FunctionAnalysisManager &AM) { auto &TTI = AM.getResult(F); OptimizationRemarkEmitter *ORE = nullptr; AAResults *AA = nullptr; DominatorTree *DT = nullptr; LoopInfo *LI = nullptr; if (!Minimal) { ORE = &AM.getResult(F); AA = &AM.getResult(F); DT = &AM.getResult(F); LI = &AM.getResult(F); } LowerMatrixIntrinsics LMT(F, TTI, AA, DT, LI, ORE); if (LMT.Visit()) { PreservedAnalyses PA; if (!Minimal) { PA.preserve(); PA.preserve(); } return PA; } return PreservedAnalyses::all(); } namespace { class LowerMatrixIntrinsicsLegacyPass : public FunctionPass { public: static char ID; LowerMatrixIntrinsicsLegacyPass() : FunctionPass(ID) { initializeLowerMatrixIntrinsicsLegacyPassPass( *PassRegistry::getPassRegistry()); } bool runOnFunction(Function &F) override { auto &TTI = getAnalysis().getTTI(F); auto &ORE = getAnalysis().getORE(); auto &AA = getAnalysis().getAAResults(); auto &DT = getAnalysis().getDomTree(); auto &LI = getAnalysis().getLoopInfo(); LowerMatrixIntrinsics LMT(F, TTI, &AA, &DT, &LI, &ORE); bool C = LMT.Visit(); return C; } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); AU.addRequired(); AU.addRequired(); AU.addRequired(); AU.addPreserved(); AU.addRequired(); AU.addPreserved(); } }; } // namespace static const char pass_name[] = "Lower the matrix intrinsics"; char LowerMatrixIntrinsicsLegacyPass::ID = 0; INITIALIZE_PASS_BEGIN(LowerMatrixIntrinsicsLegacyPass, DEBUG_TYPE, pass_name, false, false) INITIALIZE_PASS_DEPENDENCY(OptimizationRemarkEmitterWrapperPass) INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass) INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) INITIALIZE_PASS_END(LowerMatrixIntrinsicsLegacyPass, DEBUG_TYPE, pass_name, false, false) Pass *llvm::createLowerMatrixIntrinsicsPass() { return new LowerMatrixIntrinsicsLegacyPass(); } namespace { /// A lightweight version of the matrix lowering pass that only requires TTI. /// Advanced features that require DT, AA or ORE like tiling are disabled. This /// is used to lower matrix intrinsics if the main lowering pass is not run, for /// example with -O0. class LowerMatrixIntrinsicsMinimalLegacyPass : public FunctionPass { public: static char ID; LowerMatrixIntrinsicsMinimalLegacyPass() : FunctionPass(ID) { initializeLowerMatrixIntrinsicsMinimalLegacyPassPass( *PassRegistry::getPassRegistry()); } bool runOnFunction(Function &F) override { auto &TTI = getAnalysis().getTTI(F); LowerMatrixIntrinsics LMT(F, TTI, nullptr, nullptr, nullptr, nullptr); bool C = LMT.Visit(); return C; } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); AU.setPreservesCFG(); } }; } // namespace static const char pass_name_minimal[] = "Lower the matrix intrinsics (minimal)"; char LowerMatrixIntrinsicsMinimalLegacyPass::ID = 0; INITIALIZE_PASS_BEGIN(LowerMatrixIntrinsicsMinimalLegacyPass, "lower-matrix-intrinsics-minimal", pass_name_minimal, false, false) INITIALIZE_PASS_END(LowerMatrixIntrinsicsMinimalLegacyPass, "lower-matrix-intrinsics-minimal", pass_name_minimal, false, false) Pass *llvm::createLowerMatrixIntrinsicsMinimalPass() { return new LowerMatrixIntrinsicsMinimalLegacyPass(); } diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index 4db5624e34fa..7ab1d6608ccf 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -1,768 +1,768 @@ //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===// // // 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 // //===----------------------------------------------------------------------===// /// /// \file This file implements the html coverage renderer. /// //===----------------------------------------------------------------------===// #include "CoverageReport.h" #include "SourceCoverageViewHTML.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" using namespace llvm; namespace { // Return a string with the special characters in \p Str escaped. std::string escape(StringRef Str, const CoverageViewOptions &Opts) { std::string TabExpandedResult; unsigned ColNum = 0; // Record the column number. for (char C : Str) { if (C == '\t') { // Replace '\t' with up to TabSize spaces. unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize); TabExpandedResult.append(NumSpaces, ' '); ColNum += NumSpaces; } else { TabExpandedResult += C; if (C == '\n' || C == '\r') ColNum = 0; else ++ColNum; } } std::string EscapedHTML; { raw_string_ostream OS{EscapedHTML}; printHTMLEscaped(TabExpandedResult, OS); } return EscapedHTML; } // Create a \p Name tag around \p Str, and optionally set its \p ClassName. std::string tag(const std::string &Name, const std::string &Str, const std::string &ClassName = "") { std::string Tag = "<" + Name; if (!ClassName.empty()) Tag += " class='" + ClassName + "'"; return Tag + ">" + Str + ""; } // Create an anchor to \p Link with the label \p Str. std::string a(const std::string &Link, const std::string &Str, const std::string &TargetName = "") { std::string Name = TargetName.empty() ? "" : ("name='" + TargetName + "' "); return "" + Str + ""; } const char *BeginHeader = "" "" ""; const char *CSSForCoverage = R"(.red { background-color: #ffd0d0; } .cyan { background-color: cyan; } body { font-family: -apple-system, sans-serif; } pre { margin-top: 0px !important; margin-bottom: 0px !important; } .source-name-title { padding: 5px 10px; border-bottom: 1px solid #dbdbdb; background-color: #eee; line-height: 35px; } .centered { display: table; margin-left: left; margin-right: auto; border: 1px solid #dbdbdb; border-radius: 3px; } .expansion-view { background-color: rgba(0, 0, 0, 0); margin-left: 0px; margin-top: 5px; margin-right: 5px; margin-bottom: 5px; border: 1px solid #dbdbdb; border-radius: 3px; } table { border-collapse: collapse; } .light-row { background: #ffffff; border: 1px solid #dbdbdb; } .light-row-bold { background: #ffffff; border: 1px solid #dbdbdb; font-weight: bold; } .column-entry { text-align: left; } .column-entry-bold { font-weight: bold; text-align: left; } .column-entry-yellow { text-align: left; background-color: #ffffd0; } .column-entry-yellow:hover { background-color: #fffff0; } .column-entry-red { text-align: left; background-color: #ffd0d0; } .column-entry-red:hover { background-color: #fff0f0; } .column-entry-green { text-align: left; background-color: #d0ffd0; } .column-entry-green:hover { background-color: #f0fff0; } .line-number { text-align: right; color: #aaa; } .covered-line { text-align: right; color: #0080ff; } .uncovered-line { text-align: right; color: #ff3300; } .tooltip { position: relative; display: inline; background-color: #b3e6ff; text-decoration: none; } .tooltip span.tooltip-content { position: absolute; width: 100px; margin-left: -50px; color: #FFFFFF; background: #000000; height: 30px; line-height: 30px; text-align: center; visibility: hidden; border-radius: 6px; } .tooltip span.tooltip-content:after { content: ''; position: absolute; top: 100%; left: 50%; margin-left: -8px; width: 0; height: 0; border-top: 8px solid #000000; border-right: 8px solid transparent; border-left: 8px solid transparent; } :hover.tooltip span.tooltip-content { visibility: visible; opacity: 0.8; bottom: 30px; left: 50%; z-index: 999; } th, td { vertical-align: top; padding: 2px 8px; border-collapse: collapse; border-right: solid 1px #eee; border-left: solid 1px #eee; text-align: left; } td pre { display: inline-block; } td:first-child { border-left: none; } td:last-child { border-right: none; } tr:hover { background-color: #f0f0f0; } )"; const char *EndHeader = ""; const char *BeginCenteredDiv = "
"; const char *EndCenteredDiv = "
"; const char *BeginSourceNameDiv = "
"; const char *EndSourceNameDiv = "
"; const char *BeginCodeTD = ""; const char *EndCodeTD = ""; const char *BeginPre = "
";
 
 const char *EndPre = "
"; const char *BeginExpansionDiv = "
"; const char *EndExpansionDiv = "
"; const char *BeginTable = ""; const char *EndTable = "
"; const char *ProjectTitleTag = "h1"; const char *ReportTitleTag = "h2"; const char *CreatedTimeTag = "h4"; std::string getPathToStyle(StringRef ViewPath) { - std::string PathToStyle = ""; + std::string PathToStyle; std::string PathSep = std::string(sys::path::get_separator()); unsigned NumSeps = ViewPath.count(PathSep); for (unsigned I = 0, E = NumSeps; I < E; ++I) PathToStyle += ".." + PathSep; return PathToStyle + "style.css"; } void emitPrelude(raw_ostream &OS, const CoverageViewOptions &Opts, const std::string &PathToStyle = "") { OS << "" "" << BeginHeader; // Link to a stylesheet if one is available. Otherwise, use the default style. if (PathToStyle.empty()) OS << ""; else OS << ""; OS << EndHeader << ""; } void emitEpilog(raw_ostream &OS) { OS << "" << ""; } } // anonymous namespace Expected CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) { auto OSOrErr = createOutputStream(Path, "html", InToplevel); if (!OSOrErr) return OSOrErr; OwnedStream OS = std::move(OSOrErr.get()); if (!Opts.hasOutputDirectory()) { emitPrelude(*OS.get(), Opts); } else { std::string ViewPath = getOutputPath(Path, "html", InToplevel); emitPrelude(*OS.get(), Opts, getPathToStyle(ViewPath)); } return std::move(OS); } void CoveragePrinterHTML::closeViewFile(OwnedStream OS) { emitEpilog(*OS.get()); } /// Emit column labels for the table in the index. static void emitColumnLabelsForIndex(raw_ostream &OS, const CoverageViewOptions &Opts) { SmallVector Columns; Columns.emplace_back(tag("td", "Filename", "column-entry-bold")); Columns.emplace_back(tag("td", "Function Coverage", "column-entry-bold")); if (Opts.ShowInstantiationSummary) Columns.emplace_back( tag("td", "Instantiation Coverage", "column-entry-bold")); Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold")); if (Opts.ShowRegionSummary) Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold")); if (Opts.ShowBranchSummary) Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold")); OS << tag("tr", join(Columns.begin(), Columns.end(), "")); } std::string CoveragePrinterHTML::buildLinkToFile(StringRef SF, const FileCoverageSummary &FCS) const { SmallString<128> LinkTextStr(sys::path::relative_path(FCS.Name)); sys::path::remove_dots(LinkTextStr, /*remove_dot_dots=*/true); sys::path::native(LinkTextStr); std::string LinkText = escape(LinkTextStr, Opts); std::string LinkTarget = escape(getOutputPath(SF, "html", /*InToplevel=*/false), Opts); return a(LinkTarget, LinkText); } /// Render a file coverage summary (\p FCS) in a table row. If \p IsTotals is /// false, link the summary to \p SF. void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF, const FileCoverageSummary &FCS, bool IsTotals) const { SmallVector Columns; // Format a coverage triple and add the result to the list of columns. auto AddCoverageTripleToColumn = [&Columns](unsigned Hit, unsigned Total, float Pctg) { std::string S; { raw_string_ostream RSO{S}; if (Total) RSO << format("%*.2f", 7, Pctg) << "% "; else RSO << "- "; RSO << '(' << Hit << '/' << Total << ')'; } const char *CellClass = "column-entry-yellow"; if (Hit == Total) CellClass = "column-entry-green"; else if (Pctg < 80.0) CellClass = "column-entry-red"; Columns.emplace_back(tag("td", tag("pre", S), CellClass)); }; // Simplify the display file path, and wrap it in a link if requested. std::string Filename; if (IsTotals) { Filename = std::string(SF); } else { Filename = buildLinkToFile(SF, FCS); } Columns.emplace_back(tag("td", tag("pre", Filename))); AddCoverageTripleToColumn(FCS.FunctionCoverage.getExecuted(), FCS.FunctionCoverage.getNumFunctions(), FCS.FunctionCoverage.getPercentCovered()); if (Opts.ShowInstantiationSummary) AddCoverageTripleToColumn(FCS.InstantiationCoverage.getExecuted(), FCS.InstantiationCoverage.getNumFunctions(), FCS.InstantiationCoverage.getPercentCovered()); AddCoverageTripleToColumn(FCS.LineCoverage.getCovered(), FCS.LineCoverage.getNumLines(), FCS.LineCoverage.getPercentCovered()); if (Opts.ShowRegionSummary) AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(), FCS.RegionCoverage.getNumRegions(), FCS.RegionCoverage.getPercentCovered()); if (Opts.ShowBranchSummary) AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(), FCS.BranchCoverage.getNumBranches(), FCS.BranchCoverage.getPercentCovered()); if (IsTotals) OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold"); else OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row"); } Error CoveragePrinterHTML::createIndexFile( ArrayRef SourceFiles, const CoverageMapping &Coverage, const CoverageFiltersMatchAll &Filters) { // Emit the default stylesheet. auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true); if (Error E = CSSOrErr.takeError()) return E; OwnedStream CSS = std::move(CSSOrErr.get()); CSS->operator<<(CSSForCoverage); // Emit a file index along with some coverage statistics. auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true); if (Error E = OSOrErr.takeError()) return E; auto OS = std::move(OSOrErr.get()); raw_ostream &OSRef = *OS.get(); assert(Opts.hasOutputDirectory() && "No output directory for index file"); emitPrelude(OSRef, Opts, getPathToStyle("")); // Emit some basic information about the coverage report. if (Opts.hasProjectTitle()) OSRef << tag(ProjectTitleTag, escape(Opts.ProjectTitle, Opts)); OSRef << tag(ReportTitleTag, "Coverage Report"); if (Opts.hasCreatedTime()) OSRef << tag(CreatedTimeTag, escape(Opts.CreatedTimeStr, Opts)); // Emit a link to some documentation. OSRef << tag("p", "Click " + a("http://clang.llvm.org/docs/" "SourceBasedCodeCoverage.html#interpreting-reports", "here") + " for information about interpreting this report."); // Emit a table containing links to reports for each file in the covmapping. // Exclude files which don't contain any regions. OSRef << BeginCenteredDiv << BeginTable; emitColumnLabelsForIndex(OSRef, Opts); FileCoverageSummary Totals("TOTALS"); auto FileReports = CoverageReport::prepareFileReports( Coverage, Totals, SourceFiles, Opts, Filters); bool EmptyFiles = false; for (unsigned I = 0, E = FileReports.size(); I < E; ++I) { if (FileReports[I].FunctionCoverage.getNumFunctions()) emitFileSummary(OSRef, SourceFiles[I], FileReports[I]); else EmptyFiles = true; } emitFileSummary(OSRef, "Totals", Totals, /*IsTotals=*/true); OSRef << EndTable << EndCenteredDiv; // Emit links to files which don't contain any functions. These are normally // not very useful, but could be relevant for code which abuses the // preprocessor. if (EmptyFiles && Filters.empty()) { OSRef << tag("p", "Files which contain no functions. (These " "files contain code pulled into other files " "by the preprocessor.)\n"); OSRef << BeginCenteredDiv << BeginTable; for (unsigned I = 0, E = FileReports.size(); I < E; ++I) if (!FileReports[I].FunctionCoverage.getNumFunctions()) { std::string Link = buildLinkToFile(SourceFiles[I], FileReports[I]); OSRef << tag("tr", tag("td", tag("pre", Link)), "light-row") << '\n'; } OSRef << EndTable << EndCenteredDiv; } OSRef << tag("h5", escape(Opts.getLLVMVersionString(), Opts)); emitEpilog(OSRef); return Error::success(); } void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) { OS << BeginCenteredDiv << BeginTable; } void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) { OS << EndTable << EndCenteredDiv; } void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS, bool WholeFile) { OS << BeginSourceNameDiv << tag("pre", escape(getSourceName(), getOptions())) << EndSourceNameDiv; } void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) { OS << ""; } void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) { // If this view has sub-views, renderLine() cannot close the view's cell. // Take care of it here, after all sub-views have been rendered. if (hasSubViews()) OS << EndCodeTD; OS << ""; } void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) { // The table-based output makes view dividers unnecessary. } void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS, unsigned ExpansionCol, unsigned) { StringRef Line = L.Line; unsigned LineNo = L.LineNo; // Steps for handling text-escaping, highlighting, and tooltip creation: // // 1. Split the line into N+1 snippets, where N = |Segments|. The first // snippet starts from Col=1 and ends at the start of the first segment. // The last snippet starts at the last mapped column in the line and ends // at the end of the line. Both are required but may be empty. SmallVector Snippets; CoverageSegmentArray Segments = LCS.getLineSegments(); unsigned LCol = 1; auto Snip = [&](unsigned Start, unsigned Len) { Snippets.push_back(std::string(Line.substr(Start, Len))); LCol += Len; }; Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1)); for (unsigned I = 1, E = Segments.size(); I < E; ++I) Snip(LCol - 1, Segments[I]->Col - LCol); // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1. Snip(LCol - 1, Line.size() + 1 - LCol); // 2. Escape all of the snippets. for (unsigned I = 0, E = Snippets.size(); I < E; ++I) Snippets[I] = escape(Snippets[I], getOptions()); // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment // 1 to set the highlight for snippet 2, segment 2 to set the highlight for // snippet 3, and so on. Optional Color; SmallVector, 2> HighlightedRanges; auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) { if (getOptions().Debug) HighlightedRanges.emplace_back(LC, RC); return tag("span", Snippet, std::string(Color.getValue())); }; auto CheckIfUncovered = [&](const CoverageSegment *S) { return S && (!S->IsGapRegion || (Color && *Color == "red")) && S->HasCount && S->Count == 0; }; if (CheckIfUncovered(LCS.getWrappedSegment())) { Color = "red"; if (!Snippets[0].empty()) Snippets[0] = Highlight(Snippets[0], 1, 1 + Snippets[0].size()); } for (unsigned I = 0, E = Segments.size(); I < E; ++I) { const auto *CurSeg = Segments[I]; if (CheckIfUncovered(CurSeg)) Color = "red"; else if (CurSeg->Col == ExpansionCol) Color = "cyan"; else Color = None; if (Color.hasValue()) Snippets[I + 1] = Highlight(Snippets[I + 1], CurSeg->Col, CurSeg->Col + Snippets[I + 1].size()); } if (Color.hasValue() && Segments.empty()) Snippets.back() = Highlight(Snippets.back(), 1, 1 + Snippets.back().size()); if (getOptions().Debug) { for (const auto &Range : HighlightedRanges) { errs() << "Highlighted line " << LineNo << ", " << Range.first << " -> "; if (Range.second == 0) errs() << "?"; else errs() << Range.second; errs() << "\n"; } } // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate // sub-line region count tooltips if needed. if (shouldRenderRegionMarkers(LCS)) { // Just consider the segments which start *and* end on this line. for (unsigned I = 0, E = Segments.size() - 1; I < E; ++I) { const auto *CurSeg = Segments[I]; if (!CurSeg->IsRegionEntry) continue; if (CurSeg->Count == LCS.getExecutionCount()) continue; Snippets[I + 1] = tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count), "tooltip-content"), "tooltip"); if (getOptions().Debug) errs() << "Marker at " << CurSeg->Line << ":" << CurSeg->Col << " = " << formatCount(CurSeg->Count) << "\n"; } } OS << BeginCodeTD; OS << BeginPre; for (const auto &Snippet : Snippets) OS << Snippet; OS << EndPre; // If there are no sub-views left to attach to this cell, end the cell. // Otherwise, end it after the sub-views are rendered (renderLineSuffix()). if (!hasSubViews()) OS << EndCodeTD; } void SourceCoverageViewHTML::renderLineCoverageColumn( raw_ostream &OS, const LineCoverageStats &Line) { - std::string Count = ""; + std::string Count; if (Line.isMapped()) Count = tag("pre", formatCount(Line.getExecutionCount())); std::string CoverageClass = (Line.getExecutionCount() > 0) ? "covered-line" : "uncovered-line"; OS << tag("td", Count, CoverageClass); } void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) { std::string LineNoStr = utostr(uint64_t(LineNo)); std::string TargetName = "L" + LineNoStr; OS << tag("td", a("#" + TargetName, tag("pre", LineNoStr), TargetName), "line-number"); } void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &, const LineCoverageStats &Line, unsigned) { // Region markers are rendered in-line using tooltips. } void SourceCoverageViewHTML::renderExpansionSite(raw_ostream &OS, LineRef L, const LineCoverageStats &LCS, unsigned ExpansionCol, unsigned ViewDepth) { // Render the line containing the expansion site. No extra formatting needed. renderLine(OS, L, LCS, ExpansionCol, ViewDepth); } void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS, ExpansionView &ESV, unsigned ViewDepth) { OS << BeginExpansionDiv; ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false, /*ShowTitle=*/false, ViewDepth + 1); OS << EndExpansionDiv; } void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV, unsigned ViewDepth) { // Render the child subview. if (getOptions().Debug) errs() << "Branch at line " << BRV.getLine() << '\n'; OS << BeginExpansionDiv; OS << BeginPre; for (const auto &R : BRV.Regions) { // Calculate TruePercent and False Percent. double TruePercent = 0.0; double FalsePercent = 0.0; unsigned Total = R.ExecutionCount + R.FalseExecutionCount; if (!getOptions().ShowBranchCounts && Total != 0) { TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; } // Display Line + Column. std::string LineNoStr = utostr(uint64_t(R.LineStart)); std::string ColNoStr = utostr(uint64_t(R.ColumnStart)); std::string TargetName = "L" + LineNoStr; OS << " Branch ("; OS << tag("span", a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr), TargetName), "line-number") + "): ["; if (R.Folded) { OS << "Folded - Ignored]\n"; continue; } // Display TrueCount or TruePercent. std::string TrueColor = R.ExecutionCount ? "None" : "red"; std::string TrueCovClass = (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line"; OS << tag("span", "True", TrueColor); OS << ": "; if (getOptions().ShowBranchCounts) OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", "; else OS << format("%0.2f", TruePercent) << "%, "; // Display FalseCount or FalsePercent. std::string FalseColor = R.FalseExecutionCount ? "None" : "red"; std::string FalseCovClass = (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line"; OS << tag("span", "False", FalseColor); OS << ": "; if (getOptions().ShowBranchCounts) OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass); else OS << format("%0.2f", FalsePercent) << "%"; OS << "]\n"; } OS << EndPre; OS << EndExpansionDiv; } void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) { OS << BeginExpansionDiv; if (!ISV.View) OS << BeginSourceNameDiv << tag("pre", escape("Unexecuted instantiation: " + ISV.FunctionName.str(), getOptions())) << EndSourceNameDiv; else ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, /*ShowTitle=*/false, ViewDepth); OS << EndExpansionDiv; } void SourceCoverageViewHTML::renderTitle(raw_ostream &OS, StringRef Title) { if (getOptions().hasProjectTitle()) OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions())); OS << tag(ReportTitleTag, escape(Title, getOptions())); if (getOptions().hasCreatedTime()) OS << tag(CreatedTimeTag, escape(getOptions().CreatedTimeStr, getOptions())); } void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo, unsigned ViewDepth) { std::string SourceLabel; if (FirstUncoveredLineNo == 0) { SourceLabel = tag("td", tag("pre", "Source")); } else { std::string LinkTarget = "#L" + utostr(uint64_t(FirstUncoveredLineNo)); SourceLabel = tag("td", tag("pre", "Source (" + a(LinkTarget, "jump to first uncovered line") + ")")); } renderLinePrefix(OS, ViewDepth); OS << tag("td", tag("pre", "Line")) << tag("td", tag("pre", "Count")) << SourceLabel; renderLineSuffix(OS, ViewDepth); } diff --git a/llvm/tools/llvm-ifs/llvm-ifs.cpp b/llvm/tools/llvm-ifs/llvm-ifs.cpp index 8906300dca0c..4d9a5c365a78 100644 --- a/llvm/tools/llvm-ifs/llvm-ifs.cpp +++ b/llvm/tools/llvm-ifs/llvm-ifs.cpp @@ -1,512 +1,512 @@ //===- llvm-ifs.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-----------------------------------------------------------------------===/ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TextAPI/MachO/InterfaceFile.h" #include "llvm/TextAPI/MachO/TextAPIReader.h" #include "llvm/TextAPI/MachO/TextAPIWriter.h" #include #include #include using namespace llvm; using namespace llvm::yaml; using namespace llvm::MachO; #define DEBUG_TYPE "llvm-ifs" namespace { const VersionTuple IFSVersionCurrent(2, 0); } // end anonymous namespace static cl::opt Action("action", cl::desc(""), cl::value_desc("write-ifs | write-bin"), cl::init("write-ifs")); static cl::opt ForceFormat("force-format", cl::desc(""), cl::value_desc("ELF | TBD"), cl::init("")); static cl::list InputFilenames(cl::Positional, cl::desc(""), cl::ZeroOrMore); static cl::opt OutputFilename("o", cl::desc(""), cl::value_desc("path")); enum class IFSSymbolType { NoType = 0, Object, Func, // Type information is 4 bits, so 16 is safely out of range. Unknown = 16, }; static std::string getTypeName(IFSSymbolType Type) { switch (Type) { case IFSSymbolType::NoType: return "NoType"; case IFSSymbolType::Func: return "Func"; case IFSSymbolType::Object: return "Object"; case IFSSymbolType::Unknown: return "Unknown"; } llvm_unreachable("Unexpected ifs symbol type."); } struct IFSSymbol { IFSSymbol() = default; IFSSymbol(std::string SymbolName) : Name(SymbolName) {} std::string Name; uint64_t Size; IFSSymbolType Type; bool Weak; Optional Warning; bool operator<(const IFSSymbol &RHS) const { return Name < RHS.Name; } }; LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol) namespace llvm { namespace yaml { /// YAML traits for IFSSymbolType. template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, IFSSymbolType &SymbolType) { IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType); IO.enumCase(SymbolType, "Func", IFSSymbolType::Func); IO.enumCase(SymbolType, "Object", IFSSymbolType::Object); IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown); // Treat other symbol types as noise, and map to Unknown. if (!IO.outputting() && IO.matchEnumFallback()) SymbolType = IFSSymbolType::Unknown; } }; /// YAML traits for IFSSymbol. template <> struct MappingTraits { static void mapping(IO &IO, IFSSymbol &Symbol) { IO.mapRequired("Name", Symbol.Name); IO.mapRequired("Type", Symbol.Type); // The need for symbol size depends on the symbol type. if (Symbol.Type == IFSSymbolType::NoType) IO.mapOptional("Size", Symbol.Size, (uint64_t)0); else if (Symbol.Type == IFSSymbolType::Func) Symbol.Size = 0; else IO.mapRequired("Size", Symbol.Size); IO.mapOptional("Weak", Symbol.Weak, false); IO.mapOptional("Warning", Symbol.Warning); } // Compacts symbol information into a single line. static const bool flow = true; }; } // namespace yaml } // namespace llvm // A cumulative representation of ELF stubs. // Both textual and binary stubs will read into and write from this object. class IFSStub { // TODO: Add support for symbol versioning. public: VersionTuple IfsVersion; std::string Triple; std::string ObjectFileFormat; Optional SOName; std::vector NeededLibs; std::vector Symbols; IFSStub() = default; IFSStub(const IFSStub &Stub) : IfsVersion(Stub.IfsVersion), Triple(Stub.Triple), ObjectFileFormat(Stub.ObjectFileFormat), SOName(Stub.SOName), NeededLibs(Stub.NeededLibs), Symbols(Stub.Symbols) {} IFSStub(IFSStub &&Stub) : IfsVersion(std::move(Stub.IfsVersion)), Triple(std::move(Stub.Triple)), ObjectFileFormat(std::move(Stub.ObjectFileFormat)), SOName(std::move(Stub.SOName)), NeededLibs(std::move(Stub.NeededLibs)), Symbols(std::move(Stub.Symbols)) {} }; namespace llvm { namespace yaml { /// YAML traits for IFSStub objects. template <> struct MappingTraits { static void mapping(IO &IO, IFSStub &Stub) { if (!IO.mapTag("!experimental-ifs-v2", true)) IO.setError("Not a .ifs YAML file."); auto OldContext = IO.getContext(); IO.setContext(&Stub); IO.mapRequired("IfsVersion", Stub.IfsVersion); IO.mapOptional("Triple", Stub.Triple); IO.mapOptional("ObjectFileFormat", Stub.ObjectFileFormat); IO.mapOptional("SOName", Stub.SOName); IO.mapOptional("NeededLibs", Stub.NeededLibs); IO.mapRequired("Symbols", Stub.Symbols); IO.setContext(&OldContext); } }; } // namespace yaml } // namespace llvm static Expected> readInputFile(StringRef FilePath) { // Read in file. ErrorOr> BufOrError = MemoryBuffer::getFileOrSTDIN(FilePath); if (!BufOrError) return createStringError(BufOrError.getError(), "Could not open `%s`", FilePath.data()); std::unique_ptr FileReadBuffer = std::move(*BufOrError); yaml::Input YamlIn(FileReadBuffer->getBuffer()); std::unique_ptr Stub(new IFSStub()); YamlIn >> *Stub; if (std::error_code Err = YamlIn.error()) return createStringError(Err, "Failed reading Interface Stub File."); if (Stub->IfsVersion > IFSVersionCurrent) return make_error( "IFS version " + Stub->IfsVersion.getAsString() + " is unsupported.", std::make_error_code(std::errc::invalid_argument)); return std::move(Stub); } static int writeTbdStub(const Triple &T, const std::vector &Symbols, const StringRef Format, raw_ostream &Out) { auto PlatformKindOrError = [](const llvm::Triple &T) -> llvm::Expected { if (T.isMacOSX()) return llvm::MachO::PlatformKind::macOS; if (T.isTvOS()) return llvm::MachO::PlatformKind::tvOS; if (T.isWatchOS()) return llvm::MachO::PlatformKind::watchOS; // Note: put isiOS last because tvOS and watchOS are also iOS according // to the Triple. if (T.isiOS()) return llvm::MachO::PlatformKind::iOS; // TODO: Add an option for ForceTriple, but keep ForceFormat for now. if (ForceFormat == "TBD") return llvm::MachO::PlatformKind::macOS; return createStringError(errc::not_supported, "Invalid Platform.\n"); }(T); if (!PlatformKindOrError) return -1; PlatformKind Plat = PlatformKindOrError.get(); TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)}); InterfaceFile File; File.setFileType(FileType::TBD_V3); // Only supporting v3 for now. File.addTargets(Targets); for (const auto &Symbol : Symbols) { auto Name = Symbol.Name; auto Kind = SymbolKind::GlobalSymbol; switch (Symbol.Type) { default: case IFSSymbolType::NoType: Kind = SymbolKind::GlobalSymbol; break; case IFSSymbolType::Object: Kind = SymbolKind::GlobalSymbol; break; case IFSSymbolType::Func: Kind = SymbolKind::GlobalSymbol; break; } if (Symbol.Weak) File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined); else File.addSymbol(Kind, Name, Targets); } SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); if (Error Result = TextAPIWriter::writeToStream(OS, File)) return -1; Out << OS.str(); return 0; } static int writeElfStub(const Triple &T, const std::vector &Symbols, const StringRef Format, raw_ostream &Out) { SmallString<0> Storage; Storage.clear(); raw_svector_ostream OS(Storage); OS << "--- !ELF\n"; OS << "FileHeader:\n"; OS << " Class: ELFCLASS"; OS << (T.isArch64Bit() ? "64" : "32"); OS << "\n"; OS << " Data: ELFDATA2"; OS << (T.isLittleEndian() ? "LSB" : "MSB"); OS << "\n"; OS << " Type: ET_DYN\n"; OS << " Machine: " << llvm::StringSwitch(T.getArchName()) .Case("x86_64", "EM_X86_64") .Case("i386", "EM_386") .Case("i686", "EM_386") .Case("aarch64", "EM_AARCH64") .Case("amdgcn", "EM_AMDGPU") .Case("r600", "EM_AMDGPU") .Case("arm", "EM_ARM") .Case("thumb", "EM_ARM") .Case("avr", "EM_AVR") .Case("mips", "EM_MIPS") .Case("mipsel", "EM_MIPS") .Case("mips64", "EM_MIPS") .Case("mips64el", "EM_MIPS") .Case("msp430", "EM_MSP430") .Case("ppc", "EM_PPC") .Case("ppc64", "EM_PPC64") .Case("ppc64le", "EM_PPC64") .Case("x86", T.isOSIAMCU() ? "EM_IAMCU" : "EM_386") .Case("x86_64", "EM_X86_64") .Default("EM_NONE") << "\nSections:" << "\n - Name: .text" << "\n Type: SHT_PROGBITS" << "\n - Name: .data" << "\n Type: SHT_PROGBITS" << "\n - Name: .rodata" << "\n Type: SHT_PROGBITS" << "\nSymbols:\n"; for (const auto &Symbol : Symbols) { OS << " - Name: " << Symbol.Name << "\n" << " Type: STT_"; switch (Symbol.Type) { default: case IFSSymbolType::NoType: OS << "NOTYPE"; break; case IFSSymbolType::Object: OS << "OBJECT"; break; case IFSSymbolType::Func: OS << "FUNC"; break; } OS << "\n Section: .text" << "\n Binding: STB_" << (Symbol.Weak ? "WEAK" : "GLOBAL") << "\n"; } OS << "...\n"; std::string YamlStr = std::string(OS.str()); // Only or debugging. Not an offical format. LLVM_DEBUG({ if (ForceFormat == "ELFOBJYAML") { Out << YamlStr; return 0; } }); yaml::Input YIn(YamlStr); auto ErrHandler = [](const Twine &Msg) { WithColor::error(errs(), "llvm-ifs") << Msg << "\n"; }; return convertYAML(YIn, Out, ErrHandler) ? 0 : 1; } static int writeIfso(const IFSStub &Stub, bool IsWriteIfs, raw_ostream &Out) { if (IsWriteIfs) { yaml::Output YamlOut(Out, NULL, /*WrapColumn =*/0); YamlOut << const_cast(Stub); return 0; } std::string ObjectFileFormat = ForceFormat.empty() ? Stub.ObjectFileFormat : ForceFormat; if (ObjectFileFormat == "ELF" || ForceFormat == "ELFOBJYAML") return writeElfStub(llvm::Triple(Stub.Triple), Stub.Symbols, Stub.ObjectFileFormat, Out); if (ObjectFileFormat == "TBD") return writeTbdStub(llvm::Triple(Stub.Triple), Stub.Symbols, Stub.ObjectFileFormat, Out); WithColor::error() << "Invalid ObjectFileFormat: Only ELF and TBD are supported.\n"; return -1; } // TODO: Drop ObjectFileFormat, it can be subsumed from the triple. // New Interface Stubs Yaml Format: // --- !experimental-ifs-v2 // IfsVersion: 2.0 // Triple: // ObjectFileFormat: // Symbols: // _ZSymbolName: { Type: } // ... int main(int argc, char *argv[]) { // Parse arguments. cl::ParseCommandLineOptions(argc, argv); if (InputFilenames.empty()) InputFilenames.push_back("-"); IFSStub Stub; std::map SymbolMap; - std::string PreviousInputFilePath = ""; + std::string PreviousInputFilePath; for (const std::string &InputFilePath : InputFilenames) { Expected> StubOrErr = readInputFile(InputFilePath); if (!StubOrErr) { WithColor::error() << StubOrErr.takeError() << "\n"; return -1; } std::unique_ptr TargetStub = std::move(StubOrErr.get()); if (Stub.Triple.empty()) { PreviousInputFilePath = InputFilePath; Stub.IfsVersion = TargetStub->IfsVersion; Stub.Triple = TargetStub->Triple; Stub.ObjectFileFormat = TargetStub->ObjectFileFormat; Stub.SOName = TargetStub->SOName; Stub.NeededLibs = TargetStub->NeededLibs; } else { Stub.ObjectFileFormat = !Stub.ObjectFileFormat.empty() ? Stub.ObjectFileFormat : TargetStub->ObjectFileFormat; if (Stub.IfsVersion != TargetStub->IfsVersion) { if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { WithColor::error() << "Interface Stub: IfsVersion Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion << " " << TargetStub->IfsVersion << "\n"; return -1; } if (TargetStub->IfsVersion > Stub.IfsVersion) Stub.IfsVersion = TargetStub->IfsVersion; } if (Stub.ObjectFileFormat != TargetStub->ObjectFileFormat && !TargetStub->ObjectFileFormat.empty()) { WithColor::error() << "Interface Stub: ObjectFileFormat Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath << "\nObjectFileFormat Values: " << Stub.ObjectFileFormat << " " << TargetStub->ObjectFileFormat << "\n"; return -1; } if (Stub.Triple != TargetStub->Triple && !TargetStub->Triple.empty()) { WithColor::error() << "Interface Stub: Triple Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath << "\nTriple Values: " << Stub.Triple << " " << TargetStub->Triple << "\n"; return -1; } if (Stub.SOName != TargetStub->SOName) { WithColor::error() << "Interface Stub: SOName Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath << "\nSOName Values: " << Stub.SOName << " " << TargetStub->SOName << "\n"; return -1; } if (Stub.NeededLibs != TargetStub->NeededLibs) { WithColor::error() << "Interface Stub: NeededLibs Mismatch." << "\nFilenames: " << PreviousInputFilePath << " " << InputFilePath << "\n"; return -1; } } for (auto Symbol : TargetStub->Symbols) { auto SI = SymbolMap.find(Symbol.Name); if (SI == SymbolMap.end()) { SymbolMap.insert( std::pair(Symbol.Name, Symbol)); continue; } assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match."); // Check conflicts: if (Symbol.Type != SI->second.Type) { WithColor::error() << "Interface Stub: Type Mismatch for " << Symbol.Name << ".\nFilename: " << InputFilePath << "\nType Values: " << getTypeName(SI->second.Type) << " " << getTypeName(Symbol.Type) << "\n"; return -1; } if (Symbol.Size != SI->second.Size) { WithColor::error() << "Interface Stub: Size Mismatch for " << Symbol.Name << ".\nFilename: " << InputFilePath << "\nSize Values: " << SI->second.Size << " " << Symbol.Size << "\n"; return -1; } if (Symbol.Weak != SI->second.Weak) { Symbol.Weak = false; continue; } // TODO: Not checking Warning. Will be dropped. } PreviousInputFilePath = InputFilePath; } if (Stub.IfsVersion != IFSVersionCurrent) if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { WithColor::error() << "Interface Stub: Bad IfsVersion: " << Stub.IfsVersion << ", llvm-ifs supported version: " << IFSVersionCurrent << ".\n"; return -1; } for (auto &Entry : SymbolMap) Stub.Symbols.push_back(Entry.second); std::error_code SysErr; // Open file for writing. raw_fd_ostream Out(OutputFilename, SysErr); if (SysErr) { WithColor::error() << "Couldn't open " << OutputFilename << " for writing.\n"; return -1; } return writeIfso(Stub, (Action == "write-ifs"), Out); } diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 1fa9879c821a..51212d52c18b 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -1,10536 +1,10536 @@ //===-- MachODump.cpp - Object file dumping utility for llvm --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the MachO-specific dumper for llvm-objdump. // //===----------------------------------------------------------------------===// #include "MachODump.h" #include "llvm-objdump.h" #include "llvm-c/Disassembler.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Demangle/Demangle.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #ifdef HAVE_LIBXAR extern "C" { #include } #endif using namespace llvm; using namespace llvm::object; using namespace llvm::objdump; cl::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options"); cl::opt objdump::FirstPrivateHeader( "private-header", cl::desc("Display only the first format specific file header"), cl::cat(MachOCat)); cl::opt objdump::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols"), cl::cat(MachOCat)); cl::opt objdump::Rebase("rebase", cl::desc("Display mach-o rebasing info"), cl::cat(MachOCat)); cl::opt objdump::Bind("bind", cl::desc("Display mach-o binding info"), cl::cat(MachOCat)); cl::opt objdump::LazyBind("lazy-bind", cl::desc("Display mach-o lazy binding info"), cl::cat(MachOCat)); cl::opt objdump::WeakBind("weak-bind", cl::desc("Display mach-o weak binding info"), cl::cat(MachOCat)); static cl::opt UseDbg("g", cl::Grouping, cl::desc("Print line information from debug info if available"), cl::cat(MachOCat)); static cl::opt DSYMFile("dsym", cl::desc("Use .dSYM file for debug info"), cl::cat(MachOCat)); static cl::opt FullLeadingAddr("full-leading-addr", cl::desc("Print full leading address"), cl::cat(MachOCat)); static cl::opt NoLeadingHeaders("no-leading-headers", cl::desc("Print no leading headers"), cl::cat(MachOCat)); cl::opt objdump::UniversalHeaders( "universal-headers", cl::desc("Print Mach-O universal headers (requires --macho)"), cl::cat(MachOCat)); static cl::opt ArchiveMemberOffsets( "archive-member-offsets", cl::desc("Print the offset to each archive member for Mach-O archives " "(requires --macho and --archive-headers)"), cl::cat(MachOCat)); cl::opt objdump::IndirectSymbols( "indirect-symbols", cl::desc( "Print indirect symbol table for Mach-O objects (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::DataInCode( "data-in-code", cl::desc( "Print the data in code table for Mach-O objects (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::LinkOptHints("link-opt-hints", cl::desc("Print the linker optimization hints for " "Mach-O objects (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::InfoPlist("info-plist", cl::desc("Print the info plist section as strings for " "Mach-O objects (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::DylibsUsed("dylibs-used", cl::desc("Print the shared libraries used for linked " "Mach-O files (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::DylibId("dylib-id", cl::desc("Print the shared library's id for the " "dylib Mach-O file (requires --macho)"), cl::cat(MachOCat)); static cl::opt NonVerbose("non-verbose", cl::desc("Print the info for Mach-O objects in non-verbose or " "numeric form (requires --macho)"), cl::cat(MachOCat)); cl::opt objdump::ObjcMetaData("objc-meta-data", cl::desc("Print the Objective-C runtime meta data " "for Mach-O files (requires --macho)"), cl::cat(MachOCat)); static cl::opt DisSymName( "dis-symname", cl::desc("disassemble just this symbol's instructions (requires --macho)"), cl::cat(MachOCat)); static cl::opt NoSymbolicOperands( "no-symbolic-operands", cl::desc("do not symbolic operands when disassembling (requires --macho)"), cl::cat(MachOCat)); static cl::list ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), cl::ZeroOrMore, cl::cat(MachOCat)); static bool ArchAll = false; static std::string ThumbTripleName; static const Target *GetTarget(const MachOObjectFile *MachOObj, const char **McpuDefault, const Target **ThumbTarget) { // Figure out the target triple. Triple TT(TripleName); if (TripleName.empty()) { TT = MachOObj->getArchTriple(McpuDefault); TripleName = TT.str(); } if (TT.getArch() == Triple::arm) { // We've inferred a 32-bit ARM target from the object file. All MachO CPUs // that support ARM are also capable of Thumb mode. Triple ThumbTriple = TT; std::string ThumbName = (Twine("thumb") + TT.getArchName().substr(3)).str(); ThumbTriple.setArchName(ThumbName); ThumbTripleName = ThumbTriple.str(); } // Get the target specific parser. std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); if (TheTarget && ThumbTripleName.empty()) return TheTarget; *ThumbTarget = TargetRegistry::lookupTarget(ThumbTripleName, Error); if (*ThumbTarget) return TheTarget; WithColor::error(errs(), "llvm-objdump") << "unable to get target for '"; if (!TheTarget) errs() << TripleName; else errs() << ThumbTripleName; errs() << "', see --version and --triple.\n"; return nullptr; } namespace { struct SymbolSorter { bool operator()(const SymbolRef &A, const SymbolRef &B) { Expected ATypeOrErr = A.getType(); if (!ATypeOrErr) reportError(ATypeOrErr.takeError(), A.getObject()->getFileName()); SymbolRef::Type AType = *ATypeOrErr; Expected BTypeOrErr = B.getType(); if (!BTypeOrErr) reportError(BTypeOrErr.takeError(), B.getObject()->getFileName()); SymbolRef::Type BType = *BTypeOrErr; uint64_t AAddr = (AType != SymbolRef::ST_Function) ? 0 : cantFail(A.getValue()); uint64_t BAddr = (BType != SymbolRef::ST_Function) ? 0 : cantFail(B.getValue()); return AAddr < BAddr; } }; } // namespace // Types for the storted data in code table that is built before disassembly // and the predicate function to sort them. typedef std::pair DiceTableEntry; typedef std::vector DiceTable; typedef DiceTable::iterator dice_table_iterator; #ifdef HAVE_LIBXAR namespace { struct ScopedXarFile { xar_t xar; ScopedXarFile(const char *filename, int32_t flags) : xar(xar_open(filename, flags)) {} ~ScopedXarFile() { if (xar) xar_close(xar); } ScopedXarFile(const ScopedXarFile &) = delete; ScopedXarFile &operator=(const ScopedXarFile &) = delete; operator xar_t() { return xar; } }; struct ScopedXarIter { xar_iter_t iter; ScopedXarIter() : iter(xar_iter_new()) {} ~ScopedXarIter() { if (iter) xar_iter_free(iter); } ScopedXarIter(const ScopedXarIter &) = delete; ScopedXarIter &operator=(const ScopedXarIter &) = delete; operator xar_iter_t() { return iter; } }; } // namespace #endif // defined(HAVE_LIBXAR) // This is used to search for a data in code table entry for the PC being // disassembled. The j parameter has the PC in j.first. A single data in code // table entry can cover many bytes for each of its Kind's. So if the offset, // aka the i.first value, of the data in code table entry plus its Length // covers the PC being searched for this will return true. If not it will // return false. static bool compareDiceTableEntries(const DiceTableEntry &i, const DiceTableEntry &j) { uint16_t Length; i.second.getLength(Length); return j.first >= i.first && j.first < i.first + Length; } static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, unsigned short Kind) { uint32_t Value, Size = 1; switch (Kind) { default: case MachO::DICE_KIND_DATA: if (Length >= 4) { if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; Size = 4; } else if (Length >= 2) { if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[0]; outs() << "\t.byte " << Value; Size = 1; } if (Kind == MachO::DICE_KIND_DATA) outs() << "\t@ KIND_DATA\n"; else outs() << "\t@ data in code kind = " << Kind << "\n"; break; case MachO::DICE_KIND_JUMP_TABLE8: if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 1), outs()); Value = bytes[0]; outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) << "\t@ KIND_JUMP_TABLE16\n"; Size = 2; break; case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: if (!NoShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; if (Kind == MachO::DICE_KIND_JUMP_TABLE32) outs() << "\t@ KIND_JUMP_TABLE32\n"; else outs() << "\t@ KIND_ABS_JUMP_TABLE32\n"; Size = 4; break; } return Size; } static void getSectionsAndSymbols(MachOObjectFile *MachOObj, std::vector &Sections, std::vector &Symbols, SmallVectorImpl &FoundFns, uint64_t &BaseSegmentAddress) { const StringRef FileName = MachOObj->getFileName(); for (const SymbolRef &Symbol : MachOObj->symbols()) { StringRef SymName = unwrapOrError(Symbol.getName(), FileName); if (!SymName.startswith("ltmp")) Symbols.push_back(Symbol); } for (const SectionRef &Section : MachOObj->sections()) Sections.push_back(Section); bool BaseSegmentAddressSet = false; for (const auto &Command : MachOObj->load_commands()) { if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) { // We found a function starts segment, parse the addresses for later // consumption. MachO::linkedit_data_command LLC = MachOObj->getLinkeditDataLoadCommand(Command); MachOObj->ReadULEB128s(LLC.dataoff, FoundFns); } else if (Command.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command SLC = MachOObj->getSegmentLoadCommand(Command); StringRef SegName = SLC.segname; if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") { BaseSegmentAddressSet = true; BaseSegmentAddress = SLC.vmaddr; } } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 SLC = MachOObj->getSegment64LoadCommand(Command); StringRef SegName = SLC.segname; if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") { BaseSegmentAddressSet = true; BaseSegmentAddress = SLC.vmaddr; } } } } static bool DumpAndSkipDataInCode(uint64_t PC, const uint8_t *bytes, DiceTable &Dices, uint64_t &InstSize) { // Check the data in code table here to see if this is data not an // instruction to be disassembled. DiceTable Dice; Dice.push_back(std::make_pair(PC, DiceRef())); dice_table_iterator DTI = std::search(Dices.begin(), Dices.end(), Dice.begin(), Dice.end(), compareDiceTableEntries); if (DTI != Dices.end()) { uint16_t Length; DTI->second.getLength(Length); uint16_t Kind; DTI->second.getKind(Kind); InstSize = DumpDataInCode(bytes, Length, Kind); if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) && (PC == (DTI->first + Length - 1)) && (Length & 1)) InstSize++; return true; } return false; } static void printRelocationTargetName(const MachOObjectFile *O, const MachO::any_relocation_info &RE, raw_string_ostream &Fmt) { // Target of a scattered relocation is an address. In the interest of // generating pretty output, scan through the symbol table looking for a // symbol that aligns with that address. If we find one, print it. // Otherwise, we just print the hex address of the target. const StringRef FileName = O->getFileName(); if (O->isRelocationScattered(RE)) { uint32_t Val = O->getPlainRelocationSymbolNum(RE); for (const SymbolRef &Symbol : O->symbols()) { uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName); if (Addr != Val) continue; Fmt << unwrapOrError(Symbol.getName(), FileName); return; } // If we couldn't find a symbol that this relocation refers to, try // to find a section beginning instead. for (const SectionRef &Section : ToolSectionFilter(*O)) { uint64_t Addr = Section.getAddress(); if (Addr != Val) continue; StringRef NameOrErr = unwrapOrError(Section.getName(), O->getFileName()); Fmt << NameOrErr; return; } Fmt << format("0x%x", Val); return; } StringRef S; bool isExtern = O->getPlainRelocationExternal(RE); uint64_t Val = O->getPlainRelocationSymbolNum(RE); if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND && (O->getArch() == Triple::aarch64 || O->getArch() == Triple::aarch64_be)) { Fmt << format("0x%0" PRIx64, Val); return; } if (isExtern) { symbol_iterator SI = O->symbol_begin(); advance(SI, Val); S = unwrapOrError(SI->getName(), FileName); } else { section_iterator SI = O->section_begin(); // Adjust for the fact that sections are 1-indexed. if (Val == 0) { Fmt << "0 (?,?)"; return; } uint32_t I = Val - 1; while (I != 0 && SI != O->section_end()) { --I; advance(SI, 1); } if (SI == O->section_end()) { Fmt << Val << " (?,?)"; } else { if (Expected NameOrErr = SI->getName()) S = *NameOrErr; else consumeError(NameOrErr.takeError()); } } Fmt << S; } Error objdump::getMachORelocationValueString(const MachOObjectFile *Obj, const RelocationRef &RelRef, SmallVectorImpl &Result) { DataRefImpl Rel = RelRef.getRawDataRefImpl(); MachO::any_relocation_info RE = Obj->getRelocation(Rel); unsigned Arch = Obj->getArch(); std::string FmtBuf; raw_string_ostream Fmt(FmtBuf); unsigned Type = Obj->getAnyRelocationType(RE); bool IsPCRel = Obj->getAnyRelocationPCRel(RE); // Determine any addends that should be displayed with the relocation. // These require decoding the relocation type, which is triple-specific. // X86_64 has entirely custom relocation types. if (Arch == Triple::x86_64) { switch (Type) { case MachO::X86_64_RELOC_GOT_LOAD: case MachO::X86_64_RELOC_GOT: { printRelocationTargetName(Obj, RE, Fmt); Fmt << "@GOT"; if (IsPCRel) Fmt << "PCREL"; break; } case MachO::X86_64_RELOC_SUBTRACTOR: { DataRefImpl RelNext = Rel; Obj->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); // X86_64_RELOC_SUBTRACTOR must be followed by a relocation of type // X86_64_RELOC_UNSIGNED. // NOTE: Scattered relocations don't exist on x86_64. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::X86_64_RELOC_UNSIGNED) reportError(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after " "X86_64_RELOC_SUBTRACTOR."); // The X86_64_RELOC_UNSIGNED contains the minuend symbol; // X86_64_RELOC_SUBTRACTOR contains the subtrahend. printRelocationTargetName(Obj, RENext, Fmt); Fmt << "-"; printRelocationTargetName(Obj, RE, Fmt); break; } case MachO::X86_64_RELOC_TLV: printRelocationTargetName(Obj, RE, Fmt); Fmt << "@TLV"; if (IsPCRel) Fmt << "P"; break; case MachO::X86_64_RELOC_SIGNED_1: printRelocationTargetName(Obj, RE, Fmt); Fmt << "-1"; break; case MachO::X86_64_RELOC_SIGNED_2: printRelocationTargetName(Obj, RE, Fmt); Fmt << "-2"; break; case MachO::X86_64_RELOC_SIGNED_4: printRelocationTargetName(Obj, RE, Fmt); Fmt << "-4"; break; default: printRelocationTargetName(Obj, RE, Fmt); break; } // X86 and ARM share some relocation types in common. } else if (Arch == Triple::x86 || Arch == Triple::arm || Arch == Triple::ppc) { // Generic relocation types... switch (Type) { case MachO::GENERIC_RELOC_PAIR: // prints no info return Error::success(); case MachO::GENERIC_RELOC_SECTDIFF: { DataRefImpl RelNext = Rel; Obj->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); // X86 sect diff's must be followed by a relocation of type // GENERIC_RELOC_PAIR. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::GENERIC_RELOC_PAIR) reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " "GENERIC_RELOC_SECTDIFF."); printRelocationTargetName(Obj, RE, Fmt); Fmt << "-"; printRelocationTargetName(Obj, RENext, Fmt); break; } } if (Arch == Triple::x86 || Arch == Triple::ppc) { switch (Type) { case MachO::GENERIC_RELOC_LOCAL_SECTDIFF: { DataRefImpl RelNext = Rel; Obj->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); // X86 sect diff's must be followed by a relocation of type // GENERIC_RELOC_PAIR. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::GENERIC_RELOC_PAIR) reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " "GENERIC_RELOC_LOCAL_SECTDIFF."); printRelocationTargetName(Obj, RE, Fmt); Fmt << "-"; printRelocationTargetName(Obj, RENext, Fmt); break; } case MachO::GENERIC_RELOC_TLV: { printRelocationTargetName(Obj, RE, Fmt); Fmt << "@TLV"; if (IsPCRel) Fmt << "P"; break; } default: printRelocationTargetName(Obj, RE, Fmt); } } else { // ARM-specific relocations switch (Type) { case MachO::ARM_RELOC_HALF: case MachO::ARM_RELOC_HALF_SECTDIFF: { // Half relocations steal a bit from the length field to encode // whether this is an upper16 or a lower16 relocation. bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1; if (isUpper) Fmt << ":upper16:("; else Fmt << ":lower16:("; printRelocationTargetName(Obj, RE, Fmt); DataRefImpl RelNext = Rel; Obj->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); // ARM half relocs must be followed by a relocation of type // ARM_RELOC_PAIR. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::ARM_RELOC_PAIR) reportError(Obj->getFileName(), "Expected ARM_RELOC_PAIR after " "ARM_RELOC_HALF"); // NOTE: The half of the target virtual address is stashed in the // address field of the secondary relocation, but we can't reverse // engineer the constant offset from it without decoding the movw/movt // instruction to find the other half in its immediate field. // ARM_RELOC_HALF_SECTDIFF encodes the second section in the // symbol/section pointer of the follow-on relocation. if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) { Fmt << "-"; printRelocationTargetName(Obj, RENext, Fmt); } Fmt << ")"; break; } default: { printRelocationTargetName(Obj, RE, Fmt); } } } } else printRelocationTargetName(Obj, RE, Fmt); Fmt.flush(); Result.append(FmtBuf.begin(), FmtBuf.end()); return Error::success(); } static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose, uint32_t n, uint32_t count, uint32_t stride, uint64_t addr) { MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand(); uint32_t nindirectsyms = Dysymtab.nindirectsyms; if (n > nindirectsyms) outs() << " (entries start past the end of the indirect symbol " "table) (reserved1 field greater than the table size)"; else if (n + count > nindirectsyms) outs() << " (entries extends past the end of the indirect symbol " "table)"; outs() << "\n"; uint32_t cputype = O->getHeader().cputype; if (cputype & MachO::CPU_ARCH_ABI64) outs() << "address index"; else outs() << "address index"; if (verbose) outs() << " name\n"; else outs() << "\n"; for (uint32_t j = 0; j < count && n + j < nindirectsyms; j++) { if (cputype & MachO::CPU_ARCH_ABI64) outs() << format("0x%016" PRIx64, addr + j * stride) << " "; else outs() << format("0x%08" PRIx32, (uint32_t)addr + j * stride) << " "; MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand(); uint32_t indirect_symbol = O->getIndirectSymbolTableEntry(Dysymtab, n + j); if (indirect_symbol == MachO::INDIRECT_SYMBOL_LOCAL) { outs() << "LOCAL\n"; continue; } if (indirect_symbol == (MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS)) { outs() << "LOCAL ABSOLUTE\n"; continue; } if (indirect_symbol == MachO::INDIRECT_SYMBOL_ABS) { outs() << "ABSOLUTE\n"; continue; } outs() << format("%5u ", indirect_symbol); if (verbose) { MachO::symtab_command Symtab = O->getSymtabLoadCommand(); if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol); SymbolRef Symbol = *Sym; outs() << unwrapOrError(Symbol.getName(), O->getFileName()); } else { outs() << "?"; } } outs() << "\n"; } } static void PrintIndirectSymbols(MachOObjectFile *O, bool verbose) { for (const auto &Load : O->load_commands()) { if (Load.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section_64 Sec = O->getSection64(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || section_type == MachO::S_SYMBOL_STUBS) { uint32_t stride; if (section_type == MachO::S_SYMBOL_STUBS) stride = Sec.reserved2; else stride = 8; if (stride == 0) { outs() << "Can't print indirect symbols for (" << Sec.segname << "," << Sec.sectname << ") " << "(size of stubs in reserved2 field is zero)\n"; continue; } uint32_t count = Sec.size / stride; outs() << "Indirect symbols for (" << Sec.segname << "," << Sec.sectname << ") " << count << " entries"; uint32_t n = Sec.reserved1; PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr); } } } else if (Load.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command Seg = O->getSegmentLoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section Sec = O->getSection(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || section_type == MachO::S_SYMBOL_STUBS) { uint32_t stride; if (section_type == MachO::S_SYMBOL_STUBS) stride = Sec.reserved2; else stride = 4; if (stride == 0) { outs() << "Can't print indirect symbols for (" << Sec.segname << "," << Sec.sectname << ") " << "(size of stubs in reserved2 field is zero)\n"; continue; } uint32_t count = Sec.size / stride; outs() << "Indirect symbols for (" << Sec.segname << "," << Sec.sectname << ") " << count << " entries"; uint32_t n = Sec.reserved1; PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr); } } } } } static void PrintRType(const uint64_t cputype, const unsigned r_type) { static char const *generic_r_types[] = { "VANILLA ", "PAIR ", "SECTDIF ", "PBLAPTR ", "LOCSDIF ", "TLV ", " 6 (?) ", " 7 (?) ", " 8 (?) ", " 9 (?) ", " 10 (?) ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) " }; static char const *x86_64_r_types[] = { "UNSIGND ", "SIGNED ", "BRANCH ", "GOT_LD ", "GOT ", "SUB ", "SIGNED1 ", "SIGNED2 ", "SIGNED4 ", "TLV ", " 10 (?) ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) " }; static char const *arm_r_types[] = { "VANILLA ", "PAIR ", "SECTDIFF", "LOCSDIF ", "PBLAPTR ", "BR24 ", "T_BR22 ", "T_BR32 ", "HALF ", "HALFDIF ", " 10 (?) ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) " }; static char const *arm64_r_types[] = { "UNSIGND ", "SUB ", "BR26 ", "PAGE21 ", "PAGOF12 ", "GOTLDP ", "GOTLDPOF", "PTRTGOT ", "TLVLDP ", "TLVLDPOF", "ADDEND ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) " }; if (r_type > 0xf){ outs() << format("%-7u", r_type) << " "; return; } switch (cputype) { case MachO::CPU_TYPE_I386: outs() << generic_r_types[r_type]; break; case MachO::CPU_TYPE_X86_64: outs() << x86_64_r_types[r_type]; break; case MachO::CPU_TYPE_ARM: outs() << arm_r_types[r_type]; break; case MachO::CPU_TYPE_ARM64: case MachO::CPU_TYPE_ARM64_32: outs() << arm64_r_types[r_type]; break; default: outs() << format("%-7u ", r_type); } } static void PrintRLength(const uint64_t cputype, const unsigned r_type, const unsigned r_length, const bool previous_arm_half){ if (cputype == MachO::CPU_TYPE_ARM && (r_type == MachO::ARM_RELOC_HALF || r_type == MachO::ARM_RELOC_HALF_SECTDIFF || previous_arm_half == true)) { if ((r_length & 0x1) == 0) outs() << "lo/"; else outs() << "hi/"; if ((r_length & 0x1) == 0) outs() << "arm "; else outs() << "thm "; } else { switch (r_length) { case 0: outs() << "byte "; break; case 1: outs() << "word "; break; case 2: outs() << "long "; break; case 3: if (cputype == MachO::CPU_TYPE_X86_64) outs() << "quad "; else outs() << format("?(%2d) ", r_length); break; default: outs() << format("?(%2d) ", r_length); } } } static void PrintRelocationEntries(const MachOObjectFile *O, const relocation_iterator Begin, const relocation_iterator End, const uint64_t cputype, const bool verbose) { const MachO::symtab_command Symtab = O->getSymtabLoadCommand(); bool previous_arm_half = false; bool previous_sectdiff = false; uint32_t sectdiff_r_type = 0; for (relocation_iterator Reloc = Begin; Reloc != End; ++Reloc) { const DataRefImpl Rel = Reloc->getRawDataRefImpl(); const MachO::any_relocation_info RE = O->getRelocation(Rel); const unsigned r_type = O->getAnyRelocationType(RE); const bool r_scattered = O->isRelocationScattered(RE); const unsigned r_pcrel = O->getAnyRelocationPCRel(RE); const unsigned r_length = O->getAnyRelocationLength(RE); const unsigned r_address = O->getAnyRelocationAddress(RE); const bool r_extern = (r_scattered ? false : O->getPlainRelocationExternal(RE)); const uint32_t r_value = (r_scattered ? O->getScatteredRelocationValue(RE) : 0); const unsigned r_symbolnum = (r_scattered ? 0 : O->getPlainRelocationSymbolNum(RE)); if (r_scattered && cputype != MachO::CPU_TYPE_X86_64) { if (verbose) { // scattered: address if ((cputype == MachO::CPU_TYPE_I386 && r_type == MachO::GENERIC_RELOC_PAIR) || (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)) outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); // scattered: pcrel if (r_pcrel) outs() << "True "; else outs() << "False "; // scattered: length PrintRLength(cputype, r_type, r_length, previous_arm_half); // scattered: extern & type outs() << "n/a "; PrintRType(cputype, r_type); // scattered: scattered & value outs() << format("True 0x%08x", (unsigned int)r_value); if (previous_sectdiff == false) { if ((cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)) outs() << format(" half = 0x%04x ", (unsigned int)r_address); } else if (cputype == MachO::CPU_TYPE_ARM && sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF) outs() << format(" other_half = 0x%04x ", (unsigned int)r_address); if ((cputype == MachO::CPU_TYPE_I386 && (r_type == MachO::GENERIC_RELOC_SECTDIFF || r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) || (cputype == MachO::CPU_TYPE_ARM && (sectdiff_r_type == MachO::ARM_RELOC_SECTDIFF || sectdiff_r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF))) { previous_sectdiff = true; sectdiff_r_type = r_type; } else { previous_sectdiff = false; sectdiff_r_type = 0; } if (cputype == MachO::CPU_TYPE_ARM && (r_type == MachO::ARM_RELOC_HALF || r_type == MachO::ARM_RELOC_HALF_SECTDIFF)) previous_arm_half = true; else previous_arm_half = false; outs() << "\n"; } else { // scattered: address pcrel length extern type scattered value outs() << format("%08x %1d %-2d n/a %-7d 1 0x%08x\n", (unsigned int)r_address, r_pcrel, r_length, r_type, (unsigned int)r_value); } } else { if (verbose) { // plain: address if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR) outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); // plain: pcrel if (r_pcrel) outs() << "True "; else outs() << "False "; // plain: length PrintRLength(cputype, r_type, r_length, previous_arm_half); if (r_extern) { // plain: extern & type & scattered outs() << "True "; PrintRType(cputype, r_type); outs() << "False "; // plain: symbolnum/value if (r_symbolnum > Symtab.nsyms) outs() << format("?(%d)\n", r_symbolnum); else { SymbolRef Symbol = *O->getSymbolByIndex(r_symbolnum); Expected SymNameNext = Symbol.getName(); const char *name = NULL; if (SymNameNext) name = SymNameNext->data(); if (name == NULL) outs() << format("?(%d)\n", r_symbolnum); else outs() << name << "\n"; } } else { // plain: extern & type & scattered outs() << "False "; PrintRType(cputype, r_type); outs() << "False "; // plain: symbolnum/value if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR) outs() << format("other_half = 0x%04x\n", (unsigned int)r_address); else if ((cputype == MachO::CPU_TYPE_ARM64 || cputype == MachO::CPU_TYPE_ARM64_32) && r_type == MachO::ARM64_RELOC_ADDEND) outs() << format("addend = 0x%06x\n", (unsigned int)r_symbolnum); else { outs() << format("%d ", r_symbolnum); if (r_symbolnum == MachO::R_ABS) outs() << "R_ABS\n"; else { // in this case, r_symbolnum is actually a 1-based section number uint32_t nsects = O->section_end()->getRawDataRefImpl().d.a; if (r_symbolnum > 0 && r_symbolnum <= nsects) { object::DataRefImpl DRI; DRI.d.a = r_symbolnum-1; StringRef SegName = O->getSectionFinalSegmentName(DRI); if (Expected NameOrErr = O->getSectionName(DRI)) outs() << "(" << SegName << "," << *NameOrErr << ")\n"; else outs() << "(?,?)\n"; } else { outs() << "(?,?)\n"; } } } } if (cputype == MachO::CPU_TYPE_ARM && (r_type == MachO::ARM_RELOC_HALF || r_type == MachO::ARM_RELOC_HALF_SECTDIFF)) previous_arm_half = true; else previous_arm_half = false; } else { // plain: address pcrel length extern type scattered symbolnum/section outs() << format("%08x %1d %-2d %1d %-7d 0 %d\n", (unsigned int)r_address, r_pcrel, r_length, r_extern, r_type, r_symbolnum); } } } } static void PrintRelocations(const MachOObjectFile *O, const bool verbose) { const uint64_t cputype = O->getHeader().cputype; const MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand(); if (Dysymtab.nextrel != 0) { outs() << "External relocation information " << Dysymtab.nextrel << " entries"; outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->extrel_begin(), O->extrel_end(), cputype, verbose); } if (Dysymtab.nlocrel != 0) { outs() << format("Local relocation information %u entries", Dysymtab.nlocrel); outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->locrel_begin(), O->locrel_end(), cputype, verbose); } for (const auto &Load : O->load_commands()) { if (Load.C.cmd == MachO::LC_SEGMENT_64) { const MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { const MachO::section_64 Sec = O->getSection64(Load, J); if (Sec.nreloc != 0) { DataRefImpl DRI; DRI.d.a = J; const StringRef SegName = O->getSectionFinalSegmentName(DRI); if (Expected NameOrErr = O->getSectionName(DRI)) outs() << "Relocation information (" << SegName << "," << *NameOrErr << format(") %u entries", Sec.nreloc); else outs() << "Relocation information (" << SegName << ",?) " << format("%u entries", Sec.nreloc); outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->section_rel_begin(DRI), O->section_rel_end(DRI), cputype, verbose); } } } else if (Load.C.cmd == MachO::LC_SEGMENT) { const MachO::segment_command Seg = O->getSegmentLoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { const MachO::section Sec = O->getSection(Load, J); if (Sec.nreloc != 0) { DataRefImpl DRI; DRI.d.a = J; const StringRef SegName = O->getSectionFinalSegmentName(DRI); if (Expected NameOrErr = O->getSectionName(DRI)) outs() << "Relocation information (" << SegName << "," << *NameOrErr << format(") %u entries", Sec.nreloc); else outs() << "Relocation information (" << SegName << ",?) " << format("%u entries", Sec.nreloc); outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->section_rel_begin(DRI), O->section_rel_end(DRI), cputype, verbose); } } } } } static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) { MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand(); uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry); outs() << "Data in code table (" << nentries << " entries)\n"; outs() << "offset length kind\n"; for (dice_iterator DI = O->begin_dices(), DE = O->end_dices(); DI != DE; ++DI) { uint32_t Offset; DI->getOffset(Offset); outs() << format("0x%08" PRIx32, Offset) << " "; uint16_t Length; DI->getLength(Length); outs() << format("%6u", Length) << " "; uint16_t Kind; DI->getKind(Kind); if (verbose) { switch (Kind) { case MachO::DICE_KIND_DATA: outs() << "DATA"; break; case MachO::DICE_KIND_JUMP_TABLE8: outs() << "JUMP_TABLE8"; break; case MachO::DICE_KIND_JUMP_TABLE16: outs() << "JUMP_TABLE16"; break; case MachO::DICE_KIND_JUMP_TABLE32: outs() << "JUMP_TABLE32"; break; case MachO::DICE_KIND_ABS_JUMP_TABLE32: outs() << "ABS_JUMP_TABLE32"; break; default: outs() << format("0x%04" PRIx32, Kind); break; } } else outs() << format("0x%04" PRIx32, Kind); outs() << "\n"; } } static void PrintLinkOptHints(MachOObjectFile *O) { MachO::linkedit_data_command LohLC = O->getLinkOptHintsLoadCommand(); const char *loh = O->getData().substr(LohLC.dataoff, 1).data(); uint32_t nloh = LohLC.datasize; outs() << "Linker optimiztion hints (" << nloh << " total bytes)\n"; for (uint32_t i = 0; i < nloh;) { unsigned n; uint64_t identifier = decodeULEB128((const uint8_t *)(loh + i), &n); i += n; outs() << " identifier " << identifier << " "; if (i >= nloh) return; switch (identifier) { case 1: outs() << "AdrpAdrp\n"; break; case 2: outs() << "AdrpLdr\n"; break; case 3: outs() << "AdrpAddLdr\n"; break; case 4: outs() << "AdrpLdrGotLdr\n"; break; case 5: outs() << "AdrpAddStr\n"; break; case 6: outs() << "AdrpLdrGotStr\n"; break; case 7: outs() << "AdrpAdd\n"; break; case 8: outs() << "AdrpLdrGot\n"; break; default: outs() << "Unknown identifier value\n"; break; } uint64_t narguments = decodeULEB128((const uint8_t *)(loh + i), &n); i += n; outs() << " narguments " << narguments << "\n"; if (i >= nloh) return; for (uint32_t j = 0; j < narguments; j++) { uint64_t value = decodeULEB128((const uint8_t *)(loh + i), &n); i += n; outs() << "\tvalue " << format("0x%" PRIx64, value) << "\n"; if (i >= nloh) return; } } } static void PrintDylibs(MachOObjectFile *O, bool JustId) { unsigned Index = 0; for (const auto &Load : O->load_commands()) { if ((JustId && Load.C.cmd == MachO::LC_ID_DYLIB) || (!JustId && (Load.C.cmd == MachO::LC_ID_DYLIB || Load.C.cmd == MachO::LC_LOAD_DYLIB || Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || Load.C.cmd == MachO::LC_REEXPORT_DYLIB || Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB))) { MachO::dylib_command dl = O->getDylibIDLoadCommand(Load); if (dl.dylib.name < dl.cmdsize) { const char *p = (const char *)(Load.Ptr) + dl.dylib.name; if (JustId) outs() << p << "\n"; else { outs() << "\t" << p; outs() << " (compatibility version " << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "." << ((dl.dylib.compatibility_version >> 8) & 0xff) << "." << (dl.dylib.compatibility_version & 0xff) << ","; outs() << " current version " << ((dl.dylib.current_version >> 16) & 0xffff) << "." << ((dl.dylib.current_version >> 8) & 0xff) << "." << (dl.dylib.current_version & 0xff); if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) outs() << ", weak"; if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) outs() << ", reexport"; if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) outs() << ", upward"; if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) outs() << ", lazy"; outs() << ")\n"; } } else { outs() << "\tBad offset (" << dl.dylib.name << ") for name of "; if (Load.C.cmd == MachO::LC_ID_DYLIB) outs() << "LC_ID_DYLIB "; else if (Load.C.cmd == MachO::LC_LOAD_DYLIB) outs() << "LC_LOAD_DYLIB "; else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) outs() << "LC_LOAD_WEAK_DYLIB "; else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) outs() << "LC_LAZY_LOAD_DYLIB "; else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) outs() << "LC_REEXPORT_DYLIB "; else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) outs() << "LC_LOAD_UPWARD_DYLIB "; else outs() << "LC_??? "; outs() << "command " << Index++ << "\n"; } } } } typedef DenseMap SymbolAddressMap; static void CreateSymbolAddressMap(MachOObjectFile *O, SymbolAddressMap *AddrMap) { // Create a map of symbol addresses to symbol names. const StringRef FileName = O->getFileName(); for (const SymbolRef &Symbol : O->symbols()) { SymbolRef::Type ST = unwrapOrError(Symbol.getType(), FileName); if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || ST == SymbolRef::ST_Other) { uint64_t Address = cantFail(Symbol.getValue()); StringRef SymName = unwrapOrError(Symbol.getName(), FileName); if (!SymName.startswith(".objc")) (*AddrMap)[Address] = SymName; } } } // GuessSymbolName is passed the address of what might be a symbol and a // pointer to the SymbolAddressMap. It returns the name of a symbol // with that address or nullptr if no symbol is found with that address. static const char *GuessSymbolName(uint64_t value, SymbolAddressMap *AddrMap) { const char *SymbolName = nullptr; // A DenseMap can't lookup up some values. if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) { StringRef name = AddrMap->lookup(value); if (!name.empty()) SymbolName = name.data(); } return SymbolName; } static void DumpCstringChar(const char c) { char p[2]; p[0] = c; p[1] = '\0'; outs().write_escaped(p); } static void DumpCstringSection(MachOObjectFile *O, const char *sect, uint32_t sect_size, uint64_t sect_addr, bool print_addresses) { for (uint32_t i = 0; i < sect_size; i++) { if (print_addresses) { if (O->is64Bit()) outs() << format("%016" PRIx64, sect_addr + i) << " "; else outs() << format("%08" PRIx64, sect_addr + i) << " "; } for (; i < sect_size && sect[i] != '\0'; i++) DumpCstringChar(sect[i]); if (i < sect_size && sect[i] == '\0') outs() << "\n"; } } static void DumpLiteral4(uint32_t l, float f) { outs() << format("0x%08" PRIx32, l); if ((l & 0x7f800000) != 0x7f800000) outs() << format(" (%.16e)\n", f); else { if (l == 0x7f800000) outs() << " (+Infinity)\n"; else if (l == 0xff800000) outs() << " (-Infinity)\n"; else if ((l & 0x00400000) == 0x00400000) outs() << " (non-signaling Not-a-Number)\n"; else outs() << " (signaling Not-a-Number)\n"; } } static void DumpLiteral4Section(MachOObjectFile *O, const char *sect, uint32_t sect_size, uint64_t sect_addr, bool print_addresses) { for (uint32_t i = 0; i < sect_size; i += sizeof(float)) { if (print_addresses) { if (O->is64Bit()) outs() << format("%016" PRIx64, sect_addr + i) << " "; else outs() << format("%08" PRIx64, sect_addr + i) << " "; } float f; memcpy(&f, sect + i, sizeof(float)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(f); uint32_t l; memcpy(&l, sect + i, sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(l); DumpLiteral4(l, f); } } static void DumpLiteral8(MachOObjectFile *O, uint32_t l0, uint32_t l1, double d) { outs() << format("0x%08" PRIx32, l0) << " " << format("0x%08" PRIx32, l1); uint32_t Hi, Lo; Hi = (O->isLittleEndian()) ? l1 : l0; Lo = (O->isLittleEndian()) ? l0 : l1; // Hi is the high word, so this is equivalent to if(isfinite(d)) if ((Hi & 0x7ff00000) != 0x7ff00000) outs() << format(" (%.16e)\n", d); else { if (Hi == 0x7ff00000 && Lo == 0) outs() << " (+Infinity)\n"; else if (Hi == 0xfff00000 && Lo == 0) outs() << " (-Infinity)\n"; else if ((Hi & 0x00080000) == 0x00080000) outs() << " (non-signaling Not-a-Number)\n"; else outs() << " (signaling Not-a-Number)\n"; } } static void DumpLiteral8Section(MachOObjectFile *O, const char *sect, uint32_t sect_size, uint64_t sect_addr, bool print_addresses) { for (uint32_t i = 0; i < sect_size; i += sizeof(double)) { if (print_addresses) { if (O->is64Bit()) outs() << format("%016" PRIx64, sect_addr + i) << " "; else outs() << format("%08" PRIx64, sect_addr + i) << " "; } double d; memcpy(&d, sect + i, sizeof(double)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(d); uint32_t l0, l1; memcpy(&l0, sect + i, sizeof(uint32_t)); memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) { sys::swapByteOrder(l0); sys::swapByteOrder(l1); } DumpLiteral8(O, l0, l1, d); } } static void DumpLiteral16(uint32_t l0, uint32_t l1, uint32_t l2, uint32_t l3) { outs() << format("0x%08" PRIx32, l0) << " "; outs() << format("0x%08" PRIx32, l1) << " "; outs() << format("0x%08" PRIx32, l2) << " "; outs() << format("0x%08" PRIx32, l3) << "\n"; } static void DumpLiteral16Section(MachOObjectFile *O, const char *sect, uint32_t sect_size, uint64_t sect_addr, bool print_addresses) { for (uint32_t i = 0; i < sect_size; i += 16) { if (print_addresses) { if (O->is64Bit()) outs() << format("%016" PRIx64, sect_addr + i) << " "; else outs() << format("%08" PRIx64, sect_addr + i) << " "; } uint32_t l0, l1, l2, l3; memcpy(&l0, sect + i, sizeof(uint32_t)); memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t)); memcpy(&l2, sect + i + 2 * sizeof(uint32_t), sizeof(uint32_t)); memcpy(&l3, sect + i + 3 * sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) { sys::swapByteOrder(l0); sys::swapByteOrder(l1); sys::swapByteOrder(l2); sys::swapByteOrder(l3); } DumpLiteral16(l0, l1, l2, l3); } } static void DumpLiteralPointerSection(MachOObjectFile *O, const SectionRef &Section, const char *sect, uint32_t sect_size, uint64_t sect_addr, bool print_addresses) { // Collect the literal sections in this Mach-O file. std::vector LiteralSections; for (const SectionRef &Section : O->sections()) { DataRefImpl Ref = Section.getRawDataRefImpl(); uint32_t section_type; if (O->is64Bit()) { const MachO::section_64 Sec = O->getSection64(Ref); section_type = Sec.flags & MachO::SECTION_TYPE; } else { const MachO::section Sec = O->getSection(Ref); section_type = Sec.flags & MachO::SECTION_TYPE; } if (section_type == MachO::S_CSTRING_LITERALS || section_type == MachO::S_4BYTE_LITERALS || section_type == MachO::S_8BYTE_LITERALS || section_type == MachO::S_16BYTE_LITERALS) LiteralSections.push_back(Section); } // Set the size of the literal pointer. uint32_t lp_size = O->is64Bit() ? 8 : 4; // Collect the external relocation symbols for the literal pointers. std::vector> Relocs; for (const RelocationRef &Reloc : Section.relocations()) { DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; Rel = Reloc.getRawDataRefImpl(); RE = O->getRelocation(Rel); isExtern = O->getPlainRelocationExternal(RE); if (isExtern) { uint64_t RelocOffset = Reloc.getOffset(); symbol_iterator RelocSym = Reloc.getSymbol(); Relocs.push_back(std::make_pair(RelocOffset, *RelocSym)); } } array_pod_sort(Relocs.begin(), Relocs.end()); // Dump each literal pointer. for (uint32_t i = 0; i < sect_size; i += lp_size) { if (print_addresses) { if (O->is64Bit()) outs() << format("%016" PRIx64, sect_addr + i) << " "; else outs() << format("%08" PRIx64, sect_addr + i) << " "; } uint64_t lp; if (O->is64Bit()) { memcpy(&lp, sect + i, sizeof(uint64_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(lp); } else { uint32_t li; memcpy(&li, sect + i, sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(li); lp = li; } // First look for an external relocation entry for this literal pointer. auto Reloc = find_if(Relocs, [&](const std::pair &P) { return P.first == i; }); if (Reloc != Relocs.end()) { symbol_iterator RelocSym = Reloc->second; StringRef SymName = unwrapOrError(RelocSym->getName(), O->getFileName()); outs() << "external relocation entry for symbol:" << SymName << "\n"; continue; } // For local references see what the section the literal pointer points to. auto Sect = find_if(LiteralSections, [&](const SectionRef &R) { return lp >= R.getAddress() && lp < R.getAddress() + R.getSize(); }); if (Sect == LiteralSections.end()) { outs() << format("0x%" PRIx64, lp) << " (not in a literal section)\n"; continue; } uint64_t SectAddress = Sect->getAddress(); uint64_t SectSize = Sect->getSize(); StringRef SectName; Expected SectNameOrErr = Sect->getName(); if (SectNameOrErr) SectName = *SectNameOrErr; else consumeError(SectNameOrErr.takeError()); DataRefImpl Ref = Sect->getRawDataRefImpl(); StringRef SegmentName = O->getSectionFinalSegmentName(Ref); outs() << SegmentName << ":" << SectName << ":"; uint32_t section_type; if (O->is64Bit()) { const MachO::section_64 Sec = O->getSection64(Ref); section_type = Sec.flags & MachO::SECTION_TYPE; } else { const MachO::section Sec = O->getSection(Ref); section_type = Sec.flags & MachO::SECTION_TYPE; } StringRef BytesStr = unwrapOrError(Sect->getContents(), O->getFileName()); const char *Contents = reinterpret_cast(BytesStr.data()); switch (section_type) { case MachO::S_CSTRING_LITERALS: for (uint64_t i = lp - SectAddress; i < SectSize && Contents[i] != '\0'; i++) { DumpCstringChar(Contents[i]); } outs() << "\n"; break; case MachO::S_4BYTE_LITERALS: float f; memcpy(&f, Contents + (lp - SectAddress), sizeof(float)); uint32_t l; memcpy(&l, Contents + (lp - SectAddress), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) { sys::swapByteOrder(f); sys::swapByteOrder(l); } DumpLiteral4(l, f); break; case MachO::S_8BYTE_LITERALS: { double d; memcpy(&d, Contents + (lp - SectAddress), sizeof(double)); uint32_t l0, l1; memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t)); memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) { sys::swapByteOrder(f); sys::swapByteOrder(l0); sys::swapByteOrder(l1); } DumpLiteral8(O, l0, l1, d); break; } case MachO::S_16BYTE_LITERALS: { uint32_t l0, l1, l2, l3; memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t)); memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t), sizeof(uint32_t)); memcpy(&l2, Contents + (lp - SectAddress) + 2 * sizeof(uint32_t), sizeof(uint32_t)); memcpy(&l3, Contents + (lp - SectAddress) + 3 * sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) { sys::swapByteOrder(l0); sys::swapByteOrder(l1); sys::swapByteOrder(l2); sys::swapByteOrder(l3); } DumpLiteral16(l0, l1, l2, l3); break; } } } } static void DumpInitTermPointerSection(MachOObjectFile *O, const SectionRef &Section, const char *sect, uint32_t sect_size, uint64_t sect_addr, SymbolAddressMap *AddrMap, bool verbose) { uint32_t stride; stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t); // Collect the external relocation symbols for the pointers. std::vector> Relocs; for (const RelocationRef &Reloc : Section.relocations()) { DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; Rel = Reloc.getRawDataRefImpl(); RE = O->getRelocation(Rel); isExtern = O->getPlainRelocationExternal(RE); if (isExtern) { uint64_t RelocOffset = Reloc.getOffset(); symbol_iterator RelocSym = Reloc.getSymbol(); Relocs.push_back(std::make_pair(RelocOffset, *RelocSym)); } } array_pod_sort(Relocs.begin(), Relocs.end()); for (uint32_t i = 0; i < sect_size; i += stride) { const char *SymbolName = nullptr; uint64_t p; if (O->is64Bit()) { outs() << format("0x%016" PRIx64, sect_addr + i * stride) << " "; uint64_t pointer_value; memcpy(&pointer_value, sect + i, stride); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(pointer_value); outs() << format("0x%016" PRIx64, pointer_value); p = pointer_value; } else { outs() << format("0x%08" PRIx64, sect_addr + i * stride) << " "; uint32_t pointer_value; memcpy(&pointer_value, sect + i, stride); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(pointer_value); outs() << format("0x%08" PRIx32, pointer_value); p = pointer_value; } if (verbose) { // First look for an external relocation entry for this pointer. auto Reloc = find_if(Relocs, [&](const std::pair &P) { return P.first == i; }); if (Reloc != Relocs.end()) { symbol_iterator RelocSym = Reloc->second; outs() << " " << unwrapOrError(RelocSym->getName(), O->getFileName()); } else { SymbolName = GuessSymbolName(p, AddrMap); if (SymbolName) outs() << " " << SymbolName; } } outs() << "\n"; } } static void DumpRawSectionContents(MachOObjectFile *O, const char *sect, uint32_t size, uint64_t addr) { uint32_t cputype = O->getHeader().cputype; if (cputype == MachO::CPU_TYPE_I386 || cputype == MachO::CPU_TYPE_X86_64) { uint32_t j; for (uint32_t i = 0; i < size; i += j, addr += j) { if (O->is64Bit()) outs() << format("%016" PRIx64, addr) << "\t"; else outs() << format("%08" PRIx64, addr) << "\t"; for (j = 0; j < 16 && i + j < size; j++) { uint8_t byte_word = *(sect + i + j); outs() << format("%02" PRIx32, (uint32_t)byte_word) << " "; } outs() << "\n"; } } else { uint32_t j; for (uint32_t i = 0; i < size; i += j, addr += j) { if (O->is64Bit()) outs() << format("%016" PRIx64, addr) << "\t"; else outs() << format("%08" PRIx64, addr) << "\t"; for (j = 0; j < 4 * sizeof(int32_t) && i + j < size; j += sizeof(int32_t)) { if (i + j + sizeof(int32_t) <= size) { uint32_t long_word; memcpy(&long_word, sect + i + j, sizeof(int32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(long_word); outs() << format("%08" PRIx32, long_word) << " "; } else { for (uint32_t k = 0; i + j + k < size; k++) { uint8_t byte_word = *(sect + i + j + k); outs() << format("%02" PRIx32, (uint32_t)byte_word) << " "; } } } outs() << "\n"; } } } static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef DisSegName, StringRef DisSectName); static void DumpProtocolSection(MachOObjectFile *O, const char *sect, uint32_t size, uint32_t addr); #ifdef HAVE_LIBXAR static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, uint32_t size, bool verbose, bool PrintXarHeader, bool PrintXarFileHeaders, std::string XarMemberName); #endif // defined(HAVE_LIBXAR) static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, bool verbose) { SymbolAddressMap AddrMap; if (verbose) CreateSymbolAddressMap(O, &AddrMap); for (unsigned i = 0; i < FilterSections.size(); ++i) { StringRef DumpSection = FilterSections[i]; std::pair DumpSegSectName; DumpSegSectName = DumpSection.split(','); StringRef DumpSegName, DumpSectName; if (!DumpSegSectName.second.empty()) { DumpSegName = DumpSegSectName.first; DumpSectName = DumpSegSectName.second; } else { DumpSegName = ""; DumpSectName = DumpSegSectName.first; } for (const SectionRef &Section : O->sections()) { StringRef SectName; Expected SecNameOrErr = Section.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); if (!DumpSection.empty()) FoundSectionSet.insert(DumpSection); DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if ((DumpSegName.empty() || SegName == DumpSegName) && (SectName == DumpSectName)) { uint32_t section_flags; if (O->is64Bit()) { const MachO::section_64 Sec = O->getSection64(Ref); section_flags = Sec.flags; } else { const MachO::section Sec = O->getSection(Ref); section_flags = Sec.flags; } uint32_t section_type = section_flags & MachO::SECTION_TYPE; StringRef BytesStr = unwrapOrError(Section.getContents(), O->getFileName()); const char *sect = reinterpret_cast(BytesStr.data()); uint32_t sect_size = BytesStr.size(); uint64_t sect_addr = Section.getAddress(); if (!NoLeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; if (verbose) { if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) || (section_flags & MachO::S_ATTR_SOME_INSTRUCTIONS)) { DisassembleMachO(Filename, O, SegName, SectName); continue; } if (SegName == "__TEXT" && SectName == "__info_plist") { outs() << sect; continue; } if (SegName == "__OBJC" && SectName == "__protocol") { DumpProtocolSection(O, sect, sect_size, sect_addr); continue; } #ifdef HAVE_LIBXAR if (SegName == "__LLVM" && SectName == "__bundle") { DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands, ArchiveHeaders, ""); continue; } #endif // defined(HAVE_LIBXAR) switch (section_type) { case MachO::S_REGULAR: DumpRawSectionContents(O, sect, sect_size, sect_addr); break; case MachO::S_ZEROFILL: outs() << "zerofill section and has no contents in the file\n"; break; case MachO::S_CSTRING_LITERALS: DumpCstringSection(O, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_4BYTE_LITERALS: DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_8BYTE_LITERALS: DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_16BYTE_LITERALS: DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_LITERAL_POINTERS: DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_MOD_INIT_FUNC_POINTERS: case MachO::S_MOD_TERM_FUNC_POINTERS: DumpInitTermPointerSection(O, Section, sect, sect_size, sect_addr, &AddrMap, verbose); break; default: outs() << "Unknown section type (" << format("0x%08" PRIx32, section_type) << ")\n"; DumpRawSectionContents(O, sect, sect_size, sect_addr); break; } } else { if (section_type == MachO::S_ZEROFILL) outs() << "zerofill section and has no contents in the file\n"; else DumpRawSectionContents(O, sect, sect_size, sect_addr); } } } } } static void DumpInfoPlistSectionContents(StringRef Filename, MachOObjectFile *O) { for (const SectionRef &Section : O->sections()) { StringRef SectName; Expected SecNameOrErr = Section.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == "__TEXT" && SectName == "__info_plist") { if (!NoLeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; StringRef BytesStr = unwrapOrError(Section.getContents(), O->getFileName()); const char *sect = reinterpret_cast(BytesStr.data()); outs() << format("%.*s", BytesStr.size(), sect) << "\n"; return; } } } // checkMachOAndArchFlags() checks to see if the ObjectFile is a Mach-O file // and if it is and there is a list of architecture flags is specified then // check to make sure this Mach-O file is one of those architectures or all // architectures were specified. If not then an error is generated and this // routine returns false. Else it returns true. static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) { auto *MachO = dyn_cast(O); if (!MachO || ArchAll || ArchFlags.empty()) return true; MachO::mach_header H; MachO::mach_header_64 H_64; Triple T; const char *McpuDefault, *ArchFlag; if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype, &McpuDefault, &ArchFlag); } else { H = MachO->MachOObjectFile::getHeader(); T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype, &McpuDefault, &ArchFlag); } const std::string ArchFlagName(ArchFlag); if (!llvm::is_contained(ArchFlags, ArchFlagName)) { WithColor::error(errs(), "llvm-objdump") << Filename << ": no architecture specified.\n"; return false; } return true; } static void printObjcMetaData(MachOObjectFile *O, bool verbose); // ProcessMachO() is passed a single opened Mach-O file, which may be an // archive member and or in a slice of a universal file. It prints the // the file name and header info and then processes it according to the // command line options. static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, StringRef ArchiveMemberName = StringRef(), StringRef ArchitectureName = StringRef()) { // If we are doing some processing here on the Mach-O file print the header // info. And don't print it otherwise like in the case of printing the // UniversalHeaders or ArchiveHeaders. if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData || (!FilterSections.empty())) { if (!NoLeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; if (!ArchitectureName.empty()) outs() << " (architecture " << ArchitectureName << ")"; outs() << ":\n"; } } // To use the report_error() form with an ArchiveName and FileName set // these up based on what is passed for Name and ArchiveMemberName. StringRef ArchiveName; StringRef FileName; if (!ArchiveMemberName.empty()) { ArchiveName = Name; FileName = ArchiveMemberName; } else { ArchiveName = StringRef(); FileName = Name; } // If we need the symbol table to do the operation then check it here to // produce a good error message as to where the Mach-O file comes from in // the error message. if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo) if (Error Err = MachOOF->checkSymbolTable()) reportError(std::move(Err), FileName, ArchiveName, ArchitectureName); if (DisassembleAll) { for (const SectionRef &Section : MachOOF->sections()) { StringRef SectName; if (Expected NameOrErr = Section.getName()) SectName = *NameOrErr; else consumeError(NameOrErr.takeError()); if (SectName.equals("__text")) { DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = MachOOF->getSectionFinalSegmentName(Ref); DisassembleMachO(FileName, MachOOF, SegName, SectName); } } } else if (Disassemble) { if (MachOOF->getHeader().filetype == MachO::MH_KEXT_BUNDLE && MachOOF->getHeader().cputype == MachO::CPU_TYPE_ARM64) DisassembleMachO(FileName, MachOOF, "__TEXT_EXEC", "__text"); else DisassembleMachO(FileName, MachOOF, "__TEXT", "__text"); } if (IndirectSymbols) PrintIndirectSymbols(MachOOF, !NonVerbose); if (DataInCode) PrintDataInCodeTable(MachOOF, !NonVerbose); if (LinkOptHints) PrintLinkOptHints(MachOOF); if (Relocations) PrintRelocations(MachOOF, !NonVerbose); if (SectionHeaders) printSectionHeaders(MachOOF); if (SectionContents) printSectionContents(MachOOF); if (!FilterSections.empty()) DumpSectionContents(FileName, MachOOF, !NonVerbose); if (InfoPlist) DumpInfoPlistSectionContents(FileName, MachOOF); if (DylibsUsed) PrintDylibs(MachOOF, false); if (DylibId) PrintDylibs(MachOOF, true); if (SymbolTable) printSymbolTable(MachOOF, ArchiveName, ArchitectureName); if (UnwindInfo) printMachOUnwindInfo(MachOOF); if (PrivateHeaders) { printMachOFileHeader(MachOOF); printMachOLoadCommands(MachOOF); } if (FirstPrivateHeader) printMachOFileHeader(MachOOF); if (ObjcMetaData) printObjcMetaData(MachOOF, !NonVerbose); if (ExportsTrie) printExportsTrie(MachOOF); if (Rebase) printRebaseTable(MachOOF); if (Bind) printBindTable(MachOOF); if (LazyBind) printLazyBindTable(MachOOF); if (WeakBind) printWeakBindTable(MachOOF); if (DwarfDumpType != DIDT_Null) { std::unique_ptr DICtx = DWARFContext::create(*MachOOF); // Dump the complete DWARF structure. DIDumpOptions DumpOpts; DumpOpts.DumpType = DwarfDumpType; DICtx->dump(outs(), DumpOpts); } } // printUnknownCPUType() helps print_fat_headers for unknown CPU's. static void printUnknownCPUType(uint32_t cputype, uint32_t cpusubtype) { outs() << " cputype (" << cputype << ")\n"; outs() << " cpusubtype (" << cpusubtype << ")\n"; } // printCPUType() helps print_fat_headers by printing the cputype and // pusubtype (symbolically for the one's it knows about). static void printCPUType(uint32_t cputype, uint32_t cpusubtype) { switch (cputype) { case MachO::CPU_TYPE_I386: switch (cpusubtype) { case MachO::CPU_SUBTYPE_I386_ALL: outs() << " cputype CPU_TYPE_I386\n"; outs() << " cpusubtype CPU_SUBTYPE_I386_ALL\n"; break; default: printUnknownCPUType(cputype, cpusubtype); break; } break; case MachO::CPU_TYPE_X86_64: switch (cpusubtype) { case MachO::CPU_SUBTYPE_X86_64_ALL: outs() << " cputype CPU_TYPE_X86_64\n"; outs() << " cpusubtype CPU_SUBTYPE_X86_64_ALL\n"; break; case MachO::CPU_SUBTYPE_X86_64_H: outs() << " cputype CPU_TYPE_X86_64\n"; outs() << " cpusubtype CPU_SUBTYPE_X86_64_H\n"; break; default: printUnknownCPUType(cputype, cpusubtype); break; } break; case MachO::CPU_TYPE_ARM: switch (cpusubtype) { case MachO::CPU_SUBTYPE_ARM_ALL: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_ALL\n"; break; case MachO::CPU_SUBTYPE_ARM_V4T: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V4T\n"; break; case MachO::CPU_SUBTYPE_ARM_V5TEJ: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V5TEJ\n"; break; case MachO::CPU_SUBTYPE_ARM_XSCALE: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_XSCALE\n"; break; case MachO::CPU_SUBTYPE_ARM_V6: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V6\n"; break; case MachO::CPU_SUBTYPE_ARM_V6M: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V6M\n"; break; case MachO::CPU_SUBTYPE_ARM_V7: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V7\n"; break; case MachO::CPU_SUBTYPE_ARM_V7EM: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V7EM\n"; break; case MachO::CPU_SUBTYPE_ARM_V7K: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V7K\n"; break; case MachO::CPU_SUBTYPE_ARM_V7M: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V7M\n"; break; case MachO::CPU_SUBTYPE_ARM_V7S: outs() << " cputype CPU_TYPE_ARM\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM_V7S\n"; break; default: printUnknownCPUType(cputype, cpusubtype); break; } break; case MachO::CPU_TYPE_ARM64: switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_ARM64_ALL: outs() << " cputype CPU_TYPE_ARM64\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM64_ALL\n"; break; case MachO::CPU_SUBTYPE_ARM64_V8: outs() << " cputype CPU_TYPE_ARM64\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM64_V8\n"; break; case MachO::CPU_SUBTYPE_ARM64E: outs() << " cputype CPU_TYPE_ARM64\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM64E\n"; break; default: printUnknownCPUType(cputype, cpusubtype); break; } break; case MachO::CPU_TYPE_ARM64_32: switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_ARM64_32_V8: outs() << " cputype CPU_TYPE_ARM64_32\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM64_32_V8\n"; break; default: printUnknownCPUType(cputype, cpusubtype); break; } break; default: printUnknownCPUType(cputype, cpusubtype); break; } } static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB, bool verbose) { outs() << "Fat headers\n"; if (verbose) { if (UB->getMagic() == MachO::FAT_MAGIC) outs() << "fat_magic FAT_MAGIC\n"; else // UB->getMagic() == MachO::FAT_MAGIC_64 outs() << "fat_magic FAT_MAGIC_64\n"; } else outs() << "fat_magic " << format("0x%" PRIx32, MachO::FAT_MAGIC) << "\n"; uint32_t nfat_arch = UB->getNumberOfObjects(); StringRef Buf = UB->getData(); uint64_t size = Buf.size(); uint64_t big_size = sizeof(struct MachO::fat_header) + nfat_arch * sizeof(struct MachO::fat_arch); outs() << "nfat_arch " << UB->getNumberOfObjects(); if (nfat_arch == 0) outs() << " (malformed, contains zero architecture types)\n"; else if (big_size > size) outs() << " (malformed, architectures past end of file)\n"; else outs() << "\n"; for (uint32_t i = 0; i < nfat_arch; ++i) { MachOUniversalBinary::ObjectForArch OFA(UB, i); uint32_t cputype = OFA.getCPUType(); uint32_t cpusubtype = OFA.getCPUSubType(); outs() << "architecture "; for (uint32_t j = 0; i != 0 && j <= i - 1; j++) { MachOUniversalBinary::ObjectForArch other_OFA(UB, j); uint32_t other_cputype = other_OFA.getCPUType(); uint32_t other_cpusubtype = other_OFA.getCPUSubType(); if (cputype != 0 && cpusubtype != 0 && cputype == other_cputype && (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) == (other_cpusubtype & ~MachO::CPU_SUBTYPE_MASK)) { outs() << "(illegal duplicate architecture) "; break; } } if (verbose) { outs() << OFA.getArchFlagName() << "\n"; printCPUType(cputype, cpusubtype & ~MachO::CPU_SUBTYPE_MASK); } else { outs() << i << "\n"; outs() << " cputype " << cputype << "\n"; outs() << " cpusubtype " << (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) << "\n"; } if (verbose && (cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) outs() << " capabilities CPU_SUBTYPE_LIB64\n"; else outs() << " capabilities " << format("0x%" PRIx32, (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24) << "\n"; outs() << " offset " << OFA.getOffset(); if (OFA.getOffset() > size) outs() << " (past end of file)"; if (OFA.getOffset() % (1ull << OFA.getAlign()) != 0) outs() << " (not aligned on it's alignment (2^" << OFA.getAlign() << ")"; outs() << "\n"; outs() << " size " << OFA.getSize(); big_size = OFA.getOffset() + OFA.getSize(); if (big_size > size) outs() << " (past end of file)"; outs() << "\n"; outs() << " align 2^" << OFA.getAlign() << " (" << (1 << OFA.getAlign()) << ")\n"; } } static void printArchiveChild(StringRef Filename, const Archive::Child &C, size_t ChildIndex, bool verbose, bool print_offset, StringRef ArchitectureName = StringRef()) { if (print_offset) outs() << C.getChildOffset() << "\t"; sys::fs::perms Mode = unwrapOrError(C.getAccessMode(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName); if (verbose) { // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. outs() << "-"; outs() << ((Mode & sys::fs::owner_read) ? "r" : "-"); outs() << ((Mode & sys::fs::owner_write) ? "w" : "-"); outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-"); outs() << ((Mode & sys::fs::group_read) ? "r" : "-"); outs() << ((Mode & sys::fs::group_write) ? "w" : "-"); outs() << ((Mode & sys::fs::group_exe) ? "x" : "-"); outs() << ((Mode & sys::fs::others_read) ? "r" : "-"); outs() << ((Mode & sys::fs::others_write) ? "w" : "-"); outs() << ((Mode & sys::fs::others_exe) ? "x" : "-"); } else { outs() << format("0%o ", Mode); } outs() << format("%3d/%-3d %5" PRId64 " ", unwrapOrError(C.getUID(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName), unwrapOrError(C.getGID(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName), unwrapOrError(C.getRawSize(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName)); StringRef RawLastModified = C.getRawLastModified(); if (verbose) { unsigned Seconds; if (RawLastModified.getAsInteger(10, Seconds)) outs() << "(date: \"" << RawLastModified << "\" contains non-decimal chars) "; else { // Since cime(3) returns a 26 character string of the form: // "Sun Sep 16 01:03:52 1973\n\0" // just print 24 characters. time_t t = Seconds; outs() << format("%.24s ", ctime(&t)); } } else { outs() << RawLastModified << " "; } if (verbose) { Expected NameOrErr = C.getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); outs() << unwrapOrError(C.getRawName(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName) << "\n"; } else { StringRef Name = NameOrErr.get(); outs() << Name << "\n"; } } else { outs() << unwrapOrError(C.getRawName(), getFileNameForError(C, ChildIndex), Filename, ArchitectureName) << "\n"; } } static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose, bool print_offset, StringRef ArchitectureName = StringRef()) { Error Err = Error::success(); size_t I = 0; for (const auto &C : A->children(Err, false)) printArchiveChild(Filename, C, I++, verbose, print_offset, ArchitectureName); if (Err) reportError(std::move(Err), Filename, "", ArchitectureName); } static bool ValidateArchFlags() { // Check for -arch all and verifiy the -arch flags are valid. for (unsigned i = 0; i < ArchFlags.size(); ++i) { if (ArchFlags[i] == "all") { ArchAll = true; } else { if (!MachOObjectFile::isValidArch(ArchFlags[i])) { WithColor::error(errs(), "llvm-objdump") << "unknown architecture named '" + ArchFlags[i] + "'for the -arch option\n"; return false; } } } return true; } // ParseInputMachO() parses the named Mach-O file in Filename and handles the // -arch flags selecting just those slices as specified by them and also parses // archive files. Then for each individual Mach-O file ProcessMachO() is // called to process the file based on the command line options. void objdump::parseInputMachO(StringRef Filename) { if (!ValidateArchFlags()) return; // Attempt to open the binary. Expected> BinaryOrErr = createBinary(Filename); if (!BinaryOrErr) { if (Error E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) reportError(std::move(E), Filename); else outs() << Filename << ": is not an object file\n"; return; } Binary &Bin = *BinaryOrErr.get().getBinary(); if (Archive *A = dyn_cast(&Bin)) { outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { ++I; Expected> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) reportError(std::move(E), getFileNameForError(C, I), Filename); continue; } if (MachOObjectFile *O = dyn_cast(&*ChildOrErr.get())) { if (!checkMachOAndArchFlags(O, Filename)) return; ProcessMachO(Filename, O, O->getFileName()); } } if (Err) reportError(std::move(Err), Filename); return; } if (MachOUniversalBinary *UB = dyn_cast(&Bin)) { parseInputMachO(UB); return; } if (ObjectFile *O = dyn_cast(&Bin)) { if (!checkMachOAndArchFlags(O, Filename)) return; if (MachOObjectFile *MachOOF = dyn_cast(&*O)) ProcessMachO(Filename, MachOOF); else WithColor::error(errs(), "llvm-objdump") << Filename << "': " << "object is not a Mach-O file type.\n"; return; } llvm_unreachable("Input object can't be invalid at this point"); } void objdump::parseInputMachO(MachOUniversalBinary *UB) { if (!ValidateArchFlags()) return; auto Filename = UB->getFileName(); if (UniversalHeaders) printMachOUniversalHeaders(UB, !NonVerbose); // If we have a list of architecture flags specified dump only those. if (!ArchAll && !ArchFlags.empty()) { // Look for a slice in the universal binary that matches each ArchFlag. bool ArchFound; for (unsigned i = 0; i < ArchFlags.size(); ++i) { ArchFound = false; for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { if (ArchFlags[i] == I->getArchFlagName()) { ArchFound = true; Expected> ObjOrErr = I->getAsObjectFile(); - std::string ArchitectureName = ""; + std::string ArchitectureName; if (ArchFlags.size() > 1) ArchitectureName = I->getArchFlagName(); if (ObjOrErr) { ObjectFile &O = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast(&O)) ProcessMachO(Filename, MachOOF, "", ArchitectureName); } else if (Error E = isNotObjectErrorInvalidFileType( ObjOrErr.takeError())) { reportError(std::move(E), "", Filename, ArchitectureName); continue; } else if (Expected> AOrErr = I->getAsArchive()) { std::unique_ptr &A = *AOrErr; outs() << "Archive : " << Filename; if (!ArchitectureName.empty()) outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { ++I; Expected> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) reportError(std::move(E), getFileNameForError(C, I), Filename, ArchitectureName); continue; } if (MachOObjectFile *O = dyn_cast(&*ChildOrErr.get())) ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); } if (Err) reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); reportError(Filename, "Mach-O universal file for architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file"); } } } if (!ArchFound) { WithColor::error(errs(), "llvm-objdump") << "file: " + Filename + " does not contain " << "architecture: " + ArchFlags[i] + "\n"; return; } } return; } // No architecture flags were specified so if this contains a slice that // matches the host architecture dump only that. if (!ArchAll) { for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { if (MachOObjectFile::getHostArch().getArchName() == I->getArchFlagName()) { Expected> ObjOrErr = I->getAsObjectFile(); std::string ArchiveName; ArchiveName.clear(); if (ObjOrErr) { ObjectFile &O = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast(&O)) ProcessMachO(Filename, MachOOF); } else if (Error E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { reportError(std::move(E), Filename); } else if (Expected> AOrErr = I->getAsArchive()) { std::unique_ptr &A = *AOrErr; outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { ++I; Expected> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) reportError(std::move(E), getFileNameForError(C, I), Filename); continue; } if (MachOObjectFile *O = dyn_cast(&*ChildOrErr.get())) ProcessMachO(Filename, O, O->getFileName()); } if (Err) reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); reportError(Filename, "Mach-O universal file for architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file"); } return; } } } // Either all architectures have been specified or none have been specified // and this does not contain the host architecture so dump all the slices. bool moreThanOneArch = UB->getNumberOfObjects() > 1; for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { Expected> ObjOrErr = I->getAsObjectFile(); - std::string ArchitectureName = ""; + std::string ArchitectureName; if (moreThanOneArch) ArchitectureName = I->getArchFlagName(); if (ObjOrErr) { ObjectFile &Obj = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast(&Obj)) ProcessMachO(Filename, MachOOF, "", ArchitectureName); } else if (Error E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { reportError(std::move(E), Filename, "", ArchitectureName); } else if (Expected> AOrErr = I->getAsArchive()) { std::unique_ptr &A = *AOrErr; outs() << "Archive : " << Filename; if (!ArchitectureName.empty()) outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { ++I; Expected> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) reportError(std::move(E), getFileNameForError(C, I), Filename, ArchitectureName); continue; } if (MachOObjectFile *O = dyn_cast(&*ChildOrErr.get())) { if (MachOObjectFile *MachOOF = dyn_cast(O)) ProcessMachO(Filename, MachOOF, MachOOF->getFileName(), ArchitectureName); } } if (Err) reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); reportError(Filename, "Mach-O universal file for architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file"); } } } namespace { // The block of info used by the Symbolizer call backs. struct DisassembleInfo { DisassembleInfo(MachOObjectFile *O, SymbolAddressMap *AddrMap, std::vector *Sections, bool verbose) : verbose(verbose), O(O), AddrMap(AddrMap), Sections(Sections) {} bool verbose; MachOObjectFile *O; SectionRef S; SymbolAddressMap *AddrMap; std::vector *Sections; const char *class_name = nullptr; const char *selector_name = nullptr; std::unique_ptr method = nullptr; char *demangled_name = nullptr; uint64_t adrp_addr = 0; uint32_t adrp_inst = 0; std::unique_ptr bindtable; uint32_t depth = 0; }; } // namespace // SymbolizerGetOpInfo() is the operand information call back function. // This is called to get the symbolic information for operand(s) of an // instruction when it is being done. This routine does this from // the relocation information, symbol table, etc. That block of information // is a pointer to the struct DisassembleInfo that was passed when the // disassembler context was created and passed to back to here when // called back by the disassembler for instruction operands that could have // relocation information. The address of the instruction containing operand is // at the Pc parameter. The immediate value the operand has is passed in // op_info->Value and is at Offset past the start of the instruction and has a // byte Size of 1, 2 or 4. The symbolc information is returned in TagBuf is the // LLVMOpInfo1 struct defined in the header "llvm-c/Disassembler.h" as symbol // names and addends of the symbolic expression to add for the operand. The // value of TagType is currently 1 (for the LLVMOpInfo1 struct). If symbolic // information is returned then this function returns 1 else it returns 0. static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, uint64_t Size, int TagType, void *TagBuf) { struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; struct LLVMOpInfo1 *op_info = (struct LLVMOpInfo1 *)TagBuf; uint64_t value = op_info->Value; // Make sure all fields returned are zero if we don't set them. memset((void *)op_info, '\0', sizeof(struct LLVMOpInfo1)); op_info->Value = value; // If the TagType is not the value 1 which it code knows about or if no // verbose symbolic information is wanted then just return 0, indicating no // information is being returned. if (TagType != 1 || !info->verbose) return 0; unsigned int Arch = info->O->getArch(); if (Arch == Triple::x86) { if (Size != 1 && Size != 2 && Size != 4 && Size != 0) return 0; if (info->O->getHeader().filetype != MachO::MH_OBJECT) { // TODO: // Search the external relocation entries of a fully linked image // (if any) for an entry that matches this segment offset. // uint32_t seg_offset = (Pc + Offset); return 0; } // In MH_OBJECT filetypes search the section's relocation entries (if any) // for an entry for this section offset. uint32_t sect_addr = info->S.getAddress(); uint32_t sect_offset = (Pc + Offset) - sect_addr; bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; bool r_scattered = false; uint32_t r_value, pair_r_value, r_type; for (const RelocationRef &Reloc : info->S.relocations()) { uint64_t RelocOffset = Reloc.getOffset(); if (RelocOffset == sect_offset) { Rel = Reloc.getRawDataRefImpl(); RE = info->O->getRelocation(Rel); r_type = info->O->getAnyRelocationType(RE); r_scattered = info->O->isRelocationScattered(RE); if (r_scattered) { r_value = info->O->getScatteredRelocationValue(RE); if (r_type == MachO::GENERIC_RELOC_SECTDIFF || r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) { DataRefImpl RelNext = Rel; info->O->moveRelocationNext(RelNext); MachO::any_relocation_info RENext; RENext = info->O->getRelocation(RelNext); if (info->O->isRelocationScattered(RENext)) pair_r_value = info->O->getScatteredRelocationValue(RENext); else return 0; } } else { isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc.getSymbol(); Symbol = *RelocSym; } } reloc_found = true; break; } } if (reloc_found && isExtern) { op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); // For i386 extern relocation entries the value in the instruction is // the offset from the symbol, and value is already set in op_info->Value. return 1; } if (reloc_found && (r_type == MachO::GENERIC_RELOC_SECTDIFF || r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) { const char *add = GuessSymbolName(r_value, info->AddrMap); const char *sub = GuessSymbolName(pair_r_value, info->AddrMap); uint32_t offset = value - (r_value - pair_r_value); op_info->AddSymbol.Present = 1; if (add != nullptr) op_info->AddSymbol.Name = add; else op_info->AddSymbol.Value = r_value; op_info->SubtractSymbol.Present = 1; if (sub != nullptr) op_info->SubtractSymbol.Name = sub; else op_info->SubtractSymbol.Value = pair_r_value; op_info->Value = offset; return 1; } return 0; } if (Arch == Triple::x86_64) { if (Size != 1 && Size != 2 && Size != 4 && Size != 0) return 0; // For non MH_OBJECT types, like MH_KEXT_BUNDLE, Search the external // relocation entries of a linked image (if any) for an entry that matches // this segment offset. if (info->O->getHeader().filetype != MachO::MH_OBJECT) { uint64_t seg_offset = Pc + Offset; bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; for (const RelocationRef &Reloc : info->O->external_relocations()) { uint64_t RelocOffset = Reloc.getOffset(); if (RelocOffset == seg_offset) { Rel = Reloc.getRawDataRefImpl(); RE = info->O->getRelocation(Rel); // external relocation entries should always be external. isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc.getSymbol(); Symbol = *RelocSym; } reloc_found = true; break; } } if (reloc_found && isExtern) { // The Value passed in will be adjusted by the Pc if the instruction // adds the Pc. But for x86_64 external relocation entries the Value // is the offset from the external symbol. if (info->O->getAnyRelocationPCRel(RE)) op_info->Value -= Pc + Offset + Size; const char *name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; return 1; } return 0; } // In MH_OBJECT filetypes search the section's relocation entries (if any) // for an entry for this section offset. uint64_t sect_addr = info->S.getAddress(); uint64_t sect_offset = (Pc + Offset) - sect_addr; bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; for (const RelocationRef &Reloc : info->S.relocations()) { uint64_t RelocOffset = Reloc.getOffset(); if (RelocOffset == sect_offset) { Rel = Reloc.getRawDataRefImpl(); RE = info->O->getRelocation(Rel); // NOTE: Scattered relocations don't exist on x86_64. isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc.getSymbol(); Symbol = *RelocSym; } reloc_found = true; break; } } if (reloc_found && isExtern) { // The Value passed in will be adjusted by the Pc if the instruction // adds the Pc. But for x86_64 external relocation entries the Value // is the offset from the external symbol. if (info->O->getAnyRelocationPCRel(RE)) op_info->Value -= Pc + Offset + Size; const char *name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); unsigned Type = info->O->getAnyRelocationType(RE); if (Type == MachO::X86_64_RELOC_SUBTRACTOR) { DataRefImpl RelNext = Rel; info->O->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); unsigned TypeNext = info->O->getAnyRelocationType(RENext); bool isExternNext = info->O->getPlainRelocationExternal(RENext); unsigned SymbolNum = info->O->getPlainRelocationSymbolNum(RENext); if (TypeNext == MachO::X86_64_RELOC_UNSIGNED && isExternNext) { op_info->SubtractSymbol.Present = 1; op_info->SubtractSymbol.Name = name; symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum); Symbol = *RelocSymNext; name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); } } // TODO: add the VariantKinds to op_info->VariantKind for relocation types // like: X86_64_RELOC_TLV, X86_64_RELOC_GOT_LOAD and X86_64_RELOC_GOT. op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; return 1; } return 0; } if (Arch == Triple::arm) { if (Offset != 0 || (Size != 4 && Size != 2)) return 0; if (info->O->getHeader().filetype != MachO::MH_OBJECT) { // TODO: // Search the external relocation entries of a fully linked image // (if any) for an entry that matches this segment offset. // uint32_t seg_offset = (Pc + Offset); return 0; } // In MH_OBJECT filetypes search the section's relocation entries (if any) // for an entry for this section offset. uint32_t sect_addr = info->S.getAddress(); uint32_t sect_offset = (Pc + Offset) - sect_addr; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; bool r_scattered = false; uint32_t r_value, pair_r_value, r_type, r_length, other_half; auto Reloc = find_if(info->S.relocations(), [&](const RelocationRef &Reloc) { uint64_t RelocOffset = Reloc.getOffset(); return RelocOffset == sect_offset; }); if (Reloc == info->S.relocations().end()) return 0; Rel = Reloc->getRawDataRefImpl(); RE = info->O->getRelocation(Rel); r_length = info->O->getAnyRelocationLength(RE); r_scattered = info->O->isRelocationScattered(RE); if (r_scattered) { r_value = info->O->getScatteredRelocationValue(RE); r_type = info->O->getScatteredRelocationType(RE); } else { r_type = info->O->getAnyRelocationType(RE); isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc->getSymbol(); Symbol = *RelocSym; } } if (r_type == MachO::ARM_RELOC_HALF || r_type == MachO::ARM_RELOC_SECTDIFF || r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { DataRefImpl RelNext = Rel; info->O->moveRelocationNext(RelNext); MachO::any_relocation_info RENext; RENext = info->O->getRelocation(RelNext); other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff; if (info->O->isRelocationScattered(RENext)) pair_r_value = info->O->getScatteredRelocationValue(RENext); } if (isExtern) { const char *name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; switch (r_type) { case MachO::ARM_RELOC_HALF: if ((r_length & 0x1) == 1) { op_info->Value = value << 16 | other_half; op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; } else { op_info->Value = other_half << 16 | value; op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; } break; default: break; } return 1; } // If we have a branch that is not an external relocation entry then // return 0 so the code in tryAddingSymbolicOperand() can use the // SymbolLookUp call back with the branch target address to look up the // symbol and possibility add an annotation for a symbol stub. if (isExtern == 0 && (r_type == MachO::ARM_RELOC_BR24 || r_type == MachO::ARM_THUMB_RELOC_BR22)) return 0; uint32_t offset = 0; if (r_type == MachO::ARM_RELOC_HALF || r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { if ((r_length & 0x1) == 1) value = value << 16 | other_half; else value = other_half << 16 | value; } if (r_scattered && (r_type != MachO::ARM_RELOC_HALF && r_type != MachO::ARM_RELOC_HALF_SECTDIFF)) { offset = value - r_value; value = r_value; } if (r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { if ((r_length & 0x1) == 1) op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; else op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; const char *add = GuessSymbolName(r_value, info->AddrMap); const char *sub = GuessSymbolName(pair_r_value, info->AddrMap); int32_t offset = value - (r_value - pair_r_value); op_info->AddSymbol.Present = 1; if (add != nullptr) op_info->AddSymbol.Name = add; else op_info->AddSymbol.Value = r_value; op_info->SubtractSymbol.Present = 1; if (sub != nullptr) op_info->SubtractSymbol.Name = sub; else op_info->SubtractSymbol.Value = pair_r_value; op_info->Value = offset; return 1; } op_info->AddSymbol.Present = 1; op_info->Value = offset; if (r_type == MachO::ARM_RELOC_HALF) { if ((r_length & 0x1) == 1) op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; else op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; } const char *add = GuessSymbolName(value, info->AddrMap); if (add != nullptr) { op_info->AddSymbol.Name = add; return 1; } op_info->AddSymbol.Value = value; return 1; } if (Arch == Triple::aarch64) { if (Offset != 0 || Size != 4) return 0; if (info->O->getHeader().filetype != MachO::MH_OBJECT) { // TODO: // Search the external relocation entries of a fully linked image // (if any) for an entry that matches this segment offset. // uint64_t seg_offset = (Pc + Offset); return 0; } // In MH_OBJECT filetypes search the section's relocation entries (if any) // for an entry for this section offset. uint64_t sect_addr = info->S.getAddress(); uint64_t sect_offset = (Pc + Offset) - sect_addr; auto Reloc = find_if(info->S.relocations(), [&](const RelocationRef &Reloc) { uint64_t RelocOffset = Reloc.getOffset(); return RelocOffset == sect_offset; }); if (Reloc == info->S.relocations().end()) return 0; DataRefImpl Rel = Reloc->getRawDataRefImpl(); MachO::any_relocation_info RE = info->O->getRelocation(Rel); uint32_t r_type = info->O->getAnyRelocationType(RE); if (r_type == MachO::ARM64_RELOC_ADDEND) { DataRefImpl RelNext = Rel; info->O->moveRelocationNext(RelNext); MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); if (value == 0) { value = info->O->getPlainRelocationSymbolNum(RENext); op_info->Value = value; } } // NOTE: Scattered relocations don't exist on arm64. if (!info->O->getPlainRelocationExternal(RE)) return 0; const char *name = unwrapOrError(Reloc->getSymbol()->getName(), info->O->getFileName()) .data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; switch (r_type) { case MachO::ARM64_RELOC_PAGE21: /* @page */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGE; break; case MachO::ARM64_RELOC_PAGEOFF12: /* @pageoff */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGEOFF; break; case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: /* @gotpage */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGE; break; case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: /* @gotpageoff */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF; break; case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: /* @tvlppage is not implemented in llvm-mc */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVP; break; case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: /* @tvlppageoff is not implemented in llvm-mc */ op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVOFF; break; default: case MachO::ARM64_RELOC_BRANCH26: op_info->VariantKind = LLVMDisassembler_VariantKind_None; break; } return 1; } return 0; } // GuessCstringPointer is passed the address of what might be a pointer to a // literal string in a cstring section. If that address is in a cstring section // it returns a pointer to that string. Else it returns nullptr. static const char *GuessCstringPointer(uint64_t ReferenceValue, struct DisassembleInfo *info) { for (const auto &Load : info->O->load_commands()) { if (Load.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section_64 Sec = info->O->getSection64(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if (section_type == MachO::S_CSTRING_LITERALS && ReferenceValue >= Sec.addr && ReferenceValue < Sec.addr + Sec.size) { uint64_t sect_offset = ReferenceValue - Sec.addr; uint64_t object_offset = Sec.offset + sect_offset; StringRef MachOContents = info->O->getData(); uint64_t object_size = MachOContents.size(); const char *object_addr = (const char *)MachOContents.data(); if (object_offset < object_size) { const char *name = object_addr + object_offset; return name; } else { return nullptr; } } } } else if (Load.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section Sec = info->O->getSection(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if (section_type == MachO::S_CSTRING_LITERALS && ReferenceValue >= Sec.addr && ReferenceValue < Sec.addr + Sec.size) { uint64_t sect_offset = ReferenceValue - Sec.addr; uint64_t object_offset = Sec.offset + sect_offset; StringRef MachOContents = info->O->getData(); uint64_t object_size = MachOContents.size(); const char *object_addr = (const char *)MachOContents.data(); if (object_offset < object_size) { const char *name = object_addr + object_offset; return name; } else { return nullptr; } } } } } return nullptr; } // GuessIndirectSymbol returns the name of the indirect symbol for the // ReferenceValue passed in or nullptr. This is used when ReferenceValue maybe // an address of a symbol stub or a lazy or non-lazy pointer to associate the // symbol name being referenced by the stub or pointer. static const char *GuessIndirectSymbol(uint64_t ReferenceValue, struct DisassembleInfo *info) { MachO::dysymtab_command Dysymtab = info->O->getDysymtabLoadCommand(); MachO::symtab_command Symtab = info->O->getSymtabLoadCommand(); for (const auto &Load : info->O->load_commands()) { if (Load.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section_64 Sec = info->O->getSection64(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || section_type == MachO::S_SYMBOL_STUBS) && ReferenceValue >= Sec.addr && ReferenceValue < Sec.addr + Sec.size) { uint32_t stride; if (section_type == MachO::S_SYMBOL_STUBS) stride = Sec.reserved2; else stride = 8; if (stride == 0) return nullptr; uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride; if (index < Dysymtab.nindirectsyms) { uint32_t indirect_symbol = info->O->getIndirectSymbolTableEntry(Dysymtab, index); if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); return unwrapOrError(Sym->getName(), info->O->getFileName()) .data(); } } } } } else if (Load.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section Sec = info->O->getSection(Load, J); uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || section_type == MachO::S_SYMBOL_STUBS) && ReferenceValue >= Sec.addr && ReferenceValue < Sec.addr + Sec.size) { uint32_t stride; if (section_type == MachO::S_SYMBOL_STUBS) stride = Sec.reserved2; else stride = 4; if (stride == 0) return nullptr; uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride; if (index < Dysymtab.nindirectsyms) { uint32_t indirect_symbol = info->O->getIndirectSymbolTableEntry(Dysymtab, index); if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); return unwrapOrError(Sym->getName(), info->O->getFileName()) .data(); } } } } } } return nullptr; } // method_reference() is called passing it the ReferenceName that might be // a reference it to an Objective-C method call. If so then it allocates and // assembles a method call string with the values last seen and saved in // the DisassembleInfo's class_name and selector_name fields. This is saved // into the method field of the info and any previous string is free'ed. // Then the class_name field in the info is set to nullptr. The method call // string is set into ReferenceName and ReferenceType is set to // LLVMDisassembler_ReferenceType_Out_Objc_Message. If this not a method call // then both ReferenceType and ReferenceName are left unchanged. static void method_reference(struct DisassembleInfo *info, uint64_t *ReferenceType, const char **ReferenceName) { unsigned int Arch = info->O->getArch(); if (*ReferenceName != nullptr) { if (strcmp(*ReferenceName, "_objc_msgSend") == 0) { if (info->selector_name != nullptr) { if (info->class_name != nullptr) { info->method = std::make_unique( 5 + strlen(info->class_name) + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { strcpy(method, "+["); strcat(method, info->class_name); strcat(method, " "); strcat(method, info->selector_name); strcat(method, "]"); *ReferenceName = method; *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; } } else { info->method = std::make_unique(9 + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { if (Arch == Triple::x86_64) strcpy(method, "-[%rdi "); else if (Arch == Triple::aarch64) strcpy(method, "-[x0 "); else strcpy(method, "-[r? "); strcat(method, info->selector_name); strcat(method, "]"); *ReferenceName = method; *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; } } info->class_name = nullptr; } } else if (strcmp(*ReferenceName, "_objc_msgSendSuper2") == 0) { if (info->selector_name != nullptr) { info->method = std::make_unique(17 + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { if (Arch == Triple::x86_64) strcpy(method, "-[[%rdi super] "); else if (Arch == Triple::aarch64) strcpy(method, "-[[x0 super] "); else strcpy(method, "-[[r? super] "); strcat(method, info->selector_name); strcat(method, "]"); *ReferenceName = method; *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; } info->class_name = nullptr; } } } } // GuessPointerPointer() is passed the address of what might be a pointer to // a reference to an Objective-C class, selector, message ref or cfstring. // If so the value of the pointer is returned and one of the booleans are set // to true. If not zero is returned and all the booleans are set to false. static uint64_t GuessPointerPointer(uint64_t ReferenceValue, struct DisassembleInfo *info, bool &classref, bool &selref, bool &msgref, bool &cfstring) { classref = false; selref = false; msgref = false; cfstring = false; for (const auto &Load : info->O->load_commands()) { if (Load.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); for (unsigned J = 0; J < Seg.nsects; ++J) { MachO::section_64 Sec = info->O->getSection64(Load, J); if ((strncmp(Sec.sectname, "__objc_selrefs", 16) == 0 || strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 || strncmp(Sec.sectname, "__objc_superrefs", 16) == 0 || strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 || strncmp(Sec.sectname, "__cfstring", 16) == 0) && ReferenceValue >= Sec.addr && ReferenceValue < Sec.addr + Sec.size) { uint64_t sect_offset = ReferenceValue - Sec.addr; uint64_t object_offset = Sec.offset + sect_offset; StringRef MachOContents = info->O->getData(); uint64_t object_size = MachOContents.size(); const char *object_addr = (const char *)MachOContents.data(); if (object_offset < object_size) { uint64_t pointer_value; memcpy(&pointer_value, object_addr + object_offset, sizeof(uint64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(pointer_value); if (strncmp(Sec.sectname, "__objc_selrefs", 16) == 0) selref = true; else if (strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 || strncmp(Sec.sectname, "__objc_superrefs", 16) == 0) classref = true; else if (strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 && ReferenceValue + 8 < Sec.addr + Sec.size) { msgref = true; memcpy(&pointer_value, object_addr + object_offset + 8, sizeof(uint64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(pointer_value); } else if (strncmp(Sec.sectname, "__cfstring", 16) == 0) cfstring = true; return pointer_value; } else { return 0; } } } } // TODO: Look for LC_SEGMENT for 32-bit Mach-O files. } return 0; } // get_pointer_64 returns a pointer to the bytes in the object file at the // Address from a section in the Mach-O file. And indirectly returns the // offset into the section, number of bytes left in the section past the offset // and which section is was being referenced. If the Address is not in a // section nullptr is returned. static const char *get_pointer_64(uint64_t Address, uint32_t &offset, uint32_t &left, SectionRef &S, DisassembleInfo *info, bool objc_only = false) { offset = 0; left = 0; S = SectionRef(); for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) { uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress(); uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize(); if (SectSize == 0) continue; if (objc_only) { StringRef SectName; Expected SecNameOrErr = ((*(info->Sections))[SectIdx]).getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = ((*(info->Sections))[SectIdx]).getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); if (SegName != "__OBJC" && SectName != "__cstring") continue; } if (Address >= SectAddress && Address < SectAddress + SectSize) { S = (*(info->Sections))[SectIdx]; offset = Address - SectAddress; left = SectSize - offset; StringRef SectContents = unwrapOrError( ((*(info->Sections))[SectIdx]).getContents(), info->O->getFileName()); return SectContents.data() + offset; } } return nullptr; } static const char *get_pointer_32(uint32_t Address, uint32_t &offset, uint32_t &left, SectionRef &S, DisassembleInfo *info, bool objc_only = false) { return get_pointer_64(Address, offset, left, S, info, objc_only); } // get_symbol_64() returns the name of a symbol (or nullptr) and the address of // the symbol indirectly through n_value. Based on the relocation information // for the specified section offset in the specified section reference. // If no relocation information is found and a non-zero ReferenceValue for the // symbol is passed, look up that address in the info's AddrMap. static const char *get_symbol_64(uint32_t sect_offset, SectionRef S, DisassembleInfo *info, uint64_t &n_value, uint64_t ReferenceValue = 0) { n_value = 0; if (!info->verbose) return nullptr; // See if there is an external relocation entry at the sect_offset. bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; for (const RelocationRef &Reloc : S.relocations()) { uint64_t RelocOffset = Reloc.getOffset(); if (RelocOffset == sect_offset) { Rel = Reloc.getRawDataRefImpl(); RE = info->O->getRelocation(Rel); if (info->O->isRelocationScattered(RE)) continue; isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc.getSymbol(); Symbol = *RelocSym; } reloc_found = true; break; } } // If there is an external relocation entry for a symbol in this section // at this section_offset then use that symbol's value for the n_value // and return its name. const char *SymbolName = nullptr; if (reloc_found && isExtern) { n_value = cantFail(Symbol.getValue()); StringRef Name = unwrapOrError(Symbol.getName(), info->O->getFileName()); if (!Name.empty()) { SymbolName = Name.data(); return SymbolName; } } // TODO: For fully linked images, look through the external relocation // entries off the dynamic symtab command. For these the r_offset is from the // start of the first writeable segment in the Mach-O file. So the offset // to this section from that segment is passed to this routine by the caller, // as the database_offset. Which is the difference of the section's starting // address and the first writable segment. // // NOTE: need add passing the database_offset to this routine. // We did not find an external relocation entry so look up the ReferenceValue // as an address of a symbol and if found return that symbol's name. SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap); return SymbolName; } static const char *get_symbol_32(uint32_t sect_offset, SectionRef S, DisassembleInfo *info, uint32_t ReferenceValue) { uint64_t n_value64; return get_symbol_64(sect_offset, S, info, n_value64, ReferenceValue); } namespace { // These are structs in the Objective-C meta data and read to produce the // comments for disassembly. While these are part of the ABI they are no // public defintions. So the are here not in include/llvm/BinaryFormat/MachO.h // . // The cfstring object in a 64-bit Mach-O file. struct cfstring64_t { uint64_t isa; // class64_t * (64-bit pointer) uint64_t flags; // flag bits uint64_t characters; // char * (64-bit pointer) uint64_t length; // number of non-NULL characters in above }; // The class object in a 64-bit Mach-O file. struct class64_t { uint64_t isa; // class64_t * (64-bit pointer) uint64_t superclass; // class64_t * (64-bit pointer) uint64_t cache; // Cache (64-bit pointer) uint64_t vtable; // IMP * (64-bit pointer) uint64_t data; // class_ro64_t * (64-bit pointer) }; struct class32_t { uint32_t isa; /* class32_t * (32-bit pointer) */ uint32_t superclass; /* class32_t * (32-bit pointer) */ uint32_t cache; /* Cache (32-bit pointer) */ uint32_t vtable; /* IMP * (32-bit pointer) */ uint32_t data; /* class_ro32_t * (32-bit pointer) */ }; struct class_ro64_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; uint32_t reserved; uint64_t ivarLayout; // const uint8_t * (64-bit pointer) uint64_t name; // const char * (64-bit pointer) uint64_t baseMethods; // const method_list_t * (64-bit pointer) uint64_t baseProtocols; // const protocol_list_t * (64-bit pointer) uint64_t ivars; // const ivar_list_t * (64-bit pointer) uint64_t weakIvarLayout; // const uint8_t * (64-bit pointer) uint64_t baseProperties; // const struct objc_property_list (64-bit pointer) }; struct class_ro32_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; uint32_t ivarLayout; /* const uint8_t * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t baseMethods; /* const method_list_t * (32-bit pointer) */ uint32_t baseProtocols; /* const protocol_list_t * (32-bit pointer) */ uint32_t ivars; /* const ivar_list_t * (32-bit pointer) */ uint32_t weakIvarLayout; /* const uint8_t * (32-bit pointer) */ uint32_t baseProperties; /* const struct objc_property_list * (32-bit pointer) */ }; /* Values for class_ro{64,32}_t->flags */ #define RO_META (1 << 0) #define RO_ROOT (1 << 1) #define RO_HAS_CXX_STRUCTORS (1 << 2) struct method_list64_t { uint32_t entsize; uint32_t count; /* struct method64_t first; These structures follow inline */ }; struct method_list32_t { uint32_t entsize; uint32_t count; /* struct method32_t first; These structures follow inline */ }; struct method64_t { uint64_t name; /* SEL (64-bit pointer) */ uint64_t types; /* const char * (64-bit pointer) */ uint64_t imp; /* IMP (64-bit pointer) */ }; struct method32_t { uint32_t name; /* SEL (32-bit pointer) */ uint32_t types; /* const char * (32-bit pointer) */ uint32_t imp; /* IMP (32-bit pointer) */ }; struct protocol_list64_t { uint64_t count; /* uintptr_t (a 64-bit value) */ /* struct protocol64_t * list[0]; These pointers follow inline */ }; struct protocol_list32_t { uint32_t count; /* uintptr_t (a 32-bit value) */ /* struct protocol32_t * list[0]; These pointers follow inline */ }; struct protocol64_t { uint64_t isa; /* id * (64-bit pointer) */ uint64_t name; /* const char * (64-bit pointer) */ uint64_t protocols; /* struct protocol_list64_t * (64-bit pointer) */ uint64_t instanceMethods; /* method_list_t * (64-bit pointer) */ uint64_t classMethods; /* method_list_t * (64-bit pointer) */ uint64_t optionalInstanceMethods; /* method_list_t * (64-bit pointer) */ uint64_t optionalClassMethods; /* method_list_t * (64-bit pointer) */ uint64_t instanceProperties; /* struct objc_property_list * (64-bit pointer) */ }; struct protocol32_t { uint32_t isa; /* id * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */ uint32_t instanceMethods; /* method_list_t * (32-bit pointer) */ uint32_t classMethods; /* method_list_t * (32-bit pointer) */ uint32_t optionalInstanceMethods; /* method_list_t * (32-bit pointer) */ uint32_t optionalClassMethods; /* method_list_t * (32-bit pointer) */ uint32_t instanceProperties; /* struct objc_property_list * (32-bit pointer) */ }; struct ivar_list64_t { uint32_t entsize; uint32_t count; /* struct ivar64_t first; These structures follow inline */ }; struct ivar_list32_t { uint32_t entsize; uint32_t count; /* struct ivar32_t first; These structures follow inline */ }; struct ivar64_t { uint64_t offset; /* uintptr_t * (64-bit pointer) */ uint64_t name; /* const char * (64-bit pointer) */ uint64_t type; /* const char * (64-bit pointer) */ uint32_t alignment; uint32_t size; }; struct ivar32_t { uint32_t offset; /* uintptr_t * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ uint32_t type; /* const char * (32-bit pointer) */ uint32_t alignment; uint32_t size; }; struct objc_property_list64 { uint32_t entsize; uint32_t count; /* struct objc_property64 first; These structures follow inline */ }; struct objc_property_list32 { uint32_t entsize; uint32_t count; /* struct objc_property32 first; These structures follow inline */ }; struct objc_property64 { uint64_t name; /* const char * (64-bit pointer) */ uint64_t attributes; /* const char * (64-bit pointer) */ }; struct objc_property32 { uint32_t name; /* const char * (32-bit pointer) */ uint32_t attributes; /* const char * (32-bit pointer) */ }; struct category64_t { uint64_t name; /* const char * (64-bit pointer) */ uint64_t cls; /* struct class_t * (64-bit pointer) */ uint64_t instanceMethods; /* struct method_list_t * (64-bit pointer) */ uint64_t classMethods; /* struct method_list_t * (64-bit pointer) */ uint64_t protocols; /* struct protocol_list_t * (64-bit pointer) */ uint64_t instanceProperties; /* struct objc_property_list * (64-bit pointer) */ }; struct category32_t { uint32_t name; /* const char * (32-bit pointer) */ uint32_t cls; /* struct class_t * (32-bit pointer) */ uint32_t instanceMethods; /* struct method_list_t * (32-bit pointer) */ uint32_t classMethods; /* struct method_list_t * (32-bit pointer) */ uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */ uint32_t instanceProperties; /* struct objc_property_list * (32-bit pointer) */ }; struct objc_image_info64 { uint32_t version; uint32_t flags; }; struct objc_image_info32 { uint32_t version; uint32_t flags; }; struct imageInfo_t { uint32_t version; uint32_t flags; }; /* masks for objc_image_info.flags */ #define OBJC_IMAGE_IS_REPLACEMENT (1 << 0) #define OBJC_IMAGE_SUPPORTS_GC (1 << 1) #define OBJC_IMAGE_IS_SIMULATED (1 << 5) #define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES (1 << 6) struct message_ref64 { uint64_t imp; /* IMP (64-bit pointer) */ uint64_t sel; /* SEL (64-bit pointer) */ }; struct message_ref32 { uint32_t imp; /* IMP (32-bit pointer) */ uint32_t sel; /* SEL (32-bit pointer) */ }; // Objective-C 1 (32-bit only) meta data structs. struct objc_module_t { uint32_t version; uint32_t size; uint32_t name; /* char * (32-bit pointer) */ uint32_t symtab; /* struct objc_symtab * (32-bit pointer) */ }; struct objc_symtab_t { uint32_t sel_ref_cnt; uint32_t refs; /* SEL * (32-bit pointer) */ uint16_t cls_def_cnt; uint16_t cat_def_cnt; // uint32_t defs[1]; /* void * (32-bit pointer) variable size */ }; struct objc_class_t { uint32_t isa; /* struct objc_class * (32-bit pointer) */ uint32_t super_class; /* struct objc_class * (32-bit pointer) */ uint32_t name; /* const char * (32-bit pointer) */ int32_t version; int32_t info; int32_t instance_size; uint32_t ivars; /* struct objc_ivar_list * (32-bit pointer) */ uint32_t methodLists; /* struct objc_method_list ** (32-bit pointer) */ uint32_t cache; /* struct objc_cache * (32-bit pointer) */ uint32_t protocols; /* struct objc_protocol_list * (32-bit pointer) */ }; #define CLS_GETINFO(cls, infomask) ((cls)->info & (infomask)) // class is not a metaclass #define CLS_CLASS 0x1 // class is a metaclass #define CLS_META 0x2 struct objc_category_t { uint32_t category_name; /* char * (32-bit pointer) */ uint32_t class_name; /* char * (32-bit pointer) */ uint32_t instance_methods; /* struct objc_method_list * (32-bit pointer) */ uint32_t class_methods; /* struct objc_method_list * (32-bit pointer) */ uint32_t protocols; /* struct objc_protocol_list * (32-bit ptr) */ }; struct objc_ivar_t { uint32_t ivar_name; /* char * (32-bit pointer) */ uint32_t ivar_type; /* char * (32-bit pointer) */ int32_t ivar_offset; }; struct objc_ivar_list_t { int32_t ivar_count; // struct objc_ivar_t ivar_list[1]; /* variable length structure */ }; struct objc_method_list_t { uint32_t obsolete; /* struct objc_method_list * (32-bit pointer) */ int32_t method_count; // struct objc_method_t method_list[1]; /* variable length structure */ }; struct objc_method_t { uint32_t method_name; /* SEL, aka struct objc_selector * (32-bit pointer) */ uint32_t method_types; /* char * (32-bit pointer) */ uint32_t method_imp; /* IMP, aka function pointer, (*IMP)(id, SEL, ...) (32-bit pointer) */ }; struct objc_protocol_list_t { uint32_t next; /* struct objc_protocol_list * (32-bit pointer) */ int32_t count; // uint32_t list[1]; /* Protocol *, aka struct objc_protocol_t * // (32-bit pointer) */ }; struct objc_protocol_t { uint32_t isa; /* struct objc_class * (32-bit pointer) */ uint32_t protocol_name; /* char * (32-bit pointer) */ uint32_t protocol_list; /* struct objc_protocol_list * (32-bit pointer) */ uint32_t instance_methods; /* struct objc_method_description_list * (32-bit pointer) */ uint32_t class_methods; /* struct objc_method_description_list * (32-bit pointer) */ }; struct objc_method_description_list_t { int32_t count; // struct objc_method_description_t list[1]; }; struct objc_method_description_t { uint32_t name; /* SEL, aka struct objc_selector * (32-bit pointer) */ uint32_t types; /* char * (32-bit pointer) */ }; inline void swapStruct(struct cfstring64_t &cfs) { sys::swapByteOrder(cfs.isa); sys::swapByteOrder(cfs.flags); sys::swapByteOrder(cfs.characters); sys::swapByteOrder(cfs.length); } inline void swapStruct(struct class64_t &c) { sys::swapByteOrder(c.isa); sys::swapByteOrder(c.superclass); sys::swapByteOrder(c.cache); sys::swapByteOrder(c.vtable); sys::swapByteOrder(c.data); } inline void swapStruct(struct class32_t &c) { sys::swapByteOrder(c.isa); sys::swapByteOrder(c.superclass); sys::swapByteOrder(c.cache); sys::swapByteOrder(c.vtable); sys::swapByteOrder(c.data); } inline void swapStruct(struct class_ro64_t &cro) { sys::swapByteOrder(cro.flags); sys::swapByteOrder(cro.instanceStart); sys::swapByteOrder(cro.instanceSize); sys::swapByteOrder(cro.reserved); sys::swapByteOrder(cro.ivarLayout); sys::swapByteOrder(cro.name); sys::swapByteOrder(cro.baseMethods); sys::swapByteOrder(cro.baseProtocols); sys::swapByteOrder(cro.ivars); sys::swapByteOrder(cro.weakIvarLayout); sys::swapByteOrder(cro.baseProperties); } inline void swapStruct(struct class_ro32_t &cro) { sys::swapByteOrder(cro.flags); sys::swapByteOrder(cro.instanceStart); sys::swapByteOrder(cro.instanceSize); sys::swapByteOrder(cro.ivarLayout); sys::swapByteOrder(cro.name); sys::swapByteOrder(cro.baseMethods); sys::swapByteOrder(cro.baseProtocols); sys::swapByteOrder(cro.ivars); sys::swapByteOrder(cro.weakIvarLayout); sys::swapByteOrder(cro.baseProperties); } inline void swapStruct(struct method_list64_t &ml) { sys::swapByteOrder(ml.entsize); sys::swapByteOrder(ml.count); } inline void swapStruct(struct method_list32_t &ml) { sys::swapByteOrder(ml.entsize); sys::swapByteOrder(ml.count); } inline void swapStruct(struct method64_t &m) { sys::swapByteOrder(m.name); sys::swapByteOrder(m.types); sys::swapByteOrder(m.imp); } inline void swapStruct(struct method32_t &m) { sys::swapByteOrder(m.name); sys::swapByteOrder(m.types); sys::swapByteOrder(m.imp); } inline void swapStruct(struct protocol_list64_t &pl) { sys::swapByteOrder(pl.count); } inline void swapStruct(struct protocol_list32_t &pl) { sys::swapByteOrder(pl.count); } inline void swapStruct(struct protocol64_t &p) { sys::swapByteOrder(p.isa); sys::swapByteOrder(p.name); sys::swapByteOrder(p.protocols); sys::swapByteOrder(p.instanceMethods); sys::swapByteOrder(p.classMethods); sys::swapByteOrder(p.optionalInstanceMethods); sys::swapByteOrder(p.optionalClassMethods); sys::swapByteOrder(p.instanceProperties); } inline void swapStruct(struct protocol32_t &p) { sys::swapByteOrder(p.isa); sys::swapByteOrder(p.name); sys::swapByteOrder(p.protocols); sys::swapByteOrder(p.instanceMethods); sys::swapByteOrder(p.classMethods); sys::swapByteOrder(p.optionalInstanceMethods); sys::swapByteOrder(p.optionalClassMethods); sys::swapByteOrder(p.instanceProperties); } inline void swapStruct(struct ivar_list64_t &il) { sys::swapByteOrder(il.entsize); sys::swapByteOrder(il.count); } inline void swapStruct(struct ivar_list32_t &il) { sys::swapByteOrder(il.entsize); sys::swapByteOrder(il.count); } inline void swapStruct(struct ivar64_t &i) { sys::swapByteOrder(i.offset); sys::swapByteOrder(i.name); sys::swapByteOrder(i.type); sys::swapByteOrder(i.alignment); sys::swapByteOrder(i.size); } inline void swapStruct(struct ivar32_t &i) { sys::swapByteOrder(i.offset); sys::swapByteOrder(i.name); sys::swapByteOrder(i.type); sys::swapByteOrder(i.alignment); sys::swapByteOrder(i.size); } inline void swapStruct(struct objc_property_list64 &pl) { sys::swapByteOrder(pl.entsize); sys::swapByteOrder(pl.count); } inline void swapStruct(struct objc_property_list32 &pl) { sys::swapByteOrder(pl.entsize); sys::swapByteOrder(pl.count); } inline void swapStruct(struct objc_property64 &op) { sys::swapByteOrder(op.name); sys::swapByteOrder(op.attributes); } inline void swapStruct(struct objc_property32 &op) { sys::swapByteOrder(op.name); sys::swapByteOrder(op.attributes); } inline void swapStruct(struct category64_t &c) { sys::swapByteOrder(c.name); sys::swapByteOrder(c.cls); sys::swapByteOrder(c.instanceMethods); sys::swapByteOrder(c.classMethods); sys::swapByteOrder(c.protocols); sys::swapByteOrder(c.instanceProperties); } inline void swapStruct(struct category32_t &c) { sys::swapByteOrder(c.name); sys::swapByteOrder(c.cls); sys::swapByteOrder(c.instanceMethods); sys::swapByteOrder(c.classMethods); sys::swapByteOrder(c.protocols); sys::swapByteOrder(c.instanceProperties); } inline void swapStruct(struct objc_image_info64 &o) { sys::swapByteOrder(o.version); sys::swapByteOrder(o.flags); } inline void swapStruct(struct objc_image_info32 &o) { sys::swapByteOrder(o.version); sys::swapByteOrder(o.flags); } inline void swapStruct(struct imageInfo_t &o) { sys::swapByteOrder(o.version); sys::swapByteOrder(o.flags); } inline void swapStruct(struct message_ref64 &mr) { sys::swapByteOrder(mr.imp); sys::swapByteOrder(mr.sel); } inline void swapStruct(struct message_ref32 &mr) { sys::swapByteOrder(mr.imp); sys::swapByteOrder(mr.sel); } inline void swapStruct(struct objc_module_t &module) { sys::swapByteOrder(module.version); sys::swapByteOrder(module.size); sys::swapByteOrder(module.name); sys::swapByteOrder(module.symtab); } inline void swapStruct(struct objc_symtab_t &symtab) { sys::swapByteOrder(symtab.sel_ref_cnt); sys::swapByteOrder(symtab.refs); sys::swapByteOrder(symtab.cls_def_cnt); sys::swapByteOrder(symtab.cat_def_cnt); } inline void swapStruct(struct objc_class_t &objc_class) { sys::swapByteOrder(objc_class.isa); sys::swapByteOrder(objc_class.super_class); sys::swapByteOrder(objc_class.name); sys::swapByteOrder(objc_class.version); sys::swapByteOrder(objc_class.info); sys::swapByteOrder(objc_class.instance_size); sys::swapByteOrder(objc_class.ivars); sys::swapByteOrder(objc_class.methodLists); sys::swapByteOrder(objc_class.cache); sys::swapByteOrder(objc_class.protocols); } inline void swapStruct(struct objc_category_t &objc_category) { sys::swapByteOrder(objc_category.category_name); sys::swapByteOrder(objc_category.class_name); sys::swapByteOrder(objc_category.instance_methods); sys::swapByteOrder(objc_category.class_methods); sys::swapByteOrder(objc_category.protocols); } inline void swapStruct(struct objc_ivar_list_t &objc_ivar_list) { sys::swapByteOrder(objc_ivar_list.ivar_count); } inline void swapStruct(struct objc_ivar_t &objc_ivar) { sys::swapByteOrder(objc_ivar.ivar_name); sys::swapByteOrder(objc_ivar.ivar_type); sys::swapByteOrder(objc_ivar.ivar_offset); } inline void swapStruct(struct objc_method_list_t &method_list) { sys::swapByteOrder(method_list.obsolete); sys::swapByteOrder(method_list.method_count); } inline void swapStruct(struct objc_method_t &method) { sys::swapByteOrder(method.method_name); sys::swapByteOrder(method.method_types); sys::swapByteOrder(method.method_imp); } inline void swapStruct(struct objc_protocol_list_t &protocol_list) { sys::swapByteOrder(protocol_list.next); sys::swapByteOrder(protocol_list.count); } inline void swapStruct(struct objc_protocol_t &protocol) { sys::swapByteOrder(protocol.isa); sys::swapByteOrder(protocol.protocol_name); sys::swapByteOrder(protocol.protocol_list); sys::swapByteOrder(protocol.instance_methods); sys::swapByteOrder(protocol.class_methods); } inline void swapStruct(struct objc_method_description_list_t &mdl) { sys::swapByteOrder(mdl.count); } inline void swapStruct(struct objc_method_description_t &md) { sys::swapByteOrder(md.name); sys::swapByteOrder(md.types); } } // namespace static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info); // get_objc2_64bit_class_name() is used for disassembly and is passed a pointer // to an Objective-C class and returns the class name. It is also passed the // address of the pointer, so when the pointer is zero as it can be in an .o // file, that is used to look for an external relocation entry with a symbol // name. static const char *get_objc2_64bit_class_name(uint64_t pointer_value, uint64_t ReferenceValue, struct DisassembleInfo *info) { const char *r; uint32_t offset, left; SectionRef S; // The pointer_value can be 0 in an object file and have a relocation // entry for the class symbol at the ReferenceValue (the address of the // pointer). if (pointer_value == 0) { r = get_pointer_64(ReferenceValue, offset, left, S, info); if (r == nullptr || left < sizeof(uint64_t)) return nullptr; uint64_t n_value; const char *symbol_name = get_symbol_64(offset, S, info, n_value); if (symbol_name == nullptr) return nullptr; const char *class_name = strrchr(symbol_name, '$'); if (class_name != nullptr && class_name[1] == '_' && class_name[2] != '\0') return class_name + 2; else return nullptr; } // The case were the pointer_value is non-zero and points to a class defined // in this Mach-O file. r = get_pointer_64(pointer_value, offset, left, S, info); if (r == nullptr || left < sizeof(struct class64_t)) return nullptr; struct class64_t c; memcpy(&c, r, sizeof(struct class64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(c); if (c.data == 0) return nullptr; r = get_pointer_64(c.data, offset, left, S, info); if (r == nullptr || left < sizeof(struct class_ro64_t)) return nullptr; struct class_ro64_t cro; memcpy(&cro, r, sizeof(struct class_ro64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(cro); if (cro.name == 0) return nullptr; const char *name = get_pointer_64(cro.name, offset, left, S, info); return name; } // get_objc2_64bit_cfstring_name is used for disassembly and is passed a // pointer to a cfstring and returns its name or nullptr. static const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue, struct DisassembleInfo *info) { const char *r, *name; uint32_t offset, left; SectionRef S; struct cfstring64_t cfs; uint64_t cfs_characters; r = get_pointer_64(ReferenceValue, offset, left, S, info); if (r == nullptr || left < sizeof(struct cfstring64_t)) return nullptr; memcpy(&cfs, r, sizeof(struct cfstring64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(cfs); if (cfs.characters == 0) { uint64_t n_value; const char *symbol_name = get_symbol_64( offset + offsetof(struct cfstring64_t, characters), S, info, n_value); if (symbol_name == nullptr) return nullptr; cfs_characters = n_value; } else cfs_characters = cfs.characters; name = get_pointer_64(cfs_characters, offset, left, S, info); return name; } // get_objc2_64bit_selref() is used for disassembly and is passed a the address // of a pointer to an Objective-C selector reference when the pointer value is // zero as in a .o file and is likely to have a external relocation entry with // who's symbol's n_value is the real pointer to the selector name. If that is // the case the real pointer to the selector name is returned else 0 is // returned static uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, struct DisassembleInfo *info) { uint32_t offset, left; SectionRef S; const char *r = get_pointer_64(ReferenceValue, offset, left, S, info); if (r == nullptr || left < sizeof(uint64_t)) return 0; uint64_t n_value; const char *symbol_name = get_symbol_64(offset, S, info, n_value); if (symbol_name == nullptr) return 0; return n_value; } static const SectionRef get_section(MachOObjectFile *O, const char *segname, const char *sectname) { for (const SectionRef &Section : O->sections()) { StringRef SectName; Expected SecNameOrErr = Section.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == segname && SectName == sectname) return Section; } return SectionRef(); } static void walk_pointer_list_64(const char *listname, const SectionRef S, MachOObjectFile *O, struct DisassembleInfo *info, void (*func)(uint64_t, struct DisassembleInfo *info)) { if (S == SectionRef()) return; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName()); const char *Contents = reinterpret_cast(BytesStr.data()); for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint64_t)) { uint32_t left = S.getSize() - i; uint32_t size = left < sizeof(uint64_t) ? left : sizeof(uint64_t); uint64_t p = 0; memcpy(&p, Contents + i, size); if (i + sizeof(uint64_t) > S.getSize()) outs() << listname << " list pointer extends past end of (" << SegName << "," << SectName << ") section\n"; outs() << format("%016" PRIx64, S.getAddress() + i) << " "; if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(p); uint64_t n_value = 0; const char *name = get_symbol_64(i, S, info, n_value, p); if (name == nullptr) name = get_dyld_bind_info_symbolname(S.getAddress() + i, info); if (n_value != 0) { outs() << format("0x%" PRIx64, n_value); if (p != 0) outs() << " + " << format("0x%" PRIx64, p); } else outs() << format("0x%" PRIx64, p); if (name != nullptr) outs() << " " << name; outs() << "\n"; p += n_value; if (func) func(p, info); } } static void walk_pointer_list_32(const char *listname, const SectionRef S, MachOObjectFile *O, struct DisassembleInfo *info, void (*func)(uint32_t, struct DisassembleInfo *info)) { if (S == SectionRef()) return; StringRef SectName = unwrapOrError(S.getName(), O->getFileName()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName()); const char *Contents = reinterpret_cast(BytesStr.data()); for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint32_t)) { uint32_t left = S.getSize() - i; uint32_t size = left < sizeof(uint32_t) ? left : sizeof(uint32_t); uint32_t p = 0; memcpy(&p, Contents + i, size); if (i + sizeof(uint32_t) > S.getSize()) outs() << listname << " list pointer extends past end of (" << SegName << "," << SectName << ") section\n"; uint32_t Address = S.getAddress() + i; outs() << format("%08" PRIx32, Address) << " "; if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(p); outs() << format("0x%" PRIx32, p); const char *name = get_symbol_32(i, S, info, p); if (name != nullptr) outs() << " " << name; outs() << "\n"; if (func) func(p, info); } } static void print_layout_map(const char *layout_map, uint32_t left) { if (layout_map == nullptr) return; outs() << " layout map: "; do { outs() << format("0x%02" PRIx32, (*layout_map) & 0xff) << " "; left--; layout_map++; } while (*layout_map != '\0' && left != 0); outs() << "\n"; } static void print_layout_map64(uint64_t p, struct DisassembleInfo *info) { uint32_t offset, left; SectionRef S; const char *layout_map; if (p == 0) return; layout_map = get_pointer_64(p, offset, left, S, info); print_layout_map(layout_map, left); } static void print_layout_map32(uint32_t p, struct DisassembleInfo *info) { uint32_t offset, left; SectionRef S; const char *layout_map; if (p == 0) return; layout_map = get_pointer_32(p, offset, left, S, info); print_layout_map(layout_map, left); } static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info, const char *indent) { struct method_list64_t ml; struct method64_t m; const char *r; uint32_t offset, xoffset, left, i; SectionRef S, xS; const char *name, *sym_name; uint64_t n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&ml, '\0', sizeof(struct method_list64_t)); if (left < sizeof(struct method_list64_t)) { memcpy(&ml, r, left); outs() << " (method_list_t entends past the end of the section)\n"; } else memcpy(&ml, r, sizeof(struct method_list64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(ml); outs() << indent << "\t\t entsize " << ml.entsize << "\n"; outs() << indent << "\t\t count " << ml.count << "\n"; p += sizeof(struct method_list64_t); offset += sizeof(struct method_list64_t); for (i = 0; i < ml.count; i++) { r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&m, '\0', sizeof(struct method64_t)); if (left < sizeof(struct method64_t)) { memcpy(&m, r, left); outs() << indent << " (method_t extends past the end of the section)\n"; } else memcpy(&m, r, sizeof(struct method64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(m); outs() << indent << "\t\t name "; sym_name = get_symbol_64(offset + offsetof(struct method64_t, name), S, info, n_value, m.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (m.name != 0) outs() << " + " << format("0x%" PRIx64, m.name); } else outs() << format("0x%" PRIx64, m.name); name = get_pointer_64(m.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << indent << "\t\t types "; sym_name = get_symbol_64(offset + offsetof(struct method64_t, types), S, info, n_value, m.types); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (m.types != 0) outs() << " + " << format("0x%" PRIx64, m.types); } else outs() << format("0x%" PRIx64, m.types); name = get_pointer_64(m.types + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << indent << "\t\t imp "; name = get_symbol_64(offset + offsetof(struct method64_t, imp), S, info, n_value, m.imp); if (info->verbose && name == nullptr) { if (n_value != 0) { outs() << format("0x%" PRIx64, n_value) << " "; if (m.imp != 0) outs() << "+ " << format("0x%" PRIx64, m.imp) << " "; } else outs() << format("0x%" PRIx64, m.imp) << " "; } if (name != nullptr) outs() << name; outs() << "\n"; p += sizeof(struct method64_t); offset += sizeof(struct method64_t); } } static void print_method_list32_t(uint64_t p, struct DisassembleInfo *info, const char *indent) { struct method_list32_t ml; struct method32_t m; const char *r, *name; uint32_t offset, xoffset, left, i; SectionRef S, xS; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&ml, '\0', sizeof(struct method_list32_t)); if (left < sizeof(struct method_list32_t)) { memcpy(&ml, r, left); outs() << " (method_list_t entends past the end of the section)\n"; } else memcpy(&ml, r, sizeof(struct method_list32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(ml); outs() << indent << "\t\t entsize " << ml.entsize << "\n"; outs() << indent << "\t\t count " << ml.count << "\n"; p += sizeof(struct method_list32_t); offset += sizeof(struct method_list32_t); for (i = 0; i < ml.count; i++) { r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&m, '\0', sizeof(struct method32_t)); if (left < sizeof(struct method32_t)) { memcpy(&ml, r, left); outs() << indent << " (method_t entends past the end of the section)\n"; } else memcpy(&m, r, sizeof(struct method32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(m); outs() << indent << "\t\t name " << format("0x%" PRIx32, m.name); name = get_pointer_32(m.name, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << indent << "\t\t types " << format("0x%" PRIx32, m.types); name = get_pointer_32(m.types, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << indent << "\t\t imp " << format("0x%" PRIx32, m.imp); name = get_symbol_32(offset + offsetof(struct method32_t, imp), S, info, m.imp); if (name != nullptr) outs() << " " << name; outs() << "\n"; p += sizeof(struct method32_t); offset += sizeof(struct method32_t); } } static bool print_method_list(uint32_t p, struct DisassembleInfo *info) { uint32_t offset, left, xleft; SectionRef S; struct objc_method_list_t method_list; struct objc_method_t method; const char *r, *methods, *name, *SymbolName; int32_t i; r = get_pointer_32(p, offset, left, S, info, true); if (r == nullptr) return true; outs() << "\n"; if (left > sizeof(struct objc_method_list_t)) { memcpy(&method_list, r, sizeof(struct objc_method_list_t)); } else { outs() << "\t\t objc_method_list extends past end of the section\n"; memset(&method_list, '\0', sizeof(struct objc_method_list_t)); memcpy(&method_list, r, left); } if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(method_list); outs() << "\t\t obsolete " << format("0x%08" PRIx32, method_list.obsolete) << "\n"; outs() << "\t\t method_count " << method_list.method_count << "\n"; methods = r + sizeof(struct objc_method_list_t); for (i = 0; i < method_list.method_count; i++) { if ((i + 1) * sizeof(struct objc_method_t) > left) { outs() << "\t\t remaining method's extend past the of the section\n"; break; } memcpy(&method, methods + i * sizeof(struct objc_method_t), sizeof(struct objc_method_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(method); outs() << "\t\t method_name " << format("0x%08" PRIx32, method.method_name); if (info->verbose) { name = get_pointer_32(method.method_name, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t method_types " << format("0x%08" PRIx32, method.method_types); if (info->verbose) { name = get_pointer_32(method.method_types, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t method_imp " << format("0x%08" PRIx32, method.method_imp) << " "; if (info->verbose) { SymbolName = GuessSymbolName(method.method_imp, info->AddrMap); if (SymbolName != nullptr) outs() << SymbolName; } outs() << "\n"; } return false; } static void print_protocol_list64_t(uint64_t p, struct DisassembleInfo *info) { struct protocol_list64_t pl; uint64_t q, n_value; struct protocol64_t pc; const char *r; uint32_t offset, xoffset, left, i; SectionRef S, xS; const char *name, *sym_name; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&pl, '\0', sizeof(struct protocol_list64_t)); if (left < sizeof(struct protocol_list64_t)) { memcpy(&pl, r, left); outs() << " (protocol_list_t entends past the end of the section)\n"; } else memcpy(&pl, r, sizeof(struct protocol_list64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(pl); outs() << " count " << pl.count << "\n"; p += sizeof(struct protocol_list64_t); offset += sizeof(struct protocol_list64_t); for (i = 0; i < pl.count; i++) { r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; q = 0; if (left < sizeof(uint64_t)) { memcpy(&q, r, left); outs() << " (protocol_t * entends past the end of the section)\n"; } else memcpy(&q, r, sizeof(uint64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(q); outs() << "\t\t list[" << i << "] "; sym_name = get_symbol_64(offset, S, info, n_value, q); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (q != 0) outs() << " + " << format("0x%" PRIx64, q); } else outs() << format("0x%" PRIx64, q); outs() << " (struct protocol_t *)\n"; r = get_pointer_64(q + n_value, offset, left, S, info); if (r == nullptr) return; memset(&pc, '\0', sizeof(struct protocol64_t)); if (left < sizeof(struct protocol64_t)) { memcpy(&pc, r, left); outs() << " (protocol_t entends past the end of the section)\n"; } else memcpy(&pc, r, sizeof(struct protocol64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(pc); outs() << "\t\t\t isa " << format("0x%" PRIx64, pc.isa) << "\n"; outs() << "\t\t\t name "; sym_name = get_symbol_64(offset + offsetof(struct protocol64_t, name), S, info, n_value, pc.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (pc.name != 0) outs() << " + " << format("0x%" PRIx64, pc.name); } else outs() << format("0x%" PRIx64, pc.name); name = get_pointer_64(pc.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\tprotocols " << format("0x%" PRIx64, pc.protocols) << "\n"; outs() << "\t\t instanceMethods "; sym_name = get_symbol_64(offset + offsetof(struct protocol64_t, instanceMethods), S, info, n_value, pc.instanceMethods); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (pc.instanceMethods != 0) outs() << " + " << format("0x%" PRIx64, pc.instanceMethods); } else outs() << format("0x%" PRIx64, pc.instanceMethods); outs() << " (struct method_list_t *)\n"; if (pc.instanceMethods + n_value != 0) print_method_list64_t(pc.instanceMethods + n_value, info, "\t"); outs() << "\t\t classMethods "; sym_name = get_symbol_64(offset + offsetof(struct protocol64_t, classMethods), S, info, n_value, pc.classMethods); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (pc.classMethods != 0) outs() << " + " << format("0x%" PRIx64, pc.classMethods); } else outs() << format("0x%" PRIx64, pc.classMethods); outs() << " (struct method_list_t *)\n"; if (pc.classMethods + n_value != 0) print_method_list64_t(pc.classMethods + n_value, info, "\t"); outs() << "\t optionalInstanceMethods " << format("0x%" PRIx64, pc.optionalInstanceMethods) << "\n"; outs() << "\t optionalClassMethods " << format("0x%" PRIx64, pc.optionalClassMethods) << "\n"; outs() << "\t instanceProperties " << format("0x%" PRIx64, pc.instanceProperties) << "\n"; p += sizeof(uint64_t); offset += sizeof(uint64_t); } } static void print_protocol_list32_t(uint32_t p, struct DisassembleInfo *info) { struct protocol_list32_t pl; uint32_t q; struct protocol32_t pc; const char *r; uint32_t offset, xoffset, left, i; SectionRef S, xS; const char *name; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&pl, '\0', sizeof(struct protocol_list32_t)); if (left < sizeof(struct protocol_list32_t)) { memcpy(&pl, r, left); outs() << " (protocol_list_t entends past the end of the section)\n"; } else memcpy(&pl, r, sizeof(struct protocol_list32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(pl); outs() << " count " << pl.count << "\n"; p += sizeof(struct protocol_list32_t); offset += sizeof(struct protocol_list32_t); for (i = 0; i < pl.count; i++) { r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; q = 0; if (left < sizeof(uint32_t)) { memcpy(&q, r, left); outs() << " (protocol_t * entends past the end of the section)\n"; } else memcpy(&q, r, sizeof(uint32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(q); outs() << "\t\t list[" << i << "] " << format("0x%" PRIx32, q) << " (struct protocol_t *)\n"; r = get_pointer_32(q, offset, left, S, info); if (r == nullptr) return; memset(&pc, '\0', sizeof(struct protocol32_t)); if (left < sizeof(struct protocol32_t)) { memcpy(&pc, r, left); outs() << " (protocol_t entends past the end of the section)\n"; } else memcpy(&pc, r, sizeof(struct protocol32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(pc); outs() << "\t\t\t isa " << format("0x%" PRIx32, pc.isa) << "\n"; outs() << "\t\t\t name " << format("0x%" PRIx32, pc.name); name = get_pointer_32(pc.name, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\tprotocols " << format("0x%" PRIx32, pc.protocols) << "\n"; outs() << "\t\t instanceMethods " << format("0x%" PRIx32, pc.instanceMethods) << " (struct method_list_t *)\n"; if (pc.instanceMethods != 0) print_method_list32_t(pc.instanceMethods, info, "\t"); outs() << "\t\t classMethods " << format("0x%" PRIx32, pc.classMethods) << " (struct method_list_t *)\n"; if (pc.classMethods != 0) print_method_list32_t(pc.classMethods, info, "\t"); outs() << "\t optionalInstanceMethods " << format("0x%" PRIx32, pc.optionalInstanceMethods) << "\n"; outs() << "\t optionalClassMethods " << format("0x%" PRIx32, pc.optionalClassMethods) << "\n"; outs() << "\t instanceProperties " << format("0x%" PRIx32, pc.instanceProperties) << "\n"; p += sizeof(uint32_t); offset += sizeof(uint32_t); } } static void print_indent(uint32_t indent) { for (uint32_t i = 0; i < indent;) { if (indent - i >= 8) { outs() << "\t"; i += 8; } else { for (uint32_t j = i; j < indent; j++) outs() << " "; return; } } } static bool print_method_description_list(uint32_t p, uint32_t indent, struct DisassembleInfo *info) { uint32_t offset, left, xleft; SectionRef S; struct objc_method_description_list_t mdl; struct objc_method_description_t md; const char *r, *list, *name; int32_t i; r = get_pointer_32(p, offset, left, S, info, true); if (r == nullptr) return true; outs() << "\n"; if (left > sizeof(struct objc_method_description_list_t)) { memcpy(&mdl, r, sizeof(struct objc_method_description_list_t)); } else { print_indent(indent); outs() << " objc_method_description_list extends past end of the section\n"; memset(&mdl, '\0', sizeof(struct objc_method_description_list_t)); memcpy(&mdl, r, left); } if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(mdl); print_indent(indent); outs() << " count " << mdl.count << "\n"; list = r + sizeof(struct objc_method_description_list_t); for (i = 0; i < mdl.count; i++) { if ((i + 1) * sizeof(struct objc_method_description_t) > left) { print_indent(indent); outs() << " remaining list entries extend past the of the section\n"; break; } print_indent(indent); outs() << " list[" << i << "]\n"; memcpy(&md, list + i * sizeof(struct objc_method_description_t), sizeof(struct objc_method_description_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(md); print_indent(indent); outs() << " name " << format("0x%08" PRIx32, md.name); if (info->verbose) { name = get_pointer_32(md.name, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; print_indent(indent); outs() << " types " << format("0x%08" PRIx32, md.types); if (info->verbose) { name = get_pointer_32(md.types, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; } return false; } static bool print_protocol_list(uint32_t p, uint32_t indent, struct DisassembleInfo *info); static bool print_protocol(uint32_t p, uint32_t indent, struct DisassembleInfo *info) { uint32_t offset, left; SectionRef S; struct objc_protocol_t protocol; const char *r, *name; r = get_pointer_32(p, offset, left, S, info, true); if (r == nullptr) return true; outs() << "\n"; if (left >= sizeof(struct objc_protocol_t)) { memcpy(&protocol, r, sizeof(struct objc_protocol_t)); } else { print_indent(indent); outs() << " Protocol extends past end of the section\n"; memset(&protocol, '\0', sizeof(struct objc_protocol_t)); memcpy(&protocol, r, left); } if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(protocol); print_indent(indent); outs() << " isa " << format("0x%08" PRIx32, protocol.isa) << "\n"; print_indent(indent); outs() << " protocol_name " << format("0x%08" PRIx32, protocol.protocol_name); if (info->verbose) { name = get_pointer_32(protocol.protocol_name, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; print_indent(indent); outs() << " protocol_list " << format("0x%08" PRIx32, protocol.protocol_list); if (print_protocol_list(protocol.protocol_list, indent + 4, info)) outs() << " (not in an __OBJC section)\n"; print_indent(indent); outs() << " instance_methods " << format("0x%08" PRIx32, protocol.instance_methods); if (print_method_description_list(protocol.instance_methods, indent, info)) outs() << " (not in an __OBJC section)\n"; print_indent(indent); outs() << " class_methods " << format("0x%08" PRIx32, protocol.class_methods); if (print_method_description_list(protocol.class_methods, indent, info)) outs() << " (not in an __OBJC section)\n"; return false; } static bool print_protocol_list(uint32_t p, uint32_t indent, struct DisassembleInfo *info) { uint32_t offset, left, l; SectionRef S; struct objc_protocol_list_t protocol_list; const char *r, *list; int32_t i; r = get_pointer_32(p, offset, left, S, info, true); if (r == nullptr) return true; outs() << "\n"; if (left > sizeof(struct objc_protocol_list_t)) { memcpy(&protocol_list, r, sizeof(struct objc_protocol_list_t)); } else { outs() << "\t\t objc_protocol_list_t extends past end of the section\n"; memset(&protocol_list, '\0', sizeof(struct objc_protocol_list_t)); memcpy(&protocol_list, r, left); } if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(protocol_list); print_indent(indent); outs() << " next " << format("0x%08" PRIx32, protocol_list.next) << "\n"; print_indent(indent); outs() << " count " << protocol_list.count << "\n"; list = r + sizeof(struct objc_protocol_list_t); for (i = 0; i < protocol_list.count; i++) { if ((i + 1) * sizeof(uint32_t) > left) { outs() << "\t\t remaining list entries extend past the of the section\n"; break; } memcpy(&l, list + i * sizeof(uint32_t), sizeof(uint32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(l); print_indent(indent); outs() << " list[" << i << "] " << format("0x%08" PRIx32, l); if (print_protocol(l, indent, info)) outs() << "(not in an __OBJC section)\n"; } return false; } static void print_ivar_list64_t(uint64_t p, struct DisassembleInfo *info) { struct ivar_list64_t il; struct ivar64_t i; const char *r; uint32_t offset, xoffset, left, j; SectionRef S, xS; const char *name, *sym_name, *ivar_offset_p; uint64_t ivar_offset, n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&il, '\0', sizeof(struct ivar_list64_t)); if (left < sizeof(struct ivar_list64_t)) { memcpy(&il, r, left); outs() << " (ivar_list_t entends past the end of the section)\n"; } else memcpy(&il, r, sizeof(struct ivar_list64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(il); outs() << " entsize " << il.entsize << "\n"; outs() << " count " << il.count << "\n"; p += sizeof(struct ivar_list64_t); offset += sizeof(struct ivar_list64_t); for (j = 0; j < il.count; j++) { r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&i, '\0', sizeof(struct ivar64_t)); if (left < sizeof(struct ivar64_t)) { memcpy(&i, r, left); outs() << " (ivar_t entends past the end of the section)\n"; } else memcpy(&i, r, sizeof(struct ivar64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(i); outs() << "\t\t\t offset "; sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, offset), S, info, n_value, i.offset); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (i.offset != 0) outs() << " + " << format("0x%" PRIx64, i.offset); } else outs() << format("0x%" PRIx64, i.offset); ivar_offset_p = get_pointer_64(i.offset + n_value, xoffset, left, xS, info); if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) { memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(ivar_offset); outs() << " " << ivar_offset << "\n"; } else outs() << "\n"; outs() << "\t\t\t name "; sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, name), S, info, n_value, i.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (i.name != 0) outs() << " + " << format("0x%" PRIx64, i.name); } else outs() << format("0x%" PRIx64, i.name); name = get_pointer_64(i.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\t type "; sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, type), S, info, n_value, i.name); name = get_pointer_64(i.type + n_value, xoffset, left, xS, info); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (i.type != 0) outs() << " + " << format("0x%" PRIx64, i.type); } else outs() << format("0x%" PRIx64, i.type); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\talignment " << i.alignment << "\n"; outs() << "\t\t\t size " << i.size << "\n"; p += sizeof(struct ivar64_t); offset += sizeof(struct ivar64_t); } } static void print_ivar_list32_t(uint32_t p, struct DisassembleInfo *info) { struct ivar_list32_t il; struct ivar32_t i; const char *r; uint32_t offset, xoffset, left, j; SectionRef S, xS; const char *name, *ivar_offset_p; uint32_t ivar_offset; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&il, '\0', sizeof(struct ivar_list32_t)); if (left < sizeof(struct ivar_list32_t)) { memcpy(&il, r, left); outs() << " (ivar_list_t entends past the end of the section)\n"; } else memcpy(&il, r, sizeof(struct ivar_list32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(il); outs() << " entsize " << il.entsize << "\n"; outs() << " count " << il.count << "\n"; p += sizeof(struct ivar_list32_t); offset += sizeof(struct ivar_list32_t); for (j = 0; j < il.count; j++) { r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&i, '\0', sizeof(struct ivar32_t)); if (left < sizeof(struct ivar32_t)) { memcpy(&i, r, left); outs() << " (ivar_t entends past the end of the section)\n"; } else memcpy(&i, r, sizeof(struct ivar32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(i); outs() << "\t\t\t offset " << format("0x%" PRIx32, i.offset); ivar_offset_p = get_pointer_32(i.offset, xoffset, left, xS, info); if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) { memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(ivar_offset); outs() << " " << ivar_offset << "\n"; } else outs() << "\n"; outs() << "\t\t\t name " << format("0x%" PRIx32, i.name); name = get_pointer_32(i.name, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\t type " << format("0x%" PRIx32, i.type); name = get_pointer_32(i.type, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\talignment " << i.alignment << "\n"; outs() << "\t\t\t size " << i.size << "\n"; p += sizeof(struct ivar32_t); offset += sizeof(struct ivar32_t); } } static void print_objc_property_list64(uint64_t p, struct DisassembleInfo *info) { struct objc_property_list64 opl; struct objc_property64 op; const char *r; uint32_t offset, xoffset, left, j; SectionRef S, xS; const char *name, *sym_name; uint64_t n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&opl, '\0', sizeof(struct objc_property_list64)); if (left < sizeof(struct objc_property_list64)) { memcpy(&opl, r, left); outs() << " (objc_property_list entends past the end of the section)\n"; } else memcpy(&opl, r, sizeof(struct objc_property_list64)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(opl); outs() << " entsize " << opl.entsize << "\n"; outs() << " count " << opl.count << "\n"; p += sizeof(struct objc_property_list64); offset += sizeof(struct objc_property_list64); for (j = 0; j < opl.count; j++) { r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&op, '\0', sizeof(struct objc_property64)); if (left < sizeof(struct objc_property64)) { memcpy(&op, r, left); outs() << " (objc_property entends past the end of the section)\n"; } else memcpy(&op, r, sizeof(struct objc_property64)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(op); outs() << "\t\t\t name "; sym_name = get_symbol_64(offset + offsetof(struct objc_property64, name), S, info, n_value, op.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (op.name != 0) outs() << " + " << format("0x%" PRIx64, op.name); } else outs() << format("0x%" PRIx64, op.name); name = get_pointer_64(op.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\tattributes "; sym_name = get_symbol_64(offset + offsetof(struct objc_property64, attributes), S, info, n_value, op.attributes); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (op.attributes != 0) outs() << " + " << format("0x%" PRIx64, op.attributes); } else outs() << format("0x%" PRIx64, op.attributes); name = get_pointer_64(op.attributes + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; p += sizeof(struct objc_property64); offset += sizeof(struct objc_property64); } } static void print_objc_property_list32(uint32_t p, struct DisassembleInfo *info) { struct objc_property_list32 opl; struct objc_property32 op; const char *r; uint32_t offset, xoffset, left, j; SectionRef S, xS; const char *name; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&opl, '\0', sizeof(struct objc_property_list32)); if (left < sizeof(struct objc_property_list32)) { memcpy(&opl, r, left); outs() << " (objc_property_list entends past the end of the section)\n"; } else memcpy(&opl, r, sizeof(struct objc_property_list32)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(opl); outs() << " entsize " << opl.entsize << "\n"; outs() << " count " << opl.count << "\n"; p += sizeof(struct objc_property_list32); offset += sizeof(struct objc_property_list32); for (j = 0; j < opl.count; j++) { r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&op, '\0', sizeof(struct objc_property32)); if (left < sizeof(struct objc_property32)) { memcpy(&op, r, left); outs() << " (objc_property entends past the end of the section)\n"; } else memcpy(&op, r, sizeof(struct objc_property32)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(op); outs() << "\t\t\t name " << format("0x%" PRIx32, op.name); name = get_pointer_32(op.name, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << "\t\t\tattributes " << format("0x%" PRIx32, op.attributes); name = get_pointer_32(op.attributes, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; p += sizeof(struct objc_property32); offset += sizeof(struct objc_property32); } } static bool print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, bool &is_meta_class) { struct class_ro64_t cro; const char *r; uint32_t offset, xoffset, left; SectionRef S, xS; const char *name, *sym_name; uint64_t n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr || left < sizeof(struct class_ro64_t)) return false; memcpy(&cro, r, sizeof(struct class_ro64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(cro); outs() << " flags " << format("0x%" PRIx32, cro.flags); if (cro.flags & RO_META) outs() << " RO_META"; if (cro.flags & RO_ROOT) outs() << " RO_ROOT"; if (cro.flags & RO_HAS_CXX_STRUCTORS) outs() << " RO_HAS_CXX_STRUCTORS"; outs() << "\n"; outs() << " instanceStart " << cro.instanceStart << "\n"; outs() << " instanceSize " << cro.instanceSize << "\n"; outs() << " reserved " << format("0x%" PRIx32, cro.reserved) << "\n"; outs() << " ivarLayout " << format("0x%" PRIx64, cro.ivarLayout) << "\n"; print_layout_map64(cro.ivarLayout, info); outs() << " name "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, name), S, info, n_value, cro.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.name != 0) outs() << " + " << format("0x%" PRIx64, cro.name); } else outs() << format("0x%" PRIx64, cro.name); name = get_pointer_64(cro.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << " baseMethods "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, baseMethods), S, info, n_value, cro.baseMethods); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.baseMethods != 0) outs() << " + " << format("0x%" PRIx64, cro.baseMethods); } else outs() << format("0x%" PRIx64, cro.baseMethods); outs() << " (struct method_list_t *)\n"; if (cro.baseMethods + n_value != 0) print_method_list64_t(cro.baseMethods + n_value, info, ""); outs() << " baseProtocols "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, baseProtocols), S, info, n_value, cro.baseProtocols); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.baseProtocols != 0) outs() << " + " << format("0x%" PRIx64, cro.baseProtocols); } else outs() << format("0x%" PRIx64, cro.baseProtocols); outs() << "\n"; if (cro.baseProtocols + n_value != 0) print_protocol_list64_t(cro.baseProtocols + n_value, info); outs() << " ivars "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, ivars), S, info, n_value, cro.ivars); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.ivars != 0) outs() << " + " << format("0x%" PRIx64, cro.ivars); } else outs() << format("0x%" PRIx64, cro.ivars); outs() << "\n"; if (cro.ivars + n_value != 0) print_ivar_list64_t(cro.ivars + n_value, info); outs() << " weakIvarLayout "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, weakIvarLayout), S, info, n_value, cro.weakIvarLayout); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.weakIvarLayout != 0) outs() << " + " << format("0x%" PRIx64, cro.weakIvarLayout); } else outs() << format("0x%" PRIx64, cro.weakIvarLayout); outs() << "\n"; print_layout_map64(cro.weakIvarLayout + n_value, info); outs() << " baseProperties "; sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, baseProperties), S, info, n_value, cro.baseProperties); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (cro.baseProperties != 0) outs() << " + " << format("0x%" PRIx64, cro.baseProperties); } else outs() << format("0x%" PRIx64, cro.baseProperties); outs() << "\n"; if (cro.baseProperties + n_value != 0) print_objc_property_list64(cro.baseProperties + n_value, info); is_meta_class = (cro.flags & RO_META) != 0; return true; } static bool print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, bool &is_meta_class) { struct class_ro32_t cro; const char *r; uint32_t offset, xoffset, left; SectionRef S, xS; const char *name; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return false; memset(&cro, '\0', sizeof(struct class_ro32_t)); if (left < sizeof(struct class_ro32_t)) { memcpy(&cro, r, left); outs() << " (class_ro_t entends past the end of the section)\n"; } else memcpy(&cro, r, sizeof(struct class_ro32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(cro); outs() << " flags " << format("0x%" PRIx32, cro.flags); if (cro.flags & RO_META) outs() << " RO_META"; if (cro.flags & RO_ROOT) outs() << " RO_ROOT"; if (cro.flags & RO_HAS_CXX_STRUCTORS) outs() << " RO_HAS_CXX_STRUCTORS"; outs() << "\n"; outs() << " instanceStart " << cro.instanceStart << "\n"; outs() << " instanceSize " << cro.instanceSize << "\n"; outs() << " ivarLayout " << format("0x%" PRIx32, cro.ivarLayout) << "\n"; print_layout_map32(cro.ivarLayout, info); outs() << " name " << format("0x%" PRIx32, cro.name); name = get_pointer_32(cro.name, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << " baseMethods " << format("0x%" PRIx32, cro.baseMethods) << " (struct method_list_t *)\n"; if (cro.baseMethods != 0) print_method_list32_t(cro.baseMethods, info, ""); outs() << " baseProtocols " << format("0x%" PRIx32, cro.baseProtocols) << "\n"; if (cro.baseProtocols != 0) print_protocol_list32_t(cro.baseProtocols, info); outs() << " ivars " << format("0x%" PRIx32, cro.ivars) << "\n"; if (cro.ivars != 0) print_ivar_list32_t(cro.ivars, info); outs() << " weakIvarLayout " << format("0x%" PRIx32, cro.weakIvarLayout) << "\n"; print_layout_map32(cro.weakIvarLayout, info); outs() << " baseProperties " << format("0x%" PRIx32, cro.baseProperties) << "\n"; if (cro.baseProperties != 0) print_objc_property_list32(cro.baseProperties, info); is_meta_class = (cro.flags & RO_META) != 0; return true; } static void print_class64_t(uint64_t p, struct DisassembleInfo *info) { struct class64_t c; const char *r; uint32_t offset, left; SectionRef S; const char *name; uint64_t isa_n_value, n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr || left < sizeof(struct class64_t)) return; memcpy(&c, r, sizeof(struct class64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(c); outs() << " isa " << format("0x%" PRIx64, c.isa); name = get_symbol_64(offset + offsetof(struct class64_t, isa), S, info, isa_n_value, c.isa); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " superclass " << format("0x%" PRIx64, c.superclass); name = get_symbol_64(offset + offsetof(struct class64_t, superclass), S, info, n_value, c.superclass); if (name != nullptr) outs() << " " << name; else { name = get_dyld_bind_info_symbolname(S.getAddress() + offset + offsetof(struct class64_t, superclass), info); if (name != nullptr) outs() << " " << name; } outs() << "\n"; outs() << " cache " << format("0x%" PRIx64, c.cache); name = get_symbol_64(offset + offsetof(struct class64_t, cache), S, info, n_value, c.cache); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " vtable " << format("0x%" PRIx64, c.vtable); name = get_symbol_64(offset + offsetof(struct class64_t, vtable), S, info, n_value, c.vtable); if (name != nullptr) outs() << " " << name; outs() << "\n"; name = get_symbol_64(offset + offsetof(struct class64_t, data), S, info, n_value, c.data); outs() << " data "; if (n_value != 0) { if (info->verbose && name != nullptr) outs() << name; else outs() << format("0x%" PRIx64, n_value); if (c.data != 0) outs() << " + " << format("0x%" PRIx64, c.data); } else outs() << format("0x%" PRIx64, c.data); outs() << " (struct class_ro_t *)"; // This is a Swift class if some of the low bits of the pointer are set. if ((c.data + n_value) & 0x7) outs() << " Swift class"; outs() << "\n"; bool is_meta_class; if (!print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class)) return; if (!is_meta_class && c.isa + isa_n_value != p && c.isa + isa_n_value != 0 && info->depth < 100) { info->depth++; outs() << "Meta Class\n"; print_class64_t(c.isa + isa_n_value, info); } } static void print_class32_t(uint32_t p, struct DisassembleInfo *info) { struct class32_t c; const char *r; uint32_t offset, left; SectionRef S; const char *name; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&c, '\0', sizeof(struct class32_t)); if (left < sizeof(struct class32_t)) { memcpy(&c, r, left); outs() << " (class_t entends past the end of the section)\n"; } else memcpy(&c, r, sizeof(struct class32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(c); outs() << " isa " << format("0x%" PRIx32, c.isa); name = get_symbol_32(offset + offsetof(struct class32_t, isa), S, info, c.isa); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " superclass " << format("0x%" PRIx32, c.superclass); name = get_symbol_32(offset + offsetof(struct class32_t, superclass), S, info, c.superclass); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " cache " << format("0x%" PRIx32, c.cache); name = get_symbol_32(offset + offsetof(struct class32_t, cache), S, info, c.cache); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " vtable " << format("0x%" PRIx32, c.vtable); name = get_symbol_32(offset + offsetof(struct class32_t, vtable), S, info, c.vtable); if (name != nullptr) outs() << " " << name; outs() << "\n"; name = get_symbol_32(offset + offsetof(struct class32_t, data), S, info, c.data); outs() << " data " << format("0x%" PRIx32, c.data) << " (struct class_ro_t *)"; // This is a Swift class if some of the low bits of the pointer are set. if (c.data & 0x3) outs() << " Swift class"; outs() << "\n"; bool is_meta_class; if (!print_class_ro32_t(c.data & ~0x3, info, is_meta_class)) return; if (!is_meta_class) { outs() << "Meta Class\n"; print_class32_t(c.isa, info); } } static void print_objc_class_t(struct objc_class_t *objc_class, struct DisassembleInfo *info) { uint32_t offset, left, xleft; const char *name, *p, *ivar_list; SectionRef S; int32_t i; struct objc_ivar_list_t objc_ivar_list; struct objc_ivar_t ivar; outs() << "\t\t isa " << format("0x%08" PRIx32, objc_class->isa); if (info->verbose && CLS_GETINFO(objc_class, CLS_META)) { name = get_pointer_32(objc_class->isa, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t super_class " << format("0x%08" PRIx32, objc_class->super_class); if (info->verbose) { name = get_pointer_32(objc_class->super_class, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t name " << format("0x%08" PRIx32, objc_class->name); if (info->verbose) { name = get_pointer_32(objc_class->name, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t version " << format("0x%08" PRIx32, objc_class->version) << "\n"; outs() << "\t\t info " << format("0x%08" PRIx32, objc_class->info); if (info->verbose) { if (CLS_GETINFO(objc_class, CLS_CLASS)) outs() << " CLS_CLASS"; else if (CLS_GETINFO(objc_class, CLS_META)) outs() << " CLS_META"; } outs() << "\n"; outs() << "\t instance_size " << format("0x%08" PRIx32, objc_class->instance_size) << "\n"; p = get_pointer_32(objc_class->ivars, offset, left, S, info, true); outs() << "\t\t ivars " << format("0x%08" PRIx32, objc_class->ivars); if (p != nullptr) { if (left > sizeof(struct objc_ivar_list_t)) { outs() << "\n"; memcpy(&objc_ivar_list, p, sizeof(struct objc_ivar_list_t)); } else { outs() << " (entends past the end of the section)\n"; memset(&objc_ivar_list, '\0', sizeof(struct objc_ivar_list_t)); memcpy(&objc_ivar_list, p, left); } if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(objc_ivar_list); outs() << "\t\t ivar_count " << objc_ivar_list.ivar_count << "\n"; ivar_list = p + sizeof(struct objc_ivar_list_t); for (i = 0; i < objc_ivar_list.ivar_count; i++) { if ((i + 1) * sizeof(struct objc_ivar_t) > left) { outs() << "\t\t remaining ivar's extend past the of the section\n"; break; } memcpy(&ivar, ivar_list + i * sizeof(struct objc_ivar_t), sizeof(struct objc_ivar_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(ivar); outs() << "\t\t\tivar_name " << format("0x%08" PRIx32, ivar.ivar_name); if (info->verbose) { name = get_pointer_32(ivar.ivar_name, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t\tivar_type " << format("0x%08" PRIx32, ivar.ivar_type); if (info->verbose) { name = get_pointer_32(ivar.ivar_type, offset, xleft, S, info, true); if (name != nullptr) outs() << format(" %.*s", xleft, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t ivar_offset " << format("0x%08" PRIx32, ivar.ivar_offset) << "\n"; } } else { outs() << " (not in an __OBJC section)\n"; } outs() << "\t\t methods " << format("0x%08" PRIx32, objc_class->methodLists); if (print_method_list(objc_class->methodLists, info)) outs() << " (not in an __OBJC section)\n"; outs() << "\t\t cache " << format("0x%08" PRIx32, objc_class->cache) << "\n"; outs() << "\t\tprotocols " << format("0x%08" PRIx32, objc_class->protocols); if (print_protocol_list(objc_class->protocols, 16, info)) outs() << " (not in an __OBJC section)\n"; } static void print_objc_objc_category_t(struct objc_category_t *objc_category, struct DisassembleInfo *info) { uint32_t offset, left; const char *name; SectionRef S; outs() << "\t category name " << format("0x%08" PRIx32, objc_category->category_name); if (info->verbose) { name = get_pointer_32(objc_category->category_name, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t\t class name " << format("0x%08" PRIx32, objc_category->class_name); if (info->verbose) { name = get_pointer_32(objc_category->class_name, offset, left, S, info, true); if (name != nullptr) outs() << format(" %.*s", left, name); else outs() << " (not in an __OBJC section)"; } outs() << "\n"; outs() << "\t instance methods " << format("0x%08" PRIx32, objc_category->instance_methods); if (print_method_list(objc_category->instance_methods, info)) outs() << " (not in an __OBJC section)\n"; outs() << "\t class methods " << format("0x%08" PRIx32, objc_category->class_methods); if (print_method_list(objc_category->class_methods, info)) outs() << " (not in an __OBJC section)\n"; } static void print_category64_t(uint64_t p, struct DisassembleInfo *info) { struct category64_t c; const char *r; uint32_t offset, xoffset, left; SectionRef S, xS; const char *name, *sym_name; uint64_t n_value; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&c, '\0', sizeof(struct category64_t)); if (left < sizeof(struct category64_t)) { memcpy(&c, r, left); outs() << " (category_t entends past the end of the section)\n"; } else memcpy(&c, r, sizeof(struct category64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(c); outs() << " name "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, name), S, info, n_value, c.name); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.name != 0) outs() << " + " << format("0x%" PRIx64, c.name); } else outs() << format("0x%" PRIx64, c.name); name = get_pointer_64(c.name + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; outs() << " cls "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, cls), S, info, n_value, c.cls); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.cls != 0) outs() << " + " << format("0x%" PRIx64, c.cls); } else outs() << format("0x%" PRIx64, c.cls); outs() << "\n"; if (c.cls + n_value != 0) print_class64_t(c.cls + n_value, info); outs() << " instanceMethods "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, instanceMethods), S, info, n_value, c.instanceMethods); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.instanceMethods != 0) outs() << " + " << format("0x%" PRIx64, c.instanceMethods); } else outs() << format("0x%" PRIx64, c.instanceMethods); outs() << "\n"; if (c.instanceMethods + n_value != 0) print_method_list64_t(c.instanceMethods + n_value, info, ""); outs() << " classMethods "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, classMethods), S, info, n_value, c.classMethods); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.classMethods != 0) outs() << " + " << format("0x%" PRIx64, c.classMethods); } else outs() << format("0x%" PRIx64, c.classMethods); outs() << "\n"; if (c.classMethods + n_value != 0) print_method_list64_t(c.classMethods + n_value, info, ""); outs() << " protocols "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, protocols), S, info, n_value, c.protocols); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.protocols != 0) outs() << " + " << format("0x%" PRIx64, c.protocols); } else outs() << format("0x%" PRIx64, c.protocols); outs() << "\n"; if (c.protocols + n_value != 0) print_protocol_list64_t(c.protocols + n_value, info); outs() << "instanceProperties "; sym_name = get_symbol_64(offset + offsetof(struct category64_t, instanceProperties), S, info, n_value, c.instanceProperties); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (c.instanceProperties != 0) outs() << " + " << format("0x%" PRIx64, c.instanceProperties); } else outs() << format("0x%" PRIx64, c.instanceProperties); outs() << "\n"; if (c.instanceProperties + n_value != 0) print_objc_property_list64(c.instanceProperties + n_value, info); } static void print_category32_t(uint32_t p, struct DisassembleInfo *info) { struct category32_t c; const char *r; uint32_t offset, left; SectionRef S, xS; const char *name; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&c, '\0', sizeof(struct category32_t)); if (left < sizeof(struct category32_t)) { memcpy(&c, r, left); outs() << " (category_t entends past the end of the section)\n"; } else memcpy(&c, r, sizeof(struct category32_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(c); outs() << " name " << format("0x%" PRIx32, c.name); name = get_symbol_32(offset + offsetof(struct category32_t, name), S, info, c.name); if (name) outs() << " " << name; outs() << "\n"; outs() << " cls " << format("0x%" PRIx32, c.cls) << "\n"; if (c.cls != 0) print_class32_t(c.cls, info); outs() << " instanceMethods " << format("0x%" PRIx32, c.instanceMethods) << "\n"; if (c.instanceMethods != 0) print_method_list32_t(c.instanceMethods, info, ""); outs() << " classMethods " << format("0x%" PRIx32, c.classMethods) << "\n"; if (c.classMethods != 0) print_method_list32_t(c.classMethods, info, ""); outs() << " protocols " << format("0x%" PRIx32, c.protocols) << "\n"; if (c.protocols != 0) print_protocol_list32_t(c.protocols, info); outs() << "instanceProperties " << format("0x%" PRIx32, c.instanceProperties) << "\n"; if (c.instanceProperties != 0) print_objc_property_list32(c.instanceProperties, info); } static void print_message_refs64(SectionRef S, struct DisassembleInfo *info) { uint32_t i, left, offset, xoffset; uint64_t p, n_value; struct message_ref64 mr; const char *name, *sym_name; const char *r; SectionRef xS; if (S == SectionRef()) return; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; offset = 0; for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) { p = S.getAddress() + i; r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&mr, '\0', sizeof(struct message_ref64)); if (left < sizeof(struct message_ref64)) { memcpy(&mr, r, left); outs() << " (message_ref entends past the end of the section)\n"; } else memcpy(&mr, r, sizeof(struct message_ref64)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(mr); outs() << " imp "; name = get_symbol_64(offset + offsetof(struct message_ref64, imp), S, info, n_value, mr.imp); if (n_value != 0) { outs() << format("0x%" PRIx64, n_value) << " "; if (mr.imp != 0) outs() << "+ " << format("0x%" PRIx64, mr.imp) << " "; } else outs() << format("0x%" PRIx64, mr.imp) << " "; if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " sel "; sym_name = get_symbol_64(offset + offsetof(struct message_ref64, sel), S, info, n_value, mr.sel); if (n_value != 0) { if (info->verbose && sym_name != nullptr) outs() << sym_name; else outs() << format("0x%" PRIx64, n_value); if (mr.sel != 0) outs() << " + " << format("0x%" PRIx64, mr.sel); } else outs() << format("0x%" PRIx64, mr.sel); name = get_pointer_64(mr.sel + n_value, xoffset, left, xS, info); if (name != nullptr) outs() << format(" %.*s", left, name); outs() << "\n"; offset += sizeof(struct message_ref64); } } static void print_message_refs32(SectionRef S, struct DisassembleInfo *info) { uint32_t i, left, offset, xoffset, p; struct message_ref32 mr; const char *name, *r; SectionRef xS; if (S == SectionRef()) return; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; offset = 0; for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) { p = S.getAddress() + i; r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&mr, '\0', sizeof(struct message_ref32)); if (left < sizeof(struct message_ref32)) { memcpy(&mr, r, left); outs() << " (message_ref entends past the end of the section)\n"; } else memcpy(&mr, r, sizeof(struct message_ref32)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(mr); outs() << " imp " << format("0x%" PRIx32, mr.imp); name = get_symbol_32(offset + offsetof(struct message_ref32, imp), S, info, mr.imp); if (name != nullptr) outs() << " " << name; outs() << "\n"; outs() << " sel " << format("0x%" PRIx32, mr.sel); name = get_pointer_32(mr.sel, xoffset, left, xS, info); if (name != nullptr) outs() << " " << name; outs() << "\n"; offset += sizeof(struct message_ref32); } } static void print_image_info64(SectionRef S, struct DisassembleInfo *info) { uint32_t left, offset, swift_version; uint64_t p; struct objc_image_info64 o; const char *r; if (S == SectionRef()) return; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; p = S.getAddress(); r = get_pointer_64(p, offset, left, S, info); if (r == nullptr) return; memset(&o, '\0', sizeof(struct objc_image_info64)); if (left < sizeof(struct objc_image_info64)) { memcpy(&o, r, left); outs() << " (objc_image_info entends past the end of the section)\n"; } else memcpy(&o, r, sizeof(struct objc_image_info64)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(o); outs() << " version " << o.version << "\n"; outs() << " flags " << format("0x%" PRIx32, o.flags); if (o.flags & OBJC_IMAGE_IS_REPLACEMENT) outs() << " OBJC_IMAGE_IS_REPLACEMENT"; if (o.flags & OBJC_IMAGE_SUPPORTS_GC) outs() << " OBJC_IMAGE_SUPPORTS_GC"; if (o.flags & OBJC_IMAGE_IS_SIMULATED) outs() << " OBJC_IMAGE_IS_SIMULATED"; if (o.flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES) outs() << " OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES"; swift_version = (o.flags >> 8) & 0xff; if (swift_version != 0) { if (swift_version == 1) outs() << " Swift 1.0"; else if (swift_version == 2) outs() << " Swift 1.1"; else if(swift_version == 3) outs() << " Swift 2.0"; else if(swift_version == 4) outs() << " Swift 3.0"; else if(swift_version == 5) outs() << " Swift 4.0"; else if(swift_version == 6) outs() << " Swift 4.1/Swift 4.2"; else if(swift_version == 7) outs() << " Swift 5 or later"; else outs() << " unknown future Swift version (" << swift_version << ")"; } outs() << "\n"; } static void print_image_info32(SectionRef S, struct DisassembleInfo *info) { uint32_t left, offset, swift_version, p; struct objc_image_info32 o; const char *r; if (S == SectionRef()) return; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; p = S.getAddress(); r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&o, '\0', sizeof(struct objc_image_info32)); if (left < sizeof(struct objc_image_info32)) { memcpy(&o, r, left); outs() << " (objc_image_info entends past the end of the section)\n"; } else memcpy(&o, r, sizeof(struct objc_image_info32)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(o); outs() << " version " << o.version << "\n"; outs() << " flags " << format("0x%" PRIx32, o.flags); if (o.flags & OBJC_IMAGE_IS_REPLACEMENT) outs() << " OBJC_IMAGE_IS_REPLACEMENT"; if (o.flags & OBJC_IMAGE_SUPPORTS_GC) outs() << " OBJC_IMAGE_SUPPORTS_GC"; swift_version = (o.flags >> 8) & 0xff; if (swift_version != 0) { if (swift_version == 1) outs() << " Swift 1.0"; else if (swift_version == 2) outs() << " Swift 1.1"; else if(swift_version == 3) outs() << " Swift 2.0"; else if(swift_version == 4) outs() << " Swift 3.0"; else if(swift_version == 5) outs() << " Swift 4.0"; else if(swift_version == 6) outs() << " Swift 4.1/Swift 4.2"; else if(swift_version == 7) outs() << " Swift 5 or later"; else outs() << " unknown future Swift version (" << swift_version << ")"; } outs() << "\n"; } static void print_image_info(SectionRef S, struct DisassembleInfo *info) { uint32_t left, offset, p; struct imageInfo_t o; const char *r; StringRef SectName; Expected SecNameOrErr = S.getName(); if (SecNameOrErr) SectName = *SecNameOrErr; else consumeError(SecNameOrErr.takeError()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; p = S.getAddress(); r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) return; memset(&o, '\0', sizeof(struct imageInfo_t)); if (left < sizeof(struct imageInfo_t)) { memcpy(&o, r, left); outs() << " (imageInfo entends past the end of the section)\n"; } else memcpy(&o, r, sizeof(struct imageInfo_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(o); outs() << " version " << o.version << "\n"; outs() << " flags " << format("0x%" PRIx32, o.flags); if (o.flags & 0x1) outs() << " F&C"; if (o.flags & 0x2) outs() << " GC"; if (o.flags & 0x4) outs() << " GC-only"; else outs() << " RR"; outs() << "\n"; } static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { SymbolAddressMap AddrMap; if (verbose) CreateSymbolAddressMap(O, &AddrMap); std::vector Sections; for (const SectionRef &Section : O->sections()) Sections.push_back(Section); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); SectionRef CL = get_section(O, "__OBJC2", "__class_list"); if (CL == SectionRef()) CL = get_section(O, "__DATA", "__objc_classlist"); if (CL == SectionRef()) CL = get_section(O, "__DATA_CONST", "__objc_classlist"); if (CL == SectionRef()) CL = get_section(O, "__DATA_DIRTY", "__objc_classlist"); info.S = CL; walk_pointer_list_64("class", CL, O, &info, print_class64_t); SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); if (CR == SectionRef()) CR = get_section(O, "__DATA", "__objc_classrefs"); if (CR == SectionRef()) CR = get_section(O, "__DATA_CONST", "__objc_classrefs"); if (CR == SectionRef()) CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs"); info.S = CR; walk_pointer_list_64("class refs", CR, O, &info, nullptr); SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); if (SR == SectionRef()) SR = get_section(O, "__DATA", "__objc_superrefs"); if (SR == SectionRef()) SR = get_section(O, "__DATA_CONST", "__objc_superrefs"); if (SR == SectionRef()) SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs"); info.S = SR; walk_pointer_list_64("super refs", SR, O, &info, nullptr); SectionRef CA = get_section(O, "__OBJC2", "__category_list"); if (CA == SectionRef()) CA = get_section(O, "__DATA", "__objc_catlist"); if (CA == SectionRef()) CA = get_section(O, "__DATA_CONST", "__objc_catlist"); if (CA == SectionRef()) CA = get_section(O, "__DATA_DIRTY", "__objc_catlist"); info.S = CA; walk_pointer_list_64("category", CA, O, &info, print_category64_t); SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); if (PL == SectionRef()) PL = get_section(O, "__DATA", "__objc_protolist"); if (PL == SectionRef()) PL = get_section(O, "__DATA_CONST", "__objc_protolist"); if (PL == SectionRef()) PL = get_section(O, "__DATA_DIRTY", "__objc_protolist"); info.S = PL; walk_pointer_list_64("protocol", PL, O, &info, nullptr); SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); if (MR == SectionRef()) MR = get_section(O, "__DATA", "__objc_msgrefs"); if (MR == SectionRef()) MR = get_section(O, "__DATA_CONST", "__objc_msgrefs"); if (MR == SectionRef()) MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs"); info.S = MR; print_message_refs64(MR, &info); SectionRef II = get_section(O, "__OBJC2", "__image_info"); if (II == SectionRef()) II = get_section(O, "__DATA", "__objc_imageinfo"); if (II == SectionRef()) II = get_section(O, "__DATA_CONST", "__objc_imageinfo"); if (II == SectionRef()) II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo"); info.S = II; print_image_info64(II, &info); } static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) { SymbolAddressMap AddrMap; if (verbose) CreateSymbolAddressMap(O, &AddrMap); std::vector Sections; for (const SectionRef &Section : O->sections()) Sections.push_back(Section); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); SectionRef CL = get_section(O, "__OBJC2", "__class_list"); if (CL == SectionRef()) CL = get_section(O, "__DATA", "__objc_classlist"); if (CL == SectionRef()) CL = get_section(O, "__DATA_CONST", "__objc_classlist"); if (CL == SectionRef()) CL = get_section(O, "__DATA_DIRTY", "__objc_classlist"); info.S = CL; walk_pointer_list_32("class", CL, O, &info, print_class32_t); SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); if (CR == SectionRef()) CR = get_section(O, "__DATA", "__objc_classrefs"); if (CR == SectionRef()) CR = get_section(O, "__DATA_CONST", "__objc_classrefs"); if (CR == SectionRef()) CR = get_section(O, "__DATA_DIRTY", "__objc_classrefs"); info.S = CR; walk_pointer_list_32("class refs", CR, O, &info, nullptr); SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); if (SR == SectionRef()) SR = get_section(O, "__DATA", "__objc_superrefs"); if (SR == SectionRef()) SR = get_section(O, "__DATA_CONST", "__objc_superrefs"); if (SR == SectionRef()) SR = get_section(O, "__DATA_DIRTY", "__objc_superrefs"); info.S = SR; walk_pointer_list_32("super refs", SR, O, &info, nullptr); SectionRef CA = get_section(O, "__OBJC2", "__category_list"); if (CA == SectionRef()) CA = get_section(O, "__DATA", "__objc_catlist"); if (CA == SectionRef()) CA = get_section(O, "__DATA_CONST", "__objc_catlist"); if (CA == SectionRef()) CA = get_section(O, "__DATA_DIRTY", "__objc_catlist"); info.S = CA; walk_pointer_list_32("category", CA, O, &info, print_category32_t); SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); if (PL == SectionRef()) PL = get_section(O, "__DATA", "__objc_protolist"); if (PL == SectionRef()) PL = get_section(O, "__DATA_CONST", "__objc_protolist"); if (PL == SectionRef()) PL = get_section(O, "__DATA_DIRTY", "__objc_protolist"); info.S = PL; walk_pointer_list_32("protocol", PL, O, &info, nullptr); SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); if (MR == SectionRef()) MR = get_section(O, "__DATA", "__objc_msgrefs"); if (MR == SectionRef()) MR = get_section(O, "__DATA_CONST", "__objc_msgrefs"); if (MR == SectionRef()) MR = get_section(O, "__DATA_DIRTY", "__objc_msgrefs"); info.S = MR; print_message_refs32(MR, &info); SectionRef II = get_section(O, "__OBJC2", "__image_info"); if (II == SectionRef()) II = get_section(O, "__DATA", "__objc_imageinfo"); if (II == SectionRef()) II = get_section(O, "__DATA_CONST", "__objc_imageinfo"); if (II == SectionRef()) II = get_section(O, "__DATA_DIRTY", "__objc_imageinfo"); info.S = II; print_image_info32(II, &info); } static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) { uint32_t i, j, p, offset, xoffset, left, defs_left, def; const char *r, *name, *defs; struct objc_module_t module; SectionRef S, xS; struct objc_symtab_t symtab; struct objc_class_t objc_class; struct objc_category_t objc_category; outs() << "Objective-C segment\n"; S = get_section(O, "__OBJC", "__module_info"); if (S == SectionRef()) return false; SymbolAddressMap AddrMap; if (verbose) CreateSymbolAddressMap(O, &AddrMap); std::vector Sections; for (const SectionRef &Section : O->sections()) Sections.push_back(Section); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); for (i = 0; i < S.getSize(); i += sizeof(struct objc_module_t)) { p = S.getAddress() + i; r = get_pointer_32(p, offset, left, S, &info, true); if (r == nullptr) return true; memset(&module, '\0', sizeof(struct objc_module_t)); if (left < sizeof(struct objc_module_t)) { memcpy(&module, r, left); outs() << " (module extends past end of __module_info section)\n"; } else memcpy(&module, r, sizeof(struct objc_module_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(module); outs() << "Module " << format("0x%" PRIx32, p) << "\n"; outs() << " version " << module.version << "\n"; outs() << " size " << module.size << "\n"; outs() << " name "; name = get_pointer_32(module.name, xoffset, left, xS, &info, true); if (name != nullptr) outs() << format("%.*s", left, name); else outs() << format("0x%08" PRIx32, module.name) << "(not in an __OBJC section)"; outs() << "\n"; r = get_pointer_32(module.symtab, xoffset, left, xS, &info, true); if (module.symtab == 0 || r == nullptr) { outs() << " symtab " << format("0x%08" PRIx32, module.symtab) << " (not in an __OBJC section)\n"; continue; } outs() << " symtab " << format("0x%08" PRIx32, module.symtab) << "\n"; memset(&symtab, '\0', sizeof(struct objc_symtab_t)); defs_left = 0; defs = nullptr; if (left < sizeof(struct objc_symtab_t)) { memcpy(&symtab, r, left); outs() << "\tsymtab extends past end of an __OBJC section)\n"; } else { memcpy(&symtab, r, sizeof(struct objc_symtab_t)); if (left > sizeof(struct objc_symtab_t)) { defs_left = left - sizeof(struct objc_symtab_t); defs = r + sizeof(struct objc_symtab_t); } } if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(symtab); outs() << "\tsel_ref_cnt " << symtab.sel_ref_cnt << "\n"; r = get_pointer_32(symtab.refs, xoffset, left, xS, &info, true); outs() << "\trefs " << format("0x%08" PRIx32, symtab.refs); if (r == nullptr) outs() << " (not in an __OBJC section)"; outs() << "\n"; outs() << "\tcls_def_cnt " << symtab.cls_def_cnt << "\n"; outs() << "\tcat_def_cnt " << symtab.cat_def_cnt << "\n"; if (symtab.cls_def_cnt > 0) outs() << "\tClass Definitions\n"; for (j = 0; j < symtab.cls_def_cnt; j++) { if ((j + 1) * sizeof(uint32_t) > defs_left) { outs() << "\t(remaining class defs entries entends past the end of the " << "section)\n"; break; } memcpy(&def, defs + j * sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(def); r = get_pointer_32(def, xoffset, left, xS, &info, true); outs() << "\tdefs[" << j << "] " << format("0x%08" PRIx32, def); if (r != nullptr) { if (left > sizeof(struct objc_class_t)) { outs() << "\n"; memcpy(&objc_class, r, sizeof(struct objc_class_t)); } else { outs() << " (entends past the end of the section)\n"; memset(&objc_class, '\0', sizeof(struct objc_class_t)); memcpy(&objc_class, r, left); } if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(objc_class); print_objc_class_t(&objc_class, &info); } else { outs() << "(not in an __OBJC section)\n"; } if (CLS_GETINFO(&objc_class, CLS_CLASS)) { outs() << "\tMeta Class"; r = get_pointer_32(objc_class.isa, xoffset, left, xS, &info, true); if (r != nullptr) { if (left > sizeof(struct objc_class_t)) { outs() << "\n"; memcpy(&objc_class, r, sizeof(struct objc_class_t)); } else { outs() << " (entends past the end of the section)\n"; memset(&objc_class, '\0', sizeof(struct objc_class_t)); memcpy(&objc_class, r, left); } if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(objc_class); print_objc_class_t(&objc_class, &info); } else { outs() << "(not in an __OBJC section)\n"; } } } if (symtab.cat_def_cnt > 0) outs() << "\tCategory Definitions\n"; for (j = 0; j < symtab.cat_def_cnt; j++) { if ((j + symtab.cls_def_cnt + 1) * sizeof(uint32_t) > defs_left) { outs() << "\t(remaining category defs entries entends past the end of " << "the section)\n"; break; } memcpy(&def, defs + (j + symtab.cls_def_cnt) * sizeof(uint32_t), sizeof(uint32_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) sys::swapByteOrder(def); r = get_pointer_32(def, xoffset, left, xS, &info, true); outs() << "\tdefs[" << j + symtab.cls_def_cnt << "] " << format("0x%08" PRIx32, def); if (r != nullptr) { if (left > sizeof(struct objc_category_t)) { outs() << "\n"; memcpy(&objc_category, r, sizeof(struct objc_category_t)); } else { outs() << " (entends past the end of the section)\n"; memset(&objc_category, '\0', sizeof(struct objc_category_t)); memcpy(&objc_category, r, left); } if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(objc_category); print_objc_objc_category_t(&objc_category, &info); } else { outs() << "(not in an __OBJC section)\n"; } } } const SectionRef II = get_section(O, "__OBJC", "__image_info"); if (II != SectionRef()) print_image_info(II, &info); return true; } static void DumpProtocolSection(MachOObjectFile *O, const char *sect, uint32_t size, uint32_t addr) { SymbolAddressMap AddrMap; CreateSymbolAddressMap(O, &AddrMap); std::vector Sections; for (const SectionRef &Section : O->sections()) Sections.push_back(Section); struct DisassembleInfo info(O, &AddrMap, &Sections, true); const char *p; struct objc_protocol_t protocol; uint32_t left, paddr; for (p = sect; p < sect + size; p += sizeof(struct objc_protocol_t)) { memset(&protocol, '\0', sizeof(struct objc_protocol_t)); left = size - (p - sect); if (left < sizeof(struct objc_protocol_t)) { outs() << "Protocol extends past end of __protocol section\n"; memcpy(&protocol, p, left); } else memcpy(&protocol, p, sizeof(struct objc_protocol_t)); if (O->isLittleEndian() != sys::IsLittleEndianHost) swapStruct(protocol); paddr = addr + (p - sect); outs() << "Protocol " << format("0x%" PRIx32, paddr); if (print_protocol(paddr, 0, &info)) outs() << "(not in an __OBJC section)\n"; } } #ifdef HAVE_LIBXAR static inline void swapStruct(struct xar_header &xar) { sys::swapByteOrder(xar.magic); sys::swapByteOrder(xar.size); sys::swapByteOrder(xar.version); sys::swapByteOrder(xar.toc_length_compressed); sys::swapByteOrder(xar.toc_length_uncompressed); sys::swapByteOrder(xar.cksum_alg); } static void PrintModeVerbose(uint32_t mode) { switch(mode & S_IFMT){ case S_IFDIR: outs() << "d"; break; case S_IFCHR: outs() << "c"; break; case S_IFBLK: outs() << "b"; break; case S_IFREG: outs() << "-"; break; case S_IFLNK: outs() << "l"; break; case S_IFSOCK: outs() << "s"; break; default: outs() << "?"; break; } /* owner permissions */ if(mode & S_IREAD) outs() << "r"; else outs() << "-"; if(mode & S_IWRITE) outs() << "w"; else outs() << "-"; if(mode & S_ISUID) outs() << "s"; else if(mode & S_IEXEC) outs() << "x"; else outs() << "-"; /* group permissions */ if(mode & (S_IREAD >> 3)) outs() << "r"; else outs() << "-"; if(mode & (S_IWRITE >> 3)) outs() << "w"; else outs() << "-"; if(mode & S_ISGID) outs() << "s"; else if(mode & (S_IEXEC >> 3)) outs() << "x"; else outs() << "-"; /* other permissions */ if(mode & (S_IREAD >> 6)) outs() << "r"; else outs() << "-"; if(mode & (S_IWRITE >> 6)) outs() << "w"; else outs() << "-"; if(mode & S_ISVTX) outs() << "t"; else if(mode & (S_IEXEC >> 6)) outs() << "x"; else outs() << "-"; } static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) { xar_file_t xf; const char *key, *type, *mode, *user, *group, *size, *mtime, *name, *m; char *endp; uint32_t mode_value; ScopedXarIter xi; if (!xi) { WithColor::error(errs(), "llvm-objdump") << "can't obtain an xar iterator for xar archive " << XarFilename << "\n"; return; } // Go through the xar's files. for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) { ScopedXarIter xp; if(!xp){ WithColor::error(errs(), "llvm-objdump") << "can't obtain an xar iterator for xar archive " << XarFilename << "\n"; return; } type = nullptr; mode = nullptr; user = nullptr; group = nullptr; size = nullptr; mtime = nullptr; name = nullptr; for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){ const char *val = nullptr; xar_prop_get(xf, key, &val); #if 0 // Useful for debugging. outs() << "key: " << key << " value: " << val << "\n"; #endif if(strcmp(key, "type") == 0) type = val; if(strcmp(key, "mode") == 0) mode = val; if(strcmp(key, "user") == 0) user = val; if(strcmp(key, "group") == 0) group = val; if(strcmp(key, "data/size") == 0) size = val; if(strcmp(key, "mtime") == 0) mtime = val; if(strcmp(key, "name") == 0) name = val; } if(mode != nullptr){ mode_value = strtoul(mode, &endp, 8); if(*endp != '\0') outs() << "(mode: \"" << mode << "\" contains non-octal chars) "; if(strcmp(type, "file") == 0) mode_value |= S_IFREG; PrintModeVerbose(mode_value); outs() << " "; } if(user != nullptr) outs() << format("%10s/", user); if(group != nullptr) outs() << format("%-10s ", group); if(size != nullptr) outs() << format("%7s ", size); if(mtime != nullptr){ for(m = mtime; *m != 'T' && *m != '\0'; m++) outs() << *m; if(*m == 'T') m++; outs() << " "; for( ; *m != 'Z' && *m != '\0'; m++) outs() << *m; outs() << " "; } if(name != nullptr) outs() << name; outs() << "\n"; } } static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, uint32_t size, bool verbose, bool PrintXarHeader, bool PrintXarFileHeaders, std::string XarMemberName) { if(size < sizeof(struct xar_header)) { outs() << "size of (__LLVM,__bundle) section too small (smaller than size " "of struct xar_header)\n"; return; } struct xar_header XarHeader; memcpy(&XarHeader, sect, sizeof(struct xar_header)); if (sys::IsLittleEndianHost) swapStruct(XarHeader); if (PrintXarHeader) { if (!XarMemberName.empty()) outs() << "In xar member " << XarMemberName << ": "; else outs() << "For (__LLVM,__bundle) section: "; outs() << "xar header\n"; if (XarHeader.magic == XAR_HEADER_MAGIC) outs() << " magic XAR_HEADER_MAGIC\n"; else outs() << " magic " << format_hex(XarHeader.magic, 10, true) << " (not XAR_HEADER_MAGIC)\n"; outs() << " size " << XarHeader.size << "\n"; outs() << " version " << XarHeader.version << "\n"; outs() << " toc_length_compressed " << XarHeader.toc_length_compressed << "\n"; outs() << "toc_length_uncompressed " << XarHeader.toc_length_uncompressed << "\n"; outs() << " cksum_alg "; switch (XarHeader.cksum_alg) { case XAR_CKSUM_NONE: outs() << "XAR_CKSUM_NONE\n"; break; case XAR_CKSUM_SHA1: outs() << "XAR_CKSUM_SHA1\n"; break; case XAR_CKSUM_MD5: outs() << "XAR_CKSUM_MD5\n"; break; #ifdef XAR_CKSUM_SHA256 case XAR_CKSUM_SHA256: outs() << "XAR_CKSUM_SHA256\n"; break; #endif #ifdef XAR_CKSUM_SHA512 case XAR_CKSUM_SHA512: outs() << "XAR_CKSUM_SHA512\n"; break; #endif default: outs() << XarHeader.cksum_alg << "\n"; } } SmallString<128> XarFilename; int FD; std::error_code XarEC = sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename); if (XarEC) { WithColor::error(errs(), "llvm-objdump") << XarEC.message() << "\n"; return; } ToolOutputFile XarFile(XarFilename, FD); raw_fd_ostream &XarOut = XarFile.os(); StringRef XarContents(sect, size); XarOut << XarContents; XarOut.close(); if (XarOut.has_error()) return; ScopedXarFile xar(XarFilename.c_str(), READ); if (!xar) { WithColor::error(errs(), "llvm-objdump") << "can't create temporary xar archive " << XarFilename << "\n"; return; } SmallString<128> TocFilename; std::error_code TocEC = sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename); if (TocEC) { WithColor::error(errs(), "llvm-objdump") << TocEC.message() << "\n"; return; } xar_serialize(xar, TocFilename.c_str()); if (PrintXarFileHeaders) { if (!XarMemberName.empty()) outs() << "In xar member " << XarMemberName << ": "; else outs() << "For (__LLVM,__bundle) section: "; outs() << "xar archive files:\n"; PrintXarFilesSummary(XarFilename.c_str(), xar); } ErrorOr> FileOrErr = MemoryBuffer::getFileOrSTDIN(TocFilename.c_str()); if (std::error_code EC = FileOrErr.getError()) { WithColor::error(errs(), "llvm-objdump") << EC.message() << "\n"; return; } std::unique_ptr &Buffer = FileOrErr.get(); if (!XarMemberName.empty()) outs() << "In xar member " << XarMemberName << ": "; else outs() << "For (__LLVM,__bundle) section: "; outs() << "xar table of contents:\n"; outs() << Buffer->getBuffer() << "\n"; // TODO: Go through the xar's files. ScopedXarIter xi; if(!xi){ WithColor::error(errs(), "llvm-objdump") << "can't obtain an xar iterator for xar archive " << XarFilename.c_str() << "\n"; return; } for(xar_file_t xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)){ const char *key; const char *member_name, *member_type, *member_size_string; size_t member_size; ScopedXarIter xp; if(!xp){ WithColor::error(errs(), "llvm-objdump") << "can't obtain an xar iterator for xar archive " << XarFilename.c_str() << "\n"; return; } member_name = NULL; member_type = NULL; member_size_string = NULL; for(key = xar_prop_first(xf, xp); key; key = xar_prop_next(xp)){ const char *val = nullptr; xar_prop_get(xf, key, &val); #if 0 // Useful for debugging. outs() << "key: " << key << " value: " << val << "\n"; #endif if (strcmp(key, "name") == 0) member_name = val; if (strcmp(key, "type") == 0) member_type = val; if (strcmp(key, "data/size") == 0) member_size_string = val; } /* * If we find a file with a name, date/size and type properties * and with the type being "file" see if that is a xar file. */ if (member_name != NULL && member_type != NULL && strcmp(member_type, "file") == 0 && member_size_string != NULL){ // Extract the file into a buffer. char *endptr; member_size = strtoul(member_size_string, &endptr, 10); if (*endptr == '\0' && member_size != 0) { char *buffer; if (xar_extract_tobuffersz(xar, xf, &buffer, &member_size) == 0) { #if 0 // Useful for debugging. outs() << "xar member: " << member_name << " extracted\n"; #endif // Set the XarMemberName we want to see printed in the header. std::string OldXarMemberName; // If XarMemberName is already set this is nested. So // save the old name and create the nested name. if (!XarMemberName.empty()) { OldXarMemberName = XarMemberName; XarMemberName = (Twine("[") + XarMemberName + "]" + member_name).str(); } else { OldXarMemberName = ""; XarMemberName = member_name; } // See if this is could be a xar file (nested). if (member_size >= sizeof(struct xar_header)) { #if 0 // Useful for debugging. outs() << "could be a xar file: " << member_name << "\n"; #endif memcpy((char *)&XarHeader, buffer, sizeof(struct xar_header)); if (sys::IsLittleEndianHost) swapStruct(XarHeader); if (XarHeader.magic == XAR_HEADER_MAGIC) DumpBitcodeSection(O, buffer, member_size, verbose, PrintXarHeader, PrintXarFileHeaders, XarMemberName); } XarMemberName = OldXarMemberName; delete buffer; } } } } } #endif // defined(HAVE_LIBXAR) static void printObjcMetaData(MachOObjectFile *O, bool verbose) { if (O->is64Bit()) printObjc2_64bit_MetaData(O, verbose); else { MachO::mach_header H; H = O->getHeader(); if (H.cputype == MachO::CPU_TYPE_ARM) printObjc2_32bit_MetaData(O, verbose); else { // This is the 32-bit non-arm cputype case. Which is normally // the first Objective-C ABI. But it may be the case of a // binary for the iOS simulator which is the second Objective-C // ABI. In that case printObjc1_32bit_MetaData() will determine that // and return false. if (!printObjc1_32bit_MetaData(O, verbose)) printObjc2_32bit_MetaData(O, verbose); } } } // GuessLiteralPointer returns a string which for the item in the Mach-O file // for the address passed in as ReferenceValue for printing as a comment with // the instruction and also returns the corresponding type of that item // indirectly through ReferenceType. // // If ReferenceValue is an address of literal cstring then a pointer to the // cstring is returned and ReferenceType is set to // LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr . // // If ReferenceValue is an address of an Objective-C CFString, Selector ref or // Class ref that name is returned and the ReferenceType is set accordingly. // // Lastly, literals which are Symbol address in a literal pool are looked for // and if found the symbol name is returned and ReferenceType is set to // LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr . // // If there is no item in the Mach-O file for the address passed in as // ReferenceValue nullptr is returned and ReferenceType is unchanged. static const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, uint64_t *ReferenceType, struct DisassembleInfo *info) { // First see if there is an external relocation entry at the ReferencePC. if (info->O->getHeader().filetype == MachO::MH_OBJECT) { uint64_t sect_addr = info->S.getAddress(); uint64_t sect_offset = ReferencePC - sect_addr; bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; for (const RelocationRef &Reloc : info->S.relocations()) { uint64_t RelocOffset = Reloc.getOffset(); if (RelocOffset == sect_offset) { Rel = Reloc.getRawDataRefImpl(); RE = info->O->getRelocation(Rel); if (info->O->isRelocationScattered(RE)) continue; isExtern = info->O->getPlainRelocationExternal(RE); if (isExtern) { symbol_iterator RelocSym = Reloc.getSymbol(); Symbol = *RelocSym; } reloc_found = true; break; } } // If there is an external relocation entry for a symbol in a section // then used that symbol's value for the value of the reference. if (reloc_found && isExtern) { if (info->O->getAnyRelocationPCRel(RE)) { unsigned Type = info->O->getAnyRelocationType(RE); if (Type == MachO::X86_64_RELOC_SIGNED) { ReferenceValue = cantFail(Symbol.getValue()); } } } } // Look for literals such as Objective-C CFStrings refs, Selector refs, // Message refs and Class refs. bool classref, selref, msgref, cfstring; uint64_t pointer_value = GuessPointerPointer(ReferenceValue, info, classref, selref, msgref, cfstring); if (classref && pointer_value == 0) { // Note the ReferenceValue is a pointer into the __objc_classrefs section. // And the pointer_value in that section is typically zero as it will be // set by dyld as part of the "bind information". const char *name = get_dyld_bind_info_symbolname(ReferenceValue, info); if (name != nullptr) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref; const char *class_name = strrchr(name, '$'); if (class_name != nullptr && class_name[1] == '_' && class_name[2] != '\0') { info->class_name = class_name + 2; return name; } } } if (classref) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref; const char *name = get_objc2_64bit_class_name(pointer_value, ReferenceValue, info); if (name != nullptr) info->class_name = name; else name = "bad class ref"; return name; } if (cfstring) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref; const char *name = get_objc2_64bit_cfstring_name(ReferenceValue, info); return name; } if (selref && pointer_value == 0) pointer_value = get_objc2_64bit_selref(ReferenceValue, info); if (pointer_value != 0) ReferenceValue = pointer_value; const char *name = GuessCstringPointer(ReferenceValue, info); if (name) { if (pointer_value != 0 && selref) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref; info->selector_name = name; } else if (pointer_value != 0 && msgref) { info->class_name = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref; info->selector_name = name; } else *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr; return name; } // Lastly look for an indirect symbol with this ReferenceValue which is in // a literal pool. If found return that symbol name. name = GuessIndirectSymbol(ReferenceValue, info); if (name) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr; return name; } return nullptr; } // SymbolizerSymbolLookUp is the symbol lookup function passed when creating // the Symbolizer. It looks up the ReferenceValue using the info passed via the // pointer to the struct DisassembleInfo that was passed when MCSymbolizer // is created and returns the symbol name that matches the ReferenceValue or // nullptr if none. The ReferenceType is passed in for the IN type of // reference the instruction is making from the values in defined in the header // "llvm-c/Disassembler.h". On return the ReferenceType can set to a specific // Out type and the ReferenceName will also be set which is added as a comment // to the disassembled instruction. // // If the symbol name is a C++ mangled name then the demangled name is // returned through ReferenceName and ReferenceType is set to // LLVMDisassembler_ReferenceType_DeMangled_Name . // // When this is called to get a symbol name for a branch target then the // ReferenceType will be LLVMDisassembler_ReferenceType_In_Branch and then // SymbolValue will be looked for in the indirect symbol table to determine if // it is an address for a symbol stub. If so then the symbol name for that // stub is returned indirectly through ReferenceName and then ReferenceType is // set to LLVMDisassembler_ReferenceType_Out_SymbolStub. // // When this is called with an value loaded via a PC relative load then // ReferenceType will be LLVMDisassembler_ReferenceType_In_PCrel_Load then the // SymbolValue is checked to be an address of literal pointer, symbol pointer, // or an Objective-C meta data reference. If so the output ReferenceType is // set to correspond to that as well as setting the ReferenceName. static const char *SymbolizerSymbolLookUp(void *DisInfo, uint64_t ReferenceValue, uint64_t *ReferenceType, uint64_t ReferencePC, const char **ReferenceName) { struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; // If no verbose symbolic information is wanted then just return nullptr. if (!info->verbose) { *ReferenceName = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; return nullptr; } const char *SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap); if (*ReferenceType == LLVMDisassembler_ReferenceType_In_Branch) { *ReferenceName = GuessIndirectSymbol(ReferenceValue, info); if (*ReferenceName != nullptr) { method_reference(info, ReferenceType, ReferenceName); if (*ReferenceType != LLVMDisassembler_ReferenceType_Out_Objc_Message) *ReferenceType = LLVMDisassembler_ReferenceType_Out_SymbolStub; } else if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) { if (info->demangled_name != nullptr) free(info->demangled_name); int status; info->demangled_name = itaniumDemangle(SymbolName + 1, nullptr, nullptr, &status); if (info->demangled_name != nullptr) { *ReferenceName = info->demangled_name; *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name; } else *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; } else *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; } else if (*ReferenceType == LLVMDisassembler_ReferenceType_In_PCrel_Load) { *ReferenceName = GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); if (*ReferenceName) method_reference(info, ReferenceType, ReferenceName); else *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; // If this is arm64 and the reference is an adrp instruction save the // instruction, passed in ReferenceValue and the address of the instruction // for use later if we see and add immediate instruction. } else if (info->O->getArch() == Triple::aarch64 && *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADRP) { info->adrp_inst = ReferenceValue; info->adrp_addr = ReferencePC; SymbolName = nullptr; *ReferenceName = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; // If this is arm64 and reference is an add immediate instruction and we // have // seen an adrp instruction just before it and the adrp's Xd register // matches // this add's Xn register reconstruct the value being referenced and look to // see if it is a literal pointer. Note the add immediate instruction is // passed in ReferenceValue. } else if (info->O->getArch() == Triple::aarch64 && *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADDXri && ReferencePC - 4 == info->adrp_addr && (info->adrp_inst & 0x9f000000) == 0x90000000 && (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) { uint32_t addxri_inst; uint64_t adrp_imm, addxri_imm; adrp_imm = ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3); if (info->adrp_inst & 0x0200000) adrp_imm |= 0xfffffffffc000000LL; addxri_inst = ReferenceValue; addxri_imm = (addxri_inst >> 10) & 0xfff; if (((addxri_inst >> 22) & 0x3) == 1) addxri_imm <<= 12; ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) + (adrp_imm << 12) + addxri_imm; *ReferenceName = GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); if (*ReferenceName == nullptr) *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; // If this is arm64 and the reference is a load register instruction and we // have seen an adrp instruction just before it and the adrp's Xd register // matches this add's Xn register reconstruct the value being referenced and // look to see if it is a literal pointer. Note the load register // instruction is passed in ReferenceValue. } else if (info->O->getArch() == Triple::aarch64 && *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXui && ReferencePC - 4 == info->adrp_addr && (info->adrp_inst & 0x9f000000) == 0x90000000 && (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) { uint32_t ldrxui_inst; uint64_t adrp_imm, ldrxui_imm; adrp_imm = ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3); if (info->adrp_inst & 0x0200000) adrp_imm |= 0xfffffffffc000000LL; ldrxui_inst = ReferenceValue; ldrxui_imm = (ldrxui_inst >> 10) & 0xfff; ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) + (adrp_imm << 12) + (ldrxui_imm << 3); *ReferenceName = GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); if (*ReferenceName == nullptr) *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; } // If this arm64 and is an load register (PC-relative) instruction the // ReferenceValue is the PC plus the immediate value. else if (info->O->getArch() == Triple::aarch64 && (*ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXl || *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADR)) { *ReferenceName = GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); if (*ReferenceName == nullptr) *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; } else if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) { if (info->demangled_name != nullptr) free(info->demangled_name); int status; info->demangled_name = itaniumDemangle(SymbolName + 1, nullptr, nullptr, &status); if (info->demangled_name != nullptr) { *ReferenceName = info->demangled_name; *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name; } } else { *ReferenceName = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; } return SymbolName; } /// Emits the comments that are stored in the CommentStream. /// Each comment in the CommentStream must end with a newline. static void emitComments(raw_svector_ostream &CommentStream, SmallString<128> &CommentsToEmit, formatted_raw_ostream &FormattedOS, const MCAsmInfo &MAI) { // Flush the stream before taking its content. StringRef Comments = CommentsToEmit.str(); // Get the default information for printing a comment. StringRef CommentBegin = MAI.getCommentString(); unsigned CommentColumn = MAI.getCommentColumn(); bool IsFirst = true; while (!Comments.empty()) { if (!IsFirst) FormattedOS << '\n'; // Emit a line of comments. FormattedOS.PadToColumn(CommentColumn); size_t Position = Comments.find('\n'); FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position); // Move after the newline character. Comments = Comments.substr(Position + 1); IsFirst = false; } FormattedOS.flush(); // Tell the comment stream that the vector changed underneath it. CommentsToEmit.clear(); } static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef DisSegName, StringRef DisSectName) { const char *McpuDefault = nullptr; const Target *ThumbTarget = nullptr; const Target *TheTarget = GetTarget(MachOOF, &McpuDefault, &ThumbTarget); if (!TheTarget) { // GetTarget prints out stuff. return; } std::string MachOMCPU; if (MCPU.empty() && McpuDefault) MachOMCPU = McpuDefault; else MachOMCPU = MCPU; #define CHECK_TARGET_INFO_CREATION(NAME) \ do { \ if (!NAME) { \ WithColor::error(errs(), "llvm-objdump") \ << "couldn't initialize disassembler for target " << TripleName \ << '\n'; \ return; \ } \ } while (false) #define CHECK_THUMB_TARGET_INFO_CREATION(NAME) \ do { \ if (!NAME) { \ WithColor::error(errs(), "llvm-objdump") \ << "couldn't initialize disassembler for target " << ThumbTripleName \ << '\n'; \ return; \ } \ } while (false) std::unique_ptr InstrInfo(TheTarget->createMCInstrInfo()); CHECK_TARGET_INFO_CREATION(InstrInfo); std::unique_ptr ThumbInstrInfo; if (ThumbTarget) { ThumbInstrInfo.reset(ThumbTarget->createMCInstrInfo()); CHECK_THUMB_TARGET_INFO_CREATION(ThumbInstrInfo); } // Package up features to be passed to target/subtarget std::string FeaturesStr; if (!MAttrs.empty()) { SubtargetFeatures Features; for (unsigned i = 0; i != MAttrs.size(); ++i) Features.AddFeature(MAttrs[i]); FeaturesStr = Features.getString(); } MCTargetOptions MCOptions; // Set up disassembler. std::unique_ptr MRI( TheTarget->createMCRegInfo(TripleName)); CHECK_TARGET_INFO_CREATION(MRI); std::unique_ptr AsmInfo( TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); CHECK_TARGET_INFO_CREATION(AsmInfo); std::unique_ptr STI( TheTarget->createMCSubtargetInfo(TripleName, MachOMCPU, FeaturesStr)); CHECK_TARGET_INFO_CREATION(STI); MCContext Ctx(AsmInfo.get(), MRI.get(), nullptr); std::unique_ptr DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); CHECK_TARGET_INFO_CREATION(DisAsm); std::unique_ptr Symbolizer; struct DisassembleInfo SymbolizerInfo(nullptr, nullptr, nullptr, false); std::unique_ptr RelInfo( TheTarget->createMCRelocationInfo(TripleName, Ctx)); if (RelInfo) { Symbolizer.reset(TheTarget->createMCSymbolizer( TripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, &SymbolizerInfo, &Ctx, std::move(RelInfo))); DisAsm->setSymbolizer(std::move(Symbolizer)); } int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); std::unique_ptr IP(TheTarget->createMCInstPrinter( Triple(TripleName), AsmPrinterVariant, *AsmInfo, *InstrInfo, *MRI)); CHECK_TARGET_INFO_CREATION(IP); // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(PrintImmHex); // Comment stream and backing vector. SmallString<128> CommentsToEmit; raw_svector_ostream CommentStream(CommentsToEmit); // FIXME: Setting the CommentStream in the InstPrinter is problematic in that // if it is done then arm64 comments for string literals don't get printed // and some constant get printed instead and not setting it causes intel // (32-bit and 64-bit) comments printed with different spacing before the // comment causing different diffs with the 'C' disassembler library API. // IP->setCommentStream(CommentStream); // Set up separate thumb disassembler if needed. std::unique_ptr ThumbMRI; std::unique_ptr ThumbAsmInfo; std::unique_ptr ThumbSTI; std::unique_ptr ThumbDisAsm; std::unique_ptr ThumbIP; std::unique_ptr ThumbCtx; std::unique_ptr ThumbSymbolizer; struct DisassembleInfo ThumbSymbolizerInfo(nullptr, nullptr, nullptr, false); std::unique_ptr ThumbRelInfo; if (ThumbTarget) { ThumbMRI.reset(ThumbTarget->createMCRegInfo(ThumbTripleName)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbMRI); ThumbAsmInfo.reset( ThumbTarget->createMCAsmInfo(*ThumbMRI, ThumbTripleName, MCOptions)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbAsmInfo); ThumbSTI.reset( ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MachOMCPU, FeaturesStr)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbSTI); ThumbCtx.reset(new MCContext(ThumbAsmInfo.get(), ThumbMRI.get(), nullptr)); ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbDisAsm); MCContext *PtrThumbCtx = ThumbCtx.get(); ThumbRelInfo.reset( ThumbTarget->createMCRelocationInfo(ThumbTripleName, *PtrThumbCtx)); if (ThumbRelInfo) { ThumbSymbolizer.reset(ThumbTarget->createMCSymbolizer( ThumbTripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, &ThumbSymbolizerInfo, PtrThumbCtx, std::move(ThumbRelInfo))); ThumbDisAsm->setSymbolizer(std::move(ThumbSymbolizer)); } int ThumbAsmPrinterVariant = ThumbAsmInfo->getAssemblerDialect(); ThumbIP.reset(ThumbTarget->createMCInstPrinter( Triple(ThumbTripleName), ThumbAsmPrinterVariant, *ThumbAsmInfo, *ThumbInstrInfo, *ThumbMRI)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbIP); // Set the display preference for hex vs. decimal immediates. ThumbIP->setPrintImmHex(PrintImmHex); } #undef CHECK_TARGET_INFO_CREATION #undef CHECK_THUMB_TARGET_INFO_CREATION MachO::mach_header Header = MachOOF->getHeader(); // FIXME: Using the -cfg command line option, this code used to be able to // annotate relocations with the referenced symbol's name, and if this was // inside a __[cf]string section, the data it points to. This is now replaced // by the upcoming MCSymbolizer, which needs the appropriate setup done above. std::vector Sections; std::vector Symbols; SmallVector FoundFns; uint64_t BaseSegmentAddress = 0; getSectionsAndSymbols(MachOOF, Sections, Symbols, FoundFns, BaseSegmentAddress); // Sort the symbols by address, just in case they didn't come in that way. llvm::sort(Symbols, SymbolSorter()); // Build a data in code table that is sorted on by the address of each entry. uint64_t BaseAddress = 0; if (Header.filetype == MachO::MH_OBJECT) BaseAddress = Sections[0].getAddress(); else BaseAddress = BaseSegmentAddress; DiceTable Dices; for (dice_iterator DI = MachOOF->begin_dices(), DE = MachOOF->end_dices(); DI != DE; ++DI) { uint32_t Offset; DI->getOffset(Offset); Dices.push_back(std::make_pair(BaseAddress + Offset, *DI)); } array_pod_sort(Dices.begin(), Dices.end()); // Try to find debug info and set up the DIContext for it. std::unique_ptr diContext; std::unique_ptr DSYMBinary; std::unique_ptr DSYMBuf; if (UseDbg) { ObjectFile *DbgObj = MachOOF; // A separate DSym file path was specified, parse it as a macho file, // get the sections and supply it to the section name parsing machinery. if (!DSYMFile.empty()) { std::string DSYMPath(DSYMFile); // If DSYMPath is a .dSYM directory, append the Mach-O file. if (llvm::sys::fs::is_directory(DSYMPath) && llvm::sys::path::extension(DSYMPath) == ".dSYM") { SmallString<128> ShortName(llvm::sys::path::filename(DSYMPath)); llvm::sys::path::replace_extension(ShortName, ""); SmallString<1024> FullPath(DSYMPath); llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF", ShortName); DSYMPath = std::string(FullPath.str()); } // Load the file. ErrorOr> BufOrErr = MemoryBuffer::getFileOrSTDIN(DSYMPath); if (std::error_code EC = BufOrErr.getError()) { reportError(errorCodeToError(EC), DSYMPath); return; } // We need to keep the file alive, because we're replacing DbgObj with it. DSYMBuf = std::move(BufOrErr.get()); Expected> BinaryOrErr = createBinary(DSYMBuf.get()->getMemBufferRef()); if (!BinaryOrErr) { reportError(BinaryOrErr.takeError(), DSYMPath); return; } // We need to keep the Binary alive with the buffer DSYMBinary = std::move(BinaryOrErr.get()); if (ObjectFile *O = dyn_cast(DSYMBinary.get())) { // this is a Mach-O object file, use it if (MachOObjectFile *MachDSYM = dyn_cast(&*O)) { DbgObj = MachDSYM; } else { WithColor::error(errs(), "llvm-objdump") << DSYMPath << " is not a Mach-O file type.\n"; return; } } else if (auto UB = dyn_cast(DSYMBinary.get())){ // this is a Universal Binary, find a Mach-O for this architecture uint32_t CPUType, CPUSubType; const char *ArchFlag; if (MachOOF->is64Bit()) { const MachO::mach_header_64 H_64 = MachOOF->getHeader64(); CPUType = H_64.cputype; CPUSubType = H_64.cpusubtype; } else { const MachO::mach_header H = MachOOF->getHeader(); CPUType = H.cputype; CPUSubType = H.cpusubtype; } Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr, &ArchFlag); Expected> MachDSYM = UB->getMachOObjectForArch(ArchFlag); if (!MachDSYM) { reportError(MachDSYM.takeError(), DSYMPath); return; } // We need to keep the Binary alive with the buffer DbgObj = &*MachDSYM.get(); DSYMBinary = std::move(*MachDSYM); } else { WithColor::error(errs(), "llvm-objdump") << DSYMPath << " is not a Mach-O or Universal file type.\n"; return; } } // Setup the DIContext diContext = DWARFContext::create(*DbgObj); } if (FilterSections.empty()) outs() << "(" << DisSegName << "," << DisSectName << ") section\n"; for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { Expected SecNameOrErr = Sections[SectIdx].getName(); if (!SecNameOrErr) { consumeError(SecNameOrErr.takeError()); continue; } if (*SecNameOrErr != DisSectName) continue; DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl(); StringRef SegmentName = MachOOF->getSectionFinalSegmentName(DR); if (SegmentName != DisSegName) continue; StringRef BytesStr = unwrapOrError(Sections[SectIdx].getContents(), Filename); ArrayRef Bytes = arrayRefFromStringRef(BytesStr); uint64_t SectAddress = Sections[SectIdx].getAddress(); bool symbolTableWorked = false; // Create a map of symbol addresses to symbol names for use by // the SymbolizerSymbolLookUp() routine. SymbolAddressMap AddrMap; bool DisSymNameFound = false; for (const SymbolRef &Symbol : MachOOF->symbols()) { SymbolRef::Type ST = unwrapOrError(Symbol.getType(), MachOOF->getFileName()); if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || ST == SymbolRef::ST_Other) { uint64_t Address = cantFail(Symbol.getValue()); StringRef SymName = unwrapOrError(Symbol.getName(), MachOOF->getFileName()); AddrMap[Address] = SymName; if (!DisSymName.empty() && DisSymName == SymName) DisSymNameFound = true; } } if (!DisSymName.empty() && !DisSymNameFound) { outs() << "Can't find -dis-symname: " << DisSymName << "\n"; return; } // Set up the block of info used by the Symbolizer call backs. SymbolizerInfo.verbose = !NoSymbolicOperands; SymbolizerInfo.O = MachOOF; SymbolizerInfo.S = Sections[SectIdx]; SymbolizerInfo.AddrMap = &AddrMap; SymbolizerInfo.Sections = &Sections; // Same for the ThumbSymbolizer ThumbSymbolizerInfo.verbose = !NoSymbolicOperands; ThumbSymbolizerInfo.O = MachOOF; ThumbSymbolizerInfo.S = Sections[SectIdx]; ThumbSymbolizerInfo.AddrMap = &AddrMap; ThumbSymbolizerInfo.Sections = &Sections; unsigned int Arch = MachOOF->getArch(); // Skip all symbols if this is a stubs file. if (Bytes.empty()) return; // If the section has symbols but no symbol at the start of the section // these are used to make sure the bytes before the first symbol are // disassembled. bool FirstSymbol = true; bool FirstSymbolAtSectionStart = true; // Disassemble symbol by symbol. for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) { StringRef SymName = unwrapOrError(Symbols[SymIdx].getName(), MachOOF->getFileName()); SymbolRef::Type ST = unwrapOrError(Symbols[SymIdx].getType(), MachOOF->getFileName()); if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data) continue; // Make sure the symbol is defined in this section. bool containsSym = Sections[SectIdx].containsSymbol(Symbols[SymIdx]); if (!containsSym) { if (!DisSymName.empty() && DisSymName == SymName) { outs() << "-dis-symname: " << DisSymName << " not in the section\n"; return; } continue; } // The __mh_execute_header is special and we need to deal with that fact // this symbol is before the start of the (__TEXT,__text) section and at the // address of the start of the __TEXT segment. This is because this symbol // is an N_SECT symbol in the (__TEXT,__text) but its address is before the // start of the section in a standard MH_EXECUTE filetype. if (!DisSymName.empty() && DisSymName == "__mh_execute_header") { outs() << "-dis-symname: __mh_execute_header not in any section\n"; return; } // When this code is trying to disassemble a symbol at a time and in the // case there is only the __mh_execute_header symbol left as in a stripped // executable, we need to deal with this by ignoring this symbol so the // whole section is disassembled and this symbol is then not displayed. if (SymName == "__mh_execute_header" || SymName == "__mh_dylib_header" || SymName == "__mh_bundle_header" || SymName == "__mh_object_header" || SymName == "__mh_preload_header" || SymName == "__mh_dylinker_header") continue; // If we are only disassembling one symbol see if this is that symbol. if (!DisSymName.empty() && DisSymName != SymName) continue; // Start at the address of the symbol relative to the section's address. uint64_t SectSize = Sections[SectIdx].getSize(); uint64_t Start = cantFail(Symbols[SymIdx].getValue()); uint64_t SectionAddress = Sections[SectIdx].getAddress(); Start -= SectionAddress; if (Start > SectSize) { outs() << "section data ends, " << SymName << " lies outside valid range\n"; return; } // Stop disassembling either at the beginning of the next symbol or at // the end of the section. bool containsNextSym = false; uint64_t NextSym = 0; uint64_t NextSymIdx = SymIdx + 1; while (Symbols.size() > NextSymIdx) { SymbolRef::Type NextSymType = unwrapOrError( Symbols[NextSymIdx].getType(), MachOOF->getFileName()); if (NextSymType == SymbolRef::ST_Function) { containsNextSym = Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]); NextSym = cantFail(Symbols[NextSymIdx].getValue()); NextSym -= SectionAddress; break; } ++NextSymIdx; } uint64_t End = containsNextSym ? std::min(NextSym, SectSize) : SectSize; uint64_t Size; symbolTableWorked = true; DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl(); uint32_t SymbolFlags = cantFail(MachOOF->getSymbolFlags(Symb)); bool IsThumb = SymbolFlags & SymbolRef::SF_Thumb; // We only need the dedicated Thumb target if there's a real choice // (i.e. we're not targeting M-class) and the function is Thumb. bool UseThumbTarget = IsThumb && ThumbTarget; // If we are not specifying a symbol to start disassembly with and this // is the first symbol in the section but not at the start of the section // then move the disassembly index to the start of the section and // don't print the symbol name just yet. This is so the bytes before the // first symbol are disassembled. uint64_t SymbolStart = Start; if (DisSymName.empty() && FirstSymbol && Start != 0) { FirstSymbolAtSectionStart = false; Start = 0; } else outs() << SymName << ":\n"; DILineInfo lastLine; for (uint64_t Index = Start; Index < End; Index += Size) { MCInst Inst; // If this is the first symbol in the section and it was not at the // start of the section, see if we are at its Index now and if so print // the symbol name. if (FirstSymbol && !FirstSymbolAtSectionStart && Index == SymbolStart) outs() << SymName << ":\n"; uint64_t PC = SectAddress + Index; if (!NoLeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); else outs() << format("%08" PRIx64, PC); } else { outs() << format("%8" PRIx64 ":", PC); } } if (!NoShowRawInsn || Arch == Triple::arm) outs() << "\t"; if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size)) continue; SmallVector AnnotationsBytes; raw_svector_ostream Annotations(AnnotationsBytes); bool gotInst; if (UseThumbTarget) gotInst = ThumbDisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC, Annotations); else gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC, Annotations); if (gotInst) { if (!NoShowRawInsn || Arch == Triple::arm) { dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs()); } formatted_raw_ostream FormattedOS(outs()); StringRef AnnotationsStr = Annotations.str(); if (UseThumbTarget) ThumbIP->printInst(&Inst, PC, AnnotationsStr, *ThumbSTI, FormattedOS); else IP->printInst(&Inst, PC, AnnotationsStr, *STI, FormattedOS); emitComments(CommentStream, CommentsToEmit, FormattedOS, *AsmInfo); // Print debug info. if (diContext) { DILineInfo dli = diContext->getLineInfoForAddress({PC, SectIdx}); // Print valid line info if it changed. if (dli != lastLine && dli.Line != 0) outs() << "\t## " << dli.FileName << ':' << dli.Line << ':' << dli.Column; lastLine = dli; } outs() << "\n"; } else { if (MachOOF->getArchTriple().isX86()) { outs() << format("\t.byte 0x%02x #bad opcode\n", *(Bytes.data() + Index) & 0xff); Size = 1; // skip exactly one illegible byte and move on. } else if (Arch == Triple::aarch64 || (Arch == Triple::arm && !IsThumb)) { uint32_t opcode = (*(Bytes.data() + Index) & 0xff) | (*(Bytes.data() + Index + 1) & 0xff) << 8 | (*(Bytes.data() + Index + 2) & 0xff) << 16 | (*(Bytes.data() + Index + 3) & 0xff) << 24; outs() << format("\t.long\t0x%08x\n", opcode); Size = 4; } else if (Arch == Triple::arm) { assert(IsThumb && "ARM mode should have been dealt with above"); uint32_t opcode = (*(Bytes.data() + Index) & 0xff) | (*(Bytes.data() + Index + 1) & 0xff) << 8; outs() << format("\t.short\t0x%04x\n", opcode); Size = 2; } else{ WithColor::warning(errs(), "llvm-objdump") << "invalid instruction encoding\n"; if (Size == 0) Size = 1; // skip illegible bytes } } } // Now that we are done disassembled the first symbol set the bool that // were doing this to false. FirstSymbol = false; } if (!symbolTableWorked) { // Reading the symbol table didn't work, disassemble the whole section. uint64_t SectAddress = Sections[SectIdx].getAddress(); uint64_t SectSize = Sections[SectIdx].getSize(); uint64_t InstSize; for (uint64_t Index = 0; Index < SectSize; Index += InstSize) { MCInst Inst; uint64_t PC = SectAddress + Index; if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, InstSize)) continue; SmallVector AnnotationsBytes; raw_svector_ostream Annotations(AnnotationsBytes); if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, Annotations)) { if (!NoLeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); else outs() << format("%08" PRIx64, PC); } else { outs() << format("%8" PRIx64 ":", PC); } } if (!NoShowRawInsn || Arch == Triple::arm) { outs() << "\t"; dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); } StringRef AnnotationsStr = Annotations.str(); IP->printInst(&Inst, PC, AnnotationsStr, *STI, outs()); outs() << "\n"; } else { if (MachOOF->getArchTriple().isX86()) { outs() << format("\t.byte 0x%02x #bad opcode\n", *(Bytes.data() + Index) & 0xff); InstSize = 1; // skip exactly one illegible byte and move on. } else { WithColor::warning(errs(), "llvm-objdump") << "invalid instruction encoding\n"; if (InstSize == 0) InstSize = 1; // skip illegible bytes } } } } // The TripleName's need to be reset if we are called again for a different // architecture. TripleName = ""; ThumbTripleName = ""; if (SymbolizerInfo.demangled_name != nullptr) free(SymbolizerInfo.demangled_name); if (ThumbSymbolizerInfo.demangled_name != nullptr) free(ThumbSymbolizerInfo.demangled_name); } } //===----------------------------------------------------------------------===// // __compact_unwind section dumping //===----------------------------------------------------------------------===// namespace { template static uint64_t read(StringRef Contents, ptrdiff_t Offset) { using llvm::support::little; using llvm::support::unaligned; if (Offset + sizeof(T) > Contents.size()) { outs() << "warning: attempt to read past end of buffer\n"; return T(); } uint64_t Val = support::endian::read(Contents.data() + Offset); return Val; } template static uint64_t readNext(StringRef Contents, ptrdiff_t &Offset) { T Val = read(Contents, Offset); Offset += sizeof(T); return Val; } struct CompactUnwindEntry { uint32_t OffsetInSection; uint64_t FunctionAddr; uint32_t Length; uint32_t CompactEncoding; uint64_t PersonalityAddr; uint64_t LSDAAddr; RelocationRef FunctionReloc; RelocationRef PersonalityReloc; RelocationRef LSDAReloc; CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64) : OffsetInSection(Offset) { if (Is64) read(Contents, Offset); else read(Contents, Offset); } private: template void read(StringRef Contents, ptrdiff_t Offset) { FunctionAddr = readNext(Contents, Offset); Length = readNext(Contents, Offset); CompactEncoding = readNext(Contents, Offset); PersonalityAddr = readNext(Contents, Offset); LSDAAddr = readNext(Contents, Offset); } }; } /// Given a relocation from __compact_unwind, consisting of the RelocationRef /// and data being relocated, determine the best base Name and Addend to use for /// display purposes. /// /// 1. An Extern relocation will directly reference a symbol (and the data is /// then already an addend), so use that. /// 2. Otherwise the data is an offset in the object file's layout; try to find // a symbol before it in the same section, and use the offset from there. /// 3. Finally, if all that fails, fall back to an offset from the start of the /// referenced section. static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, std::map &Symbols, const RelocationRef &Reloc, uint64_t Addr, StringRef &Name, uint64_t &Addend) { if (Reloc.getSymbol() != Obj->symbol_end()) { Name = unwrapOrError(Reloc.getSymbol()->getName(), Obj->getFileName()); Addend = Addr; return; } auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl()); SectionRef RelocSection = Obj->getAnyRelocationSection(RE); uint64_t SectionAddr = RelocSection.getAddress(); auto Sym = Symbols.upper_bound(Addr); if (Sym == Symbols.begin()) { // The first symbol in the object is after this reference, the best we can // do is section-relative notation. if (Expected NameOrErr = RelocSection.getName()) Name = *NameOrErr; else consumeError(NameOrErr.takeError()); Addend = Addr - SectionAddr; return; } // Go back one so that SymbolAddress <= Addr. --Sym; section_iterator SymSection = unwrapOrError(Sym->second.getSection(), Obj->getFileName()); if (RelocSection == *SymSection) { // There's a valid symbol in the same section before this reference. Name = unwrapOrError(Sym->second.getName(), Obj->getFileName()); Addend = Addr - Sym->first; return; } // There is a symbol before this reference, but it's in a different // section. Probably not helpful to mention it, so use the section name. if (Expected NameOrErr = RelocSection.getName()) Name = *NameOrErr; else consumeError(NameOrErr.takeError()); Addend = Addr - SectionAddr; } static void printUnwindRelocDest(const MachOObjectFile *Obj, std::map &Symbols, const RelocationRef &Reloc, uint64_t Addr) { StringRef Name; uint64_t Addend; if (!Reloc.getObject()) return; findUnwindRelocNameAddend(Obj, Symbols, Reloc, Addr, Name, Addend); outs() << Name; if (Addend) outs() << " + " << format("0x%" PRIx64, Addend); } static void printMachOCompactUnwindSection(const MachOObjectFile *Obj, std::map &Symbols, const SectionRef &CompactUnwind) { if (!Obj->isLittleEndian()) { outs() << "Skipping big-endian __compact_unwind section\n"; return; } bool Is64 = Obj->is64Bit(); uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t); uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t); StringRef Contents = unwrapOrError(CompactUnwind.getContents(), Obj->getFileName()); SmallVector CompactUnwinds; // First populate the initial raw offsets, encodings and so on from the entry. for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) { CompactUnwindEntry Entry(Contents, Offset, Is64); CompactUnwinds.push_back(Entry); } // Next we need to look at the relocations to find out what objects are // actually being referred to. for (const RelocationRef &Reloc : CompactUnwind.relocations()) { uint64_t RelocAddress = Reloc.getOffset(); uint32_t EntryIdx = RelocAddress / EntrySize; uint32_t OffsetInEntry = RelocAddress - EntryIdx * EntrySize; CompactUnwindEntry &Entry = CompactUnwinds[EntryIdx]; if (OffsetInEntry == 0) Entry.FunctionReloc = Reloc; else if (OffsetInEntry == PointerSize + 2 * sizeof(uint32_t)) Entry.PersonalityReloc = Reloc; else if (OffsetInEntry == 2 * PointerSize + 2 * sizeof(uint32_t)) Entry.LSDAReloc = Reloc; else { outs() << "Invalid relocation in __compact_unwind section\n"; return; } } // Finally, we're ready to print the data we've gathered. outs() << "Contents of __compact_unwind section:\n"; for (auto &Entry : CompactUnwinds) { outs() << " Entry at offset " << format("0x%" PRIx32, Entry.OffsetInSection) << ":\n"; // 1. Start of the region this entry applies to. outs() << " start: " << format("0x%" PRIx64, Entry.FunctionAddr) << ' '; printUnwindRelocDest(Obj, Symbols, Entry.FunctionReloc, Entry.FunctionAddr); outs() << '\n'; // 2. Length of the region this entry applies to. outs() << " length: " << format("0x%" PRIx32, Entry.Length) << '\n'; // 3. The 32-bit compact encoding. outs() << " compact encoding: " << format("0x%08" PRIx32, Entry.CompactEncoding) << '\n'; // 4. The personality function, if present. if (Entry.PersonalityReloc.getObject()) { outs() << " personality function: " << format("0x%" PRIx64, Entry.PersonalityAddr) << ' '; printUnwindRelocDest(Obj, Symbols, Entry.PersonalityReloc, Entry.PersonalityAddr); outs() << '\n'; } // 5. This entry's language-specific data area. if (Entry.LSDAReloc.getObject()) { outs() << " LSDA: " << format("0x%" PRIx64, Entry.LSDAAddr) << ' '; printUnwindRelocDest(Obj, Symbols, Entry.LSDAReloc, Entry.LSDAAddr); outs() << '\n'; } } } //===----------------------------------------------------------------------===// // __unwind_info section dumping //===----------------------------------------------------------------------===// static void printRegularSecondLevelUnwindPage(StringRef PageData) { ptrdiff_t Pos = 0; uint32_t Kind = readNext(PageData, Pos); (void)Kind; assert(Kind == 2 && "kind for a regular 2nd level index should be 2"); uint16_t EntriesStart = readNext(PageData, Pos); uint16_t NumEntries = readNext(PageData, Pos); Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { uint32_t FunctionOffset = readNext(PageData, Pos); uint32_t Encoding = readNext(PageData, Pos); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) << ", " << "encoding=" << format("0x%08" PRIx32, Encoding) << '\n'; } } static void printCompressedSecondLevelUnwindPage( StringRef PageData, uint32_t FunctionBase, const SmallVectorImpl &CommonEncodings) { ptrdiff_t Pos = 0; uint32_t Kind = readNext(PageData, Pos); (void)Kind; assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); uint16_t EntriesStart = readNext(PageData, Pos); uint16_t NumEntries = readNext(PageData, Pos); uint16_t EncodingsStart = readNext(PageData, Pos); readNext(PageData, Pos); StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos); Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { uint32_t Entry = readNext(PageData, Pos); uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff); uint32_t EncodingIdx = Entry >> 24; uint32_t Encoding; if (EncodingIdx < CommonEncodings.size()) Encoding = CommonEncodings[EncodingIdx]; else Encoding = read(PageEncodings, sizeof(uint32_t) * (EncodingIdx - CommonEncodings.size())); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) << ", " << "encoding[" << EncodingIdx << "]=" << format("0x%08" PRIx32, Encoding) << '\n'; } } static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, std::map &Symbols, const SectionRef &UnwindInfo) { if (!Obj->isLittleEndian()) { outs() << "Skipping big-endian __unwind_info section\n"; return; } outs() << "Contents of __unwind_info section:\n"; StringRef Contents = unwrapOrError(UnwindInfo.getContents(), Obj->getFileName()); ptrdiff_t Pos = 0; //===---------------------------------- // Section header //===---------------------------------- uint32_t Version = readNext(Contents, Pos); outs() << " Version: " << format("0x%" PRIx32, Version) << '\n'; if (Version != 1) { outs() << " Skipping section with unknown version\n"; return; } uint32_t CommonEncodingsStart = readNext(Contents, Pos); outs() << " Common encodings array section offset: " << format("0x%" PRIx32, CommonEncodingsStart) << '\n'; uint32_t NumCommonEncodings = readNext(Contents, Pos); outs() << " Number of common encodings in array: " << format("0x%" PRIx32, NumCommonEncodings) << '\n'; uint32_t PersonalitiesStart = readNext(Contents, Pos); outs() << " Personality function array section offset: " << format("0x%" PRIx32, PersonalitiesStart) << '\n'; uint32_t NumPersonalities = readNext(Contents, Pos); outs() << " Number of personality functions in array: " << format("0x%" PRIx32, NumPersonalities) << '\n'; uint32_t IndicesStart = readNext(Contents, Pos); outs() << " Index array section offset: " << format("0x%" PRIx32, IndicesStart) << '\n'; uint32_t NumIndices = readNext(Contents, Pos); outs() << " Number of indices in array: " << format("0x%" PRIx32, NumIndices) << '\n'; //===---------------------------------- // A shared list of common encodings //===---------------------------------- // These occupy indices in the range [0, N] whenever an encoding is referenced // from a compressed 2nd level index table. In practice the linker only // creates ~128 of these, so that indices are available to embed encodings in // the 2nd level index. SmallVector CommonEncodings; outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n"; Pos = CommonEncodingsStart; for (unsigned i = 0; i < NumCommonEncodings; ++i) { uint32_t Encoding = readNext(Contents, Pos); CommonEncodings.push_back(Encoding); outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding) << '\n'; } //===---------------------------------- // Personality functions used in this executable //===---------------------------------- // There should be only a handful of these (one per source language, // roughly). Particularly since they only get 2 bits in the compact encoding. outs() << " Personality functions: (count = " << NumPersonalities << ")\n"; Pos = PersonalitiesStart; for (unsigned i = 0; i < NumPersonalities; ++i) { uint32_t PersonalityFn = readNext(Contents, Pos); outs() << " personality[" << i + 1 << "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n'; } //===---------------------------------- // The level 1 index entries //===---------------------------------- // These specify an approximate place to start searching for the more detailed // information, sorted by PC. struct IndexEntry { uint32_t FunctionOffset; uint32_t SecondLevelPageStart; uint32_t LSDAStart; }; SmallVector IndexEntries; outs() << " Top level indices: (count = " << NumIndices << ")\n"; Pos = IndicesStart; for (unsigned i = 0; i < NumIndices; ++i) { IndexEntry Entry; Entry.FunctionOffset = readNext(Contents, Pos); Entry.SecondLevelPageStart = readNext(Contents, Pos); Entry.LSDAStart = readNext(Contents, Pos); IndexEntries.push_back(Entry); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, Entry.FunctionOffset) << ", " << "2nd level page offset=" << format("0x%08" PRIx32, Entry.SecondLevelPageStart) << ", " << "LSDA offset=" << format("0x%08" PRIx32, Entry.LSDAStart) << '\n'; } //===---------------------------------- // Next come the LSDA tables //===---------------------------------- // The LSDA layout is rather implicit: it's a contiguous array of entries from // the first top-level index's LSDAOffset to the last (sentinel). outs() << " LSDA descriptors:\n"; Pos = IndexEntries[0].LSDAStart; const uint32_t LSDASize = 2 * sizeof(uint32_t); int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / LSDASize; for (int i = 0; i < NumLSDAs; ++i) { uint32_t FunctionOffset = readNext(Contents, Pos); uint32_t LSDAOffset = readNext(Contents, Pos); outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) << ", " << "LSDA offset=" << format("0x%08" PRIx32, LSDAOffset) << '\n'; } //===---------------------------------- // Finally, the 2nd level indices //===---------------------------------- // Generally these are 4K in size, and have 2 possible forms: // + Regular stores up to 511 entries with disparate encodings // + Compressed stores up to 1021 entries if few enough compact encoding // values are used. outs() << " Second level indices:\n"; for (unsigned i = 0; i < IndexEntries.size() - 1; ++i) { // The final sentinel top-level index has no associated 2nd level page if (IndexEntries[i].SecondLevelPageStart == 0) break; outs() << " Second level index[" << i << "]: " << "offset in section=" << format("0x%08" PRIx32, IndexEntries[i].SecondLevelPageStart) << ", " << "base function offset=" << format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n'; Pos = IndexEntries[i].SecondLevelPageStart; if (Pos + sizeof(uint32_t) > Contents.size()) { outs() << "warning: invalid offset for second level page: " << Pos << '\n'; continue; } uint32_t Kind = *reinterpret_cast(Contents.data() + Pos); if (Kind == 2) printRegularSecondLevelUnwindPage(Contents.substr(Pos, 4096)); else if (Kind == 3) printCompressedSecondLevelUnwindPage(Contents.substr(Pos, 4096), IndexEntries[i].FunctionOffset, CommonEncodings); else outs() << " Skipping 2nd level page with unknown kind " << Kind << '\n'; } } void objdump::printMachOUnwindInfo(const MachOObjectFile *Obj) { std::map Symbols; for (const SymbolRef &SymRef : Obj->symbols()) { // Discard any undefined or absolute symbols. They're not going to take part // in the convenience lookup for unwind info and just take up resources. auto SectOrErr = SymRef.getSection(); if (!SectOrErr) { // TODO: Actually report errors helpfully. consumeError(SectOrErr.takeError()); continue; } section_iterator Section = *SectOrErr; if (Section == Obj->section_end()) continue; uint64_t Addr = cantFail(SymRef.getValue()); Symbols.insert(std::make_pair(Addr, SymRef)); } for (const SectionRef &Section : Obj->sections()) { StringRef SectName; if (Expected NameOrErr = Section.getName()) SectName = *NameOrErr; else consumeError(NameOrErr.takeError()); if (SectName == "__compact_unwind") printMachOCompactUnwindSection(Obj, Symbols, Section); else if (SectName == "__unwind_info") printMachOUnwindInfoSection(Obj, Symbols, Section); } } static void PrintMachHeader(uint32_t magic, uint32_t cputype, uint32_t cpusubtype, uint32_t filetype, uint32_t ncmds, uint32_t sizeofcmds, uint32_t flags, bool verbose) { outs() << "Mach header\n"; outs() << " magic cputype cpusubtype caps filetype ncmds " "sizeofcmds flags\n"; if (verbose) { if (magic == MachO::MH_MAGIC) outs() << " MH_MAGIC"; else if (magic == MachO::MH_MAGIC_64) outs() << "MH_MAGIC_64"; else outs() << format(" 0x%08" PRIx32, magic); switch (cputype) { case MachO::CPU_TYPE_I386: outs() << " I386"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_I386_ALL: outs() << " ALL"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_X86_64: outs() << " X86_64"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_X86_64_ALL: outs() << " ALL"; break; case MachO::CPU_SUBTYPE_X86_64_H: outs() << " Haswell"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_ARM: outs() << " ARM"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_ARM_ALL: outs() << " ALL"; break; case MachO::CPU_SUBTYPE_ARM_V4T: outs() << " V4T"; break; case MachO::CPU_SUBTYPE_ARM_V5TEJ: outs() << " V5TEJ"; break; case MachO::CPU_SUBTYPE_ARM_XSCALE: outs() << " XSCALE"; break; case MachO::CPU_SUBTYPE_ARM_V6: outs() << " V6"; break; case MachO::CPU_SUBTYPE_ARM_V6M: outs() << " V6M"; break; case MachO::CPU_SUBTYPE_ARM_V7: outs() << " V7"; break; case MachO::CPU_SUBTYPE_ARM_V7EM: outs() << " V7EM"; break; case MachO::CPU_SUBTYPE_ARM_V7K: outs() << " V7K"; break; case MachO::CPU_SUBTYPE_ARM_V7M: outs() << " V7M"; break; case MachO::CPU_SUBTYPE_ARM_V7S: outs() << " V7S"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_ARM64: outs() << " ARM64"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_ARM64_ALL: outs() << " ALL"; break; case MachO::CPU_SUBTYPE_ARM64_V8: outs() << " V8"; break; case MachO::CPU_SUBTYPE_ARM64E: outs() << " E"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_ARM64_32: outs() << " ARM64_32"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_ARM64_32_V8: outs() << " V8"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_POWERPC: outs() << " PPC"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_POWERPC_ALL: outs() << " ALL"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; case MachO::CPU_TYPE_POWERPC64: outs() << " PPC64"; switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { case MachO::CPU_SUBTYPE_POWERPC_ALL: outs() << " ALL"; break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } break; default: outs() << format(" %7d", cputype); outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; } if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) { outs() << " LIB64"; } else { outs() << format(" 0x%02" PRIx32, (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24); } switch (filetype) { case MachO::MH_OBJECT: outs() << " OBJECT"; break; case MachO::MH_EXECUTE: outs() << " EXECUTE"; break; case MachO::MH_FVMLIB: outs() << " FVMLIB"; break; case MachO::MH_CORE: outs() << " CORE"; break; case MachO::MH_PRELOAD: outs() << " PRELOAD"; break; case MachO::MH_DYLIB: outs() << " DYLIB"; break; case MachO::MH_DYLIB_STUB: outs() << " DYLIB_STUB"; break; case MachO::MH_DYLINKER: outs() << " DYLINKER"; break; case MachO::MH_BUNDLE: outs() << " BUNDLE"; break; case MachO::MH_DSYM: outs() << " DSYM"; break; case MachO::MH_KEXT_BUNDLE: outs() << " KEXTBUNDLE"; break; default: outs() << format(" %10u", filetype); break; } outs() << format(" %5u", ncmds); outs() << format(" %10u", sizeofcmds); uint32_t f = flags; if (f & MachO::MH_NOUNDEFS) { outs() << " NOUNDEFS"; f &= ~MachO::MH_NOUNDEFS; } if (f & MachO::MH_INCRLINK) { outs() << " INCRLINK"; f &= ~MachO::MH_INCRLINK; } if (f & MachO::MH_DYLDLINK) { outs() << " DYLDLINK"; f &= ~MachO::MH_DYLDLINK; } if (f & MachO::MH_BINDATLOAD) { outs() << " BINDATLOAD"; f &= ~MachO::MH_BINDATLOAD; } if (f & MachO::MH_PREBOUND) { outs() << " PREBOUND"; f &= ~MachO::MH_PREBOUND; } if (f & MachO::MH_SPLIT_SEGS) { outs() << " SPLIT_SEGS"; f &= ~MachO::MH_SPLIT_SEGS; } if (f & MachO::MH_LAZY_INIT) { outs() << " LAZY_INIT"; f &= ~MachO::MH_LAZY_INIT; } if (f & MachO::MH_TWOLEVEL) { outs() << " TWOLEVEL"; f &= ~MachO::MH_TWOLEVEL; } if (f & MachO::MH_FORCE_FLAT) { outs() << " FORCE_FLAT"; f &= ~MachO::MH_FORCE_FLAT; } if (f & MachO::MH_NOMULTIDEFS) { outs() << " NOMULTIDEFS"; f &= ~MachO::MH_NOMULTIDEFS; } if (f & MachO::MH_NOFIXPREBINDING) { outs() << " NOFIXPREBINDING"; f &= ~MachO::MH_NOFIXPREBINDING; } if (f & MachO::MH_PREBINDABLE) { outs() << " PREBINDABLE"; f &= ~MachO::MH_PREBINDABLE; } if (f & MachO::MH_ALLMODSBOUND) { outs() << " ALLMODSBOUND"; f &= ~MachO::MH_ALLMODSBOUND; } if (f & MachO::MH_SUBSECTIONS_VIA_SYMBOLS) { outs() << " SUBSECTIONS_VIA_SYMBOLS"; f &= ~MachO::MH_SUBSECTIONS_VIA_SYMBOLS; } if (f & MachO::MH_CANONICAL) { outs() << " CANONICAL"; f &= ~MachO::MH_CANONICAL; } if (f & MachO::MH_WEAK_DEFINES) { outs() << " WEAK_DEFINES"; f &= ~MachO::MH_WEAK_DEFINES; } if (f & MachO::MH_BINDS_TO_WEAK) { outs() << " BINDS_TO_WEAK"; f &= ~MachO::MH_BINDS_TO_WEAK; } if (f & MachO::MH_ALLOW_STACK_EXECUTION) { outs() << " ALLOW_STACK_EXECUTION"; f &= ~MachO::MH_ALLOW_STACK_EXECUTION; } if (f & MachO::MH_DEAD_STRIPPABLE_DYLIB) { outs() << " DEAD_STRIPPABLE_DYLIB"; f &= ~MachO::MH_DEAD_STRIPPABLE_DYLIB; } if (f & MachO::MH_PIE) { outs() << " PIE"; f &= ~MachO::MH_PIE; } if (f & MachO::MH_NO_REEXPORTED_DYLIBS) { outs() << " NO_REEXPORTED_DYLIBS"; f &= ~MachO::MH_NO_REEXPORTED_DYLIBS; } if (f & MachO::MH_HAS_TLV_DESCRIPTORS) { outs() << " MH_HAS_TLV_DESCRIPTORS"; f &= ~MachO::MH_HAS_TLV_DESCRIPTORS; } if (f & MachO::MH_NO_HEAP_EXECUTION) { outs() << " MH_NO_HEAP_EXECUTION"; f &= ~MachO::MH_NO_HEAP_EXECUTION; } if (f & MachO::MH_APP_EXTENSION_SAFE) { outs() << " APP_EXTENSION_SAFE"; f &= ~MachO::MH_APP_EXTENSION_SAFE; } if (f & MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO) { outs() << " NLIST_OUTOFSYNC_WITH_DYLDINFO"; f &= ~MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO; } if (f != 0 || flags == 0) outs() << format(" 0x%08" PRIx32, f); } else { outs() << format(" 0x%08" PRIx32, magic); outs() << format(" %7d", cputype); outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); outs() << format(" 0x%02" PRIx32, (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24); outs() << format(" %10u", filetype); outs() << format(" %5u", ncmds); outs() << format(" %10u", sizeofcmds); outs() << format(" 0x%08" PRIx32, flags); } outs() << "\n"; } static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, StringRef SegName, uint64_t vmaddr, uint64_t vmsize, uint64_t fileoff, uint64_t filesize, uint32_t maxprot, uint32_t initprot, uint32_t nsects, uint32_t flags, uint32_t object_size, bool verbose) { uint64_t expected_cmdsize; if (cmd == MachO::LC_SEGMENT) { outs() << " cmd LC_SEGMENT\n"; expected_cmdsize = nsects; expected_cmdsize *= sizeof(struct MachO::section); expected_cmdsize += sizeof(struct MachO::segment_command); } else { outs() << " cmd LC_SEGMENT_64\n"; expected_cmdsize = nsects; expected_cmdsize *= sizeof(struct MachO::section_64); expected_cmdsize += sizeof(struct MachO::segment_command_64); } outs() << " cmdsize " << cmdsize; if (cmdsize != expected_cmdsize) outs() << " Inconsistent size\n"; else outs() << "\n"; outs() << " segname " << SegName << "\n"; if (cmd == MachO::LC_SEGMENT_64) { outs() << " vmaddr " << format("0x%016" PRIx64, vmaddr) << "\n"; outs() << " vmsize " << format("0x%016" PRIx64, vmsize) << "\n"; } else { outs() << " vmaddr " << format("0x%08" PRIx64, vmaddr) << "\n"; outs() << " vmsize " << format("0x%08" PRIx64, vmsize) << "\n"; } outs() << " fileoff " << fileoff; if (fileoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " filesize " << filesize; if (fileoff + filesize > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; if (verbose) { if ((maxprot & ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE)) != 0) outs() << " maxprot ?" << format("0x%08" PRIx32, maxprot) << "\n"; else { outs() << " maxprot "; outs() << ((maxprot & MachO::VM_PROT_READ) ? "r" : "-"); outs() << ((maxprot & MachO::VM_PROT_WRITE) ? "w" : "-"); outs() << ((maxprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n"); } if ((initprot & ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE)) != 0) outs() << " initprot ?" << format("0x%08" PRIx32, initprot) << "\n"; else { outs() << " initprot "; outs() << ((initprot & MachO::VM_PROT_READ) ? "r" : "-"); outs() << ((initprot & MachO::VM_PROT_WRITE) ? "w" : "-"); outs() << ((initprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n"); } } else { outs() << " maxprot " << format("0x%08" PRIx32, maxprot) << "\n"; outs() << " initprot " << format("0x%08" PRIx32, initprot) << "\n"; } outs() << " nsects " << nsects << "\n"; if (verbose) { outs() << " flags"; if (flags == 0) outs() << " (none)\n"; else { if (flags & MachO::SG_HIGHVM) { outs() << " HIGHVM"; flags &= ~MachO::SG_HIGHVM; } if (flags & MachO::SG_FVMLIB) { outs() << " FVMLIB"; flags &= ~MachO::SG_FVMLIB; } if (flags & MachO::SG_NORELOC) { outs() << " NORELOC"; flags &= ~MachO::SG_NORELOC; } if (flags & MachO::SG_PROTECTED_VERSION_1) { outs() << " PROTECTED_VERSION_1"; flags &= ~MachO::SG_PROTECTED_VERSION_1; } if (flags) outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n"; else outs() << "\n"; } } else { outs() << " flags " << format("0x%" PRIx32, flags) << "\n"; } } static void PrintSection(const char *sectname, const char *segname, uint64_t addr, uint64_t size, uint32_t offset, uint32_t align, uint32_t reloff, uint32_t nreloc, uint32_t flags, uint32_t reserved1, uint32_t reserved2, uint32_t cmd, const char *sg_segname, uint32_t filetype, uint32_t object_size, bool verbose) { outs() << "Section\n"; outs() << " sectname " << format("%.16s\n", sectname); outs() << " segname " << format("%.16s", segname); if (filetype != MachO::MH_OBJECT && strncmp(sg_segname, segname, 16) != 0) outs() << " (does not match segment)\n"; else outs() << "\n"; if (cmd == MachO::LC_SEGMENT_64) { outs() << " addr " << format("0x%016" PRIx64, addr) << "\n"; outs() << " size " << format("0x%016" PRIx64, size); } else { outs() << " addr " << format("0x%08" PRIx64, addr) << "\n"; outs() << " size " << format("0x%08" PRIx64, size); } if ((flags & MachO::S_ZEROFILL) != 0 && offset + size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " offset " << offset; if (offset > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; uint32_t align_shifted = 1 << align; outs() << " align 2^" << align << " (" << align_shifted << ")\n"; outs() << " reloff " << reloff; if (reloff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nreloc " << nreloc; if (reloff + nreloc * sizeof(struct MachO::relocation_info) > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; uint32_t section_type = flags & MachO::SECTION_TYPE; if (verbose) { outs() << " type"; if (section_type == MachO::S_REGULAR) outs() << " S_REGULAR\n"; else if (section_type == MachO::S_ZEROFILL) outs() << " S_ZEROFILL\n"; else if (section_type == MachO::S_CSTRING_LITERALS) outs() << " S_CSTRING_LITERALS\n"; else if (section_type == MachO::S_4BYTE_LITERALS) outs() << " S_4BYTE_LITERALS\n"; else if (section_type == MachO::S_8BYTE_LITERALS) outs() << " S_8BYTE_LITERALS\n"; else if (section_type == MachO::S_16BYTE_LITERALS) outs() << " S_16BYTE_LITERALS\n"; else if (section_type == MachO::S_LITERAL_POINTERS) outs() << " S_LITERAL_POINTERS\n"; else if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS) outs() << " S_NON_LAZY_SYMBOL_POINTERS\n"; else if (section_type == MachO::S_LAZY_SYMBOL_POINTERS) outs() << " S_LAZY_SYMBOL_POINTERS\n"; else if (section_type == MachO::S_SYMBOL_STUBS) outs() << " S_SYMBOL_STUBS\n"; else if (section_type == MachO::S_MOD_INIT_FUNC_POINTERS) outs() << " S_MOD_INIT_FUNC_POINTERS\n"; else if (section_type == MachO::S_MOD_TERM_FUNC_POINTERS) outs() << " S_MOD_TERM_FUNC_POINTERS\n"; else if (section_type == MachO::S_COALESCED) outs() << " S_COALESCED\n"; else if (section_type == MachO::S_INTERPOSING) outs() << " S_INTERPOSING\n"; else if (section_type == MachO::S_DTRACE_DOF) outs() << " S_DTRACE_DOF\n"; else if (section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS) outs() << " S_LAZY_DYLIB_SYMBOL_POINTERS\n"; else if (section_type == MachO::S_THREAD_LOCAL_REGULAR) outs() << " S_THREAD_LOCAL_REGULAR\n"; else if (section_type == MachO::S_THREAD_LOCAL_ZEROFILL) outs() << " S_THREAD_LOCAL_ZEROFILL\n"; else if (section_type == MachO::S_THREAD_LOCAL_VARIABLES) outs() << " S_THREAD_LOCAL_VARIABLES\n"; else if (section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS) outs() << " S_THREAD_LOCAL_VARIABLE_POINTERS\n"; else if (section_type == MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS) outs() << " S_THREAD_LOCAL_INIT_FUNCTION_POINTERS\n"; else outs() << format("0x%08" PRIx32, section_type) << "\n"; outs() << "attributes"; uint32_t section_attributes = flags & MachO::SECTION_ATTRIBUTES; if (section_attributes & MachO::S_ATTR_PURE_INSTRUCTIONS) outs() << " PURE_INSTRUCTIONS"; if (section_attributes & MachO::S_ATTR_NO_TOC) outs() << " NO_TOC"; if (section_attributes & MachO::S_ATTR_STRIP_STATIC_SYMS) outs() << " STRIP_STATIC_SYMS"; if (section_attributes & MachO::S_ATTR_NO_DEAD_STRIP) outs() << " NO_DEAD_STRIP"; if (section_attributes & MachO::S_ATTR_LIVE_SUPPORT) outs() << " LIVE_SUPPORT"; if (section_attributes & MachO::S_ATTR_SELF_MODIFYING_CODE) outs() << " SELF_MODIFYING_CODE"; if (section_attributes & MachO::S_ATTR_DEBUG) outs() << " DEBUG"; if (section_attributes & MachO::S_ATTR_SOME_INSTRUCTIONS) outs() << " SOME_INSTRUCTIONS"; if (section_attributes & MachO::S_ATTR_EXT_RELOC) outs() << " EXT_RELOC"; if (section_attributes & MachO::S_ATTR_LOC_RELOC) outs() << " LOC_RELOC"; if (section_attributes == 0) outs() << " (none)"; outs() << "\n"; } else outs() << " flags " << format("0x%08" PRIx32, flags) << "\n"; outs() << " reserved1 " << reserved1; if (section_type == MachO::S_SYMBOL_STUBS || section_type == MachO::S_LAZY_SYMBOL_POINTERS || section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS) outs() << " (index into indirect symbol table)\n"; else outs() << "\n"; outs() << " reserved2 " << reserved2; if (section_type == MachO::S_SYMBOL_STUBS) outs() << " (size of stubs)\n"; else outs() << "\n"; } static void PrintSymtabLoadCommand(MachO::symtab_command st, bool Is64Bit, uint32_t object_size) { outs() << " cmd LC_SYMTAB\n"; outs() << " cmdsize " << st.cmdsize; if (st.cmdsize != sizeof(struct MachO::symtab_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " symoff " << st.symoff; if (st.symoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nsyms " << st.nsyms; uint64_t big_size; if (Is64Bit) { big_size = st.nsyms; big_size *= sizeof(struct MachO::nlist_64); big_size += st.symoff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } else { big_size = st.nsyms; big_size *= sizeof(struct MachO::nlist); big_size += st.symoff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } outs() << " stroff " << st.stroff; if (st.stroff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " strsize " << st.strsize; big_size = st.stroff; big_size += st.strsize; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } static void PrintDysymtabLoadCommand(MachO::dysymtab_command dyst, uint32_t nsyms, uint32_t object_size, bool Is64Bit) { outs() << " cmd LC_DYSYMTAB\n"; outs() << " cmdsize " << dyst.cmdsize; if (dyst.cmdsize != sizeof(struct MachO::dysymtab_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " ilocalsym " << dyst.ilocalsym; if (dyst.ilocalsym > nsyms) outs() << " (greater than the number of symbols)\n"; else outs() << "\n"; outs() << " nlocalsym " << dyst.nlocalsym; uint64_t big_size; big_size = dyst.ilocalsym; big_size += dyst.nlocalsym; if (big_size > nsyms) outs() << " (past the end of the symbol table)\n"; else outs() << "\n"; outs() << " iextdefsym " << dyst.iextdefsym; if (dyst.iextdefsym > nsyms) outs() << " (greater than the number of symbols)\n"; else outs() << "\n"; outs() << " nextdefsym " << dyst.nextdefsym; big_size = dyst.iextdefsym; big_size += dyst.nextdefsym; if (big_size > nsyms) outs() << " (past the end of the symbol table)\n"; else outs() << "\n"; outs() << " iundefsym " << dyst.iundefsym; if (dyst.iundefsym > nsyms) outs() << " (greater than the number of symbols)\n"; else outs() << "\n"; outs() << " nundefsym " << dyst.nundefsym; big_size = dyst.iundefsym; big_size += dyst.nundefsym; if (big_size > nsyms) outs() << " (past the end of the symbol table)\n"; else outs() << "\n"; outs() << " tocoff " << dyst.tocoff; if (dyst.tocoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " ntoc " << dyst.ntoc; big_size = dyst.ntoc; big_size *= sizeof(struct MachO::dylib_table_of_contents); big_size += dyst.tocoff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " modtaboff " << dyst.modtaboff; if (dyst.modtaboff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nmodtab " << dyst.nmodtab; uint64_t modtabend; if (Is64Bit) { modtabend = dyst.nmodtab; modtabend *= sizeof(struct MachO::dylib_module_64); modtabend += dyst.modtaboff; } else { modtabend = dyst.nmodtab; modtabend *= sizeof(struct MachO::dylib_module); modtabend += dyst.modtaboff; } if (modtabend > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " extrefsymoff " << dyst.extrefsymoff; if (dyst.extrefsymoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nextrefsyms " << dyst.nextrefsyms; big_size = dyst.nextrefsyms; big_size *= sizeof(struct MachO::dylib_reference); big_size += dyst.extrefsymoff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " indirectsymoff " << dyst.indirectsymoff; if (dyst.indirectsymoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nindirectsyms " << dyst.nindirectsyms; big_size = dyst.nindirectsyms; big_size *= sizeof(uint32_t); big_size += dyst.indirectsymoff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " extreloff " << dyst.extreloff; if (dyst.extreloff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nextrel " << dyst.nextrel; big_size = dyst.nextrel; big_size *= sizeof(struct MachO::relocation_info); big_size += dyst.extreloff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " locreloff " << dyst.locreloff; if (dyst.locreloff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " nlocrel " << dyst.nlocrel; big_size = dyst.nlocrel; big_size *= sizeof(struct MachO::relocation_info); big_size += dyst.locreloff; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } static void PrintDyldInfoLoadCommand(MachO::dyld_info_command dc, uint32_t object_size) { if (dc.cmd == MachO::LC_DYLD_INFO) outs() << " cmd LC_DYLD_INFO\n"; else outs() << " cmd LC_DYLD_INFO_ONLY\n"; outs() << " cmdsize " << dc.cmdsize; if (dc.cmdsize != sizeof(struct MachO::dyld_info_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " rebase_off " << dc.rebase_off; if (dc.rebase_off > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " rebase_size " << dc.rebase_size; uint64_t big_size; big_size = dc.rebase_off; big_size += dc.rebase_size; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " bind_off " << dc.bind_off; if (dc.bind_off > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " bind_size " << dc.bind_size; big_size = dc.bind_off; big_size += dc.bind_size; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " weak_bind_off " << dc.weak_bind_off; if (dc.weak_bind_off > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " weak_bind_size " << dc.weak_bind_size; big_size = dc.weak_bind_off; big_size += dc.weak_bind_size; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " lazy_bind_off " << dc.lazy_bind_off; if (dc.lazy_bind_off > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " lazy_bind_size " << dc.lazy_bind_size; big_size = dc.lazy_bind_off; big_size += dc.lazy_bind_size; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " export_off " << dc.export_off; if (dc.export_off > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " export_size " << dc.export_size; big_size = dc.export_off; big_size += dc.export_size; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } static void PrintDyldLoadCommand(MachO::dylinker_command dyld, const char *Ptr) { if (dyld.cmd == MachO::LC_ID_DYLINKER) outs() << " cmd LC_ID_DYLINKER\n"; else if (dyld.cmd == MachO::LC_LOAD_DYLINKER) outs() << " cmd LC_LOAD_DYLINKER\n"; else if (dyld.cmd == MachO::LC_DYLD_ENVIRONMENT) outs() << " cmd LC_DYLD_ENVIRONMENT\n"; else outs() << " cmd ?(" << dyld.cmd << ")\n"; outs() << " cmdsize " << dyld.cmdsize; if (dyld.cmdsize < sizeof(struct MachO::dylinker_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (dyld.name >= dyld.cmdsize) outs() << " name ?(bad offset " << dyld.name << ")\n"; else { const char *P = (const char *)(Ptr) + dyld.name; outs() << " name " << P << " (offset " << dyld.name << ")\n"; } } static void PrintUuidLoadCommand(MachO::uuid_command uuid) { outs() << " cmd LC_UUID\n"; outs() << " cmdsize " << uuid.cmdsize; if (uuid.cmdsize != sizeof(struct MachO::uuid_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " uuid "; for (int i = 0; i < 16; ++i) { outs() << format("%02" PRIX32, uuid.uuid[i]); if (i == 3 || i == 5 || i == 7 || i == 9) outs() << "-"; } outs() << "\n"; } static void PrintRpathLoadCommand(MachO::rpath_command rpath, const char *Ptr) { outs() << " cmd LC_RPATH\n"; outs() << " cmdsize " << rpath.cmdsize; if (rpath.cmdsize < sizeof(struct MachO::rpath_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (rpath.path >= rpath.cmdsize) outs() << " path ?(bad offset " << rpath.path << ")\n"; else { const char *P = (const char *)(Ptr) + rpath.path; outs() << " path " << P << " (offset " << rpath.path << ")\n"; } } static void PrintVersionMinLoadCommand(MachO::version_min_command vd) { StringRef LoadCmdName; switch (vd.cmd) { case MachO::LC_VERSION_MIN_MACOSX: LoadCmdName = "LC_VERSION_MIN_MACOSX"; break; case MachO::LC_VERSION_MIN_IPHONEOS: LoadCmdName = "LC_VERSION_MIN_IPHONEOS"; break; case MachO::LC_VERSION_MIN_TVOS: LoadCmdName = "LC_VERSION_MIN_TVOS"; break; case MachO::LC_VERSION_MIN_WATCHOS: LoadCmdName = "LC_VERSION_MIN_WATCHOS"; break; default: llvm_unreachable("Unknown version min load command"); } outs() << " cmd " << LoadCmdName << '\n'; outs() << " cmdsize " << vd.cmdsize; if (vd.cmdsize != sizeof(struct MachO::version_min_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " version " << MachOObjectFile::getVersionMinMajor(vd, false) << "." << MachOObjectFile::getVersionMinMinor(vd, false); uint32_t Update = MachOObjectFile::getVersionMinUpdate(vd, false); if (Update != 0) outs() << "." << Update; outs() << "\n"; if (vd.sdk == 0) outs() << " sdk n/a"; else { outs() << " sdk " << MachOObjectFile::getVersionMinMajor(vd, true) << "." << MachOObjectFile::getVersionMinMinor(vd, true); } Update = MachOObjectFile::getVersionMinUpdate(vd, true); if (Update != 0) outs() << "." << Update; outs() << "\n"; } static void PrintNoteLoadCommand(MachO::note_command Nt) { outs() << " cmd LC_NOTE\n"; outs() << " cmdsize " << Nt.cmdsize; if (Nt.cmdsize != sizeof(struct MachO::note_command)) outs() << " Incorrect size\n"; else outs() << "\n"; const char *d = Nt.data_owner; outs() << "data_owner " << format("%.16s\n", d); outs() << " offset " << Nt.offset << "\n"; outs() << " size " << Nt.size << "\n"; } static void PrintBuildToolVersion(MachO::build_tool_version bv) { outs() << " tool " << MachOObjectFile::getBuildTool(bv.tool) << "\n"; outs() << " version " << MachOObjectFile::getVersionString(bv.version) << "\n"; } static void PrintBuildVersionLoadCommand(const MachOObjectFile *obj, MachO::build_version_command bd) { outs() << " cmd LC_BUILD_VERSION\n"; outs() << " cmdsize " << bd.cmdsize; if (bd.cmdsize != sizeof(struct MachO::build_version_command) + bd.ntools * sizeof(struct MachO::build_tool_version)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " platform " << MachOObjectFile::getBuildPlatform(bd.platform) << "\n"; if (bd.sdk) outs() << " sdk " << MachOObjectFile::getVersionString(bd.sdk) << "\n"; else outs() << " sdk n/a\n"; outs() << " minos " << MachOObjectFile::getVersionString(bd.minos) << "\n"; outs() << " ntools " << bd.ntools << "\n"; for (unsigned i = 0; i < bd.ntools; ++i) { MachO::build_tool_version bv = obj->getBuildToolVersion(i); PrintBuildToolVersion(bv); } } static void PrintSourceVersionCommand(MachO::source_version_command sd) { outs() << " cmd LC_SOURCE_VERSION\n"; outs() << " cmdsize " << sd.cmdsize; if (sd.cmdsize != sizeof(struct MachO::source_version_command)) outs() << " Incorrect size\n"; else outs() << "\n"; uint64_t a = (sd.version >> 40) & 0xffffff; uint64_t b = (sd.version >> 30) & 0x3ff; uint64_t c = (sd.version >> 20) & 0x3ff; uint64_t d = (sd.version >> 10) & 0x3ff; uint64_t e = sd.version & 0x3ff; outs() << " version " << a << "." << b; if (e != 0) outs() << "." << c << "." << d << "." << e; else if (d != 0) outs() << "." << c << "." << d; else if (c != 0) outs() << "." << c; outs() << "\n"; } static void PrintEntryPointCommand(MachO::entry_point_command ep) { outs() << " cmd LC_MAIN\n"; outs() << " cmdsize " << ep.cmdsize; if (ep.cmdsize != sizeof(struct MachO::entry_point_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " entryoff " << ep.entryoff << "\n"; outs() << " stacksize " << ep.stacksize << "\n"; } static void PrintEncryptionInfoCommand(MachO::encryption_info_command ec, uint32_t object_size) { outs() << " cmd LC_ENCRYPTION_INFO\n"; outs() << " cmdsize " << ec.cmdsize; if (ec.cmdsize != sizeof(struct MachO::encryption_info_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " cryptoff " << ec.cryptoff; if (ec.cryptoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " cryptsize " << ec.cryptsize; if (ec.cryptsize > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " cryptid " << ec.cryptid << "\n"; } static void PrintEncryptionInfoCommand64(MachO::encryption_info_command_64 ec, uint32_t object_size) { outs() << " cmd LC_ENCRYPTION_INFO_64\n"; outs() << " cmdsize " << ec.cmdsize; if (ec.cmdsize != sizeof(struct MachO::encryption_info_command_64)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " cryptoff " << ec.cryptoff; if (ec.cryptoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " cryptsize " << ec.cryptsize; if (ec.cryptsize > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " cryptid " << ec.cryptid << "\n"; outs() << " pad " << ec.pad << "\n"; } static void PrintLinkerOptionCommand(MachO::linker_option_command lo, const char *Ptr) { outs() << " cmd LC_LINKER_OPTION\n"; outs() << " cmdsize " << lo.cmdsize; if (lo.cmdsize < sizeof(struct MachO::linker_option_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " count " << lo.count << "\n"; const char *string = Ptr + sizeof(struct MachO::linker_option_command); uint32_t left = lo.cmdsize - sizeof(struct MachO::linker_option_command); uint32_t i = 0; while (left > 0) { while (*string == '\0' && left > 0) { string++; left--; } if (left > 0) { i++; outs() << " string #" << i << " " << format("%.*s\n", left, string); uint32_t NullPos = StringRef(string, left).find('\0'); uint32_t len = std::min(NullPos, left) + 1; string += len; left -= len; } } if (lo.count != i) outs() << " count " << lo.count << " does not match number of strings " << i << "\n"; } static void PrintSubFrameworkCommand(MachO::sub_framework_command sub, const char *Ptr) { outs() << " cmd LC_SUB_FRAMEWORK\n"; outs() << " cmdsize " << sub.cmdsize; if (sub.cmdsize < sizeof(struct MachO::sub_framework_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (sub.umbrella < sub.cmdsize) { const char *P = Ptr + sub.umbrella; outs() << " umbrella " << P << " (offset " << sub.umbrella << ")\n"; } else { outs() << " umbrella ?(bad offset " << sub.umbrella << ")\n"; } } static void PrintSubUmbrellaCommand(MachO::sub_umbrella_command sub, const char *Ptr) { outs() << " cmd LC_SUB_UMBRELLA\n"; outs() << " cmdsize " << sub.cmdsize; if (sub.cmdsize < sizeof(struct MachO::sub_umbrella_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (sub.sub_umbrella < sub.cmdsize) { const char *P = Ptr + sub.sub_umbrella; outs() << " sub_umbrella " << P << " (offset " << sub.sub_umbrella << ")\n"; } else { outs() << " sub_umbrella ?(bad offset " << sub.sub_umbrella << ")\n"; } } static void PrintSubLibraryCommand(MachO::sub_library_command sub, const char *Ptr) { outs() << " cmd LC_SUB_LIBRARY\n"; outs() << " cmdsize " << sub.cmdsize; if (sub.cmdsize < sizeof(struct MachO::sub_library_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (sub.sub_library < sub.cmdsize) { const char *P = Ptr + sub.sub_library; outs() << " sub_library " << P << " (offset " << sub.sub_library << ")\n"; } else { outs() << " sub_library ?(bad offset " << sub.sub_library << ")\n"; } } static void PrintSubClientCommand(MachO::sub_client_command sub, const char *Ptr) { outs() << " cmd LC_SUB_CLIENT\n"; outs() << " cmdsize " << sub.cmdsize; if (sub.cmdsize < sizeof(struct MachO::sub_client_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (sub.client < sub.cmdsize) { const char *P = Ptr + sub.client; outs() << " client " << P << " (offset " << sub.client << ")\n"; } else { outs() << " client ?(bad offset " << sub.client << ")\n"; } } static void PrintRoutinesCommand(MachO::routines_command r) { outs() << " cmd LC_ROUTINES\n"; outs() << " cmdsize " << r.cmdsize; if (r.cmdsize != sizeof(struct MachO::routines_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " init_address " << format("0x%08" PRIx32, r.init_address) << "\n"; outs() << " init_module " << r.init_module << "\n"; outs() << " reserved1 " << r.reserved1 << "\n"; outs() << " reserved2 " << r.reserved2 << "\n"; outs() << " reserved3 " << r.reserved3 << "\n"; outs() << " reserved4 " << r.reserved4 << "\n"; outs() << " reserved5 " << r.reserved5 << "\n"; outs() << " reserved6 " << r.reserved6 << "\n"; } static void PrintRoutinesCommand64(MachO::routines_command_64 r) { outs() << " cmd LC_ROUTINES_64\n"; outs() << " cmdsize " << r.cmdsize; if (r.cmdsize != sizeof(struct MachO::routines_command_64)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " init_address " << format("0x%016" PRIx64, r.init_address) << "\n"; outs() << " init_module " << r.init_module << "\n"; outs() << " reserved1 " << r.reserved1 << "\n"; outs() << " reserved2 " << r.reserved2 << "\n"; outs() << " reserved3 " << r.reserved3 << "\n"; outs() << " reserved4 " << r.reserved4 << "\n"; outs() << " reserved5 " << r.reserved5 << "\n"; outs() << " reserved6 " << r.reserved6 << "\n"; } static void Print_x86_thread_state32_t(MachO::x86_thread_state32_t &cpu32) { outs() << "\t eax " << format("0x%08" PRIx32, cpu32.eax); outs() << " ebx " << format("0x%08" PRIx32, cpu32.ebx); outs() << " ecx " << format("0x%08" PRIx32, cpu32.ecx); outs() << " edx " << format("0x%08" PRIx32, cpu32.edx) << "\n"; outs() << "\t edi " << format("0x%08" PRIx32, cpu32.edi); outs() << " esi " << format("0x%08" PRIx32, cpu32.esi); outs() << " ebp " << format("0x%08" PRIx32, cpu32.ebp); outs() << " esp " << format("0x%08" PRIx32, cpu32.esp) << "\n"; outs() << "\t ss " << format("0x%08" PRIx32, cpu32.ss); outs() << " eflags " << format("0x%08" PRIx32, cpu32.eflags); outs() << " eip " << format("0x%08" PRIx32, cpu32.eip); outs() << " cs " << format("0x%08" PRIx32, cpu32.cs) << "\n"; outs() << "\t ds " << format("0x%08" PRIx32, cpu32.ds); outs() << " es " << format("0x%08" PRIx32, cpu32.es); outs() << " fs " << format("0x%08" PRIx32, cpu32.fs); outs() << " gs " << format("0x%08" PRIx32, cpu32.gs) << "\n"; } static void Print_x86_thread_state64_t(MachO::x86_thread_state64_t &cpu64) { outs() << " rax " << format("0x%016" PRIx64, cpu64.rax); outs() << " rbx " << format("0x%016" PRIx64, cpu64.rbx); outs() << " rcx " << format("0x%016" PRIx64, cpu64.rcx) << "\n"; outs() << " rdx " << format("0x%016" PRIx64, cpu64.rdx); outs() << " rdi " << format("0x%016" PRIx64, cpu64.rdi); outs() << " rsi " << format("0x%016" PRIx64, cpu64.rsi) << "\n"; outs() << " rbp " << format("0x%016" PRIx64, cpu64.rbp); outs() << " rsp " << format("0x%016" PRIx64, cpu64.rsp); outs() << " r8 " << format("0x%016" PRIx64, cpu64.r8) << "\n"; outs() << " r9 " << format("0x%016" PRIx64, cpu64.r9); outs() << " r10 " << format("0x%016" PRIx64, cpu64.r10); outs() << " r11 " << format("0x%016" PRIx64, cpu64.r11) << "\n"; outs() << " r12 " << format("0x%016" PRIx64, cpu64.r12); outs() << " r13 " << format("0x%016" PRIx64, cpu64.r13); outs() << " r14 " << format("0x%016" PRIx64, cpu64.r14) << "\n"; outs() << " r15 " << format("0x%016" PRIx64, cpu64.r15); outs() << " rip " << format("0x%016" PRIx64, cpu64.rip) << "\n"; outs() << "rflags " << format("0x%016" PRIx64, cpu64.rflags); outs() << " cs " << format("0x%016" PRIx64, cpu64.cs); outs() << " fs " << format("0x%016" PRIx64, cpu64.fs) << "\n"; outs() << " gs " << format("0x%016" PRIx64, cpu64.gs) << "\n"; } static void Print_mmst_reg(MachO::mmst_reg_t &r) { uint32_t f; outs() << "\t mmst_reg "; for (f = 0; f < 10; f++) outs() << format("%02" PRIx32, (r.mmst_reg[f] & 0xff)) << " "; outs() << "\n"; outs() << "\t mmst_rsrv "; for (f = 0; f < 6; f++) outs() << format("%02" PRIx32, (r.mmst_rsrv[f] & 0xff)) << " "; outs() << "\n"; } static void Print_xmm_reg(MachO::xmm_reg_t &r) { uint32_t f; outs() << "\t xmm_reg "; for (f = 0; f < 16; f++) outs() << format("%02" PRIx32, (r.xmm_reg[f] & 0xff)) << " "; outs() << "\n"; } static void Print_x86_float_state_t(MachO::x86_float_state64_t &fpu) { outs() << "\t fpu_reserved[0] " << fpu.fpu_reserved[0]; outs() << " fpu_reserved[1] " << fpu.fpu_reserved[1] << "\n"; outs() << "\t control: invalid " << fpu.fpu_fcw.invalid; outs() << " denorm " << fpu.fpu_fcw.denorm; outs() << " zdiv " << fpu.fpu_fcw.zdiv; outs() << " ovrfl " << fpu.fpu_fcw.ovrfl; outs() << " undfl " << fpu.fpu_fcw.undfl; outs() << " precis " << fpu.fpu_fcw.precis << "\n"; outs() << "\t\t pc "; if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_24B) outs() << "FP_PREC_24B "; else if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_53B) outs() << "FP_PREC_53B "; else if (fpu.fpu_fcw.pc == MachO::x86_FP_PREC_64B) outs() << "FP_PREC_64B "; else outs() << fpu.fpu_fcw.pc << " "; outs() << "rc "; if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_NEAR) outs() << "FP_RND_NEAR "; else if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_DOWN) outs() << "FP_RND_DOWN "; else if (fpu.fpu_fcw.rc == MachO::x86_FP_RND_UP) outs() << "FP_RND_UP "; else if (fpu.fpu_fcw.rc == MachO::x86_FP_CHOP) outs() << "FP_CHOP "; outs() << "\n"; outs() << "\t status: invalid " << fpu.fpu_fsw.invalid; outs() << " denorm " << fpu.fpu_fsw.denorm; outs() << " zdiv " << fpu.fpu_fsw.zdiv; outs() << " ovrfl " << fpu.fpu_fsw.ovrfl; outs() << " undfl " << fpu.fpu_fsw.undfl; outs() << " precis " << fpu.fpu_fsw.precis; outs() << " stkflt " << fpu.fpu_fsw.stkflt << "\n"; outs() << "\t errsumm " << fpu.fpu_fsw.errsumm; outs() << " c0 " << fpu.fpu_fsw.c0; outs() << " c1 " << fpu.fpu_fsw.c1; outs() << " c2 " << fpu.fpu_fsw.c2; outs() << " tos " << fpu.fpu_fsw.tos; outs() << " c3 " << fpu.fpu_fsw.c3; outs() << " busy " << fpu.fpu_fsw.busy << "\n"; outs() << "\t fpu_ftw " << format("0x%02" PRIx32, fpu.fpu_ftw); outs() << " fpu_rsrv1 " << format("0x%02" PRIx32, fpu.fpu_rsrv1); outs() << " fpu_fop " << format("0x%04" PRIx32, fpu.fpu_fop); outs() << " fpu_ip " << format("0x%08" PRIx32, fpu.fpu_ip) << "\n"; outs() << "\t fpu_cs " << format("0x%04" PRIx32, fpu.fpu_cs); outs() << " fpu_rsrv2 " << format("0x%04" PRIx32, fpu.fpu_rsrv2); outs() << " fpu_dp " << format("0x%08" PRIx32, fpu.fpu_dp); outs() << " fpu_ds " << format("0x%04" PRIx32, fpu.fpu_ds) << "\n"; outs() << "\t fpu_rsrv3 " << format("0x%04" PRIx32, fpu.fpu_rsrv3); outs() << " fpu_mxcsr " << format("0x%08" PRIx32, fpu.fpu_mxcsr); outs() << " fpu_mxcsrmask " << format("0x%08" PRIx32, fpu.fpu_mxcsrmask); outs() << "\n"; outs() << "\t fpu_stmm0:\n"; Print_mmst_reg(fpu.fpu_stmm0); outs() << "\t fpu_stmm1:\n"; Print_mmst_reg(fpu.fpu_stmm1); outs() << "\t fpu_stmm2:\n"; Print_mmst_reg(fpu.fpu_stmm2); outs() << "\t fpu_stmm3:\n"; Print_mmst_reg(fpu.fpu_stmm3); outs() << "\t fpu_stmm4:\n"; Print_mmst_reg(fpu.fpu_stmm4); outs() << "\t fpu_stmm5:\n"; Print_mmst_reg(fpu.fpu_stmm5); outs() << "\t fpu_stmm6:\n"; Print_mmst_reg(fpu.fpu_stmm6); outs() << "\t fpu_stmm7:\n"; Print_mmst_reg(fpu.fpu_stmm7); outs() << "\t fpu_xmm0:\n"; Print_xmm_reg(fpu.fpu_xmm0); outs() << "\t fpu_xmm1:\n"; Print_xmm_reg(fpu.fpu_xmm1); outs() << "\t fpu_xmm2:\n"; Print_xmm_reg(fpu.fpu_xmm2); outs() << "\t fpu_xmm3:\n"; Print_xmm_reg(fpu.fpu_xmm3); outs() << "\t fpu_xmm4:\n"; Print_xmm_reg(fpu.fpu_xmm4); outs() << "\t fpu_xmm5:\n"; Print_xmm_reg(fpu.fpu_xmm5); outs() << "\t fpu_xmm6:\n"; Print_xmm_reg(fpu.fpu_xmm6); outs() << "\t fpu_xmm7:\n"; Print_xmm_reg(fpu.fpu_xmm7); outs() << "\t fpu_xmm8:\n"; Print_xmm_reg(fpu.fpu_xmm8); outs() << "\t fpu_xmm9:\n"; Print_xmm_reg(fpu.fpu_xmm9); outs() << "\t fpu_xmm10:\n"; Print_xmm_reg(fpu.fpu_xmm10); outs() << "\t fpu_xmm11:\n"; Print_xmm_reg(fpu.fpu_xmm11); outs() << "\t fpu_xmm12:\n"; Print_xmm_reg(fpu.fpu_xmm12); outs() << "\t fpu_xmm13:\n"; Print_xmm_reg(fpu.fpu_xmm13); outs() << "\t fpu_xmm14:\n"; Print_xmm_reg(fpu.fpu_xmm14); outs() << "\t fpu_xmm15:\n"; Print_xmm_reg(fpu.fpu_xmm15); outs() << "\t fpu_rsrv4:\n"; for (uint32_t f = 0; f < 6; f++) { outs() << "\t "; for (uint32_t g = 0; g < 16; g++) outs() << format("%02" PRIx32, fpu.fpu_rsrv4[f * g]) << " "; outs() << "\n"; } outs() << "\t fpu_reserved1 " << format("0x%08" PRIx32, fpu.fpu_reserved1); outs() << "\n"; } static void Print_x86_exception_state_t(MachO::x86_exception_state64_t &exc64) { outs() << "\t trapno " << format("0x%08" PRIx32, exc64.trapno); outs() << " err " << format("0x%08" PRIx32, exc64.err); outs() << " faultvaddr " << format("0x%016" PRIx64, exc64.faultvaddr) << "\n"; } static void Print_arm_thread_state32_t(MachO::arm_thread_state32_t &cpu32) { outs() << "\t r0 " << format("0x%08" PRIx32, cpu32.r[0]); outs() << " r1 " << format("0x%08" PRIx32, cpu32.r[1]); outs() << " r2 " << format("0x%08" PRIx32, cpu32.r[2]); outs() << " r3 " << format("0x%08" PRIx32, cpu32.r[3]) << "\n"; outs() << "\t r4 " << format("0x%08" PRIx32, cpu32.r[4]); outs() << " r5 " << format("0x%08" PRIx32, cpu32.r[5]); outs() << " r6 " << format("0x%08" PRIx32, cpu32.r[6]); outs() << " r7 " << format("0x%08" PRIx32, cpu32.r[7]) << "\n"; outs() << "\t r8 " << format("0x%08" PRIx32, cpu32.r[8]); outs() << " r9 " << format("0x%08" PRIx32, cpu32.r[9]); outs() << " r10 " << format("0x%08" PRIx32, cpu32.r[10]); outs() << " r11 " << format("0x%08" PRIx32, cpu32.r[11]) << "\n"; outs() << "\t r12 " << format("0x%08" PRIx32, cpu32.r[12]); outs() << " sp " << format("0x%08" PRIx32, cpu32.sp); outs() << " lr " << format("0x%08" PRIx32, cpu32.lr); outs() << " pc " << format("0x%08" PRIx32, cpu32.pc) << "\n"; outs() << "\t cpsr " << format("0x%08" PRIx32, cpu32.cpsr) << "\n"; } static void Print_arm_thread_state64_t(MachO::arm_thread_state64_t &cpu64) { outs() << "\t x0 " << format("0x%016" PRIx64, cpu64.x[0]); outs() << " x1 " << format("0x%016" PRIx64, cpu64.x[1]); outs() << " x2 " << format("0x%016" PRIx64, cpu64.x[2]) << "\n"; outs() << "\t x3 " << format("0x%016" PRIx64, cpu64.x[3]); outs() << " x4 " << format("0x%016" PRIx64, cpu64.x[4]); outs() << " x5 " << format("0x%016" PRIx64, cpu64.x[5]) << "\n"; outs() << "\t x6 " << format("0x%016" PRIx64, cpu64.x[6]); outs() << " x7 " << format("0x%016" PRIx64, cpu64.x[7]); outs() << " x8 " << format("0x%016" PRIx64, cpu64.x[8]) << "\n"; outs() << "\t x9 " << format("0x%016" PRIx64, cpu64.x[9]); outs() << " x10 " << format("0x%016" PRIx64, cpu64.x[10]); outs() << " x11 " << format("0x%016" PRIx64, cpu64.x[11]) << "\n"; outs() << "\t x12 " << format("0x%016" PRIx64, cpu64.x[12]); outs() << " x13 " << format("0x%016" PRIx64, cpu64.x[13]); outs() << " x14 " << format("0x%016" PRIx64, cpu64.x[14]) << "\n"; outs() << "\t x15 " << format("0x%016" PRIx64, cpu64.x[15]); outs() << " x16 " << format("0x%016" PRIx64, cpu64.x[16]); outs() << " x17 " << format("0x%016" PRIx64, cpu64.x[17]) << "\n"; outs() << "\t x18 " << format("0x%016" PRIx64, cpu64.x[18]); outs() << " x19 " << format("0x%016" PRIx64, cpu64.x[19]); outs() << " x20 " << format("0x%016" PRIx64, cpu64.x[20]) << "\n"; outs() << "\t x21 " << format("0x%016" PRIx64, cpu64.x[21]); outs() << " x22 " << format("0x%016" PRIx64, cpu64.x[22]); outs() << " x23 " << format("0x%016" PRIx64, cpu64.x[23]) << "\n"; outs() << "\t x24 " << format("0x%016" PRIx64, cpu64.x[24]); outs() << " x25 " << format("0x%016" PRIx64, cpu64.x[25]); outs() << " x26 " << format("0x%016" PRIx64, cpu64.x[26]) << "\n"; outs() << "\t x27 " << format("0x%016" PRIx64, cpu64.x[27]); outs() << " x28 " << format("0x%016" PRIx64, cpu64.x[28]); outs() << " fp " << format("0x%016" PRIx64, cpu64.fp) << "\n"; outs() << "\t lr " << format("0x%016" PRIx64, cpu64.lr); outs() << " sp " << format("0x%016" PRIx64, cpu64.sp); outs() << " pc " << format("0x%016" PRIx64, cpu64.pc) << "\n"; outs() << "\t cpsr " << format("0x%08" PRIx32, cpu64.cpsr) << "\n"; } static void PrintThreadCommand(MachO::thread_command t, const char *Ptr, bool isLittleEndian, uint32_t cputype) { if (t.cmd == MachO::LC_THREAD) outs() << " cmd LC_THREAD\n"; else if (t.cmd == MachO::LC_UNIXTHREAD) outs() << " cmd LC_UNIXTHREAD\n"; else outs() << " cmd " << t.cmd << " (unknown)\n"; outs() << " cmdsize " << t.cmdsize; if (t.cmdsize < sizeof(struct MachO::thread_command) + 2 * sizeof(uint32_t)) outs() << " Incorrect size\n"; else outs() << "\n"; const char *begin = Ptr + sizeof(struct MachO::thread_command); const char *end = Ptr + t.cmdsize; uint32_t flavor, count, left; if (cputype == MachO::CPU_TYPE_I386) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { flavor = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(flavor); if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&count, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { count = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(count); if (flavor == MachO::x86_THREAD_STATE32) { outs() << " flavor i386_THREAD_STATE\n"; if (count == MachO::x86_THREAD_STATE32_COUNT) outs() << " count i386_THREAD_STATE_COUNT\n"; else outs() << " count " << count << " (not x86_THREAD_STATE32_COUNT)\n"; MachO::x86_thread_state32_t cpu32; left = end - begin; if (left >= sizeof(MachO::x86_thread_state32_t)) { memcpy(&cpu32, begin, sizeof(MachO::x86_thread_state32_t)); begin += sizeof(MachO::x86_thread_state32_t); } else { memset(&cpu32, '\0', sizeof(MachO::x86_thread_state32_t)); memcpy(&cpu32, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(cpu32); Print_x86_thread_state32_t(cpu32); } else if (flavor == MachO::x86_THREAD_STATE) { outs() << " flavor x86_THREAD_STATE\n"; if (count == MachO::x86_THREAD_STATE_COUNT) outs() << " count x86_THREAD_STATE_COUNT\n"; else outs() << " count " << count << " (not x86_THREAD_STATE_COUNT)\n"; struct MachO::x86_thread_state_t ts; left = end - begin; if (left >= sizeof(MachO::x86_thread_state_t)) { memcpy(&ts, begin, sizeof(MachO::x86_thread_state_t)); begin += sizeof(MachO::x86_thread_state_t); } else { memset(&ts, '\0', sizeof(MachO::x86_thread_state_t)); memcpy(&ts, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(ts); if (ts.tsh.flavor == MachO::x86_THREAD_STATE32) { outs() << "\t tsh.flavor x86_THREAD_STATE32 "; if (ts.tsh.count == MachO::x86_THREAD_STATE32_COUNT) outs() << "tsh.count x86_THREAD_STATE32_COUNT\n"; else outs() << "tsh.count " << ts.tsh.count << " (not x86_THREAD_STATE32_COUNT\n"; Print_x86_thread_state32_t(ts.uts.ts32); } else { outs() << "\t tsh.flavor " << ts.tsh.flavor << " tsh.count " << ts.tsh.count << "\n"; } } else { outs() << " flavor " << flavor << " (unknown)\n"; outs() << " count " << count << "\n"; outs() << " state (unknown)\n"; begin += count * sizeof(uint32_t); } } } else if (cputype == MachO::CPU_TYPE_X86_64) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { flavor = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(flavor); if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&count, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { count = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(count); if (flavor == MachO::x86_THREAD_STATE64) { outs() << " flavor x86_THREAD_STATE64\n"; if (count == MachO::x86_THREAD_STATE64_COUNT) outs() << " count x86_THREAD_STATE64_COUNT\n"; else outs() << " count " << count << " (not x86_THREAD_STATE64_COUNT)\n"; MachO::x86_thread_state64_t cpu64; left = end - begin; if (left >= sizeof(MachO::x86_thread_state64_t)) { memcpy(&cpu64, begin, sizeof(MachO::x86_thread_state64_t)); begin += sizeof(MachO::x86_thread_state64_t); } else { memset(&cpu64, '\0', sizeof(MachO::x86_thread_state64_t)); memcpy(&cpu64, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(cpu64); Print_x86_thread_state64_t(cpu64); } else if (flavor == MachO::x86_THREAD_STATE) { outs() << " flavor x86_THREAD_STATE\n"; if (count == MachO::x86_THREAD_STATE_COUNT) outs() << " count x86_THREAD_STATE_COUNT\n"; else outs() << " count " << count << " (not x86_THREAD_STATE_COUNT)\n"; struct MachO::x86_thread_state_t ts; left = end - begin; if (left >= sizeof(MachO::x86_thread_state_t)) { memcpy(&ts, begin, sizeof(MachO::x86_thread_state_t)); begin += sizeof(MachO::x86_thread_state_t); } else { memset(&ts, '\0', sizeof(MachO::x86_thread_state_t)); memcpy(&ts, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(ts); if (ts.tsh.flavor == MachO::x86_THREAD_STATE64) { outs() << "\t tsh.flavor x86_THREAD_STATE64 "; if (ts.tsh.count == MachO::x86_THREAD_STATE64_COUNT) outs() << "tsh.count x86_THREAD_STATE64_COUNT\n"; else outs() << "tsh.count " << ts.tsh.count << " (not x86_THREAD_STATE64_COUNT\n"; Print_x86_thread_state64_t(ts.uts.ts64); } else { outs() << "\t tsh.flavor " << ts.tsh.flavor << " tsh.count " << ts.tsh.count << "\n"; } } else if (flavor == MachO::x86_FLOAT_STATE) { outs() << " flavor x86_FLOAT_STATE\n"; if (count == MachO::x86_FLOAT_STATE_COUNT) outs() << " count x86_FLOAT_STATE_COUNT\n"; else outs() << " count " << count << " (not x86_FLOAT_STATE_COUNT)\n"; struct MachO::x86_float_state_t fs; left = end - begin; if (left >= sizeof(MachO::x86_float_state_t)) { memcpy(&fs, begin, sizeof(MachO::x86_float_state_t)); begin += sizeof(MachO::x86_float_state_t); } else { memset(&fs, '\0', sizeof(MachO::x86_float_state_t)); memcpy(&fs, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(fs); if (fs.fsh.flavor == MachO::x86_FLOAT_STATE64) { outs() << "\t fsh.flavor x86_FLOAT_STATE64 "; if (fs.fsh.count == MachO::x86_FLOAT_STATE64_COUNT) outs() << "fsh.count x86_FLOAT_STATE64_COUNT\n"; else outs() << "fsh.count " << fs.fsh.count << " (not x86_FLOAT_STATE64_COUNT\n"; Print_x86_float_state_t(fs.ufs.fs64); } else { outs() << "\t fsh.flavor " << fs.fsh.flavor << " fsh.count " << fs.fsh.count << "\n"; } } else if (flavor == MachO::x86_EXCEPTION_STATE) { outs() << " flavor x86_EXCEPTION_STATE\n"; if (count == MachO::x86_EXCEPTION_STATE_COUNT) outs() << " count x86_EXCEPTION_STATE_COUNT\n"; else outs() << " count " << count << " (not x86_EXCEPTION_STATE_COUNT)\n"; struct MachO::x86_exception_state_t es; left = end - begin; if (left >= sizeof(MachO::x86_exception_state_t)) { memcpy(&es, begin, sizeof(MachO::x86_exception_state_t)); begin += sizeof(MachO::x86_exception_state_t); } else { memset(&es, '\0', sizeof(MachO::x86_exception_state_t)); memcpy(&es, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(es); if (es.esh.flavor == MachO::x86_EXCEPTION_STATE64) { outs() << "\t esh.flavor x86_EXCEPTION_STATE64\n"; if (es.esh.count == MachO::x86_EXCEPTION_STATE64_COUNT) outs() << "\t esh.count x86_EXCEPTION_STATE64_COUNT\n"; else outs() << "\t esh.count " << es.esh.count << " (not x86_EXCEPTION_STATE64_COUNT\n"; Print_x86_exception_state_t(es.ues.es64); } else { outs() << "\t esh.flavor " << es.esh.flavor << " esh.count " << es.esh.count << "\n"; } } else if (flavor == MachO::x86_EXCEPTION_STATE64) { outs() << " flavor x86_EXCEPTION_STATE64\n"; if (count == MachO::x86_EXCEPTION_STATE64_COUNT) outs() << " count x86_EXCEPTION_STATE64_COUNT\n"; else outs() << " count " << count << " (not x86_EXCEPTION_STATE64_COUNT)\n"; struct MachO::x86_exception_state64_t es64; left = end - begin; if (left >= sizeof(MachO::x86_exception_state64_t)) { memcpy(&es64, begin, sizeof(MachO::x86_exception_state64_t)); begin += sizeof(MachO::x86_exception_state64_t); } else { memset(&es64, '\0', sizeof(MachO::x86_exception_state64_t)); memcpy(&es64, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(es64); Print_x86_exception_state_t(es64); } else { outs() << " flavor " << flavor << " (unknown)\n"; outs() << " count " << count << "\n"; outs() << " state (unknown)\n"; begin += count * sizeof(uint32_t); } } } else if (cputype == MachO::CPU_TYPE_ARM) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { flavor = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(flavor); if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&count, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { count = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(count); if (flavor == MachO::ARM_THREAD_STATE) { outs() << " flavor ARM_THREAD_STATE\n"; if (count == MachO::ARM_THREAD_STATE_COUNT) outs() << " count ARM_THREAD_STATE_COUNT\n"; else outs() << " count " << count << " (not ARM_THREAD_STATE_COUNT)\n"; MachO::arm_thread_state32_t cpu32; left = end - begin; if (left >= sizeof(MachO::arm_thread_state32_t)) { memcpy(&cpu32, begin, sizeof(MachO::arm_thread_state32_t)); begin += sizeof(MachO::arm_thread_state32_t); } else { memset(&cpu32, '\0', sizeof(MachO::arm_thread_state32_t)); memcpy(&cpu32, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(cpu32); Print_arm_thread_state32_t(cpu32); } else { outs() << " flavor " << flavor << " (unknown)\n"; outs() << " count " << count << "\n"; outs() << " state (unknown)\n"; begin += count * sizeof(uint32_t); } } } else if (cputype == MachO::CPU_TYPE_ARM64 || cputype == MachO::CPU_TYPE_ARM64_32) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { flavor = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(flavor); if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&count, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { count = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(count); if (flavor == MachO::ARM_THREAD_STATE64) { outs() << " flavor ARM_THREAD_STATE64\n"; if (count == MachO::ARM_THREAD_STATE64_COUNT) outs() << " count ARM_THREAD_STATE64_COUNT\n"; else outs() << " count " << count << " (not ARM_THREAD_STATE64_COUNT)\n"; MachO::arm_thread_state64_t cpu64; left = end - begin; if (left >= sizeof(MachO::arm_thread_state64_t)) { memcpy(&cpu64, begin, sizeof(MachO::arm_thread_state64_t)); begin += sizeof(MachO::arm_thread_state64_t); } else { memset(&cpu64, '\0', sizeof(MachO::arm_thread_state64_t)); memcpy(&cpu64, begin, left); begin += left; } if (isLittleEndian != sys::IsLittleEndianHost) swapStruct(cpu64); Print_arm_thread_state64_t(cpu64); } else { outs() << " flavor " << flavor << " (unknown)\n"; outs() << " count " << count << "\n"; outs() << " state (unknown)\n"; begin += count * sizeof(uint32_t); } } } else { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { flavor = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(flavor); if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&count, begin, sizeof(uint32_t)); begin += sizeof(uint32_t); } else { count = 0; begin = end; } if (isLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(count); outs() << " flavor " << flavor << "\n"; outs() << " count " << count << "\n"; outs() << " state (Unknown cputype/cpusubtype)\n"; begin += count * sizeof(uint32_t); } } } static void PrintDylibCommand(MachO::dylib_command dl, const char *Ptr) { if (dl.cmd == MachO::LC_ID_DYLIB) outs() << " cmd LC_ID_DYLIB\n"; else if (dl.cmd == MachO::LC_LOAD_DYLIB) outs() << " cmd LC_LOAD_DYLIB\n"; else if (dl.cmd == MachO::LC_LOAD_WEAK_DYLIB) outs() << " cmd LC_LOAD_WEAK_DYLIB\n"; else if (dl.cmd == MachO::LC_REEXPORT_DYLIB) outs() << " cmd LC_REEXPORT_DYLIB\n"; else if (dl.cmd == MachO::LC_LAZY_LOAD_DYLIB) outs() << " cmd LC_LAZY_LOAD_DYLIB\n"; else if (dl.cmd == MachO::LC_LOAD_UPWARD_DYLIB) outs() << " cmd LC_LOAD_UPWARD_DYLIB\n"; else outs() << " cmd " << dl.cmd << " (unknown)\n"; outs() << " cmdsize " << dl.cmdsize; if (dl.cmdsize < sizeof(struct MachO::dylib_command)) outs() << " Incorrect size\n"; else outs() << "\n"; if (dl.dylib.name < dl.cmdsize) { const char *P = (const char *)(Ptr) + dl.dylib.name; outs() << " name " << P << " (offset " << dl.dylib.name << ")\n"; } else { outs() << " name ?(bad offset " << dl.dylib.name << ")\n"; } outs() << " time stamp " << dl.dylib.timestamp << " "; time_t t = dl.dylib.timestamp; outs() << ctime(&t); outs() << " current version "; if (dl.dylib.current_version == 0xffffffff) outs() << "n/a\n"; else outs() << ((dl.dylib.current_version >> 16) & 0xffff) << "." << ((dl.dylib.current_version >> 8) & 0xff) << "." << (dl.dylib.current_version & 0xff) << "\n"; outs() << "compatibility version "; if (dl.dylib.compatibility_version == 0xffffffff) outs() << "n/a\n"; else outs() << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "." << ((dl.dylib.compatibility_version >> 8) & 0xff) << "." << (dl.dylib.compatibility_version & 0xff) << "\n"; } static void PrintLinkEditDataCommand(MachO::linkedit_data_command ld, uint32_t object_size) { if (ld.cmd == MachO::LC_CODE_SIGNATURE) outs() << " cmd LC_CODE_SIGNATURE\n"; else if (ld.cmd == MachO::LC_SEGMENT_SPLIT_INFO) outs() << " cmd LC_SEGMENT_SPLIT_INFO\n"; else if (ld.cmd == MachO::LC_FUNCTION_STARTS) outs() << " cmd LC_FUNCTION_STARTS\n"; else if (ld.cmd == MachO::LC_DATA_IN_CODE) outs() << " cmd LC_DATA_IN_CODE\n"; else if (ld.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS) outs() << " cmd LC_DYLIB_CODE_SIGN_DRS\n"; else if (ld.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT) outs() << " cmd LC_LINKER_OPTIMIZATION_HINT\n"; else outs() << " cmd " << ld.cmd << " (?)\n"; outs() << " cmdsize " << ld.cmdsize; if (ld.cmdsize != sizeof(struct MachO::linkedit_data_command)) outs() << " Incorrect size\n"; else outs() << "\n"; outs() << " dataoff " << ld.dataoff; if (ld.dataoff > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; outs() << " datasize " << ld.datasize; uint64_t big_size = ld.dataoff; big_size += ld.datasize; if (big_size > object_size) outs() << " (past end of file)\n"; else outs() << "\n"; } static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype, uint32_t cputype, bool verbose) { StringRef Buf = Obj->getData(); unsigned Index = 0; for (const auto &Command : Obj->load_commands()) { outs() << "Load command " << Index++ << "\n"; if (Command.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command SLC = Obj->getSegmentLoadCommand(Command); const char *sg_segname = SLC.segname; PrintSegmentCommand(SLC.cmd, SLC.cmdsize, SLC.segname, SLC.vmaddr, SLC.vmsize, SLC.fileoff, SLC.filesize, SLC.maxprot, SLC.initprot, SLC.nsects, SLC.flags, Buf.size(), verbose); for (unsigned j = 0; j < SLC.nsects; j++) { MachO::section S = Obj->getSection(Command, j); PrintSection(S.sectname, S.segname, S.addr, S.size, S.offset, S.align, S.reloff, S.nreloc, S.flags, S.reserved1, S.reserved2, SLC.cmd, sg_segname, filetype, Buf.size(), verbose); } } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 SLC_64 = Obj->getSegment64LoadCommand(Command); const char *sg_segname = SLC_64.segname; PrintSegmentCommand(SLC_64.cmd, SLC_64.cmdsize, SLC_64.segname, SLC_64.vmaddr, SLC_64.vmsize, SLC_64.fileoff, SLC_64.filesize, SLC_64.maxprot, SLC_64.initprot, SLC_64.nsects, SLC_64.flags, Buf.size(), verbose); for (unsigned j = 0; j < SLC_64.nsects; j++) { MachO::section_64 S_64 = Obj->getSection64(Command, j); PrintSection(S_64.sectname, S_64.segname, S_64.addr, S_64.size, S_64.offset, S_64.align, S_64.reloff, S_64.nreloc, S_64.flags, S_64.reserved1, S_64.reserved2, SLC_64.cmd, sg_segname, filetype, Buf.size(), verbose); } } else if (Command.C.cmd == MachO::LC_SYMTAB) { MachO::symtab_command Symtab = Obj->getSymtabLoadCommand(); PrintSymtabLoadCommand(Symtab, Obj->is64Bit(), Buf.size()); } else if (Command.C.cmd == MachO::LC_DYSYMTAB) { MachO::dysymtab_command Dysymtab = Obj->getDysymtabLoadCommand(); MachO::symtab_command Symtab = Obj->getSymtabLoadCommand(); PrintDysymtabLoadCommand(Dysymtab, Symtab.nsyms, Buf.size(), Obj->is64Bit()); } else if (Command.C.cmd == MachO::LC_DYLD_INFO || Command.C.cmd == MachO::LC_DYLD_INFO_ONLY) { MachO::dyld_info_command DyldInfo = Obj->getDyldInfoLoadCommand(Command); PrintDyldInfoLoadCommand(DyldInfo, Buf.size()); } else if (Command.C.cmd == MachO::LC_LOAD_DYLINKER || Command.C.cmd == MachO::LC_ID_DYLINKER || Command.C.cmd == MachO::LC_DYLD_ENVIRONMENT) { MachO::dylinker_command Dyld = Obj->getDylinkerCommand(Command); PrintDyldLoadCommand(Dyld, Command.Ptr); } else if (Command.C.cmd == MachO::LC_UUID) { MachO::uuid_command Uuid = Obj->getUuidCommand(Command); PrintUuidLoadCommand(Uuid); } else if (Command.C.cmd == MachO::LC_RPATH) { MachO::rpath_command Rpath = Obj->getRpathCommand(Command); PrintRpathLoadCommand(Rpath, Command.Ptr); } else if (Command.C.cmd == MachO::LC_VERSION_MIN_MACOSX || Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS || Command.C.cmd == MachO::LC_VERSION_MIN_TVOS || Command.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) { MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command); PrintVersionMinLoadCommand(Vd); } else if (Command.C.cmd == MachO::LC_NOTE) { MachO::note_command Nt = Obj->getNoteLoadCommand(Command); PrintNoteLoadCommand(Nt); } else if (Command.C.cmd == MachO::LC_BUILD_VERSION) { MachO::build_version_command Bv = Obj->getBuildVersionLoadCommand(Command); PrintBuildVersionLoadCommand(Obj, Bv); } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) { MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command); PrintSourceVersionCommand(Sd); } else if (Command.C.cmd == MachO::LC_MAIN) { MachO::entry_point_command Ep = Obj->getEntryPointCommand(Command); PrintEntryPointCommand(Ep); } else if (Command.C.cmd == MachO::LC_ENCRYPTION_INFO) { MachO::encryption_info_command Ei = Obj->getEncryptionInfoCommand(Command); PrintEncryptionInfoCommand(Ei, Buf.size()); } else if (Command.C.cmd == MachO::LC_ENCRYPTION_INFO_64) { MachO::encryption_info_command_64 Ei = Obj->getEncryptionInfoCommand64(Command); PrintEncryptionInfoCommand64(Ei, Buf.size()); } else if (Command.C.cmd == MachO::LC_LINKER_OPTION) { MachO::linker_option_command Lo = Obj->getLinkerOptionLoadCommand(Command); PrintLinkerOptionCommand(Lo, Command.Ptr); } else if (Command.C.cmd == MachO::LC_SUB_FRAMEWORK) { MachO::sub_framework_command Sf = Obj->getSubFrameworkCommand(Command); PrintSubFrameworkCommand(Sf, Command.Ptr); } else if (Command.C.cmd == MachO::LC_SUB_UMBRELLA) { MachO::sub_umbrella_command Sf = Obj->getSubUmbrellaCommand(Command); PrintSubUmbrellaCommand(Sf, Command.Ptr); } else if (Command.C.cmd == MachO::LC_SUB_LIBRARY) { MachO::sub_library_command Sl = Obj->getSubLibraryCommand(Command); PrintSubLibraryCommand(Sl, Command.Ptr); } else if (Command.C.cmd == MachO::LC_SUB_CLIENT) { MachO::sub_client_command Sc = Obj->getSubClientCommand(Command); PrintSubClientCommand(Sc, Command.Ptr); } else if (Command.C.cmd == MachO::LC_ROUTINES) { MachO::routines_command Rc = Obj->getRoutinesCommand(Command); PrintRoutinesCommand(Rc); } else if (Command.C.cmd == MachO::LC_ROUTINES_64) { MachO::routines_command_64 Rc = Obj->getRoutinesCommand64(Command); PrintRoutinesCommand64(Rc); } else if (Command.C.cmd == MachO::LC_THREAD || Command.C.cmd == MachO::LC_UNIXTHREAD) { MachO::thread_command Tc = Obj->getThreadCommand(Command); PrintThreadCommand(Tc, Command.Ptr, Obj->isLittleEndian(), cputype); } else if (Command.C.cmd == MachO::LC_LOAD_DYLIB || Command.C.cmd == MachO::LC_ID_DYLIB || Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || Command.C.cmd == MachO::LC_REEXPORT_DYLIB || Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command); PrintDylibCommand(Dl, Command.Ptr); } else if (Command.C.cmd == MachO::LC_CODE_SIGNATURE || Command.C.cmd == MachO::LC_SEGMENT_SPLIT_INFO || Command.C.cmd == MachO::LC_FUNCTION_STARTS || Command.C.cmd == MachO::LC_DATA_IN_CODE || Command.C.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS || Command.C.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT) { MachO::linkedit_data_command Ld = Obj->getLinkeditDataLoadCommand(Command); PrintLinkEditDataCommand(Ld, Buf.size()); } else { outs() << " cmd ?(" << format("0x%08" PRIx32, Command.C.cmd) << ")\n"; outs() << " cmdsize " << Command.C.cmdsize << "\n"; // TODO: get and print the raw bytes of the load command. } // TODO: print all the other kinds of load commands. } } static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) { if (Obj->is64Bit()) { MachO::mach_header_64 H_64; H_64 = Obj->getHeader64(); PrintMachHeader(H_64.magic, H_64.cputype, H_64.cpusubtype, H_64.filetype, H_64.ncmds, H_64.sizeofcmds, H_64.flags, verbose); } else { MachO::mach_header H; H = Obj->getHeader(); PrintMachHeader(H.magic, H.cputype, H.cpusubtype, H.filetype, H.ncmds, H.sizeofcmds, H.flags, verbose); } } void objdump::printMachOFileHeader(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast(Obj); PrintMachHeader(file, !NonVerbose); } void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast(Obj); uint32_t filetype = 0; uint32_t cputype = 0; if (file->is64Bit()) { MachO::mach_header_64 H_64; H_64 = file->getHeader64(); filetype = H_64.filetype; cputype = H_64.cputype; } else { MachO::mach_header H; H = file->getHeader(); filetype = H.filetype; cputype = H.cputype; } PrintLoadCommands(file, filetype, cputype, !NonVerbose); } //===----------------------------------------------------------------------===// // export trie dumping //===----------------------------------------------------------------------===// static void printMachOExportsTrie(const object::MachOObjectFile *Obj) { uint64_t BaseSegmentAddress = 0; for (const auto &Command : Obj->load_commands()) { if (Command.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command Seg = Obj->getSegmentLoadCommand(Command); if (Seg.fileoff == 0 && Seg.filesize != 0) { BaseSegmentAddress = Seg.vmaddr; break; } } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = Obj->getSegment64LoadCommand(Command); if (Seg.fileoff == 0 && Seg.filesize != 0) { BaseSegmentAddress = Seg.vmaddr; break; } } } Error Err = Error::success(); for (const object::ExportEntry &Entry : Obj->exports(Err)) { uint64_t Flags = Entry.flags(); bool ReExport = (Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); bool WeakDef = (Flags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); bool ThreadLocal = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) == MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); bool Abs = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) == MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); bool Resolver = (Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); if (ReExport) outs() << "[re-export] "; else outs() << format("0x%08llX ", Entry.address() + BaseSegmentAddress); outs() << Entry.name(); if (WeakDef || ThreadLocal || Resolver || Abs) { bool NeedsComma = false; outs() << " ["; if (WeakDef) { outs() << "weak_def"; NeedsComma = true; } if (ThreadLocal) { if (NeedsComma) outs() << ", "; outs() << "per-thread"; NeedsComma = true; } if (Abs) { if (NeedsComma) outs() << ", "; outs() << "absolute"; NeedsComma = true; } if (Resolver) { if (NeedsComma) outs() << ", "; outs() << format("resolver=0x%08llX", Entry.other()); NeedsComma = true; } outs() << "]"; } if (ReExport) { StringRef DylibName = "unknown"; int Ordinal = Entry.other() - 1; Obj->getLibraryShortNameByIndex(Ordinal, DylibName); if (Entry.otherName().empty()) outs() << " (from " << DylibName << ")"; else outs() << " (" << Entry.otherName() << " from " << DylibName << ")"; } outs() << "\n"; } if (Err) reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // rebase table dumping //===----------------------------------------------------------------------===// static void printMachORebaseTable(object::MachOObjectFile *Obj) { outs() << "segment section address type\n"; Error Err = Error::success(); for (const object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); // Table lines look like: __DATA __nl_symbol_ptr 0x0000F00C pointer outs() << format("%-8s %-18s 0x%08" PRIX64 " %s\n", SegmentName.str().c_str(), SectionName.str().c_str(), Address, Entry.typeName().str().c_str()); } if (Err) reportError(std::move(Err), Obj->getFileName()); } static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { StringRef DylibName; switch (Ordinal) { case MachO::BIND_SPECIAL_DYLIB_SELF: return "this-image"; case MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: return "main-executable"; case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP: return "flat-namespace"; default: if (Ordinal > 0) { std::error_code EC = Obj->getLibraryShortNameByIndex(Ordinal - 1, DylibName); if (EC) return "<>"; return DylibName; } } return "<>"; } //===----------------------------------------------------------------------===// // bind table dumping //===----------------------------------------------------------------------===// static void printMachOBindTable(object::MachOObjectFile *Obj) { // Build table of sections so names can used in final output. outs() << "segment section address type " "addend dylib symbol\n"; Error Err = Error::success(); for (const object::MachOBindEntry &Entry : Obj->bindTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard StringRef Attr; if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT) Attr = " (weak_import)"; outs() << left_justify(SegmentName, 8) << " " << left_justify(SectionName, 18) << " " << format_hex(Address, 10, true) << " " << left_justify(Entry.typeName(), 8) << " " << format_decimal(Entry.addend(), 8) << " " << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " << Entry.symbolName() << Attr << "\n"; } if (Err) reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // lazy bind table dumping //===----------------------------------------------------------------------===// static void printMachOLazyBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "dylib symbol\n"; Error Err = Error::success(); for (const object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); // Table lines look like: // __DATA __got 0x00012010 libSystem ___stack_chk_guard outs() << left_justify(SegmentName, 8) << " " << left_justify(SectionName, 18) << " " << format_hex(Address, 10, true) << " " << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " << Entry.symbolName() << "\n"; } if (Err) reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // weak bind table dumping //===----------------------------------------------------------------------===// static void printMachOWeakBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "type addend symbol\n"; Error Err = Error::success(); for (const object::MachOBindEntry &Entry : Obj->weakBindTable(Err)) { // Strong symbols don't have a location to update. if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { outs() << " strong " << Entry.symbolName() << "\n"; continue; } StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); // Table lines look like: // __DATA __data 0x00001000 pointer 0 _foo outs() << left_justify(SegmentName, 8) << " " << left_justify(SectionName, 18) << " " << format_hex(Address, 10, true) << " " << left_justify(Entry.typeName(), 8) << " " << format_decimal(Entry.addend(), 8) << " " << Entry.symbolName() << "\n"; } if (Err) reportError(std::move(Err), Obj->getFileName()); } // get_dyld_bind_info_symbolname() is used for disassembly and passed an // address, ReferenceValue, in the Mach-O file and looks in the dyld bind // information for that address. If the address is found its binding symbol // name is returned. If not nullptr is returned. static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info) { if (info->bindtable == nullptr) { info->bindtable = std::make_unique(); Error Err = Error::success(); for (const object::MachOBindEntry &Entry : info->O->bindTable(Err)) { uint64_t Address = Entry.address(); StringRef name = Entry.symbolName(); if (!name.empty()) (*info->bindtable)[Address] = name; } if (Err) reportError(std::move(Err), info->O->getFileName()); } auto name = info->bindtable->lookup(ReferenceValue); return !name.empty() ? name.data() : nullptr; } void objdump::printLazyBindTable(ObjectFile *o) { outs() << "Lazy bind table:\n"; if (MachOObjectFile *MachO = dyn_cast(o)) printMachOLazyBindTable(MachO); else WithColor::error() << "This operation is only currently supported " "for Mach-O executable files.\n"; } void objdump::printWeakBindTable(ObjectFile *o) { outs() << "Weak bind table:\n"; if (MachOObjectFile *MachO = dyn_cast(o)) printMachOWeakBindTable(MachO); else WithColor::error() << "This operation is only currently supported " "for Mach-O executable files.\n"; } void objdump::printExportsTrie(const ObjectFile *o) { outs() << "Exports trie:\n"; if (const MachOObjectFile *MachO = dyn_cast(o)) printMachOExportsTrie(MachO); else WithColor::error() << "This operation is only currently supported " "for Mach-O executable files.\n"; } void objdump::printRebaseTable(ObjectFile *o) { outs() << "Rebase table:\n"; if (MachOObjectFile *MachO = dyn_cast(o)) printMachORebaseTable(MachO); else WithColor::error() << "This operation is only currently supported " "for Mach-O executable files.\n"; } void objdump::printBindTable(ObjectFile *o) { outs() << "Bind table:\n"; if (MachOObjectFile *MachO = dyn_cast(o)) printMachOBindTable(MachO); else WithColor::error() << "This operation is only currently supported " "for Mach-O executable files.\n"; } diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp index 857d7f56ad77..1ca4a68eb155 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -1,4735 +1,4735 @@ //===- CodeGenDAGPatterns.cpp - Read DAG patterns from .td file -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the CodeGenDAGPatterns class, which is used to read and // represent the patterns present in a .td file for instructions. // //===----------------------------------------------------------------------===// #include "CodeGenDAGPatterns.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TypeSize.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include #include #include #include using namespace llvm; #define DEBUG_TYPE "dag-patterns" static inline bool isIntegerOrPtr(MVT VT) { return VT.isInteger() || VT == MVT::iPTR; } static inline bool isFloatingPoint(MVT VT) { return VT.isFloatingPoint(); } static inline bool isVector(MVT VT) { return VT.isVector(); } static inline bool isScalar(MVT VT) { return !VT.isVector(); } template static bool berase_if(MachineValueTypeSet &S, Predicate P) { bool Erased = false; // It is ok to iterate over MachineValueTypeSet and remove elements from it // at the same time. for (MVT T : S) { if (!P(T)) continue; Erased = true; S.erase(T); } return Erased; } // --- TypeSetByHwMode // This is a parameterized type-set class. For each mode there is a list // of types that are currently possible for a given tree node. Type // inference will apply to each mode separately. TypeSetByHwMode::TypeSetByHwMode(ArrayRef VTList) { for (const ValueTypeByHwMode &VVT : VTList) { insert(VVT); AddrSpaces.push_back(VVT.PtrAddrSpace); } } bool TypeSetByHwMode::isValueTypeByHwMode(bool AllowEmpty) const { for (const auto &I : *this) { if (I.second.size() > 1) return false; if (!AllowEmpty && I.second.empty()) return false; } return true; } ValueTypeByHwMode TypeSetByHwMode::getValueTypeByHwMode() const { assert(isValueTypeByHwMode(true) && "The type set has multiple types for at least one HW mode"); ValueTypeByHwMode VVT; auto ASI = AddrSpaces.begin(); for (const auto &I : *this) { MVT T = I.second.empty() ? MVT::Other : *I.second.begin(); VVT.getOrCreateTypeForMode(I.first, T); if (ASI != AddrSpaces.end()) VVT.PtrAddrSpace = *ASI++; } return VVT; } bool TypeSetByHwMode::isPossible() const { for (const auto &I : *this) if (!I.second.empty()) return true; return false; } bool TypeSetByHwMode::insert(const ValueTypeByHwMode &VVT) { bool Changed = false; bool ContainsDefault = false; MVT DT = MVT::Other; SmallDenseSet Modes; for (const auto &P : VVT) { unsigned M = P.first; Modes.insert(M); // Make sure there exists a set for each specific mode from VVT. Changed |= getOrCreate(M).insert(P.second).second; // Cache VVT's default mode. if (DefaultMode == M) { ContainsDefault = true; DT = P.second; } } // If VVT has a default mode, add the corresponding type to all // modes in "this" that do not exist in VVT. if (ContainsDefault) for (auto &I : *this) if (!Modes.count(I.first)) Changed |= I.second.insert(DT).second; return Changed; } // Constrain the type set to be the intersection with VTS. bool TypeSetByHwMode::constrain(const TypeSetByHwMode &VTS) { bool Changed = false; if (hasDefault()) { for (const auto &I : VTS) { unsigned M = I.first; if (M == DefaultMode || hasMode(M)) continue; Map.insert({M, Map.at(DefaultMode)}); Changed = true; } } for (auto &I : *this) { unsigned M = I.first; SetType &S = I.second; if (VTS.hasMode(M) || VTS.hasDefault()) { Changed |= intersect(I.second, VTS.get(M)); } else if (!S.empty()) { S.clear(); Changed = true; } } return Changed; } template bool TypeSetByHwMode::constrain(Predicate P) { bool Changed = false; for (auto &I : *this) Changed |= berase_if(I.second, [&P](MVT VT) { return !P(VT); }); return Changed; } template bool TypeSetByHwMode::assign_if(const TypeSetByHwMode &VTS, Predicate P) { assert(empty()); for (const auto &I : VTS) { SetType &S = getOrCreate(I.first); for (auto J : I.second) if (P(J)) S.insert(J); } return !empty(); } void TypeSetByHwMode::writeToStream(raw_ostream &OS) const { SmallVector Modes; Modes.reserve(Map.size()); for (const auto &I : *this) Modes.push_back(I.first); if (Modes.empty()) { OS << "{}"; return; } array_pod_sort(Modes.begin(), Modes.end()); OS << '{'; for (unsigned M : Modes) { OS << ' ' << getModeName(M) << ':'; writeToStream(get(M), OS); } OS << " }"; } void TypeSetByHwMode::writeToStream(const SetType &S, raw_ostream &OS) { SmallVector Types(S.begin(), S.end()); array_pod_sort(Types.begin(), Types.end()); OS << '['; for (unsigned i = 0, e = Types.size(); i != e; ++i) { OS << ValueTypeByHwMode::getMVTName(Types[i]); if (i != e-1) OS << ' '; } OS << ']'; } bool TypeSetByHwMode::operator==(const TypeSetByHwMode &VTS) const { // The isSimple call is much quicker than hasDefault - check this first. bool IsSimple = isSimple(); bool VTSIsSimple = VTS.isSimple(); if (IsSimple && VTSIsSimple) return *begin() == *VTS.begin(); // Speedup: We have a default if the set is simple. bool HaveDefault = IsSimple || hasDefault(); bool VTSHaveDefault = VTSIsSimple || VTS.hasDefault(); if (HaveDefault != VTSHaveDefault) return false; SmallDenseSet Modes; for (auto &I : *this) Modes.insert(I.first); for (const auto &I : VTS) Modes.insert(I.first); if (HaveDefault) { // Both sets have default mode. for (unsigned M : Modes) { if (get(M) != VTS.get(M)) return false; } } else { // Neither set has default mode. for (unsigned M : Modes) { // If there is no default mode, an empty set is equivalent to not having // the corresponding mode. bool NoModeThis = !hasMode(M) || get(M).empty(); bool NoModeVTS = !VTS.hasMode(M) || VTS.get(M).empty(); if (NoModeThis != NoModeVTS) return false; if (!NoModeThis) if (get(M) != VTS.get(M)) return false; } } return true; } namespace llvm { raw_ostream &operator<<(raw_ostream &OS, const TypeSetByHwMode &T) { T.writeToStream(OS); return OS; } } LLVM_DUMP_METHOD void TypeSetByHwMode::dump() const { dbgs() << *this << '\n'; } bool TypeSetByHwMode::intersect(SetType &Out, const SetType &In) { bool OutP = Out.count(MVT::iPTR), InP = In.count(MVT::iPTR); auto Int = [&In](MVT T) -> bool { return !In.count(T); }; if (OutP == InP) return berase_if(Out, Int); // Compute the intersection of scalars separately to account for only // one set containing iPTR. // The intersection of iPTR with a set of integer scalar types that does not // include iPTR will result in the most specific scalar type: // - iPTR is more specific than any set with two elements or more // - iPTR is less specific than any single integer scalar type. // For example // { iPTR } * { i32 } -> { i32 } // { iPTR } * { i32 i64 } -> { iPTR } // and // { iPTR i32 } * { i32 } -> { i32 } // { iPTR i32 } * { i32 i64 } -> { i32 i64 } // { iPTR i32 } * { i32 i64 i128 } -> { iPTR i32 } // Compute the difference between the two sets in such a way that the // iPTR is in the set that is being subtracted. This is to see if there // are any extra scalars in the set without iPTR that are not in the // set containing iPTR. Then the iPTR could be considered a "wildcard" // matching these scalars. If there is only one such scalar, it would // replace the iPTR, if there are more, the iPTR would be retained. SetType Diff; if (InP) { Diff = Out; berase_if(Diff, [&In](MVT T) { return In.count(T); }); // Pre-remove these elements and rely only on InP/OutP to determine // whether a change has been made. berase_if(Out, [&Diff](MVT T) { return Diff.count(T); }); } else { Diff = In; berase_if(Diff, [&Out](MVT T) { return Out.count(T); }); Out.erase(MVT::iPTR); } // The actual intersection. bool Changed = berase_if(Out, Int); unsigned NumD = Diff.size(); if (NumD == 0) return Changed; if (NumD == 1) { Out.insert(*Diff.begin()); // This is a change only if Out was the one with iPTR (which is now // being replaced). Changed |= OutP; } else { // Multiple elements from Out are now replaced with iPTR. Out.insert(MVT::iPTR); Changed |= !OutP; } return Changed; } bool TypeSetByHwMode::validate() const { #ifndef NDEBUG if (empty()) return true; bool AllEmpty = true; for (const auto &I : *this) AllEmpty &= I.second.empty(); return !AllEmpty; #endif return true; } // --- TypeInfer bool TypeInfer::MergeInTypeInfo(TypeSetByHwMode &Out, const TypeSetByHwMode &In) { ValidateOnExit _1(Out, *this); In.validate(); if (In.empty() || Out == In || TP.hasError()) return false; if (Out.empty()) { Out = In; return true; } bool Changed = Out.constrain(In); if (Changed && Out.empty()) TP.error("Type contradiction"); return Changed; } bool TypeInfer::forceArbitrary(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError()) return false; assert(!Out.empty() && "cannot pick from an empty set"); bool Changed = false; for (auto &I : Out) { TypeSetByHwMode::SetType &S = I.second; if (S.size() <= 1) continue; MVT T = *S.begin(); // Pick the first element. S.clear(); S.insert(T); Changed = true; } return Changed; } bool TypeInfer::EnforceInteger(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError()) return false; if (!Out.empty()) return Out.constrain(isIntegerOrPtr); return Out.assign_if(getLegalTypes(), isIntegerOrPtr); } bool TypeInfer::EnforceFloatingPoint(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError()) return false; if (!Out.empty()) return Out.constrain(isFloatingPoint); return Out.assign_if(getLegalTypes(), isFloatingPoint); } bool TypeInfer::EnforceScalar(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError()) return false; if (!Out.empty()) return Out.constrain(isScalar); return Out.assign_if(getLegalTypes(), isScalar); } bool TypeInfer::EnforceVector(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError()) return false; if (!Out.empty()) return Out.constrain(isVector); return Out.assign_if(getLegalTypes(), isVector); } bool TypeInfer::EnforceAny(TypeSetByHwMode &Out) { ValidateOnExit _1(Out, *this); if (TP.hasError() || !Out.empty()) return false; Out = getLegalTypes(); return true; } template static Iter min_if(Iter B, Iter E, Pred P, Less L) { if (B == E) return E; Iter Min = E; for (Iter I = B; I != E; ++I) { if (!P(*I)) continue; if (Min == E || L(*I, *Min)) Min = I; } return Min; } template static Iter max_if(Iter B, Iter E, Pred P, Less L) { if (B == E) return E; Iter Max = E; for (Iter I = B; I != E; ++I) { if (!P(*I)) continue; if (Max == E || L(*Max, *I)) Max = I; } return Max; } /// Make sure that for each type in Small, there exists a larger type in Big. bool TypeInfer::EnforceSmallerThan(TypeSetByHwMode &Small, TypeSetByHwMode &Big) { ValidateOnExit _1(Small, *this), _2(Big, *this); if (TP.hasError()) return false; bool Changed = false; if (Small.empty()) Changed |= EnforceAny(Small); if (Big.empty()) Changed |= EnforceAny(Big); assert(Small.hasDefault() && Big.hasDefault()); std::vector Modes = union_modes(Small, Big); // 1. Only allow integer or floating point types and make sure that // both sides are both integer or both floating point. // 2. Make sure that either both sides have vector types, or neither // of them does. for (unsigned M : Modes) { TypeSetByHwMode::SetType &S = Small.get(M); TypeSetByHwMode::SetType &B = Big.get(M); if (any_of(S, isIntegerOrPtr) && any_of(S, isIntegerOrPtr)) { auto NotInt = [](MVT VT) { return !isIntegerOrPtr(VT); }; Changed |= berase_if(S, NotInt); Changed |= berase_if(B, NotInt); } else if (any_of(S, isFloatingPoint) && any_of(B, isFloatingPoint)) { auto NotFP = [](MVT VT) { return !isFloatingPoint(VT); }; Changed |= berase_if(S, NotFP); Changed |= berase_if(B, NotFP); } else if (S.empty() || B.empty()) { Changed = !S.empty() || !B.empty(); S.clear(); B.clear(); } else { TP.error("Incompatible types"); return Changed; } if (none_of(S, isVector) || none_of(B, isVector)) { Changed |= berase_if(S, isVector); Changed |= berase_if(B, isVector); } } auto LT = [](MVT A, MVT B) -> bool { // Always treat non-scalable MVTs as smaller than scalable MVTs for the // purposes of ordering. auto ASize = std::make_tuple(A.isScalableVector(), A.getScalarSizeInBits(), A.getSizeInBits().getKnownMinSize()); auto BSize = std::make_tuple(B.isScalableVector(), B.getScalarSizeInBits(), B.getSizeInBits().getKnownMinSize()); return ASize < BSize; }; auto SameKindLE = [](MVT A, MVT B) -> bool { // This function is used when removing elements: when a vector is compared // to a non-vector or a scalable vector to any non-scalable MVT, it should // return false (to avoid removal). if (std::make_tuple(A.isVector(), A.isScalableVector()) != std::make_tuple(B.isVector(), B.isScalableVector())) return false; return std::make_tuple(A.getScalarSizeInBits(), A.getSizeInBits().getKnownMinSize()) <= std::make_tuple(B.getScalarSizeInBits(), B.getSizeInBits().getKnownMinSize()); }; for (unsigned M : Modes) { TypeSetByHwMode::SetType &S = Small.get(M); TypeSetByHwMode::SetType &B = Big.get(M); // MinS = min scalar in Small, remove all scalars from Big that are // smaller-or-equal than MinS. auto MinS = min_if(S.begin(), S.end(), isScalar, LT); if (MinS != S.end()) Changed |= berase_if(B, std::bind(SameKindLE, std::placeholders::_1, *MinS)); // MaxS = max scalar in Big, remove all scalars from Small that are // larger than MaxS. auto MaxS = max_if(B.begin(), B.end(), isScalar, LT); if (MaxS != B.end()) Changed |= berase_if(S, std::bind(SameKindLE, *MaxS, std::placeholders::_1)); // MinV = min vector in Small, remove all vectors from Big that are // smaller-or-equal than MinV. auto MinV = min_if(S.begin(), S.end(), isVector, LT); if (MinV != S.end()) Changed |= berase_if(B, std::bind(SameKindLE, std::placeholders::_1, *MinV)); // MaxV = max vector in Big, remove all vectors from Small that are // larger than MaxV. auto MaxV = max_if(B.begin(), B.end(), isVector, LT); if (MaxV != B.end()) Changed |= berase_if(S, std::bind(SameKindLE, *MaxV, std::placeholders::_1)); } return Changed; } /// 1. Ensure that for each type T in Vec, T is a vector type, and that /// for each type U in Elem, U is a scalar type. /// 2. Ensure that for each (scalar) type U in Elem, there exists a (vector) /// type T in Vec, such that U is the element type of T. bool TypeInfer::EnforceVectorEltTypeIs(TypeSetByHwMode &Vec, TypeSetByHwMode &Elem) { ValidateOnExit _1(Vec, *this), _2(Elem, *this); if (TP.hasError()) return false; bool Changed = false; if (Vec.empty()) Changed |= EnforceVector(Vec); if (Elem.empty()) Changed |= EnforceScalar(Elem); for (unsigned M : union_modes(Vec, Elem)) { TypeSetByHwMode::SetType &V = Vec.get(M); TypeSetByHwMode::SetType &E = Elem.get(M); Changed |= berase_if(V, isScalar); // Scalar = !vector Changed |= berase_if(E, isVector); // Vector = !scalar assert(!V.empty() && !E.empty()); SmallSet VT, ST; // Collect element types from the "vector" set. for (MVT T : V) VT.insert(T.getVectorElementType()); // Collect scalar types from the "element" set. for (MVT T : E) ST.insert(T); // Remove from V all (vector) types whose element type is not in S. Changed |= berase_if(V, [&ST](MVT T) -> bool { return !ST.count(T.getVectorElementType()); }); // Remove from E all (scalar) types, for which there is no corresponding // type in V. Changed |= berase_if(E, [&VT](MVT T) -> bool { return !VT.count(T); }); } return Changed; } bool TypeInfer::EnforceVectorEltTypeIs(TypeSetByHwMode &Vec, const ValueTypeByHwMode &VVT) { TypeSetByHwMode Tmp(VVT); ValidateOnExit _1(Vec, *this), _2(Tmp, *this); return EnforceVectorEltTypeIs(Vec, Tmp); } /// Ensure that for each type T in Sub, T is a vector type, and there /// exists a type U in Vec such that U is a vector type with the same /// element type as T and at least as many elements as T. bool TypeInfer::EnforceVectorSubVectorTypeIs(TypeSetByHwMode &Vec, TypeSetByHwMode &Sub) { ValidateOnExit _1(Vec, *this), _2(Sub, *this); if (TP.hasError()) return false; /// Return true if B is a suB-vector of P, i.e. P is a suPer-vector of B. auto IsSubVec = [](MVT B, MVT P) -> bool { if (!B.isVector() || !P.isVector()) return false; // Logically a <4 x i32> is a valid subvector of // but until there are obvious use-cases for this, keep the // types separate. if (B.isScalableVector() != P.isScalableVector()) return false; if (B.getVectorElementType() != P.getVectorElementType()) return false; return B.getVectorNumElements() < P.getVectorNumElements(); }; /// Return true if S has no element (vector type) that T is a sub-vector of, /// i.e. has the same element type as T and more elements. auto NoSubV = [&IsSubVec](const TypeSetByHwMode::SetType &S, MVT T) -> bool { for (auto I : S) if (IsSubVec(T, I)) return false; return true; }; /// Return true if S has no element (vector type) that T is a super-vector /// of, i.e. has the same element type as T and fewer elements. auto NoSupV = [&IsSubVec](const TypeSetByHwMode::SetType &S, MVT T) -> bool { for (auto I : S) if (IsSubVec(I, T)) return false; return true; }; bool Changed = false; if (Vec.empty()) Changed |= EnforceVector(Vec); if (Sub.empty()) Changed |= EnforceVector(Sub); for (unsigned M : union_modes(Vec, Sub)) { TypeSetByHwMode::SetType &S = Sub.get(M); TypeSetByHwMode::SetType &V = Vec.get(M); Changed |= berase_if(S, isScalar); // Erase all types from S that are not sub-vectors of a type in V. Changed |= berase_if(S, std::bind(NoSubV, V, std::placeholders::_1)); // Erase all types from V that are not super-vectors of a type in S. Changed |= berase_if(V, std::bind(NoSupV, S, std::placeholders::_1)); } return Changed; } /// 1. Ensure that V has a scalar type iff W has a scalar type. /// 2. Ensure that for each vector type T in V, there exists a vector /// type U in W, such that T and U have the same number of elements. /// 3. Ensure that for each vector type U in W, there exists a vector /// type T in V, such that T and U have the same number of elements /// (reverse of 2). bool TypeInfer::EnforceSameNumElts(TypeSetByHwMode &V, TypeSetByHwMode &W) { ValidateOnExit _1(V, *this), _2(W, *this); if (TP.hasError()) return false; bool Changed = false; if (V.empty()) Changed |= EnforceAny(V); if (W.empty()) Changed |= EnforceAny(W); // An actual vector type cannot have 0 elements, so we can treat scalars // as zero-length vectors. This way both vectors and scalars can be // processed identically. auto NoLength = [](const SmallSet &Lengths, MVT T) -> bool { return !Lengths.count(T.isVector() ? T.getVectorNumElements() : 0); }; for (unsigned M : union_modes(V, W)) { TypeSetByHwMode::SetType &VS = V.get(M); TypeSetByHwMode::SetType &WS = W.get(M); SmallSet VN, WN; for (MVT T : VS) VN.insert(T.isVector() ? T.getVectorNumElements() : 0); for (MVT T : WS) WN.insert(T.isVector() ? T.getVectorNumElements() : 0); Changed |= berase_if(VS, std::bind(NoLength, WN, std::placeholders::_1)); Changed |= berase_if(WS, std::bind(NoLength, VN, std::placeholders::_1)); } return Changed; } /// 1. Ensure that for each type T in A, there exists a type U in B, /// such that T and U have equal size in bits. /// 2. Ensure that for each type U in B, there exists a type T in A /// such that T and U have equal size in bits (reverse of 1). bool TypeInfer::EnforceSameSize(TypeSetByHwMode &A, TypeSetByHwMode &B) { ValidateOnExit _1(A, *this), _2(B, *this); if (TP.hasError()) return false; bool Changed = false; if (A.empty()) Changed |= EnforceAny(A); if (B.empty()) Changed |= EnforceAny(B); auto NoSize = [](const SmallSet &Sizes, MVT T) -> bool { return !Sizes.count(T.getSizeInBits()); }; for (unsigned M : union_modes(A, B)) { TypeSetByHwMode::SetType &AS = A.get(M); TypeSetByHwMode::SetType &BS = B.get(M); SmallSet AN, BN; for (MVT T : AS) AN.insert(T.getSizeInBits()); for (MVT T : BS) BN.insert(T.getSizeInBits()); Changed |= berase_if(AS, std::bind(NoSize, BN, std::placeholders::_1)); Changed |= berase_if(BS, std::bind(NoSize, AN, std::placeholders::_1)); } return Changed; } void TypeInfer::expandOverloads(TypeSetByHwMode &VTS) { ValidateOnExit _1(VTS, *this); const TypeSetByHwMode &Legal = getLegalTypes(); assert(Legal.isDefaultOnly() && "Default-mode only expected"); const TypeSetByHwMode::SetType &LegalTypes = Legal.get(DefaultMode); for (auto &I : VTS) expandOverloads(I.second, LegalTypes); } void TypeInfer::expandOverloads(TypeSetByHwMode::SetType &Out, const TypeSetByHwMode::SetType &Legal) { std::set Ovs; for (MVT T : Out) { if (!T.isOverloaded()) continue; Ovs.insert(T); // MachineValueTypeSet allows iteration and erasing. Out.erase(T); } for (MVT Ov : Ovs) { switch (Ov.SimpleTy) { case MVT::iPTRAny: Out.insert(MVT::iPTR); return; case MVT::iAny: for (MVT T : MVT::integer_valuetypes()) if (Legal.count(T)) Out.insert(T); for (MVT T : MVT::integer_fixedlen_vector_valuetypes()) if (Legal.count(T)) Out.insert(T); for (MVT T : MVT::integer_scalable_vector_valuetypes()) if (Legal.count(T)) Out.insert(T); return; case MVT::fAny: for (MVT T : MVT::fp_valuetypes()) if (Legal.count(T)) Out.insert(T); for (MVT T : MVT::fp_fixedlen_vector_valuetypes()) if (Legal.count(T)) Out.insert(T); for (MVT T : MVT::fp_scalable_vector_valuetypes()) if (Legal.count(T)) Out.insert(T); return; case MVT::vAny: for (MVT T : MVT::vector_valuetypes()) if (Legal.count(T)) Out.insert(T); return; case MVT::Any: for (MVT T : MVT::all_valuetypes()) if (Legal.count(T)) Out.insert(T); return; default: break; } } } const TypeSetByHwMode &TypeInfer::getLegalTypes() { if (!LegalTypesCached) { TypeSetByHwMode::SetType &LegalTypes = LegalCache.getOrCreate(DefaultMode); // Stuff all types from all modes into the default mode. const TypeSetByHwMode <S = TP.getDAGPatterns().getLegalTypes(); for (const auto &I : LTS) LegalTypes.insert(I.second); LegalTypesCached = true; } assert(LegalCache.isDefaultOnly() && "Default-mode only expected"); return LegalCache; } #ifndef NDEBUG TypeInfer::ValidateOnExit::~ValidateOnExit() { if (Infer.Validate && !VTS.validate()) { dbgs() << "Type set is empty for each HW mode:\n" "possible type contradiction in the pattern below " "(use -print-records with llvm-tblgen to see all " "expanded records).\n"; Infer.TP.dump(); llvm_unreachable(nullptr); } } #endif //===----------------------------------------------------------------------===// // ScopedName Implementation //===----------------------------------------------------------------------===// bool ScopedName::operator==(const ScopedName &o) const { return Scope == o.Scope && Identifier == o.Identifier; } bool ScopedName::operator!=(const ScopedName &o) const { return !(*this == o); } //===----------------------------------------------------------------------===// // TreePredicateFn Implementation //===----------------------------------------------------------------------===// /// TreePredicateFn constructor. Here 'N' is a subclass of PatFrag. TreePredicateFn::TreePredicateFn(TreePattern *N) : PatFragRec(N) { assert( (!hasPredCode() || !hasImmCode()) && ".td file corrupt: can't have a node predicate *and* an imm predicate"); } bool TreePredicateFn::hasPredCode() const { return isLoad() || isStore() || isAtomic() || !PatFragRec->getRecord()->getValueAsString("PredicateCode").empty(); } std::string TreePredicateFn::getPredCode() const { - std::string Code = ""; + std::string Code; if (!isLoad() && !isStore() && !isAtomic()) { Record *MemoryVT = getMemoryVT(); if (MemoryVT) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "MemoryVT requires IsLoad or IsStore"); } if (!isLoad() && !isStore()) { if (isUnindexed()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsUnindexed requires IsLoad or IsStore"); Record *ScalarMemoryVT = getScalarMemoryVT(); if (ScalarMemoryVT) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "ScalarMemoryVT requires IsLoad or IsStore"); } if (isLoad() + isStore() + isAtomic() > 1) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsLoad, IsStore, and IsAtomic are mutually exclusive"); if (isLoad()) { if (!isUnindexed() && !isNonExtLoad() && !isAnyExtLoad() && !isSignExtLoad() && !isZeroExtLoad() && getMemoryVT() == nullptr && getScalarMemoryVT() == nullptr && getAddressSpaces() == nullptr && getMinAlignment() < 1) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsLoad cannot be used by itself"); } else { if (isNonExtLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonExtLoad requires IsLoad"); if (isAnyExtLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAnyExtLoad requires IsLoad"); if (isSignExtLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsSignExtLoad requires IsLoad"); if (isZeroExtLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsZeroExtLoad requires IsLoad"); } if (isStore()) { if (!isUnindexed() && !isTruncStore() && !isNonTruncStore() && getMemoryVT() == nullptr && getScalarMemoryVT() == nullptr && getAddressSpaces() == nullptr && getMinAlignment() < 1) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsStore cannot be used by itself"); } else { if (isNonTruncStore()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonTruncStore requires IsStore"); if (isTruncStore()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsTruncStore requires IsStore"); } if (isAtomic()) { if (getMemoryVT() == nullptr && !isAtomicOrderingMonotonic() && getAddressSpaces() == nullptr && !isAtomicOrderingAcquire() && !isAtomicOrderingRelease() && !isAtomicOrderingAcquireRelease() && !isAtomicOrderingSequentiallyConsistent() && !isAtomicOrderingAcquireOrStronger() && !isAtomicOrderingReleaseOrStronger() && !isAtomicOrderingWeakerThanAcquire() && !isAtomicOrderingWeakerThanRelease()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomic cannot be used by itself"); } else { if (isAtomicOrderingMonotonic()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingMonotonic requires IsAtomic"); if (isAtomicOrderingAcquire()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingAcquire requires IsAtomic"); if (isAtomicOrderingRelease()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingRelease requires IsAtomic"); if (isAtomicOrderingAcquireRelease()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingAcquireRelease requires IsAtomic"); if (isAtomicOrderingSequentiallyConsistent()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingSequentiallyConsistent requires IsAtomic"); if (isAtomicOrderingAcquireOrStronger()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingAcquireOrStronger requires IsAtomic"); if (isAtomicOrderingReleaseOrStronger()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingReleaseOrStronger requires IsAtomic"); if (isAtomicOrderingWeakerThanAcquire()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsAtomicOrderingWeakerThanAcquire requires IsAtomic"); } if (isLoad() || isStore() || isAtomic()) { if (ListInit *AddressSpaces = getAddressSpaces()) { Code += "unsigned AddrSpace = cast(N)->getAddressSpace();\n" " if ("; bool First = true; for (Init *Val : AddressSpaces->getValues()) { if (First) First = false; else Code += " && "; IntInit *IntVal = dyn_cast(Val); if (!IntVal) { PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "AddressSpaces element must be integer"); } Code += "AddrSpace != " + utostr(IntVal->getValue()); } Code += ")\nreturn false;\n"; } int64_t MinAlign = getMinAlignment(); if (MinAlign > 0) { Code += "if (cast(N)->getAlign() < Align("; Code += utostr(MinAlign); Code += "))\nreturn false;\n"; } Record *MemoryVT = getMemoryVT(); if (MemoryVT) Code += ("if (cast(N)->getMemoryVT() != MVT::" + MemoryVT->getName() + ") return false;\n") .str(); } if (isAtomic() && isAtomicOrderingMonotonic()) Code += "if (cast(N)->getOrdering() != " "AtomicOrdering::Monotonic) return false;\n"; if (isAtomic() && isAtomicOrderingAcquire()) Code += "if (cast(N)->getOrdering() != " "AtomicOrdering::Acquire) return false;\n"; if (isAtomic() && isAtomicOrderingRelease()) Code += "if (cast(N)->getOrdering() != " "AtomicOrdering::Release) return false;\n"; if (isAtomic() && isAtomicOrderingAcquireRelease()) Code += "if (cast(N)->getOrdering() != " "AtomicOrdering::AcquireRelease) return false;\n"; if (isAtomic() && isAtomicOrderingSequentiallyConsistent()) Code += "if (cast(N)->getOrdering() != " "AtomicOrdering::SequentiallyConsistent) return false;\n"; if (isAtomic() && isAtomicOrderingAcquireOrStronger()) Code += "if (!isAcquireOrStronger(cast(N)->getOrdering())) " "return false;\n"; if (isAtomic() && isAtomicOrderingWeakerThanAcquire()) Code += "if (isAcquireOrStronger(cast(N)->getOrdering())) " "return false;\n"; if (isAtomic() && isAtomicOrderingReleaseOrStronger()) Code += "if (!isReleaseOrStronger(cast(N)->getOrdering())) " "return false;\n"; if (isAtomic() && isAtomicOrderingWeakerThanRelease()) Code += "if (isReleaseOrStronger(cast(N)->getOrdering())) " "return false;\n"; if (isLoad() || isStore()) { StringRef SDNodeName = isLoad() ? "LoadSDNode" : "StoreSDNode"; if (isUnindexed()) Code += ("if (cast<" + SDNodeName + ">(N)->getAddressingMode() != ISD::UNINDEXED) " "return false;\n") .str(); if (isLoad()) { if ((isNonExtLoad() + isAnyExtLoad() + isSignExtLoad() + isZeroExtLoad()) > 1) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonExtLoad, IsAnyExtLoad, IsSignExtLoad, and " "IsZeroExtLoad are mutually exclusive"); if (isNonExtLoad()) Code += "if (cast(N)->getExtensionType() != " "ISD::NON_EXTLOAD) return false;\n"; if (isAnyExtLoad()) Code += "if (cast(N)->getExtensionType() != ISD::EXTLOAD) " "return false;\n"; if (isSignExtLoad()) Code += "if (cast(N)->getExtensionType() != ISD::SEXTLOAD) " "return false;\n"; if (isZeroExtLoad()) Code += "if (cast(N)->getExtensionType() != ISD::ZEXTLOAD) " "return false;\n"; } else { if ((isNonTruncStore() + isTruncStore()) > 1) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonTruncStore, and IsTruncStore are mutually exclusive"); if (isNonTruncStore()) Code += " if (cast(N)->isTruncatingStore()) return false;\n"; if (isTruncStore()) Code += " if (!cast(N)->isTruncatingStore()) return false;\n"; } Record *ScalarMemoryVT = getScalarMemoryVT(); if (ScalarMemoryVT) Code += ("if (cast<" + SDNodeName + ">(N)->getMemoryVT().getScalarType() != MVT::" + ScalarMemoryVT->getName() + ") return false;\n") .str(); } std::string PredicateCode = std::string(PatFragRec->getRecord()->getValueAsString("PredicateCode")); Code += PredicateCode; if (PredicateCode.empty() && !Code.empty()) Code += "return true;\n"; return Code; } bool TreePredicateFn::hasImmCode() const { return !PatFragRec->getRecord()->getValueAsString("ImmediateCode").empty(); } std::string TreePredicateFn::getImmCode() const { return std::string( PatFragRec->getRecord()->getValueAsString("ImmediateCode")); } bool TreePredicateFn::immCodeUsesAPInt() const { return getOrigPatFragRecord()->getRecord()->getValueAsBit("IsAPInt"); } bool TreePredicateFn::immCodeUsesAPFloat() const { bool Unset; // The return value will be false when IsAPFloat is unset. return getOrigPatFragRecord()->getRecord()->getValueAsBitOrUnset("IsAPFloat", Unset); } bool TreePredicateFn::isPredefinedPredicateEqualTo(StringRef Field, bool Value) const { bool Unset; bool Result = getOrigPatFragRecord()->getRecord()->getValueAsBitOrUnset(Field, Unset); if (Unset) return false; return Result == Value; } bool TreePredicateFn::usesOperands() const { return isPredefinedPredicateEqualTo("PredicateCodeUsesOperands", true); } bool TreePredicateFn::isLoad() const { return isPredefinedPredicateEqualTo("IsLoad", true); } bool TreePredicateFn::isStore() const { return isPredefinedPredicateEqualTo("IsStore", true); } bool TreePredicateFn::isAtomic() const { return isPredefinedPredicateEqualTo("IsAtomic", true); } bool TreePredicateFn::isUnindexed() const { return isPredefinedPredicateEqualTo("IsUnindexed", true); } bool TreePredicateFn::isNonExtLoad() const { return isPredefinedPredicateEqualTo("IsNonExtLoad", true); } bool TreePredicateFn::isAnyExtLoad() const { return isPredefinedPredicateEqualTo("IsAnyExtLoad", true); } bool TreePredicateFn::isSignExtLoad() const { return isPredefinedPredicateEqualTo("IsSignExtLoad", true); } bool TreePredicateFn::isZeroExtLoad() const { return isPredefinedPredicateEqualTo("IsZeroExtLoad", true); } bool TreePredicateFn::isNonTruncStore() const { return isPredefinedPredicateEqualTo("IsTruncStore", false); } bool TreePredicateFn::isTruncStore() const { return isPredefinedPredicateEqualTo("IsTruncStore", true); } bool TreePredicateFn::isAtomicOrderingMonotonic() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingMonotonic", true); } bool TreePredicateFn::isAtomicOrderingAcquire() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingAcquire", true); } bool TreePredicateFn::isAtomicOrderingRelease() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingRelease", true); } bool TreePredicateFn::isAtomicOrderingAcquireRelease() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingAcquireRelease", true); } bool TreePredicateFn::isAtomicOrderingSequentiallyConsistent() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingSequentiallyConsistent", true); } bool TreePredicateFn::isAtomicOrderingAcquireOrStronger() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingAcquireOrStronger", true); } bool TreePredicateFn::isAtomicOrderingWeakerThanAcquire() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingAcquireOrStronger", false); } bool TreePredicateFn::isAtomicOrderingReleaseOrStronger() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingReleaseOrStronger", true); } bool TreePredicateFn::isAtomicOrderingWeakerThanRelease() const { return isPredefinedPredicateEqualTo("IsAtomicOrderingReleaseOrStronger", false); } Record *TreePredicateFn::getMemoryVT() const { Record *R = getOrigPatFragRecord()->getRecord(); if (R->isValueUnset("MemoryVT")) return nullptr; return R->getValueAsDef("MemoryVT"); } ListInit *TreePredicateFn::getAddressSpaces() const { Record *R = getOrigPatFragRecord()->getRecord(); if (R->isValueUnset("AddressSpaces")) return nullptr; return R->getValueAsListInit("AddressSpaces"); } int64_t TreePredicateFn::getMinAlignment() const { Record *R = getOrigPatFragRecord()->getRecord(); if (R->isValueUnset("MinAlignment")) return 0; return R->getValueAsInt("MinAlignment"); } Record *TreePredicateFn::getScalarMemoryVT() const { Record *R = getOrigPatFragRecord()->getRecord(); if (R->isValueUnset("ScalarMemoryVT")) return nullptr; return R->getValueAsDef("ScalarMemoryVT"); } bool TreePredicateFn::hasGISelPredicateCode() const { return !PatFragRec->getRecord() ->getValueAsString("GISelPredicateCode") .empty(); } std::string TreePredicateFn::getGISelPredicateCode() const { return std::string( PatFragRec->getRecord()->getValueAsString("GISelPredicateCode")); } StringRef TreePredicateFn::getImmType() const { if (immCodeUsesAPInt()) return "const APInt &"; if (immCodeUsesAPFloat()) return "const APFloat &"; return "int64_t"; } StringRef TreePredicateFn::getImmTypeIdentifier() const { if (immCodeUsesAPInt()) return "APInt"; else if (immCodeUsesAPFloat()) return "APFloat"; return "I64"; } /// isAlwaysTrue - Return true if this is a noop predicate. bool TreePredicateFn::isAlwaysTrue() const { return !hasPredCode() && !hasImmCode(); } /// Return the name to use in the generated code to reference this, this is /// "Predicate_foo" if from a pattern fragment "foo". std::string TreePredicateFn::getFnName() const { return "Predicate_" + PatFragRec->getRecord()->getName().str(); } /// getCodeToRunOnSDNode - Return the code for the function body that /// evaluates this predicate. The argument is expected to be in "Node", /// not N. This handles casting and conversion to a concrete node type as /// appropriate. std::string TreePredicateFn::getCodeToRunOnSDNode() const { // Handle immediate predicates first. std::string ImmCode = getImmCode(); if (!ImmCode.empty()) { if (isLoad()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsLoad cannot be used with ImmLeaf or its subclasses"); if (isStore()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "IsStore cannot be used with ImmLeaf or its subclasses"); if (isUnindexed()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsUnindexed cannot be used with ImmLeaf or its subclasses"); if (isNonExtLoad()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonExtLoad cannot be used with ImmLeaf or its subclasses"); if (isAnyExtLoad()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsAnyExtLoad cannot be used with ImmLeaf or its subclasses"); if (isSignExtLoad()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsSignExtLoad cannot be used with ImmLeaf or its subclasses"); if (isZeroExtLoad()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsZeroExtLoad cannot be used with ImmLeaf or its subclasses"); if (isNonTruncStore()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsNonTruncStore cannot be used with ImmLeaf or its subclasses"); if (isTruncStore()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "IsTruncStore cannot be used with ImmLeaf or its subclasses"); if (getMemoryVT()) PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "MemoryVT cannot be used with ImmLeaf or its subclasses"); if (getScalarMemoryVT()) PrintFatalError( getOrigPatFragRecord()->getRecord()->getLoc(), "ScalarMemoryVT cannot be used with ImmLeaf or its subclasses"); std::string Result = (" " + getImmType() + " Imm = ").str(); if (immCodeUsesAPFloat()) Result += "cast(Node)->getValueAPF();\n"; else if (immCodeUsesAPInt()) Result += "cast(Node)->getAPIntValue();\n"; else Result += "cast(Node)->getSExtValue();\n"; return Result + ImmCode; } // Handle arbitrary node predicates. assert(hasPredCode() && "Don't have any predicate code!"); // If this is using PatFrags, there are multiple trees to search. They should // all have the same class. FIXME: Is there a way to find a common // superclass? StringRef ClassName; for (const auto &Tree : PatFragRec->getTrees()) { StringRef TreeClassName; if (Tree->isLeaf()) TreeClassName = "SDNode"; else { Record *Op = Tree->getOperator(); const SDNodeInfo &Info = PatFragRec->getDAGPatterns().getSDNodeInfo(Op); TreeClassName = Info.getSDClassName(); } if (ClassName.empty()) ClassName = TreeClassName; else if (ClassName != TreeClassName) { PrintFatalError(getOrigPatFragRecord()->getRecord()->getLoc(), "PatFrags trees do not have consistent class"); } } std::string Result; if (ClassName == "SDNode") Result = " SDNode *N = Node;\n"; else Result = " auto *N = cast<" + ClassName.str() + ">(Node);\n"; return (Twine(Result) + " (void)N;\n" + getPredCode()).str(); } //===----------------------------------------------------------------------===// // PatternToMatch implementation // static bool isImmAllOnesAllZerosMatch(const TreePatternNode *P) { if (!P->isLeaf()) return false; DefInit *DI = dyn_cast(P->getLeafValue()); if (!DI) return false; Record *R = DI->getDef(); return R->getName() == "immAllOnesV" || R->getName() == "immAllZerosV"; } /// getPatternSize - Return the 'size' of this pattern. We want to match large /// patterns before small ones. This is used to determine the size of a /// pattern. static unsigned getPatternSize(const TreePatternNode *P, const CodeGenDAGPatterns &CGP) { unsigned Size = 3; // The node itself. // If the root node is a ConstantSDNode, increases its size. // e.g. (set R32:$dst, 0). if (P->isLeaf() && isa(P->getLeafValue())) Size += 2; if (const ComplexPattern *AM = P->getComplexPatternInfo(CGP)) { Size += AM->getComplexity(); // We don't want to count any children twice, so return early. return Size; } // If this node has some predicate function that must match, it adds to the // complexity of this node. if (!P->getPredicateCalls().empty()) ++Size; // Count children in the count if they are also nodes. for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i) { const TreePatternNode *Child = P->getChild(i); if (!Child->isLeaf() && Child->getNumTypes()) { const TypeSetByHwMode &T0 = Child->getExtType(0); // At this point, all variable type sets should be simple, i.e. only // have a default mode. if (T0.getMachineValueType() != MVT::Other) { Size += getPatternSize(Child, CGP); continue; } } if (Child->isLeaf()) { if (isa(Child->getLeafValue())) Size += 5; // Matches a ConstantSDNode (+3) and a specific value (+2). else if (Child->getComplexPatternInfo(CGP)) Size += getPatternSize(Child, CGP); else if (isImmAllOnesAllZerosMatch(Child)) Size += 4; // Matches a build_vector(+3) and a predicate (+1). else if (!Child->getPredicateCalls().empty()) ++Size; } } return Size; } /// Compute the complexity metric for the input pattern. This roughly /// corresponds to the number of nodes that are covered. int PatternToMatch:: getPatternComplexity(const CodeGenDAGPatterns &CGP) const { return getPatternSize(getSrcPattern(), CGP) + getAddedComplexity(); } /// getPredicateCheck - Return a single string containing all of this /// pattern's predicates concatenated with "&&" operators. /// std::string PatternToMatch::getPredicateCheck() const { SmallVector PredList; for (const Predicate &P : Predicates) { if (!P.getCondString().empty()) PredList.push_back(&P); } llvm::sort(PredList, deref>()); std::string Check; for (unsigned i = 0, e = PredList.size(); i != e; ++i) { if (i != 0) Check += " && "; Check += '(' + PredList[i]->getCondString() + ')'; } return Check; } //===----------------------------------------------------------------------===// // SDTypeConstraint implementation // SDTypeConstraint::SDTypeConstraint(Record *R, const CodeGenHwModes &CGH) { OperandNo = R->getValueAsInt("OperandNum"); if (R->isSubClassOf("SDTCisVT")) { ConstraintType = SDTCisVT; VVT = getValueTypeByHwMode(R->getValueAsDef("VT"), CGH); for (const auto &P : VVT) if (P.second == MVT::isVoid) PrintFatalError(R->getLoc(), "Cannot use 'Void' as type to SDTCisVT"); } else if (R->isSubClassOf("SDTCisPtrTy")) { ConstraintType = SDTCisPtrTy; } else if (R->isSubClassOf("SDTCisInt")) { ConstraintType = SDTCisInt; } else if (R->isSubClassOf("SDTCisFP")) { ConstraintType = SDTCisFP; } else if (R->isSubClassOf("SDTCisVec")) { ConstraintType = SDTCisVec; } else if (R->isSubClassOf("SDTCisSameAs")) { ConstraintType = SDTCisSameAs; x.SDTCisSameAs_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum"); } else if (R->isSubClassOf("SDTCisVTSmallerThanOp")) { ConstraintType = SDTCisVTSmallerThanOp; x.SDTCisVTSmallerThanOp_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum"); } else if (R->isSubClassOf("SDTCisOpSmallerThanOp")) { ConstraintType = SDTCisOpSmallerThanOp; x.SDTCisOpSmallerThanOp_Info.BigOperandNum = R->getValueAsInt("BigOperandNum"); } else if (R->isSubClassOf("SDTCisEltOfVec")) { ConstraintType = SDTCisEltOfVec; x.SDTCisEltOfVec_Info.OtherOperandNum = R->getValueAsInt("OtherOpNum"); } else if (R->isSubClassOf("SDTCisSubVecOfVec")) { ConstraintType = SDTCisSubVecOfVec; x.SDTCisSubVecOfVec_Info.OtherOperandNum = R->getValueAsInt("OtherOpNum"); } else if (R->isSubClassOf("SDTCVecEltisVT")) { ConstraintType = SDTCVecEltisVT; VVT = getValueTypeByHwMode(R->getValueAsDef("VT"), CGH); for (const auto &P : VVT) { MVT T = P.second; if (T.isVector()) PrintFatalError(R->getLoc(), "Cannot use vector type as SDTCVecEltisVT"); if (!T.isInteger() && !T.isFloatingPoint()) PrintFatalError(R->getLoc(), "Must use integer or floating point type " "as SDTCVecEltisVT"); } } else if (R->isSubClassOf("SDTCisSameNumEltsAs")) { ConstraintType = SDTCisSameNumEltsAs; x.SDTCisSameNumEltsAs_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum"); } else if (R->isSubClassOf("SDTCisSameSizeAs")) { ConstraintType = SDTCisSameSizeAs; x.SDTCisSameSizeAs_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum"); } else { PrintFatalError(R->getLoc(), "Unrecognized SDTypeConstraint '" + R->getName() + "'!\n"); } } /// getOperandNum - Return the node corresponding to operand #OpNo in tree /// N, and the result number in ResNo. static TreePatternNode *getOperandNum(unsigned OpNo, TreePatternNode *N, const SDNodeInfo &NodeInfo, unsigned &ResNo) { unsigned NumResults = NodeInfo.getNumResults(); if (OpNo < NumResults) { ResNo = OpNo; return N; } OpNo -= NumResults; if (OpNo >= N->getNumChildren()) { std::string S; raw_string_ostream OS(S); OS << "Invalid operand number in type constraint " << (OpNo+NumResults) << " "; N->print(OS); PrintFatalError(OS.str()); } return N->getChild(OpNo); } /// ApplyTypeConstraint - Given a node in a pattern, apply this type /// constraint to the nodes operands. This returns true if it makes a /// change, false otherwise. If a type contradiction is found, flag an error. bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode *N, const SDNodeInfo &NodeInfo, TreePattern &TP) const { if (TP.hasError()) return false; unsigned ResNo = 0; // The result number being referenced. TreePatternNode *NodeToApply = getOperandNum(OperandNo, N, NodeInfo, ResNo); TypeInfer &TI = TP.getInfer(); switch (ConstraintType) { case SDTCisVT: // Operand must be a particular type. return NodeToApply->UpdateNodeType(ResNo, VVT, TP); case SDTCisPtrTy: // Operand must be same as target pointer type. return NodeToApply->UpdateNodeType(ResNo, MVT::iPTR, TP); case SDTCisInt: // Require it to be one of the legal integer VTs. return TI.EnforceInteger(NodeToApply->getExtType(ResNo)); case SDTCisFP: // Require it to be one of the legal fp VTs. return TI.EnforceFloatingPoint(NodeToApply->getExtType(ResNo)); case SDTCisVec: // Require it to be one of the legal vector VTs. return TI.EnforceVector(NodeToApply->getExtType(ResNo)); case SDTCisSameAs: { unsigned OResNo = 0; TreePatternNode *OtherNode = getOperandNum(x.SDTCisSameAs_Info.OtherOperandNum, N, NodeInfo, OResNo); return NodeToApply->UpdateNodeType(ResNo, OtherNode->getExtType(OResNo),TP)| OtherNode->UpdateNodeType(OResNo,NodeToApply->getExtType(ResNo),TP); } case SDTCisVTSmallerThanOp: { // The NodeToApply must be a leaf node that is a VT. OtherOperandNum must // have an integer type that is smaller than the VT. if (!NodeToApply->isLeaf() || !isa(NodeToApply->getLeafValue()) || !static_cast(NodeToApply->getLeafValue())->getDef() ->isSubClassOf("ValueType")) { TP.error(N->getOperator()->getName() + " expects a VT operand!"); return false; } DefInit *DI = static_cast(NodeToApply->getLeafValue()); const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); auto VVT = getValueTypeByHwMode(DI->getDef(), T.getHwModes()); TypeSetByHwMode TypeListTmp(VVT); unsigned OResNo = 0; TreePatternNode *OtherNode = getOperandNum(x.SDTCisVTSmallerThanOp_Info.OtherOperandNum, N, NodeInfo, OResNo); return TI.EnforceSmallerThan(TypeListTmp, OtherNode->getExtType(OResNo)); } case SDTCisOpSmallerThanOp: { unsigned BResNo = 0; TreePatternNode *BigOperand = getOperandNum(x.SDTCisOpSmallerThanOp_Info.BigOperandNum, N, NodeInfo, BResNo); return TI.EnforceSmallerThan(NodeToApply->getExtType(ResNo), BigOperand->getExtType(BResNo)); } case SDTCisEltOfVec: { unsigned VResNo = 0; TreePatternNode *VecOperand = getOperandNum(x.SDTCisEltOfVec_Info.OtherOperandNum, N, NodeInfo, VResNo); // Filter vector types out of VecOperand that don't have the right element // type. return TI.EnforceVectorEltTypeIs(VecOperand->getExtType(VResNo), NodeToApply->getExtType(ResNo)); } case SDTCisSubVecOfVec: { unsigned VResNo = 0; TreePatternNode *BigVecOperand = getOperandNum(x.SDTCisSubVecOfVec_Info.OtherOperandNum, N, NodeInfo, VResNo); // Filter vector types out of BigVecOperand that don't have the // right subvector type. return TI.EnforceVectorSubVectorTypeIs(BigVecOperand->getExtType(VResNo), NodeToApply->getExtType(ResNo)); } case SDTCVecEltisVT: { return TI.EnforceVectorEltTypeIs(NodeToApply->getExtType(ResNo), VVT); } case SDTCisSameNumEltsAs: { unsigned OResNo = 0; TreePatternNode *OtherNode = getOperandNum(x.SDTCisSameNumEltsAs_Info.OtherOperandNum, N, NodeInfo, OResNo); return TI.EnforceSameNumElts(OtherNode->getExtType(OResNo), NodeToApply->getExtType(ResNo)); } case SDTCisSameSizeAs: { unsigned OResNo = 0; TreePatternNode *OtherNode = getOperandNum(x.SDTCisSameSizeAs_Info.OtherOperandNum, N, NodeInfo, OResNo); return TI.EnforceSameSize(OtherNode->getExtType(OResNo), NodeToApply->getExtType(ResNo)); } } llvm_unreachable("Invalid ConstraintType!"); } // Update the node type to match an instruction operand or result as specified // in the ins or outs lists on the instruction definition. Return true if the // type was actually changed. bool TreePatternNode::UpdateNodeTypeFromInst(unsigned ResNo, Record *Operand, TreePattern &TP) { // The 'unknown' operand indicates that types should be inferred from the // context. if (Operand->isSubClassOf("unknown_class")) return false; // The Operand class specifies a type directly. if (Operand->isSubClassOf("Operand")) { Record *R = Operand->getValueAsDef("Type"); const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); return UpdateNodeType(ResNo, getValueTypeByHwMode(R, T.getHwModes()), TP); } // PointerLikeRegClass has a type that is determined at runtime. if (Operand->isSubClassOf("PointerLikeRegClass")) return UpdateNodeType(ResNo, MVT::iPTR, TP); // Both RegisterClass and RegisterOperand operands derive their types from a // register class def. Record *RC = nullptr; if (Operand->isSubClassOf("RegisterClass")) RC = Operand; else if (Operand->isSubClassOf("RegisterOperand")) RC = Operand->getValueAsDef("RegClass"); assert(RC && "Unknown operand type"); CodeGenTarget &Tgt = TP.getDAGPatterns().getTargetInfo(); return UpdateNodeType(ResNo, Tgt.getRegisterClass(RC).getValueTypes(), TP); } bool TreePatternNode::ContainsUnresolvedType(TreePattern &TP) const { for (unsigned i = 0, e = Types.size(); i != e; ++i) if (!TP.getInfer().isConcrete(Types[i], true)) return true; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) if (getChild(i)->ContainsUnresolvedType(TP)) return true; return false; } bool TreePatternNode::hasProperTypeByHwMode() const { for (const TypeSetByHwMode &S : Types) if (!S.isDefaultOnly()) return true; for (const TreePatternNodePtr &C : Children) if (C->hasProperTypeByHwMode()) return true; return false; } bool TreePatternNode::hasPossibleType() const { for (const TypeSetByHwMode &S : Types) if (!S.isPossible()) return false; for (const TreePatternNodePtr &C : Children) if (!C->hasPossibleType()) return false; return true; } bool TreePatternNode::setDefaultMode(unsigned Mode) { for (TypeSetByHwMode &S : Types) { S.makeSimple(Mode); // Check if the selected mode had a type conflict. if (S.get(DefaultMode).empty()) return false; } for (const TreePatternNodePtr &C : Children) if (!C->setDefaultMode(Mode)) return false; return true; } //===----------------------------------------------------------------------===// // SDNodeInfo implementation // SDNodeInfo::SDNodeInfo(Record *R, const CodeGenHwModes &CGH) : Def(R) { EnumName = R->getValueAsString("Opcode"); SDClassName = R->getValueAsString("SDClass"); Record *TypeProfile = R->getValueAsDef("TypeProfile"); NumResults = TypeProfile->getValueAsInt("NumResults"); NumOperands = TypeProfile->getValueAsInt("NumOperands"); // Parse the properties. Properties = parseSDPatternOperatorProperties(R); // Parse the type constraints. std::vector ConstraintList = TypeProfile->getValueAsListOfDefs("Constraints"); for (Record *R : ConstraintList) TypeConstraints.emplace_back(R, CGH); } /// getKnownType - If the type constraints on this node imply a fixed type /// (e.g. all stores return void, etc), then return it as an /// MVT::SimpleValueType. Otherwise, return EEVT::Other. MVT::SimpleValueType SDNodeInfo::getKnownType(unsigned ResNo) const { unsigned NumResults = getNumResults(); assert(NumResults <= 1 && "We only work with nodes with zero or one result so far!"); assert(ResNo == 0 && "Only handles single result nodes so far"); for (const SDTypeConstraint &Constraint : TypeConstraints) { // Make sure that this applies to the correct node result. if (Constraint.OperandNo >= NumResults) // FIXME: need value # continue; switch (Constraint.ConstraintType) { default: break; case SDTypeConstraint::SDTCisVT: if (Constraint.VVT.isSimple()) return Constraint.VVT.getSimple().SimpleTy; break; case SDTypeConstraint::SDTCisPtrTy: return MVT::iPTR; } } return MVT::Other; } //===----------------------------------------------------------------------===// // TreePatternNode implementation // static unsigned GetNumNodeResults(Record *Operator, CodeGenDAGPatterns &CDP) { if (Operator->getName() == "set" || Operator->getName() == "implicit") return 0; // All return nothing. if (Operator->isSubClassOf("Intrinsic")) return CDP.getIntrinsic(Operator).IS.RetVTs.size(); if (Operator->isSubClassOf("SDNode")) return CDP.getSDNodeInfo(Operator).getNumResults(); if (Operator->isSubClassOf("PatFrags")) { // If we've already parsed this pattern fragment, get it. Otherwise, handle // the forward reference case where one pattern fragment references another // before it is processed. if (TreePattern *PFRec = CDP.getPatternFragmentIfRead(Operator)) { // The number of results of a fragment with alternative records is the // maximum number of results across all alternatives. unsigned NumResults = 0; for (auto T : PFRec->getTrees()) NumResults = std::max(NumResults, T->getNumTypes()); return NumResults; } ListInit *LI = Operator->getValueAsListInit("Fragments"); assert(LI && "Invalid Fragment"); unsigned NumResults = 0; for (Init *I : LI->getValues()) { Record *Op = nullptr; if (DagInit *Dag = dyn_cast(I)) if (DefInit *DI = dyn_cast(Dag->getOperator())) Op = DI->getDef(); assert(Op && "Invalid Fragment"); NumResults = std::max(NumResults, GetNumNodeResults(Op, CDP)); } return NumResults; } if (Operator->isSubClassOf("Instruction")) { CodeGenInstruction &InstInfo = CDP.getTargetInfo().getInstruction(Operator); unsigned NumDefsToAdd = InstInfo.Operands.NumDefs; // Subtract any defaulted outputs. for (unsigned i = 0; i != InstInfo.Operands.NumDefs; ++i) { Record *OperandNode = InstInfo.Operands[i].Rec; if (OperandNode->isSubClassOf("OperandWithDefaultOps") && !CDP.getDefaultOperand(OperandNode).DefaultOps.empty()) --NumDefsToAdd; } // Add on one implicit def if it has a resolvable type. if (InstInfo.HasOneImplicitDefWithKnownVT(CDP.getTargetInfo()) !=MVT::Other) ++NumDefsToAdd; return NumDefsToAdd; } if (Operator->isSubClassOf("SDNodeXForm")) return 1; // FIXME: Generalize SDNodeXForm if (Operator->isSubClassOf("ValueType")) return 1; // A type-cast of one result. if (Operator->isSubClassOf("ComplexPattern")) return 1; errs() << *Operator; PrintFatalError("Unhandled node in GetNumNodeResults"); } void TreePatternNode::print(raw_ostream &OS) const { if (isLeaf()) OS << *getLeafValue(); else OS << '(' << getOperator()->getName(); for (unsigned i = 0, e = Types.size(); i != e; ++i) { OS << ':'; getExtType(i).writeToStream(OS); } if (!isLeaf()) { if (getNumChildren() != 0) { OS << " "; getChild(0)->print(OS); for (unsigned i = 1, e = getNumChildren(); i != e; ++i) { OS << ", "; getChild(i)->print(OS); } } OS << ")"; } for (const TreePredicateCall &Pred : PredicateCalls) { OS << "<>"; } if (TransformFn) OS << "<getName() << ">>"; if (!getName().empty()) OS << ":$" << getName(); for (const ScopedName &Name : NamesAsPredicateArg) OS << ":$pred:" << Name.getScope() << ":" << Name.getIdentifier(); } void TreePatternNode::dump() const { print(errs()); } /// isIsomorphicTo - Return true if this node is recursively /// isomorphic to the specified node. For this comparison, the node's /// entire state is considered. The assigned name is ignored, since /// nodes with differing names are considered isomorphic. However, if /// the assigned name is present in the dependent variable set, then /// the assigned name is considered significant and the node is /// isomorphic if the names match. bool TreePatternNode::isIsomorphicTo(const TreePatternNode *N, const MultipleUseVarSet &DepVars) const { if (N == this) return true; if (N->isLeaf() != isLeaf() || getExtTypes() != N->getExtTypes() || getPredicateCalls() != N->getPredicateCalls() || getTransformFn() != N->getTransformFn()) return false; if (isLeaf()) { if (DefInit *DI = dyn_cast(getLeafValue())) { if (DefInit *NDI = dyn_cast(N->getLeafValue())) { return ((DI->getDef() == NDI->getDef()) && (DepVars.find(getName()) == DepVars.end() || getName() == N->getName())); } } return getLeafValue() == N->getLeafValue(); } if (N->getOperator() != getOperator() || N->getNumChildren() != getNumChildren()) return false; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) if (!getChild(i)->isIsomorphicTo(N->getChild(i), DepVars)) return false; return true; } /// clone - Make a copy of this tree and all of its children. /// TreePatternNodePtr TreePatternNode::clone() const { TreePatternNodePtr New; if (isLeaf()) { New = std::make_shared(getLeafValue(), getNumTypes()); } else { std::vector CChildren; CChildren.reserve(Children.size()); for (unsigned i = 0, e = getNumChildren(); i != e; ++i) CChildren.push_back(getChild(i)->clone()); New = std::make_shared(getOperator(), std::move(CChildren), getNumTypes()); } New->setName(getName()); New->setNamesAsPredicateArg(getNamesAsPredicateArg()); New->Types = Types; New->setPredicateCalls(getPredicateCalls()); New->setTransformFn(getTransformFn()); return New; } /// RemoveAllTypes - Recursively strip all the types of this tree. void TreePatternNode::RemoveAllTypes() { // Reset to unknown type. std::fill(Types.begin(), Types.end(), TypeSetByHwMode()); if (isLeaf()) return; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) getChild(i)->RemoveAllTypes(); } /// SubstituteFormalArguments - Replace the formal arguments in this tree /// with actual values specified by ArgMap. void TreePatternNode::SubstituteFormalArguments( std::map &ArgMap) { if (isLeaf()) return; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) { TreePatternNode *Child = getChild(i); if (Child->isLeaf()) { Init *Val = Child->getLeafValue(); // Note that, when substituting into an output pattern, Val might be an // UnsetInit. if (isa(Val) || (isa(Val) && cast(Val)->getDef()->getName() == "node")) { // We found a use of a formal argument, replace it with its value. TreePatternNodePtr NewChild = ArgMap[Child->getName()]; assert(NewChild && "Couldn't find formal argument!"); assert((Child->getPredicateCalls().empty() || NewChild->getPredicateCalls() == Child->getPredicateCalls()) && "Non-empty child predicate clobbered!"); setChild(i, std::move(NewChild)); } } else { getChild(i)->SubstituteFormalArguments(ArgMap); } } } /// InlinePatternFragments - If this pattern refers to any pattern /// fragments, return the set of inlined versions (this can be more than /// one if a PatFrags record has multiple alternatives). void TreePatternNode::InlinePatternFragments( TreePatternNodePtr T, TreePattern &TP, std::vector &OutAlternatives) { if (TP.hasError()) return; if (isLeaf()) { OutAlternatives.push_back(T); // nothing to do. return; } Record *Op = getOperator(); if (!Op->isSubClassOf("PatFrags")) { if (getNumChildren() == 0) { OutAlternatives.push_back(T); return; } // Recursively inline children nodes. std::vector > ChildAlternatives; ChildAlternatives.resize(getNumChildren()); for (unsigned i = 0, e = getNumChildren(); i != e; ++i) { TreePatternNodePtr Child = getChildShared(i); Child->InlinePatternFragments(Child, TP, ChildAlternatives[i]); // If there are no alternatives for any child, there are no // alternatives for this expression as whole. if (ChildAlternatives[i].empty()) return; for (auto NewChild : ChildAlternatives[i]) assert((Child->getPredicateCalls().empty() || NewChild->getPredicateCalls() == Child->getPredicateCalls()) && "Non-empty child predicate clobbered!"); } // The end result is an all-pairs construction of the resultant pattern. std::vector Idxs; Idxs.resize(ChildAlternatives.size()); bool NotDone; do { // Create the variant and add it to the output list. std::vector NewChildren; for (unsigned i = 0, e = ChildAlternatives.size(); i != e; ++i) NewChildren.push_back(ChildAlternatives[i][Idxs[i]]); TreePatternNodePtr R = std::make_shared( getOperator(), std::move(NewChildren), getNumTypes()); // Copy over properties. R->setName(getName()); R->setNamesAsPredicateArg(getNamesAsPredicateArg()); R->setPredicateCalls(getPredicateCalls()); R->setTransformFn(getTransformFn()); for (unsigned i = 0, e = getNumTypes(); i != e; ++i) R->setType(i, getExtType(i)); for (unsigned i = 0, e = getNumResults(); i != e; ++i) R->setResultIndex(i, getResultIndex(i)); // Register alternative. OutAlternatives.push_back(R); // Increment indices to the next permutation by incrementing the // indices from last index backward, e.g., generate the sequence // [0, 0], [0, 1], [1, 0], [1, 1]. int IdxsIdx; for (IdxsIdx = Idxs.size() - 1; IdxsIdx >= 0; --IdxsIdx) { if (++Idxs[IdxsIdx] == ChildAlternatives[IdxsIdx].size()) Idxs[IdxsIdx] = 0; else break; } NotDone = (IdxsIdx >= 0); } while (NotDone); return; } // Otherwise, we found a reference to a fragment. First, look up its // TreePattern record. TreePattern *Frag = TP.getDAGPatterns().getPatternFragment(Op); // Verify that we are passing the right number of operands. if (Frag->getNumArgs() != Children.size()) { TP.error("'" + Op->getName() + "' fragment requires " + Twine(Frag->getNumArgs()) + " operands!"); return; } TreePredicateFn PredFn(Frag); unsigned Scope = 0; if (TreePredicateFn(Frag).usesOperands()) Scope = TP.getDAGPatterns().allocateScope(); // Compute the map of formal to actual arguments. std::map ArgMap; for (unsigned i = 0, e = Frag->getNumArgs(); i != e; ++i) { TreePatternNodePtr Child = getChildShared(i); if (Scope != 0) { Child = Child->clone(); Child->addNameAsPredicateArg(ScopedName(Scope, Frag->getArgName(i))); } ArgMap[Frag->getArgName(i)] = Child; } // Loop over all fragment alternatives. for (auto Alternative : Frag->getTrees()) { TreePatternNodePtr FragTree = Alternative->clone(); if (!PredFn.isAlwaysTrue()) FragTree->addPredicateCall(PredFn, Scope); // Resolve formal arguments to their actual value. if (Frag->getNumArgs()) FragTree->SubstituteFormalArguments(ArgMap); // Transfer types. Note that the resolved alternative may have fewer // (but not more) results than the PatFrags node. FragTree->setName(getName()); for (unsigned i = 0, e = FragTree->getNumTypes(); i != e; ++i) FragTree->UpdateNodeType(i, getExtType(i), TP); // Transfer in the old predicates. for (const TreePredicateCall &Pred : getPredicateCalls()) FragTree->addPredicateCall(Pred); // The fragment we inlined could have recursive inlining that is needed. See // if there are any pattern fragments in it and inline them as needed. FragTree->InlinePatternFragments(FragTree, TP, OutAlternatives); } } /// getImplicitType - Check to see if the specified record has an implicit /// type which should be applied to it. This will infer the type of register /// references from the register file information, for example. /// /// When Unnamed is set, return the type of a DAG operand with no name, such as /// the F8RC register class argument in: /// /// (COPY_TO_REGCLASS GPR:$src, F8RC) /// /// When Unnamed is false, return the type of a named DAG operand such as the /// GPR:$src operand above. /// static TypeSetByHwMode getImplicitType(Record *R, unsigned ResNo, bool NotRegisters, bool Unnamed, TreePattern &TP) { CodeGenDAGPatterns &CDP = TP.getDAGPatterns(); // Check to see if this is a register operand. if (R->isSubClassOf("RegisterOperand")) { assert(ResNo == 0 && "Regoperand ref only has one result!"); if (NotRegisters) return TypeSetByHwMode(); // Unknown. Record *RegClass = R->getValueAsDef("RegClass"); const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); return TypeSetByHwMode(T.getRegisterClass(RegClass).getValueTypes()); } // Check to see if this is a register or a register class. if (R->isSubClassOf("RegisterClass")) { assert(ResNo == 0 && "Regclass ref only has one result!"); // An unnamed register class represents itself as an i32 immediate, for // example on a COPY_TO_REGCLASS instruction. if (Unnamed) return TypeSetByHwMode(MVT::i32); // In a named operand, the register class provides the possible set of // types. if (NotRegisters) return TypeSetByHwMode(); // Unknown. const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); return TypeSetByHwMode(T.getRegisterClass(R).getValueTypes()); } if (R->isSubClassOf("PatFrags")) { assert(ResNo == 0 && "FIXME: PatFrag with multiple results?"); // Pattern fragment types will be resolved when they are inlined. return TypeSetByHwMode(); // Unknown. } if (R->isSubClassOf("Register")) { assert(ResNo == 0 && "Registers only produce one result!"); if (NotRegisters) return TypeSetByHwMode(); // Unknown. const CodeGenTarget &T = TP.getDAGPatterns().getTargetInfo(); return TypeSetByHwMode(T.getRegisterVTs(R)); } if (R->isSubClassOf("SubRegIndex")) { assert(ResNo == 0 && "SubRegisterIndices only produce one result!"); return TypeSetByHwMode(MVT::i32); } if (R->isSubClassOf("ValueType")) { assert(ResNo == 0 && "This node only has one result!"); // An unnamed VTSDNode represents itself as an MVT::Other immediate. // // (sext_inreg GPR:$src, i16) // ~~~ if (Unnamed) return TypeSetByHwMode(MVT::Other); // With a name, the ValueType simply provides the type of the named // variable. // // (sext_inreg i32:$src, i16) // ~~~~~~~~ if (NotRegisters) return TypeSetByHwMode(); // Unknown. const CodeGenHwModes &CGH = CDP.getTargetInfo().getHwModes(); return TypeSetByHwMode(getValueTypeByHwMode(R, CGH)); } if (R->isSubClassOf("CondCode")) { assert(ResNo == 0 && "This node only has one result!"); // Using a CondCodeSDNode. return TypeSetByHwMode(MVT::Other); } if (R->isSubClassOf("ComplexPattern")) { assert(ResNo == 0 && "FIXME: ComplexPattern with multiple results?"); if (NotRegisters) return TypeSetByHwMode(); // Unknown. return TypeSetByHwMode(CDP.getComplexPattern(R).getValueType()); } if (R->isSubClassOf("PointerLikeRegClass")) { assert(ResNo == 0 && "Regclass can only have one result!"); TypeSetByHwMode VTS(MVT::iPTR); TP.getInfer().expandOverloads(VTS); return VTS; } if (R->getName() == "node" || R->getName() == "srcvalue" || R->getName() == "zero_reg" || R->getName() == "immAllOnesV" || R->getName() == "immAllZerosV" || R->getName() == "undef_tied_input") { // Placeholder. return TypeSetByHwMode(); // Unknown. } if (R->isSubClassOf("Operand")) { const CodeGenHwModes &CGH = CDP.getTargetInfo().getHwModes(); Record *T = R->getValueAsDef("Type"); return TypeSetByHwMode(getValueTypeByHwMode(T, CGH)); } TP.error("Unknown node flavor used in pattern: " + R->getName()); return TypeSetByHwMode(MVT::Other); } /// getIntrinsicInfo - If this node corresponds to an intrinsic, return the /// CodeGenIntrinsic information for it, otherwise return a null pointer. const CodeGenIntrinsic *TreePatternNode:: getIntrinsicInfo(const CodeGenDAGPatterns &CDP) const { if (getOperator() != CDP.get_intrinsic_void_sdnode() && getOperator() != CDP.get_intrinsic_w_chain_sdnode() && getOperator() != CDP.get_intrinsic_wo_chain_sdnode()) return nullptr; unsigned IID = cast(getChild(0)->getLeafValue())->getValue(); return &CDP.getIntrinsicInfo(IID); } /// getComplexPatternInfo - If this node corresponds to a ComplexPattern, /// return the ComplexPattern information, otherwise return null. const ComplexPattern * TreePatternNode::getComplexPatternInfo(const CodeGenDAGPatterns &CGP) const { Record *Rec; if (isLeaf()) { DefInit *DI = dyn_cast(getLeafValue()); if (!DI) return nullptr; Rec = DI->getDef(); } else Rec = getOperator(); if (!Rec->isSubClassOf("ComplexPattern")) return nullptr; return &CGP.getComplexPattern(Rec); } unsigned TreePatternNode::getNumMIResults(const CodeGenDAGPatterns &CGP) const { // A ComplexPattern specifically declares how many results it fills in. if (const ComplexPattern *CP = getComplexPatternInfo(CGP)) return CP->getNumOperands(); // If MIOperandInfo is specified, that gives the count. if (isLeaf()) { DefInit *DI = dyn_cast(getLeafValue()); if (DI && DI->getDef()->isSubClassOf("Operand")) { DagInit *MIOps = DI->getDef()->getValueAsDag("MIOperandInfo"); if (MIOps->getNumArgs()) return MIOps->getNumArgs(); } } // Otherwise there is just one result. return 1; } /// NodeHasProperty - Return true if this node has the specified property. bool TreePatternNode::NodeHasProperty(SDNP Property, const CodeGenDAGPatterns &CGP) const { if (isLeaf()) { if (const ComplexPattern *CP = getComplexPatternInfo(CGP)) return CP->hasProperty(Property); return false; } if (Property != SDNPHasChain) { // The chain proprety is already present on the different intrinsic node // types (intrinsic_w_chain, intrinsic_void), and is not explicitly listed // on the intrinsic. Anything else is specific to the individual intrinsic. if (const CodeGenIntrinsic *Int = getIntrinsicInfo(CGP)) return Int->hasProperty(Property); } if (!Operator->isSubClassOf("SDPatternOperator")) return false; return CGP.getSDNodeInfo(Operator).hasProperty(Property); } /// TreeHasProperty - Return true if any node in this tree has the specified /// property. bool TreePatternNode::TreeHasProperty(SDNP Property, const CodeGenDAGPatterns &CGP) const { if (NodeHasProperty(Property, CGP)) return true; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) if (getChild(i)->TreeHasProperty(Property, CGP)) return true; return false; } /// isCommutativeIntrinsic - Return true if the node corresponds to a /// commutative intrinsic. bool TreePatternNode::isCommutativeIntrinsic(const CodeGenDAGPatterns &CDP) const { if (const CodeGenIntrinsic *Int = getIntrinsicInfo(CDP)) return Int->isCommutative; return false; } static bool isOperandClass(const TreePatternNode *N, StringRef Class) { if (!N->isLeaf()) return N->getOperator()->isSubClassOf(Class); DefInit *DI = dyn_cast(N->getLeafValue()); if (DI && DI->getDef()->isSubClassOf(Class)) return true; return false; } static void emitTooManyOperandsError(TreePattern &TP, StringRef InstName, unsigned Expected, unsigned Actual) { TP.error("Instruction '" + InstName + "' was provided " + Twine(Actual) + " operands but expected only " + Twine(Expected) + "!"); } static void emitTooFewOperandsError(TreePattern &TP, StringRef InstName, unsigned Actual) { TP.error("Instruction '" + InstName + "' expects more than the provided " + Twine(Actual) + " operands!"); } /// ApplyTypeConstraints - Apply all of the type constraints relevant to /// this node and its children in the tree. This returns true if it makes a /// change, false otherwise. If a type contradiction is found, flag an error. bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { if (TP.hasError()) return false; CodeGenDAGPatterns &CDP = TP.getDAGPatterns(); if (isLeaf()) { if (DefInit *DI = dyn_cast(getLeafValue())) { // If it's a regclass or something else known, include the type. bool MadeChange = false; for (unsigned i = 0, e = Types.size(); i != e; ++i) MadeChange |= UpdateNodeType(i, getImplicitType(DI->getDef(), i, NotRegisters, !hasName(), TP), TP); return MadeChange; } if (IntInit *II = dyn_cast(getLeafValue())) { assert(Types.size() == 1 && "Invalid IntInit"); // Int inits are always integers. :) bool MadeChange = TP.getInfer().EnforceInteger(Types[0]); if (!TP.getInfer().isConcrete(Types[0], false)) return MadeChange; ValueTypeByHwMode VVT = TP.getInfer().getConcrete(Types[0], false); for (auto &P : VVT) { MVT::SimpleValueType VT = P.second.SimpleTy; if (VT == MVT::iPTR || VT == MVT::iPTRAny) continue; unsigned Size = MVT(VT).getFixedSizeInBits(); // Make sure that the value is representable for this type. if (Size >= 32) continue; // Check that the value doesn't use more bits than we have. It must // either be a sign- or zero-extended equivalent of the original. int64_t SignBitAndAbove = II->getValue() >> (Size - 1); if (SignBitAndAbove == -1 || SignBitAndAbove == 0 || SignBitAndAbove == 1) continue; TP.error("Integer value '" + Twine(II->getValue()) + "' is out of range for type '" + getEnumName(VT) + "'!"); break; } return MadeChange; } return false; } if (const CodeGenIntrinsic *Int = getIntrinsicInfo(CDP)) { bool MadeChange = false; // Apply the result type to the node. unsigned NumRetVTs = Int->IS.RetVTs.size(); unsigned NumParamVTs = Int->IS.ParamVTs.size(); for (unsigned i = 0, e = NumRetVTs; i != e; ++i) MadeChange |= UpdateNodeType(i, Int->IS.RetVTs[i], TP); if (getNumChildren() != NumParamVTs + 1) { TP.error("Intrinsic '" + Int->Name + "' expects " + Twine(NumParamVTs) + " operands, not " + Twine(getNumChildren() - 1) + " operands!"); return false; } // Apply type info to the intrinsic ID. MadeChange |= getChild(0)->UpdateNodeType(0, MVT::iPTR, TP); for (unsigned i = 0, e = getNumChildren()-1; i != e; ++i) { MadeChange |= getChild(i+1)->ApplyTypeConstraints(TP, NotRegisters); MVT::SimpleValueType OpVT = Int->IS.ParamVTs[i]; assert(getChild(i+1)->getNumTypes() == 1 && "Unhandled case"); MadeChange |= getChild(i+1)->UpdateNodeType(0, OpVT, TP); } return MadeChange; } if (getOperator()->isSubClassOf("SDNode")) { const SDNodeInfo &NI = CDP.getSDNodeInfo(getOperator()); // Check that the number of operands is sane. Negative operands -> varargs. if (NI.getNumOperands() >= 0 && getNumChildren() != (unsigned)NI.getNumOperands()) { TP.error(getOperator()->getName() + " node requires exactly " + Twine(NI.getNumOperands()) + " operands!"); return false; } bool MadeChange = false; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) MadeChange |= getChild(i)->ApplyTypeConstraints(TP, NotRegisters); MadeChange |= NI.ApplyTypeConstraints(this, TP); return MadeChange; } if (getOperator()->isSubClassOf("Instruction")) { const DAGInstruction &Inst = CDP.getInstruction(getOperator()); CodeGenInstruction &InstInfo = CDP.getTargetInfo().getInstruction(getOperator()); bool MadeChange = false; // Apply the result types to the node, these come from the things in the // (outs) list of the instruction. unsigned NumResultsToAdd = std::min(InstInfo.Operands.NumDefs, Inst.getNumResults()); for (unsigned ResNo = 0; ResNo != NumResultsToAdd; ++ResNo) MadeChange |= UpdateNodeTypeFromInst(ResNo, Inst.getResult(ResNo), TP); // If the instruction has implicit defs, we apply the first one as a result. // FIXME: This sucks, it should apply all implicit defs. if (!InstInfo.ImplicitDefs.empty()) { unsigned ResNo = NumResultsToAdd; // FIXME: Generalize to multiple possible types and multiple possible // ImplicitDefs. MVT::SimpleValueType VT = InstInfo.HasOneImplicitDefWithKnownVT(CDP.getTargetInfo()); if (VT != MVT::Other) MadeChange |= UpdateNodeType(ResNo, VT, TP); } // If this is an INSERT_SUBREG, constrain the source and destination VTs to // be the same. if (getOperator()->getName() == "INSERT_SUBREG") { assert(getChild(0)->getNumTypes() == 1 && "FIXME: Unhandled"); MadeChange |= UpdateNodeType(0, getChild(0)->getExtType(0), TP); MadeChange |= getChild(0)->UpdateNodeType(0, getExtType(0), TP); } else if (getOperator()->getName() == "REG_SEQUENCE") { // We need to do extra, custom typechecking for REG_SEQUENCE since it is // variadic. unsigned NChild = getNumChildren(); if (NChild < 3) { TP.error("REG_SEQUENCE requires at least 3 operands!"); return false; } if (NChild % 2 == 0) { TP.error("REG_SEQUENCE requires an odd number of operands!"); return false; } if (!isOperandClass(getChild(0), "RegisterClass")) { TP.error("REG_SEQUENCE requires a RegisterClass for first operand!"); return false; } for (unsigned I = 1; I < NChild; I += 2) { TreePatternNode *SubIdxChild = getChild(I + 1); if (!isOperandClass(SubIdxChild, "SubRegIndex")) { TP.error("REG_SEQUENCE requires a SubRegIndex for operand " + Twine(I + 1) + "!"); return false; } } } unsigned NumResults = Inst.getNumResults(); unsigned NumFixedOperands = InstInfo.Operands.size(); // If one or more operands with a default value appear at the end of the // formal operand list for an instruction, we allow them to be overridden // by optional operands provided in the pattern. // // But if an operand B without a default appears at any point after an // operand A with a default, then we don't allow A to be overridden, // because there would be no way to specify whether the next operand in // the pattern was intended to override A or skip it. unsigned NonOverridableOperands = NumFixedOperands; while (NonOverridableOperands > NumResults && CDP.operandHasDefault(InstInfo.Operands[NonOverridableOperands-1].Rec)) --NonOverridableOperands; unsigned ChildNo = 0; assert(NumResults <= NumFixedOperands); for (unsigned i = NumResults, e = NumFixedOperands; i != e; ++i) { Record *OperandNode = InstInfo.Operands[i].Rec; // If the operand has a default value, do we use it? We must use the // default if we've run out of children of the pattern DAG to consume, // or if the operand is followed by a non-defaulted one. if (CDP.operandHasDefault(OperandNode) && (i < NonOverridableOperands || ChildNo >= getNumChildren())) continue; // If we have run out of child nodes and there _isn't_ a default // value we can use for the next operand, give an error. if (ChildNo >= getNumChildren()) { emitTooFewOperandsError(TP, getOperator()->getName(), getNumChildren()); return false; } TreePatternNode *Child = getChild(ChildNo++); unsigned ChildResNo = 0; // Instructions always use res #0 of their op. // If the operand has sub-operands, they may be provided by distinct // child patterns, so attempt to match each sub-operand separately. if (OperandNode->isSubClassOf("Operand")) { DagInit *MIOpInfo = OperandNode->getValueAsDag("MIOperandInfo"); if (unsigned NumArgs = MIOpInfo->getNumArgs()) { // But don't do that if the whole operand is being provided by // a single ComplexPattern-related Operand. if (Child->getNumMIResults(CDP) < NumArgs) { // Match first sub-operand against the child we already have. Record *SubRec = cast(MIOpInfo->getArg(0))->getDef(); MadeChange |= Child->UpdateNodeTypeFromInst(ChildResNo, SubRec, TP); // And the remaining sub-operands against subsequent children. for (unsigned Arg = 1; Arg < NumArgs; ++Arg) { if (ChildNo >= getNumChildren()) { emitTooFewOperandsError(TP, getOperator()->getName(), getNumChildren()); return false; } Child = getChild(ChildNo++); SubRec = cast(MIOpInfo->getArg(Arg))->getDef(); MadeChange |= Child->UpdateNodeTypeFromInst(ChildResNo, SubRec, TP); } continue; } } } // If we didn't match by pieces above, attempt to match the whole // operand now. MadeChange |= Child->UpdateNodeTypeFromInst(ChildResNo, OperandNode, TP); } if (!InstInfo.Operands.isVariadic && ChildNo != getNumChildren()) { emitTooManyOperandsError(TP, getOperator()->getName(), ChildNo, getNumChildren()); return false; } for (unsigned i = 0, e = getNumChildren(); i != e; ++i) MadeChange |= getChild(i)->ApplyTypeConstraints(TP, NotRegisters); return MadeChange; } if (getOperator()->isSubClassOf("ComplexPattern")) { bool MadeChange = false; for (unsigned i = 0; i < getNumChildren(); ++i) MadeChange |= getChild(i)->ApplyTypeConstraints(TP, NotRegisters); return MadeChange; } assert(getOperator()->isSubClassOf("SDNodeXForm") && "Unknown node type!"); // Node transforms always take one operand. if (getNumChildren() != 1) { TP.error("Node transform '" + getOperator()->getName() + "' requires one operand!"); return false; } bool MadeChange = getChild(0)->ApplyTypeConstraints(TP, NotRegisters); return MadeChange; } /// OnlyOnRHSOfCommutative - Return true if this value is only allowed on the /// RHS of a commutative operation, not the on LHS. static bool OnlyOnRHSOfCommutative(TreePatternNode *N) { if (!N->isLeaf() && N->getOperator()->getName() == "imm") return true; if (N->isLeaf() && isa(N->getLeafValue())) return true; return false; } /// canPatternMatch - If it is impossible for this pattern to match on this /// target, fill in Reason and return false. Otherwise, return true. This is /// used as a sanity check for .td files (to prevent people from writing stuff /// that can never possibly work), and to prevent the pattern permuter from /// generating stuff that is useless. bool TreePatternNode::canPatternMatch(std::string &Reason, const CodeGenDAGPatterns &CDP) { if (isLeaf()) return true; for (unsigned i = 0, e = getNumChildren(); i != e; ++i) if (!getChild(i)->canPatternMatch(Reason, CDP)) return false; // If this is an intrinsic, handle cases that would make it not match. For // example, if an operand is required to be an immediate. if (getOperator()->isSubClassOf("Intrinsic")) { // TODO: return true; } if (getOperator()->isSubClassOf("ComplexPattern")) return true; // If this node is a commutative operator, check that the LHS isn't an // immediate. const SDNodeInfo &NodeInfo = CDP.getSDNodeInfo(getOperator()); bool isCommIntrinsic = isCommutativeIntrinsic(CDP); if (NodeInfo.hasProperty(SDNPCommutative) || isCommIntrinsic) { // Scan all of the operands of the node and make sure that only the last one // is a constant node, unless the RHS also is. if (!OnlyOnRHSOfCommutative(getChild(getNumChildren()-1))) { unsigned Skip = isCommIntrinsic ? 1 : 0; // First operand is intrinsic id. for (unsigned i = Skip, e = getNumChildren()-1; i != e; ++i) if (OnlyOnRHSOfCommutative(getChild(i))) { Reason="Immediate value must be on the RHS of commutative operators!"; return false; } } } return true; } //===----------------------------------------------------------------------===// // TreePattern implementation // TreePattern::TreePattern(Record *TheRec, ListInit *RawPat, bool isInput, CodeGenDAGPatterns &cdp) : TheRecord(TheRec), CDP(cdp), isInputPattern(isInput), HasError(false), Infer(*this) { for (Init *I : RawPat->getValues()) Trees.push_back(ParseTreePattern(I, "")); } TreePattern::TreePattern(Record *TheRec, DagInit *Pat, bool isInput, CodeGenDAGPatterns &cdp) : TheRecord(TheRec), CDP(cdp), isInputPattern(isInput), HasError(false), Infer(*this) { Trees.push_back(ParseTreePattern(Pat, "")); } TreePattern::TreePattern(Record *TheRec, TreePatternNodePtr Pat, bool isInput, CodeGenDAGPatterns &cdp) : TheRecord(TheRec), CDP(cdp), isInputPattern(isInput), HasError(false), Infer(*this) { Trees.push_back(Pat); } void TreePattern::error(const Twine &Msg) { if (HasError) return; dump(); PrintError(TheRecord->getLoc(), "In " + TheRecord->getName() + ": " + Msg); HasError = true; } void TreePattern::ComputeNamedNodes() { for (TreePatternNodePtr &Tree : Trees) ComputeNamedNodes(Tree.get()); } void TreePattern::ComputeNamedNodes(TreePatternNode *N) { if (!N->getName().empty()) NamedNodes[N->getName()].push_back(N); for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) ComputeNamedNodes(N->getChild(i)); } TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit, StringRef OpName) { if (DefInit *DI = dyn_cast(TheInit)) { Record *R = DI->getDef(); // Direct reference to a leaf DagNode or PatFrag? Turn it into a // TreePatternNode of its own. For example: /// (foo GPR, imm) -> (foo GPR, (imm)) if (R->isSubClassOf("SDNode") || R->isSubClassOf("PatFrags")) return ParseTreePattern( DagInit::get(DI, nullptr, std::vector >()), OpName); // Input argument? TreePatternNodePtr Res = std::make_shared(DI, 1); if (R->getName() == "node" && !OpName.empty()) { if (OpName.empty()) error("'node' argument requires a name to match with operand list"); Args.push_back(std::string(OpName)); } Res->setName(OpName); return Res; } // ?:$name or just $name. if (isa(TheInit)) { if (OpName.empty()) error("'?' argument requires a name to match with operand list"); TreePatternNodePtr Res = std::make_shared(TheInit, 1); Args.push_back(std::string(OpName)); Res->setName(OpName); return Res; } if (isa(TheInit) || isa(TheInit)) { if (!OpName.empty()) error("Constant int or bit argument should not have a name!"); if (isa(TheInit)) TheInit = TheInit->convertInitializerTo(IntRecTy::get()); return std::make_shared(TheInit, 1); } if (BitsInit *BI = dyn_cast(TheInit)) { // Turn this into an IntInit. Init *II = BI->convertInitializerTo(IntRecTy::get()); if (!II || !isa(II)) error("Bits value must be constants!"); return ParseTreePattern(II, OpName); } DagInit *Dag = dyn_cast(TheInit); if (!Dag) { TheInit->print(errs()); error("Pattern has unexpected init kind!"); } DefInit *OpDef = dyn_cast(Dag->getOperator()); if (!OpDef) error("Pattern has unexpected operator type!"); Record *Operator = OpDef->getDef(); if (Operator->isSubClassOf("ValueType")) { // If the operator is a ValueType, then this must be "type cast" of a leaf // node. if (Dag->getNumArgs() != 1) error("Type cast only takes one operand!"); TreePatternNodePtr New = ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0)); // Apply the type cast. assert(New->getNumTypes() == 1 && "FIXME: Unhandled"); const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes(); New->UpdateNodeType(0, getValueTypeByHwMode(Operator, CGH), *this); if (!OpName.empty()) error("ValueType cast should not have a name!"); return New; } // Verify that this is something that makes sense for an operator. if (!Operator->isSubClassOf("PatFrags") && !Operator->isSubClassOf("SDNode") && !Operator->isSubClassOf("Instruction") && !Operator->isSubClassOf("SDNodeXForm") && !Operator->isSubClassOf("Intrinsic") && !Operator->isSubClassOf("ComplexPattern") && Operator->getName() != "set" && Operator->getName() != "implicit") error("Unrecognized node '" + Operator->getName() + "'!"); // Check to see if this is something that is illegal in an input pattern. if (isInputPattern) { if (Operator->isSubClassOf("Instruction") || Operator->isSubClassOf("SDNodeXForm")) error("Cannot use '" + Operator->getName() + "' in an input pattern!"); } else { if (Operator->isSubClassOf("Intrinsic")) error("Cannot use '" + Operator->getName() + "' in an output pattern!"); if (Operator->isSubClassOf("SDNode") && Operator->getName() != "imm" && Operator->getName() != "timm" && Operator->getName() != "fpimm" && Operator->getName() != "tglobaltlsaddr" && Operator->getName() != "tconstpool" && Operator->getName() != "tjumptable" && Operator->getName() != "tframeindex" && Operator->getName() != "texternalsym" && Operator->getName() != "tblockaddress" && Operator->getName() != "tglobaladdr" && Operator->getName() != "bb" && Operator->getName() != "vt" && Operator->getName() != "mcsym") error("Cannot use '" + Operator->getName() + "' in an output pattern!"); } std::vector Children; // Parse all the operands. for (unsigned i = 0, e = Dag->getNumArgs(); i != e; ++i) Children.push_back(ParseTreePattern(Dag->getArg(i), Dag->getArgNameStr(i))); // Get the actual number of results before Operator is converted to an intrinsic // node (which is hard-coded to have either zero or one result). unsigned NumResults = GetNumNodeResults(Operator, CDP); // If the operator is an intrinsic, then this is just syntactic sugar for // (intrinsic_* , ..children..). Pick the right intrinsic node, and // convert the intrinsic name to a number. if (Operator->isSubClassOf("Intrinsic")) { const CodeGenIntrinsic &Int = getDAGPatterns().getIntrinsic(Operator); unsigned IID = getDAGPatterns().getIntrinsicID(Operator)+1; // If this intrinsic returns void, it must have side-effects and thus a // chain. if (Int.IS.RetVTs.empty()) Operator = getDAGPatterns().get_intrinsic_void_sdnode(); else if (Int.ModRef != CodeGenIntrinsic::NoMem || Int.hasSideEffects) // Has side-effects, requires chain. Operator = getDAGPatterns().get_intrinsic_w_chain_sdnode(); else // Otherwise, no chain. Operator = getDAGPatterns().get_intrinsic_wo_chain_sdnode(); Children.insert(Children.begin(), std::make_shared(IntInit::get(IID), 1)); } if (Operator->isSubClassOf("ComplexPattern")) { for (unsigned i = 0; i < Children.size(); ++i) { TreePatternNodePtr Child = Children[i]; if (Child->getName().empty()) error("All arguments to a ComplexPattern must be named"); // Check that the ComplexPattern uses are consistent: "(MY_PAT $a, $b)" // and "(MY_PAT $b, $a)" should not be allowed in the same pattern; // neither should "(MY_PAT_1 $a, $b)" and "(MY_PAT_2 $a, $b)". auto OperandId = std::make_pair(Operator, i); auto PrevOp = ComplexPatternOperands.find(Child->getName()); if (PrevOp != ComplexPatternOperands.end()) { if (PrevOp->getValue() != OperandId) error("All ComplexPattern operands must appear consistently: " "in the same order in just one ComplexPattern instance."); } else ComplexPatternOperands[Child->getName()] = OperandId; } } TreePatternNodePtr Result = std::make_shared(Operator, std::move(Children), NumResults); Result->setName(OpName); if (Dag->getName()) { assert(Result->getName().empty()); Result->setName(Dag->getNameStr()); } return Result; } /// SimplifyTree - See if we can simplify this tree to eliminate something that /// will never match in favor of something obvious that will. This is here /// strictly as a convenience to target authors because it allows them to write /// more type generic things and have useless type casts fold away. /// /// This returns true if any change is made. static bool SimplifyTree(TreePatternNodePtr &N) { if (N->isLeaf()) return false; // If we have a bitconvert with a resolved type and if the source and // destination types are the same, then the bitconvert is useless, remove it. // // We make an exception if the types are completely empty. This can come up // when the pattern being simplified is in the Fragments list of a PatFrags, // so that the operand is just an untyped "node". In that situation we leave // bitconverts unsimplified, and simplify them later once the fragment is // expanded into its true context. if (N->getOperator()->getName() == "bitconvert" && N->getExtType(0).isValueTypeByHwMode(false) && !N->getExtType(0).empty() && N->getExtType(0) == N->getChild(0)->getExtType(0) && N->getName().empty()) { N = N->getChildShared(0); SimplifyTree(N); return true; } // Walk all children. bool MadeChange = false; for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) { TreePatternNodePtr Child = N->getChildShared(i); MadeChange |= SimplifyTree(Child); N->setChild(i, std::move(Child)); } return MadeChange; } /// InferAllTypes - Infer/propagate as many types throughout the expression /// patterns as possible. Return true if all types are inferred, false /// otherwise. Flags an error if a type contradiction is found. bool TreePattern:: InferAllTypes(const StringMap > *InNamedTypes) { if (NamedNodes.empty()) ComputeNamedNodes(); bool MadeChange = true; while (MadeChange) { MadeChange = false; for (TreePatternNodePtr &Tree : Trees) { MadeChange |= Tree->ApplyTypeConstraints(*this, false); MadeChange |= SimplifyTree(Tree); } // If there are constraints on our named nodes, apply them. for (auto &Entry : NamedNodes) { SmallVectorImpl &Nodes = Entry.second; // If we have input named node types, propagate their types to the named // values here. if (InNamedTypes) { if (!InNamedTypes->count(Entry.getKey())) { error("Node '" + std::string(Entry.getKey()) + "' in output pattern but not input pattern"); return true; } const SmallVectorImpl &InNodes = InNamedTypes->find(Entry.getKey())->second; // The input types should be fully resolved by now. for (TreePatternNode *Node : Nodes) { // If this node is a register class, and it is the root of the pattern // then we're mapping something onto an input register. We allow // changing the type of the input register in this case. This allows // us to match things like: // def : Pat<(v1i64 (bitconvert(v2i32 DPR:$src))), (v1i64 DPR:$src)>; if (Node == Trees[0].get() && Node->isLeaf()) { DefInit *DI = dyn_cast(Node->getLeafValue()); if (DI && (DI->getDef()->isSubClassOf("RegisterClass") || DI->getDef()->isSubClassOf("RegisterOperand"))) continue; } assert(Node->getNumTypes() == 1 && InNodes[0]->getNumTypes() == 1 && "FIXME: cannot name multiple result nodes yet"); MadeChange |= Node->UpdateNodeType(0, InNodes[0]->getExtType(0), *this); } } // If there are multiple nodes with the same name, they must all have the // same type. if (Entry.second.size() > 1) { for (unsigned i = 0, e = Nodes.size()-1; i != e; ++i) { TreePatternNode *N1 = Nodes[i], *N2 = Nodes[i+1]; assert(N1->getNumTypes() == 1 && N2->getNumTypes() == 1 && "FIXME: cannot name multiple result nodes yet"); MadeChange |= N1->UpdateNodeType(0, N2->getExtType(0), *this); MadeChange |= N2->UpdateNodeType(0, N1->getExtType(0), *this); } } } } bool HasUnresolvedTypes = false; for (const TreePatternNodePtr &Tree : Trees) HasUnresolvedTypes |= Tree->ContainsUnresolvedType(*this); return !HasUnresolvedTypes; } void TreePattern::print(raw_ostream &OS) const { OS << getRecord()->getName(); if (!Args.empty()) { OS << "(" << Args[0]; for (unsigned i = 1, e = Args.size(); i != e; ++i) OS << ", " << Args[i]; OS << ")"; } OS << ": "; if (Trees.size() > 1) OS << "[\n"; for (const TreePatternNodePtr &Tree : Trees) { OS << "\t"; Tree->print(OS); OS << "\n"; } if (Trees.size() > 1) OS << "]\n"; } void TreePattern::dump() const { print(errs()); } //===----------------------------------------------------------------------===// // CodeGenDAGPatterns implementation // CodeGenDAGPatterns::CodeGenDAGPatterns(RecordKeeper &R, PatternRewriterFn PatternRewriter) : Records(R), Target(R), LegalVTS(Target.getLegalValueTypes()), PatternRewriter(PatternRewriter) { Intrinsics = CodeGenIntrinsicTable(Records); ParseNodeInfo(); ParseNodeTransforms(); ParseComplexPatterns(); ParsePatternFragments(); ParseDefaultOperands(); ParseInstructions(); ParsePatternFragments(/*OutFrags*/true); ParsePatterns(); // Break patterns with parameterized types into a series of patterns, // where each one has a fixed type and is predicated on the conditions // of the associated HW mode. ExpandHwModeBasedTypes(); // Generate variants. For example, commutative patterns can match // multiple ways. Add them to PatternsToMatch as well. GenerateVariants(); // Infer instruction flags. For example, we can detect loads, // stores, and side effects in many cases by examining an // instruction's pattern. InferInstructionFlags(); // Verify that instruction flags match the patterns. VerifyInstructionFlags(); } Record *CodeGenDAGPatterns::getSDNodeNamed(StringRef Name) const { Record *N = Records.getDef(Name); if (!N || !N->isSubClassOf("SDNode")) PrintFatalError("Error getting SDNode '" + Name + "'!"); return N; } // Parse all of the SDNode definitions for the target, populating SDNodes. void CodeGenDAGPatterns::ParseNodeInfo() { std::vector Nodes = Records.getAllDerivedDefinitions("SDNode"); const CodeGenHwModes &CGH = getTargetInfo().getHwModes(); while (!Nodes.empty()) { Record *R = Nodes.back(); SDNodes.insert(std::make_pair(R, SDNodeInfo(R, CGH))); Nodes.pop_back(); } // Get the builtin intrinsic nodes. intrinsic_void_sdnode = getSDNodeNamed("intrinsic_void"); intrinsic_w_chain_sdnode = getSDNodeNamed("intrinsic_w_chain"); intrinsic_wo_chain_sdnode = getSDNodeNamed("intrinsic_wo_chain"); } /// ParseNodeTransforms - Parse all SDNodeXForm instances into the SDNodeXForms /// map, and emit them to the file as functions. void CodeGenDAGPatterns::ParseNodeTransforms() { std::vector Xforms = Records.getAllDerivedDefinitions("SDNodeXForm"); while (!Xforms.empty()) { Record *XFormNode = Xforms.back(); Record *SDNode = XFormNode->getValueAsDef("Opcode"); StringRef Code = XFormNode->getValueAsString("XFormFunction"); SDNodeXForms.insert( std::make_pair(XFormNode, NodeXForm(SDNode, std::string(Code)))); Xforms.pop_back(); } } void CodeGenDAGPatterns::ParseComplexPatterns() { std::vector AMs = Records.getAllDerivedDefinitions("ComplexPattern"); while (!AMs.empty()) { ComplexPatterns.insert(std::make_pair(AMs.back(), AMs.back())); AMs.pop_back(); } } /// ParsePatternFragments - Parse all of the PatFrag definitions in the .td /// file, building up the PatternFragments map. After we've collected them all, /// inline fragments together as necessary, so that there are no references left /// inside a pattern fragment to a pattern fragment. /// void CodeGenDAGPatterns::ParsePatternFragments(bool OutFrags) { std::vector Fragments = Records.getAllDerivedDefinitions("PatFrags"); // First step, parse all of the fragments. for (Record *Frag : Fragments) { if (OutFrags != Frag->isSubClassOf("OutPatFrag")) continue; ListInit *LI = Frag->getValueAsListInit("Fragments"); TreePattern *P = (PatternFragments[Frag] = std::make_unique( Frag, LI, !Frag->isSubClassOf("OutPatFrag"), *this)).get(); // Validate the argument list, converting it to set, to discard duplicates. std::vector &Args = P->getArgList(); // Copy the args so we can take StringRefs to them. auto ArgsCopy = Args; SmallDenseSet OperandsSet; OperandsSet.insert(ArgsCopy.begin(), ArgsCopy.end()); if (OperandsSet.count("")) P->error("Cannot have unnamed 'node' values in pattern fragment!"); // Parse the operands list. DagInit *OpsList = Frag->getValueAsDag("Operands"); DefInit *OpsOp = dyn_cast(OpsList->getOperator()); // Special cases: ops == outs == ins. Different names are used to // improve readability. if (!OpsOp || (OpsOp->getDef()->getName() != "ops" && OpsOp->getDef()->getName() != "outs" && OpsOp->getDef()->getName() != "ins")) P->error("Operands list should start with '(ops ... '!"); // Copy over the arguments. Args.clear(); for (unsigned j = 0, e = OpsList->getNumArgs(); j != e; ++j) { if (!isa(OpsList->getArg(j)) || cast(OpsList->getArg(j))->getDef()->getName() != "node") P->error("Operands list should all be 'node' values."); if (!OpsList->getArgName(j)) P->error("Operands list should have names for each operand!"); StringRef ArgNameStr = OpsList->getArgNameStr(j); if (!OperandsSet.count(ArgNameStr)) P->error("'" + ArgNameStr + "' does not occur in pattern or was multiply specified!"); OperandsSet.erase(ArgNameStr); Args.push_back(std::string(ArgNameStr)); } if (!OperandsSet.empty()) P->error("Operands list does not contain an entry for operand '" + *OperandsSet.begin() + "'!"); // If there is a node transformation corresponding to this, keep track of // it. Record *Transform = Frag->getValueAsDef("OperandTransform"); if (!getSDNodeTransform(Transform).second.empty()) // not noop xform? for (auto T : P->getTrees()) T->setTransformFn(Transform); } // Now that we've parsed all of the tree fragments, do a closure on them so // that there are not references to PatFrags left inside of them. for (Record *Frag : Fragments) { if (OutFrags != Frag->isSubClassOf("OutPatFrag")) continue; TreePattern &ThePat = *PatternFragments[Frag]; ThePat.InlinePatternFragments(); // Infer as many types as possible. Don't worry about it if we don't infer // all of them, some may depend on the inputs of the pattern. Also, don't // validate type sets; validation may cause spurious failures e.g. if a // fragment needs floating-point types but the current target does not have // any (this is only an error if that fragment is ever used!). { TypeInfer::SuppressValidation SV(ThePat.getInfer()); ThePat.InferAllTypes(); ThePat.resetError(); } // If debugging, print out the pattern fragment result. LLVM_DEBUG(ThePat.dump()); } } void CodeGenDAGPatterns::ParseDefaultOperands() { std::vector DefaultOps; DefaultOps = Records.getAllDerivedDefinitions("OperandWithDefaultOps"); // Find some SDNode. assert(!SDNodes.empty() && "No SDNodes parsed?"); Init *SomeSDNode = DefInit::get(SDNodes.begin()->first); for (unsigned i = 0, e = DefaultOps.size(); i != e; ++i) { DagInit *DefaultInfo = DefaultOps[i]->getValueAsDag("DefaultOps"); // Clone the DefaultInfo dag node, changing the operator from 'ops' to // SomeSDnode so that we can parse this. std::vector > Ops; for (unsigned op = 0, e = DefaultInfo->getNumArgs(); op != e; ++op) Ops.push_back(std::make_pair(DefaultInfo->getArg(op), DefaultInfo->getArgName(op))); DagInit *DI = DagInit::get(SomeSDNode, nullptr, Ops); // Create a TreePattern to parse this. TreePattern P(DefaultOps[i], DI, false, *this); assert(P.getNumTrees() == 1 && "This ctor can only produce one tree!"); // Copy the operands over into a DAGDefaultOperand. DAGDefaultOperand DefaultOpInfo; const TreePatternNodePtr &T = P.getTree(0); for (unsigned op = 0, e = T->getNumChildren(); op != e; ++op) { TreePatternNodePtr TPN = T->getChildShared(op); while (TPN->ApplyTypeConstraints(P, false)) /* Resolve all types */; if (TPN->ContainsUnresolvedType(P)) { PrintFatalError("Value #" + Twine(i) + " of OperandWithDefaultOps '" + DefaultOps[i]->getName() + "' doesn't have a concrete type!"); } DefaultOpInfo.DefaultOps.push_back(std::move(TPN)); } // Insert it into the DefaultOperands map so we can find it later. DefaultOperands[DefaultOps[i]] = DefaultOpInfo; } } /// HandleUse - Given "Pat" a leaf in the pattern, check to see if it is an /// instruction input. Return true if this is a real use. static bool HandleUse(TreePattern &I, TreePatternNodePtr Pat, std::map &InstInputs) { // No name -> not interesting. if (Pat->getName().empty()) { if (Pat->isLeaf()) { DefInit *DI = dyn_cast(Pat->getLeafValue()); if (DI && (DI->getDef()->isSubClassOf("RegisterClass") || DI->getDef()->isSubClassOf("RegisterOperand"))) I.error("Input " + DI->getDef()->getName() + " must be named!"); } return false; } Record *Rec; if (Pat->isLeaf()) { DefInit *DI = dyn_cast(Pat->getLeafValue()); if (!DI) I.error("Input $" + Pat->getName() + " must be an identifier!"); Rec = DI->getDef(); } else { Rec = Pat->getOperator(); } // SRCVALUE nodes are ignored. if (Rec->getName() == "srcvalue") return false; TreePatternNodePtr &Slot = InstInputs[Pat->getName()]; if (!Slot) { Slot = Pat; return true; } Record *SlotRec; if (Slot->isLeaf()) { SlotRec = cast(Slot->getLeafValue())->getDef(); } else { assert(Slot->getNumChildren() == 0 && "can't be a use with children!"); SlotRec = Slot->getOperator(); } // Ensure that the inputs agree if we've already seen this input. if (Rec != SlotRec) I.error("All $" + Pat->getName() + " inputs must agree with each other"); // Ensure that the types can agree as well. Slot->UpdateNodeType(0, Pat->getExtType(0), I); Pat->UpdateNodeType(0, Slot->getExtType(0), I); if (Slot->getExtTypes() != Pat->getExtTypes()) I.error("All $" + Pat->getName() + " inputs must agree with each other"); return true; } /// FindPatternInputsAndOutputs - Scan the specified TreePatternNode (which is /// part of "I", the instruction), computing the set of inputs and outputs of /// the pattern. Report errors if we see anything naughty. void CodeGenDAGPatterns::FindPatternInputsAndOutputs( TreePattern &I, TreePatternNodePtr Pat, std::map &InstInputs, MapVector> &InstResults, std::vector &InstImpResults) { // The instruction pattern still has unresolved fragments. For *named* // nodes we must resolve those here. This may not result in multiple // alternatives. if (!Pat->getName().empty()) { TreePattern SrcPattern(I.getRecord(), Pat, true, *this); SrcPattern.InlinePatternFragments(); SrcPattern.InferAllTypes(); Pat = SrcPattern.getOnlyTree(); } if (Pat->isLeaf()) { bool isUse = HandleUse(I, Pat, InstInputs); if (!isUse && Pat->getTransformFn()) I.error("Cannot specify a transform function for a non-input value!"); return; } if (Pat->getOperator()->getName() == "implicit") { for (unsigned i = 0, e = Pat->getNumChildren(); i != e; ++i) { TreePatternNode *Dest = Pat->getChild(i); if (!Dest->isLeaf()) I.error("implicitly defined value should be a register!"); DefInit *Val = dyn_cast(Dest->getLeafValue()); if (!Val || !Val->getDef()->isSubClassOf("Register")) I.error("implicitly defined value should be a register!"); InstImpResults.push_back(Val->getDef()); } return; } if (Pat->getOperator()->getName() != "set") { // If this is not a set, verify that the children nodes are not void typed, // and recurse. for (unsigned i = 0, e = Pat->getNumChildren(); i != e; ++i) { if (Pat->getChild(i)->getNumTypes() == 0) I.error("Cannot have void nodes inside of patterns!"); FindPatternInputsAndOutputs(I, Pat->getChildShared(i), InstInputs, InstResults, InstImpResults); } // If this is a non-leaf node with no children, treat it basically as if // it were a leaf. This handles nodes like (imm). bool isUse = HandleUse(I, Pat, InstInputs); if (!isUse && Pat->getTransformFn()) I.error("Cannot specify a transform function for a non-input value!"); return; } // Otherwise, this is a set, validate and collect instruction results. if (Pat->getNumChildren() == 0) I.error("set requires operands!"); if (Pat->getTransformFn()) I.error("Cannot specify a transform function on a set node!"); // Check the set destinations. unsigned NumDests = Pat->getNumChildren()-1; for (unsigned i = 0; i != NumDests; ++i) { TreePatternNodePtr Dest = Pat->getChildShared(i); // For set destinations we also must resolve fragments here. TreePattern DestPattern(I.getRecord(), Dest, false, *this); DestPattern.InlinePatternFragments(); DestPattern.InferAllTypes(); Dest = DestPattern.getOnlyTree(); if (!Dest->isLeaf()) I.error("set destination should be a register!"); DefInit *Val = dyn_cast(Dest->getLeafValue()); if (!Val) { I.error("set destination should be a register!"); continue; } if (Val->getDef()->isSubClassOf("RegisterClass") || Val->getDef()->isSubClassOf("ValueType") || Val->getDef()->isSubClassOf("RegisterOperand") || Val->getDef()->isSubClassOf("PointerLikeRegClass")) { if (Dest->getName().empty()) I.error("set destination must have a name!"); if (InstResults.count(Dest->getName())) I.error("cannot set '" + Dest->getName() + "' multiple times"); InstResults[Dest->getName()] = Dest; } else if (Val->getDef()->isSubClassOf("Register")) { InstImpResults.push_back(Val->getDef()); } else { I.error("set destination should be a register!"); } } // Verify and collect info from the computation. FindPatternInputsAndOutputs(I, Pat->getChildShared(NumDests), InstInputs, InstResults, InstImpResults); } //===----------------------------------------------------------------------===// // Instruction Analysis //===----------------------------------------------------------------------===// class InstAnalyzer { const CodeGenDAGPatterns &CDP; public: bool hasSideEffects; bool mayStore; bool mayLoad; bool isBitcast; bool isVariadic; bool hasChain; InstAnalyzer(const CodeGenDAGPatterns &cdp) : CDP(cdp), hasSideEffects(false), mayStore(false), mayLoad(false), isBitcast(false), isVariadic(false), hasChain(false) {} void Analyze(const PatternToMatch &Pat) { const TreePatternNode *N = Pat.getSrcPattern(); AnalyzeNode(N); // These properties are detected only on the root node. isBitcast = IsNodeBitcast(N); } private: bool IsNodeBitcast(const TreePatternNode *N) const { if (hasSideEffects || mayLoad || mayStore || isVariadic) return false; if (N->isLeaf()) return false; if (N->getNumChildren() != 1 || !N->getChild(0)->isLeaf()) return false; const SDNodeInfo &OpInfo = CDP.getSDNodeInfo(N->getOperator()); if (OpInfo.getNumResults() != 1 || OpInfo.getNumOperands() != 1) return false; return OpInfo.getEnumName() == "ISD::BITCAST"; } public: void AnalyzeNode(const TreePatternNode *N) { if (N->isLeaf()) { if (DefInit *DI = dyn_cast(N->getLeafValue())) { Record *LeafRec = DI->getDef(); // Handle ComplexPattern leaves. if (LeafRec->isSubClassOf("ComplexPattern")) { const ComplexPattern &CP = CDP.getComplexPattern(LeafRec); if (CP.hasProperty(SDNPMayStore)) mayStore = true; if (CP.hasProperty(SDNPMayLoad)) mayLoad = true; if (CP.hasProperty(SDNPSideEffect)) hasSideEffects = true; } } return; } // Analyze children. for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) AnalyzeNode(N->getChild(i)); // Notice properties of the node. if (N->NodeHasProperty(SDNPMayStore, CDP)) mayStore = true; if (N->NodeHasProperty(SDNPMayLoad, CDP)) mayLoad = true; if (N->NodeHasProperty(SDNPSideEffect, CDP)) hasSideEffects = true; if (N->NodeHasProperty(SDNPVariadic, CDP)) isVariadic = true; if (N->NodeHasProperty(SDNPHasChain, CDP)) hasChain = true; if (const CodeGenIntrinsic *IntInfo = N->getIntrinsicInfo(CDP)) { // If this is an intrinsic, analyze it. if (IntInfo->ModRef & CodeGenIntrinsic::MR_Ref) mayLoad = true;// These may load memory. if (IntInfo->ModRef & CodeGenIntrinsic::MR_Mod) mayStore = true;// Intrinsics that can write to memory are 'mayStore'. if (IntInfo->ModRef >= CodeGenIntrinsic::ReadWriteMem || IntInfo->hasSideEffects) // ReadWriteMem intrinsics can have other strange effects. hasSideEffects = true; } } }; static bool InferFromPattern(CodeGenInstruction &InstInfo, const InstAnalyzer &PatInfo, Record *PatDef) { bool Error = false; // Remember where InstInfo got its flags. if (InstInfo.hasUndefFlags()) InstInfo.InferredFrom = PatDef; // Check explicitly set flags for consistency. if (InstInfo.hasSideEffects != PatInfo.hasSideEffects && !InstInfo.hasSideEffects_Unset) { // Allow explicitly setting hasSideEffects = 1 on instructions, even when // the pattern has no side effects. That could be useful for div/rem // instructions that may trap. if (!InstInfo.hasSideEffects) { Error = true; PrintError(PatDef->getLoc(), "Pattern doesn't match hasSideEffects = " + Twine(InstInfo.hasSideEffects)); } } if (InstInfo.mayStore != PatInfo.mayStore && !InstInfo.mayStore_Unset) { Error = true; PrintError(PatDef->getLoc(), "Pattern doesn't match mayStore = " + Twine(InstInfo.mayStore)); } if (InstInfo.mayLoad != PatInfo.mayLoad && !InstInfo.mayLoad_Unset) { // Allow explicitly setting mayLoad = 1, even when the pattern has no loads. // Some targets translate immediates to loads. if (!InstInfo.mayLoad) { Error = true; PrintError(PatDef->getLoc(), "Pattern doesn't match mayLoad = " + Twine(InstInfo.mayLoad)); } } // Transfer inferred flags. InstInfo.hasSideEffects |= PatInfo.hasSideEffects; InstInfo.mayStore |= PatInfo.mayStore; InstInfo.mayLoad |= PatInfo.mayLoad; // These flags are silently added without any verification. // FIXME: To match historical behavior of TableGen, for now add those flags // only when we're inferring from the primary instruction pattern. if (PatDef->isSubClassOf("Instruction")) { InstInfo.isBitcast |= PatInfo.isBitcast; InstInfo.hasChain |= PatInfo.hasChain; InstInfo.hasChain_Inferred = true; } // Don't infer isVariadic. This flag means something different on SDNodes and // instructions. For example, a CALL SDNode is variadic because it has the // call arguments as operands, but a CALL instruction is not variadic - it // has argument registers as implicit, not explicit uses. return Error; } /// hasNullFragReference - Return true if the DAG has any reference to the /// null_frag operator. static bool hasNullFragReference(DagInit *DI) { DefInit *OpDef = dyn_cast(DI->getOperator()); if (!OpDef) return false; Record *Operator = OpDef->getDef(); // If this is the null fragment, return true. if (Operator->getName() == "null_frag") return true; // If any of the arguments reference the null fragment, return true. for (unsigned i = 0, e = DI->getNumArgs(); i != e; ++i) { if (auto Arg = dyn_cast(DI->getArg(i))) if (Arg->getDef()->getName() == "null_frag") return true; DagInit *Arg = dyn_cast(DI->getArg(i)); if (Arg && hasNullFragReference(Arg)) return true; } return false; } /// hasNullFragReference - Return true if any DAG in the list references /// the null_frag operator. static bool hasNullFragReference(ListInit *LI) { for (Init *I : LI->getValues()) { DagInit *DI = dyn_cast(I); assert(DI && "non-dag in an instruction Pattern list?!"); if (hasNullFragReference(DI)) return true; } return false; } /// Get all the instructions in a tree. static void getInstructionsInTree(TreePatternNode *Tree, SmallVectorImpl &Instrs) { if (Tree->isLeaf()) return; if (Tree->getOperator()->isSubClassOf("Instruction")) Instrs.push_back(Tree->getOperator()); for (unsigned i = 0, e = Tree->getNumChildren(); i != e; ++i) getInstructionsInTree(Tree->getChild(i), Instrs); } /// Check the class of a pattern leaf node against the instruction operand it /// represents. static bool checkOperandClass(CGIOperandList::OperandInfo &OI, Record *Leaf) { if (OI.Rec == Leaf) return true; // Allow direct value types to be used in instruction set patterns. // The type will be checked later. if (Leaf->isSubClassOf("ValueType")) return true; // Patterns can also be ComplexPattern instances. if (Leaf->isSubClassOf("ComplexPattern")) return true; return false; } void CodeGenDAGPatterns::parseInstructionPattern( CodeGenInstruction &CGI, ListInit *Pat, DAGInstMap &DAGInsts) { assert(!DAGInsts.count(CGI.TheDef) && "Instruction already parsed!"); // Parse the instruction. TreePattern I(CGI.TheDef, Pat, true, *this); // InstInputs - Keep track of all of the inputs of the instruction, along // with the record they are declared as. std::map InstInputs; // InstResults - Keep track of all the virtual registers that are 'set' // in the instruction, including what reg class they are. MapVector> InstResults; std::vector InstImpResults; // Verify that the top-level forms in the instruction are of void type, and // fill in the InstResults map. SmallString<32> TypesString; for (unsigned j = 0, e = I.getNumTrees(); j != e; ++j) { TypesString.clear(); TreePatternNodePtr Pat = I.getTree(j); if (Pat->getNumTypes() != 0) { raw_svector_ostream OS(TypesString); for (unsigned k = 0, ke = Pat->getNumTypes(); k != ke; ++k) { if (k > 0) OS << ", "; Pat->getExtType(k).writeToStream(OS); } I.error("Top-level forms in instruction pattern should have" " void types, has types " + OS.str()); } // Find inputs and outputs, and verify the structure of the uses/defs. FindPatternInputsAndOutputs(I, Pat, InstInputs, InstResults, InstImpResults); } // Now that we have inputs and outputs of the pattern, inspect the operands // list for the instruction. This determines the order that operands are // added to the machine instruction the node corresponds to. unsigned NumResults = InstResults.size(); // Parse the operands list from the (ops) list, validating it. assert(I.getArgList().empty() && "Args list should still be empty here!"); // Check that all of the results occur first in the list. std::vector Results; std::vector ResultIndices; SmallVector ResNodes; for (unsigned i = 0; i != NumResults; ++i) { if (i == CGI.Operands.size()) { const std::string &OpName = llvm::find_if( InstResults, [](const std::pair &P) { return P.second; }) ->first; I.error("'" + OpName + "' set but does not appear in operand list!"); } const std::string &OpName = CGI.Operands[i].Name; // Check that it exists in InstResults. auto InstResultIter = InstResults.find(OpName); if (InstResultIter == InstResults.end() || !InstResultIter->second) I.error("Operand $" + OpName + " does not exist in operand list!"); TreePatternNodePtr RNode = InstResultIter->second; Record *R = cast(RNode->getLeafValue())->getDef(); ResNodes.push_back(std::move(RNode)); if (!R) I.error("Operand $" + OpName + " should be a set destination: all " "outputs must occur before inputs in operand list!"); if (!checkOperandClass(CGI.Operands[i], R)) I.error("Operand $" + OpName + " class mismatch!"); // Remember the return type. Results.push_back(CGI.Operands[i].Rec); // Remember the result index. ResultIndices.push_back(std::distance(InstResults.begin(), InstResultIter)); // Okay, this one checks out. InstResultIter->second = nullptr; } // Loop over the inputs next. std::vector ResultNodeOperands; std::vector Operands; for (unsigned i = NumResults, e = CGI.Operands.size(); i != e; ++i) { CGIOperandList::OperandInfo &Op = CGI.Operands[i]; const std::string &OpName = Op.Name; if (OpName.empty()) I.error("Operand #" + Twine(i) + " in operands list has no name!"); if (!InstInputs.count(OpName)) { // If this is an operand with a DefaultOps set filled in, we can ignore // this. When we codegen it, we will do so as always executed. if (Op.Rec->isSubClassOf("OperandWithDefaultOps")) { // Does it have a non-empty DefaultOps field? If so, ignore this // operand. if (!getDefaultOperand(Op.Rec).DefaultOps.empty()) continue; } I.error("Operand $" + OpName + " does not appear in the instruction pattern"); } TreePatternNodePtr InVal = InstInputs[OpName]; InstInputs.erase(OpName); // It occurred, remove from map. if (InVal->isLeaf() && isa(InVal->getLeafValue())) { Record *InRec = static_cast(InVal->getLeafValue())->getDef(); if (!checkOperandClass(Op, InRec)) I.error("Operand $" + OpName + "'s register class disagrees" " between the operand and pattern"); } Operands.push_back(Op.Rec); // Construct the result for the dest-pattern operand list. TreePatternNodePtr OpNode = InVal->clone(); // No predicate is useful on the result. OpNode->clearPredicateCalls(); // Promote the xform function to be an explicit node if set. if (Record *Xform = OpNode->getTransformFn()) { OpNode->setTransformFn(nullptr); std::vector Children; Children.push_back(OpNode); OpNode = std::make_shared(Xform, std::move(Children), OpNode->getNumTypes()); } ResultNodeOperands.push_back(std::move(OpNode)); } if (!InstInputs.empty()) I.error("Input operand $" + InstInputs.begin()->first + " occurs in pattern but not in operands list!"); TreePatternNodePtr ResultPattern = std::make_shared( I.getRecord(), std::move(ResultNodeOperands), GetNumNodeResults(I.getRecord(), *this)); // Copy fully inferred output node types to instruction result pattern. for (unsigned i = 0; i != NumResults; ++i) { assert(ResNodes[i]->getNumTypes() == 1 && "FIXME: Unhandled"); ResultPattern->setType(i, ResNodes[i]->getExtType(0)); ResultPattern->setResultIndex(i, ResultIndices[i]); } // FIXME: Assume only the first tree is the pattern. The others are clobber // nodes. TreePatternNodePtr Pattern = I.getTree(0); TreePatternNodePtr SrcPattern; if (Pattern->getOperator()->getName() == "set") { SrcPattern = Pattern->getChild(Pattern->getNumChildren()-1)->clone(); } else{ // Not a set (store or something?) SrcPattern = Pattern; } // Create and insert the instruction. // FIXME: InstImpResults should not be part of DAGInstruction. Record *R = I.getRecord(); DAGInsts.emplace(std::piecewise_construct, std::forward_as_tuple(R), std::forward_as_tuple(Results, Operands, InstImpResults, SrcPattern, ResultPattern)); LLVM_DEBUG(I.dump()); } /// ParseInstructions - Parse all of the instructions, inlining and resolving /// any fragments involved. This populates the Instructions list with fully /// resolved instructions. void CodeGenDAGPatterns::ParseInstructions() { std::vector Instrs = Records.getAllDerivedDefinitions("Instruction"); for (Record *Instr : Instrs) { ListInit *LI = nullptr; if (isa(Instr->getValueInit("Pattern"))) LI = Instr->getValueAsListInit("Pattern"); // If there is no pattern, only collect minimal information about the // instruction for its operand list. We have to assume that there is one // result, as we have no detailed info. A pattern which references the // null_frag operator is as-if no pattern were specified. Normally this // is from a multiclass expansion w/ a SDPatternOperator passed in as // null_frag. if (!LI || LI->empty() || hasNullFragReference(LI)) { std::vector Results; std::vector Operands; CodeGenInstruction &InstInfo = Target.getInstruction(Instr); if (InstInfo.Operands.size() != 0) { for (unsigned j = 0, e = InstInfo.Operands.NumDefs; j < e; ++j) Results.push_back(InstInfo.Operands[j].Rec); // The rest are inputs. for (unsigned j = InstInfo.Operands.NumDefs, e = InstInfo.Operands.size(); j < e; ++j) Operands.push_back(InstInfo.Operands[j].Rec); } // Create and insert the instruction. std::vector ImpResults; Instructions.insert(std::make_pair(Instr, DAGInstruction(Results, Operands, ImpResults))); continue; // no pattern. } CodeGenInstruction &CGI = Target.getInstruction(Instr); parseInstructionPattern(CGI, LI, Instructions); } // If we can, convert the instructions to be patterns that are matched! for (auto &Entry : Instructions) { Record *Instr = Entry.first; DAGInstruction &TheInst = Entry.second; TreePatternNodePtr SrcPattern = TheInst.getSrcPattern(); TreePatternNodePtr ResultPattern = TheInst.getResultPattern(); if (SrcPattern && ResultPattern) { TreePattern Pattern(Instr, SrcPattern, true, *this); TreePattern Result(Instr, ResultPattern, false, *this); ParseOnePattern(Instr, Pattern, Result, TheInst.getImpResults()); } } } typedef std::pair NameRecord; static void FindNames(TreePatternNode *P, std::map &Names, TreePattern *PatternTop) { if (!P->getName().empty()) { NameRecord &Rec = Names[P->getName()]; // If this is the first instance of the name, remember the node. if (Rec.second++ == 0) Rec.first = P; else if (Rec.first->getExtTypes() != P->getExtTypes()) PatternTop->error("repetition of value: $" + P->getName() + " where different uses have different types!"); } if (!P->isLeaf()) { for (unsigned i = 0, e = P->getNumChildren(); i != e; ++i) FindNames(P->getChild(i), Names, PatternTop); } } std::vector CodeGenDAGPatterns::makePredList(ListInit *L) { std::vector Preds; for (Init *I : L->getValues()) { if (DefInit *Pred = dyn_cast(I)) Preds.push_back(Pred->getDef()); else llvm_unreachable("Non-def on the list"); } // Sort so that different orders get canonicalized to the same string. llvm::sort(Preds); return Preds; } void CodeGenDAGPatterns::AddPatternToMatch(TreePattern *Pattern, PatternToMatch &&PTM) { // Do some sanity checking on the pattern we're about to match. std::string Reason; if (!PTM.getSrcPattern()->canPatternMatch(Reason, *this)) { PrintWarning(Pattern->getRecord()->getLoc(), Twine("Pattern can never match: ") + Reason); return; } // If the source pattern's root is a complex pattern, that complex pattern // must specify the nodes it can potentially match. if (const ComplexPattern *CP = PTM.getSrcPattern()->getComplexPatternInfo(*this)) if (CP->getRootNodes().empty()) Pattern->error("ComplexPattern at root must specify list of opcodes it" " could match"); // Find all of the named values in the input and output, ensure they have the // same type. std::map SrcNames, DstNames; FindNames(PTM.getSrcPattern(), SrcNames, Pattern); FindNames(PTM.getDstPattern(), DstNames, Pattern); // Scan all of the named values in the destination pattern, rejecting them if // they don't exist in the input pattern. for (const auto &Entry : DstNames) { if (SrcNames[Entry.first].first == nullptr) Pattern->error("Pattern has input without matching name in output: $" + Entry.first); } // Scan all of the named values in the source pattern, rejecting them if the // name isn't used in the dest, and isn't used to tie two values together. for (const auto &Entry : SrcNames) if (DstNames[Entry.first].first == nullptr && SrcNames[Entry.first].second == 1) Pattern->error("Pattern has dead named input: $" + Entry.first); PatternsToMatch.push_back(PTM); } void CodeGenDAGPatterns::InferInstructionFlags() { ArrayRef Instructions = Target.getInstructionsByEnumValue(); unsigned Errors = 0; // Try to infer flags from all patterns in PatternToMatch. These include // both the primary instruction patterns (which always come first) and // patterns defined outside the instruction. for (const PatternToMatch &PTM : ptms()) { // We can only infer from single-instruction patterns, otherwise we won't // know which instruction should get the flags. SmallVector PatInstrs; getInstructionsInTree(PTM.getDstPattern(), PatInstrs); if (PatInstrs.size() != 1) continue; // Get the single instruction. CodeGenInstruction &InstInfo = Target.getInstruction(PatInstrs.front()); // Only infer properties from the first pattern. We'll verify the others. if (InstInfo.InferredFrom) continue; InstAnalyzer PatInfo(*this); PatInfo.Analyze(PTM); Errors += InferFromPattern(InstInfo, PatInfo, PTM.getSrcRecord()); } if (Errors) PrintFatalError("pattern conflicts"); // If requested by the target, guess any undefined properties. if (Target.guessInstructionProperties()) { for (unsigned i = 0, e = Instructions.size(); i != e; ++i) { CodeGenInstruction *InstInfo = const_cast(Instructions[i]); if (InstInfo->InferredFrom) continue; // The mayLoad and mayStore flags default to false. // Conservatively assume hasSideEffects if it wasn't explicit. if (InstInfo->hasSideEffects_Unset) InstInfo->hasSideEffects = true; } return; } // Complain about any flags that are still undefined. for (unsigned i = 0, e = Instructions.size(); i != e; ++i) { CodeGenInstruction *InstInfo = const_cast(Instructions[i]); if (InstInfo->InferredFrom) continue; if (InstInfo->hasSideEffects_Unset) PrintError(InstInfo->TheDef->getLoc(), "Can't infer hasSideEffects from patterns"); if (InstInfo->mayStore_Unset) PrintError(InstInfo->TheDef->getLoc(), "Can't infer mayStore from patterns"); if (InstInfo->mayLoad_Unset) PrintError(InstInfo->TheDef->getLoc(), "Can't infer mayLoad from patterns"); } } /// Verify instruction flags against pattern node properties. void CodeGenDAGPatterns::VerifyInstructionFlags() { unsigned Errors = 0; for (ptm_iterator I = ptm_begin(), E = ptm_end(); I != E; ++I) { const PatternToMatch &PTM = *I; SmallVector Instrs; getInstructionsInTree(PTM.getDstPattern(), Instrs); if (Instrs.empty()) continue; // Count the number of instructions with each flag set. unsigned NumSideEffects = 0; unsigned NumStores = 0; unsigned NumLoads = 0; for (const Record *Instr : Instrs) { const CodeGenInstruction &InstInfo = Target.getInstruction(Instr); NumSideEffects += InstInfo.hasSideEffects; NumStores += InstInfo.mayStore; NumLoads += InstInfo.mayLoad; } // Analyze the source pattern. InstAnalyzer PatInfo(*this); PatInfo.Analyze(PTM); // Collect error messages. SmallVector Msgs; // Check for missing flags in the output. // Permit extra flags for now at least. if (PatInfo.hasSideEffects && !NumSideEffects) Msgs.push_back("pattern has side effects, but hasSideEffects isn't set"); // Don't verify store flags on instructions with side effects. At least for // intrinsics, side effects implies mayStore. if (!PatInfo.hasSideEffects && PatInfo.mayStore && !NumStores) Msgs.push_back("pattern may store, but mayStore isn't set"); // Similarly, mayStore implies mayLoad on intrinsics. if (!PatInfo.mayStore && PatInfo.mayLoad && !NumLoads) Msgs.push_back("pattern may load, but mayLoad isn't set"); // Print error messages. if (Msgs.empty()) continue; ++Errors; for (const std::string &Msg : Msgs) PrintError(PTM.getSrcRecord()->getLoc(), Twine(Msg) + " on the " + (Instrs.size() == 1 ? "instruction" : "output instructions")); // Provide the location of the relevant instruction definitions. for (const Record *Instr : Instrs) { if (Instr != PTM.getSrcRecord()) PrintError(Instr->getLoc(), "defined here"); const CodeGenInstruction &InstInfo = Target.getInstruction(Instr); if (InstInfo.InferredFrom && InstInfo.InferredFrom != InstInfo.TheDef && InstInfo.InferredFrom != PTM.getSrcRecord()) PrintError(InstInfo.InferredFrom->getLoc(), "inferred from pattern"); } } if (Errors) PrintFatalError("Errors in DAG patterns"); } /// Given a pattern result with an unresolved type, see if we can find one /// instruction with an unresolved result type. Force this result type to an /// arbitrary element if it's possible types to converge results. static bool ForceArbitraryInstResultType(TreePatternNode *N, TreePattern &TP) { if (N->isLeaf()) return false; // Analyze children. for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) if (ForceArbitraryInstResultType(N->getChild(i), TP)) return true; if (!N->getOperator()->isSubClassOf("Instruction")) return false; // If this type is already concrete or completely unknown we can't do // anything. TypeInfer &TI = TP.getInfer(); for (unsigned i = 0, e = N->getNumTypes(); i != e; ++i) { if (N->getExtType(i).empty() || TI.isConcrete(N->getExtType(i), false)) continue; // Otherwise, force its type to an arbitrary choice. if (TI.forceArbitrary(N->getExtType(i))) return true; } return false; } // Promote xform function to be an explicit node wherever set. static TreePatternNodePtr PromoteXForms(TreePatternNodePtr N) { if (Record *Xform = N->getTransformFn()) { N->setTransformFn(nullptr); std::vector Children; Children.push_back(PromoteXForms(N)); return std::make_shared(Xform, std::move(Children), N->getNumTypes()); } if (!N->isLeaf()) for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) { TreePatternNodePtr Child = N->getChildShared(i); N->setChild(i, PromoteXForms(Child)); } return N; } void CodeGenDAGPatterns::ParseOnePattern(Record *TheDef, TreePattern &Pattern, TreePattern &Result, const std::vector &InstImpResults) { // Inline pattern fragments and expand multiple alternatives. Pattern.InlinePatternFragments(); Result.InlinePatternFragments(); if (Result.getNumTrees() != 1) Result.error("Cannot use multi-alternative fragments in result pattern!"); // Infer types. bool IterateInference; bool InferredAllPatternTypes, InferredAllResultTypes; do { // Infer as many types as possible. If we cannot infer all of them, we // can never do anything with this pattern: report it to the user. InferredAllPatternTypes = Pattern.InferAllTypes(&Pattern.getNamedNodesMap()); // Infer as many types as possible. If we cannot infer all of them, we // can never do anything with this pattern: report it to the user. InferredAllResultTypes = Result.InferAllTypes(&Pattern.getNamedNodesMap()); IterateInference = false; // Apply the type of the result to the source pattern. This helps us // resolve cases where the input type is known to be a pointer type (which // is considered resolved), but the result knows it needs to be 32- or // 64-bits. Infer the other way for good measure. for (auto T : Pattern.getTrees()) for (unsigned i = 0, e = std::min(Result.getOnlyTree()->getNumTypes(), T->getNumTypes()); i != e; ++i) { IterateInference |= T->UpdateNodeType( i, Result.getOnlyTree()->getExtType(i), Result); IterateInference |= Result.getOnlyTree()->UpdateNodeType( i, T->getExtType(i), Result); } // If our iteration has converged and the input pattern's types are fully // resolved but the result pattern is not fully resolved, we may have a // situation where we have two instructions in the result pattern and // the instructions require a common register class, but don't care about // what actual MVT is used. This is actually a bug in our modelling: // output patterns should have register classes, not MVTs. // // In any case, to handle this, we just go through and disambiguate some // arbitrary types to the result pattern's nodes. if (!IterateInference && InferredAllPatternTypes && !InferredAllResultTypes) IterateInference = ForceArbitraryInstResultType(Result.getTree(0).get(), Result); } while (IterateInference); // Verify that we inferred enough types that we can do something with the // pattern and result. If these fire the user has to add type casts. if (!InferredAllPatternTypes) Pattern.error("Could not infer all types in pattern!"); if (!InferredAllResultTypes) { Pattern.dump(); Result.error("Could not infer all types in pattern result!"); } // Promote xform function to be an explicit node wherever set. TreePatternNodePtr DstShared = PromoteXForms(Result.getOnlyTree()); TreePattern Temp(Result.getRecord(), DstShared, false, *this); Temp.InferAllTypes(); ListInit *Preds = TheDef->getValueAsListInit("Predicates"); int Complexity = TheDef->getValueAsInt("AddedComplexity"); if (PatternRewriter) PatternRewriter(&Pattern); // A pattern may end up with an "impossible" type, i.e. a situation // where all types have been eliminated for some node in this pattern. // This could occur for intrinsics that only make sense for a specific // value type, and use a specific register class. If, for some mode, // that register class does not accept that type, the type inference // will lead to a contradiction, which is not an error however, but // a sign that this pattern will simply never match. if (Temp.getOnlyTree()->hasPossibleType()) for (auto T : Pattern.getTrees()) if (T->hasPossibleType()) AddPatternToMatch(&Pattern, PatternToMatch(TheDef, makePredList(Preds), T, Temp.getOnlyTree(), InstImpResults, Complexity, TheDef->getID())); } void CodeGenDAGPatterns::ParsePatterns() { std::vector Patterns = Records.getAllDerivedDefinitions("Pattern"); for (Record *CurPattern : Patterns) { DagInit *Tree = CurPattern->getValueAsDag("PatternToMatch"); // If the pattern references the null_frag, there's nothing to do. if (hasNullFragReference(Tree)) continue; TreePattern Pattern(CurPattern, Tree, true, *this); ListInit *LI = CurPattern->getValueAsListInit("ResultInstrs"); if (LI->empty()) continue; // no pattern. // Parse the instruction. TreePattern Result(CurPattern, LI, false, *this); if (Result.getNumTrees() != 1) Result.error("Cannot handle instructions producing instructions " "with temporaries yet!"); // Validate that the input pattern is correct. std::map InstInputs; MapVector> InstResults; std::vector InstImpResults; for (unsigned j = 0, ee = Pattern.getNumTrees(); j != ee; ++j) FindPatternInputsAndOutputs(Pattern, Pattern.getTree(j), InstInputs, InstResults, InstImpResults); ParseOnePattern(CurPattern, Pattern, Result, InstImpResults); } } static void collectModes(std::set &Modes, const TreePatternNode *N) { for (const TypeSetByHwMode &VTS : N->getExtTypes()) for (const auto &I : VTS) Modes.insert(I.first); for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) collectModes(Modes, N->getChild(i)); } void CodeGenDAGPatterns::ExpandHwModeBasedTypes() { const CodeGenHwModes &CGH = getTargetInfo().getHwModes(); std::map> ModeChecks; std::vector Copy = PatternsToMatch; PatternsToMatch.clear(); auto AppendPattern = [this, &ModeChecks](PatternToMatch &P, unsigned Mode) { TreePatternNodePtr NewSrc = P.SrcPattern->clone(); TreePatternNodePtr NewDst = P.DstPattern->clone(); if (!NewSrc->setDefaultMode(Mode) || !NewDst->setDefaultMode(Mode)) { return; } std::vector Preds = P.Predicates; const std::vector &MC = ModeChecks[Mode]; llvm::append_range(Preds, MC); PatternsToMatch.emplace_back(P.getSrcRecord(), Preds, std::move(NewSrc), std::move(NewDst), P.getDstRegs(), P.getAddedComplexity(), Record::getNewUID(), Mode); }; for (PatternToMatch &P : Copy) { TreePatternNodePtr SrcP = nullptr, DstP = nullptr; if (P.SrcPattern->hasProperTypeByHwMode()) SrcP = P.SrcPattern; if (P.DstPattern->hasProperTypeByHwMode()) DstP = P.DstPattern; if (!SrcP && !DstP) { PatternsToMatch.push_back(P); continue; } std::set Modes; if (SrcP) collectModes(Modes, SrcP.get()); if (DstP) collectModes(Modes, DstP.get()); // The predicate for the default mode needs to be constructed for each // pattern separately. // Since not all modes must be present in each pattern, if a mode m is // absent, then there is no point in constructing a check for m. If such // a check was created, it would be equivalent to checking the default // mode, except not all modes' predicates would be a part of the checking // code. The subsequently generated check for the default mode would then // have the exact same patterns, but a different predicate code. To avoid // duplicated patterns with different predicate checks, construct the // default check as a negation of all predicates that are actually present // in the source/destination patterns. std::vector DefaultPred; for (unsigned M : Modes) { if (M == DefaultMode) continue; if (ModeChecks.find(M) != ModeChecks.end()) continue; // Fill the map entry for this mode. const HwMode &HM = CGH.getMode(M); ModeChecks[M].emplace_back(Predicate(HM.Features, true)); // Add negations of the HM's predicates to the default predicate. DefaultPred.emplace_back(Predicate(HM.Features, false)); } for (unsigned M : Modes) { if (M == DefaultMode) continue; AppendPattern(P, M); } bool HasDefault = Modes.count(DefaultMode); if (HasDefault) AppendPattern(P, DefaultMode); } } /// Dependent variable map for CodeGenDAGPattern variant generation typedef StringMap DepVarMap; static void FindDepVarsOf(TreePatternNode *N, DepVarMap &DepMap) { if (N->isLeaf()) { if (N->hasName() && isa(N->getLeafValue())) DepMap[N->getName()]++; } else { for (size_t i = 0, e = N->getNumChildren(); i != e; ++i) FindDepVarsOf(N->getChild(i), DepMap); } } /// Find dependent variables within child patterns static void FindDepVars(TreePatternNode *N, MultipleUseVarSet &DepVars) { DepVarMap depcounts; FindDepVarsOf(N, depcounts); for (const auto &Pair : depcounts) { if (Pair.getValue() > 1) DepVars.insert(Pair.getKey()); } } #ifndef NDEBUG /// Dump the dependent variable set: static void DumpDepVars(MultipleUseVarSet &DepVars) { if (DepVars.empty()) { LLVM_DEBUG(errs() << ""); } else { LLVM_DEBUG(errs() << "[ "); for (const auto &DepVar : DepVars) { LLVM_DEBUG(errs() << DepVar.getKey() << " "); } LLVM_DEBUG(errs() << "]"); } } #endif /// CombineChildVariants - Given a bunch of permutations of each child of the /// 'operator' node, put them together in all possible ways. static void CombineChildVariants( TreePatternNodePtr Orig, const std::vector> &ChildVariants, std::vector &OutVariants, CodeGenDAGPatterns &CDP, const MultipleUseVarSet &DepVars) { // Make sure that each operand has at least one variant to choose from. for (const auto &Variants : ChildVariants) if (Variants.empty()) return; // The end result is an all-pairs construction of the resultant pattern. std::vector Idxs; Idxs.resize(ChildVariants.size()); bool NotDone; do { #ifndef NDEBUG LLVM_DEBUG(if (!Idxs.empty()) { errs() << Orig->getOperator()->getName() << ": Idxs = [ "; for (unsigned Idx : Idxs) { errs() << Idx << " "; } errs() << "]\n"; }); #endif // Create the variant and add it to the output list. std::vector NewChildren; for (unsigned i = 0, e = ChildVariants.size(); i != e; ++i) NewChildren.push_back(ChildVariants[i][Idxs[i]]); TreePatternNodePtr R = std::make_shared( Orig->getOperator(), std::move(NewChildren), Orig->getNumTypes()); // Copy over properties. R->setName(Orig->getName()); R->setNamesAsPredicateArg(Orig->getNamesAsPredicateArg()); R->setPredicateCalls(Orig->getPredicateCalls()); R->setTransformFn(Orig->getTransformFn()); for (unsigned i = 0, e = Orig->getNumTypes(); i != e; ++i) R->setType(i, Orig->getExtType(i)); // If this pattern cannot match, do not include it as a variant. std::string ErrString; // Scan to see if this pattern has already been emitted. We can get // duplication due to things like commuting: // (and GPRC:$a, GPRC:$b) -> (and GPRC:$b, GPRC:$a) // which are the same pattern. Ignore the dups. if (R->canPatternMatch(ErrString, CDP) && none_of(OutVariants, [&](TreePatternNodePtr Variant) { return R->isIsomorphicTo(Variant.get(), DepVars); })) OutVariants.push_back(R); // Increment indices to the next permutation by incrementing the // indices from last index backward, e.g., generate the sequence // [0, 0], [0, 1], [1, 0], [1, 1]. int IdxsIdx; for (IdxsIdx = Idxs.size() - 1; IdxsIdx >= 0; --IdxsIdx) { if (++Idxs[IdxsIdx] == ChildVariants[IdxsIdx].size()) Idxs[IdxsIdx] = 0; else break; } NotDone = (IdxsIdx >= 0); } while (NotDone); } /// CombineChildVariants - A helper function for binary operators. /// static void CombineChildVariants(TreePatternNodePtr Orig, const std::vector &LHS, const std::vector &RHS, std::vector &OutVariants, CodeGenDAGPatterns &CDP, const MultipleUseVarSet &DepVars) { std::vector> ChildVariants; ChildVariants.push_back(LHS); ChildVariants.push_back(RHS); CombineChildVariants(Orig, ChildVariants, OutVariants, CDP, DepVars); } static void GatherChildrenOfAssociativeOpcode(TreePatternNodePtr N, std::vector &Children) { assert(N->getNumChildren()==2 &&"Associative but doesn't have 2 children!"); Record *Operator = N->getOperator(); // Only permit raw nodes. if (!N->getName().empty() || !N->getPredicateCalls().empty() || N->getTransformFn()) { Children.push_back(N); return; } if (N->getChild(0)->isLeaf() || N->getChild(0)->getOperator() != Operator) Children.push_back(N->getChildShared(0)); else GatherChildrenOfAssociativeOpcode(N->getChildShared(0), Children); if (N->getChild(1)->isLeaf() || N->getChild(1)->getOperator() != Operator) Children.push_back(N->getChildShared(1)); else GatherChildrenOfAssociativeOpcode(N->getChildShared(1), Children); } /// GenerateVariantsOf - Given a pattern N, generate all permutations we can of /// the (potentially recursive) pattern by using algebraic laws. /// static void GenerateVariantsOf(TreePatternNodePtr N, std::vector &OutVariants, CodeGenDAGPatterns &CDP, const MultipleUseVarSet &DepVars) { // We cannot permute leaves or ComplexPattern uses. if (N->isLeaf() || N->getOperator()->isSubClassOf("ComplexPattern")) { OutVariants.push_back(N); return; } // Look up interesting info about the node. const SDNodeInfo &NodeInfo = CDP.getSDNodeInfo(N->getOperator()); // If this node is associative, re-associate. if (NodeInfo.hasProperty(SDNPAssociative)) { // Re-associate by pulling together all of the linked operators std::vector MaximalChildren; GatherChildrenOfAssociativeOpcode(N, MaximalChildren); // Only handle child sizes of 3. Otherwise we'll end up trying too many // permutations. if (MaximalChildren.size() == 3) { // Find the variants of all of our maximal children. std::vector AVariants, BVariants, CVariants; GenerateVariantsOf(MaximalChildren[0], AVariants, CDP, DepVars); GenerateVariantsOf(MaximalChildren[1], BVariants, CDP, DepVars); GenerateVariantsOf(MaximalChildren[2], CVariants, CDP, DepVars); // There are only two ways we can permute the tree: // (A op B) op C and A op (B op C) // Within these forms, we can also permute A/B/C. // Generate legal pair permutations of A/B/C. std::vector ABVariants; std::vector BAVariants; std::vector ACVariants; std::vector CAVariants; std::vector BCVariants; std::vector CBVariants; CombineChildVariants(N, AVariants, BVariants, ABVariants, CDP, DepVars); CombineChildVariants(N, BVariants, AVariants, BAVariants, CDP, DepVars); CombineChildVariants(N, AVariants, CVariants, ACVariants, CDP, DepVars); CombineChildVariants(N, CVariants, AVariants, CAVariants, CDP, DepVars); CombineChildVariants(N, BVariants, CVariants, BCVariants, CDP, DepVars); CombineChildVariants(N, CVariants, BVariants, CBVariants, CDP, DepVars); // Combine those into the result: (x op x) op x CombineChildVariants(N, ABVariants, CVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, BAVariants, CVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, ACVariants, BVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, CAVariants, BVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, BCVariants, AVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, CBVariants, AVariants, OutVariants, CDP, DepVars); // Combine those into the result: x op (x op x) CombineChildVariants(N, CVariants, ABVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, CVariants, BAVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, BVariants, ACVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, BVariants, CAVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, AVariants, BCVariants, OutVariants, CDP, DepVars); CombineChildVariants(N, AVariants, CBVariants, OutVariants, CDP, DepVars); return; } } // Compute permutations of all children. std::vector> ChildVariants; ChildVariants.resize(N->getNumChildren()); for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) GenerateVariantsOf(N->getChildShared(i), ChildVariants[i], CDP, DepVars); // Build all permutations based on how the children were formed. CombineChildVariants(N, ChildVariants, OutVariants, CDP, DepVars); // If this node is commutative, consider the commuted order. bool isCommIntrinsic = N->isCommutativeIntrinsic(CDP); if (NodeInfo.hasProperty(SDNPCommutative) || isCommIntrinsic) { assert((N->getNumChildren()>=2 || isCommIntrinsic) && "Commutative but doesn't have 2 children!"); // Don't count children which are actually register references. unsigned NC = 0; for (unsigned i = 0, e = N->getNumChildren(); i != e; ++i) { TreePatternNode *Child = N->getChild(i); if (Child->isLeaf()) if (DefInit *DI = dyn_cast(Child->getLeafValue())) { Record *RR = DI->getDef(); if (RR->isSubClassOf("Register")) continue; } NC++; } // Consider the commuted order. if (isCommIntrinsic) { // Commutative intrinsic. First operand is the intrinsic id, 2nd and 3rd // operands are the commutative operands, and there might be more operands // after those. assert(NC >= 3 && "Commutative intrinsic should have at least 3 children!"); std::vector> Variants; Variants.push_back(std::move(ChildVariants[0])); // Intrinsic id. Variants.push_back(std::move(ChildVariants[2])); Variants.push_back(std::move(ChildVariants[1])); for (unsigned i = 3; i != NC; ++i) Variants.push_back(std::move(ChildVariants[i])); CombineChildVariants(N, Variants, OutVariants, CDP, DepVars); } else if (NC == N->getNumChildren()) { std::vector> Variants; Variants.push_back(std::move(ChildVariants[1])); Variants.push_back(std::move(ChildVariants[0])); for (unsigned i = 2; i != NC; ++i) Variants.push_back(std::move(ChildVariants[i])); CombineChildVariants(N, Variants, OutVariants, CDP, DepVars); } } } // GenerateVariants - Generate variants. For example, commutative patterns can // match multiple ways. Add them to PatternsToMatch as well. void CodeGenDAGPatterns::GenerateVariants() { LLVM_DEBUG(errs() << "Generating instruction variants.\n"); // Loop over all of the patterns we've collected, checking to see if we can // generate variants of the instruction, through the exploitation of // identities. This permits the target to provide aggressive matching without // the .td file having to contain tons of variants of instructions. // // Note that this loop adds new patterns to the PatternsToMatch list, but we // intentionally do not reconsider these. Any variants of added patterns have // already been added. // const unsigned NumOriginalPatterns = PatternsToMatch.size(); BitVector MatchedPatterns(NumOriginalPatterns); std::vector MatchedPredicates(NumOriginalPatterns, BitVector(NumOriginalPatterns)); typedef std::pair> DepsAndVariants; std::map PatternsWithVariants; // Collect patterns with more than one variant. for (unsigned i = 0; i != NumOriginalPatterns; ++i) { MultipleUseVarSet DepVars; std::vector Variants; FindDepVars(PatternsToMatch[i].getSrcPattern(), DepVars); LLVM_DEBUG(errs() << "Dependent/multiply used variables: "); LLVM_DEBUG(DumpDepVars(DepVars)); LLVM_DEBUG(errs() << "\n"); GenerateVariantsOf(PatternsToMatch[i].getSrcPatternShared(), Variants, *this, DepVars); assert(!Variants.empty() && "Must create at least original variant!"); if (Variants.size() == 1) // No additional variants for this pattern. continue; LLVM_DEBUG(errs() << "FOUND VARIANTS OF: "; PatternsToMatch[i].getSrcPattern()->dump(); errs() << "\n"); PatternsWithVariants[i] = std::make_pair(DepVars, Variants); // Cache matching predicates. if (MatchedPatterns[i]) continue; const std::vector &Predicates = PatternsToMatch[i].getPredicates(); BitVector &Matches = MatchedPredicates[i]; MatchedPatterns.set(i); Matches.set(i); // Don't test patterns that have already been cached - it won't match. for (unsigned p = 0; p != NumOriginalPatterns; ++p) if (!MatchedPatterns[p]) Matches[p] = (Predicates == PatternsToMatch[p].getPredicates()); // Copy this to all the matching patterns. for (int p = Matches.find_first(); p != -1; p = Matches.find_next(p)) if (p != (int)i) { MatchedPatterns.set(p); MatchedPredicates[p] = Matches; } } for (auto it : PatternsWithVariants) { unsigned i = it.first; const MultipleUseVarSet &DepVars = it.second.first; const std::vector &Variants = it.second.second; for (unsigned v = 0, e = Variants.size(); v != e; ++v) { TreePatternNodePtr Variant = Variants[v]; BitVector &Matches = MatchedPredicates[i]; LLVM_DEBUG(errs() << " VAR#" << v << ": "; Variant->dump(); errs() << "\n"); // Scan to see if an instruction or explicit pattern already matches this. bool AlreadyExists = false; for (unsigned p = 0, e = PatternsToMatch.size(); p != e; ++p) { // Skip if the top level predicates do not match. if (!Matches[p]) continue; // Check to see if this variant already exists. if (Variant->isIsomorphicTo(PatternsToMatch[p].getSrcPattern(), DepVars)) { LLVM_DEBUG(errs() << " *** ALREADY EXISTS, ignoring variant.\n"); AlreadyExists = true; break; } } // If we already have it, ignore the variant. if (AlreadyExists) continue; // Otherwise, add it to the list of patterns we have. PatternsToMatch.push_back(PatternToMatch( PatternsToMatch[i].getSrcRecord(), PatternsToMatch[i].getPredicates(), Variant, PatternsToMatch[i].getDstPatternShared(), PatternsToMatch[i].getDstRegs(), PatternsToMatch[i].getAddedComplexity(), Record::getNewUID())); MatchedPredicates.push_back(Matches); // Add a new match the same as this pattern. for (auto &P : MatchedPredicates) P.push_back(P[i]); } LLVM_DEBUG(errs() << "\n"); } } diff --git a/llvm/utils/TableGen/CodeGenInstruction.cpp b/llvm/utils/TableGen/CodeGenInstruction.cpp index 1df5902b081e..960fe08677f7 100644 --- a/llvm/utils/TableGen/CodeGenInstruction.cpp +++ b/llvm/utils/TableGen/CodeGenInstruction.cpp @@ -1,793 +1,793 @@ //===- CodeGenInstruction.cpp - CodeGen Instruction Class Wrapper ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the CodeGenInstruction class. // //===----------------------------------------------------------------------===// #include "CodeGenInstruction.h" #include "CodeGenTarget.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include using namespace llvm; //===----------------------------------------------------------------------===// // CGIOperandList Implementation //===----------------------------------------------------------------------===// CGIOperandList::CGIOperandList(Record *R) : TheDef(R) { isPredicable = false; hasOptionalDef = false; isVariadic = false; DagInit *OutDI = R->getValueAsDag("OutOperandList"); if (DefInit *Init = dyn_cast(OutDI->getOperator())) { if (Init->getDef()->getName() != "outs") PrintFatalError(R->getLoc(), R->getName() + ": invalid def name for output list: use 'outs'"); } else PrintFatalError(R->getLoc(), R->getName() + ": invalid output list: use 'outs'"); NumDefs = OutDI->getNumArgs(); DagInit *InDI = R->getValueAsDag("InOperandList"); if (DefInit *Init = dyn_cast(InDI->getOperator())) { if (Init->getDef()->getName() != "ins") PrintFatalError(R->getLoc(), R->getName() + ": invalid def name for input list: use 'ins'"); } else PrintFatalError(R->getLoc(), R->getName() + ": invalid input list: use 'ins'"); unsigned MIOperandNo = 0; std::set OperandNames; unsigned e = InDI->getNumArgs() + OutDI->getNumArgs(); OperandList.reserve(e); bool VariadicOuts = false; for (unsigned i = 0; i != e; ++i){ Init *ArgInit; StringRef ArgName; if (i < NumDefs) { ArgInit = OutDI->getArg(i); ArgName = OutDI->getArgNameStr(i); } else { ArgInit = InDI->getArg(i-NumDefs); ArgName = InDI->getArgNameStr(i-NumDefs); } DefInit *Arg = dyn_cast(ArgInit); if (!Arg) PrintFatalError(R->getLoc(), "Illegal operand for the '" + R->getName() + "' instruction!"); Record *Rec = Arg->getDef(); std::string PrintMethod = "printOperand"; std::string EncoderMethod; std::string OperandType = "OPERAND_UNKNOWN"; std::string OperandNamespace = "MCOI"; unsigned NumOps = 1; DagInit *MIOpInfo = nullptr; if (Rec->isSubClassOf("RegisterOperand")) { PrintMethod = std::string(Rec->getValueAsString("PrintMethod")); OperandType = std::string(Rec->getValueAsString("OperandType")); OperandNamespace = std::string(Rec->getValueAsString("OperandNamespace")); EncoderMethod = std::string(Rec->getValueAsString("EncoderMethod")); } else if (Rec->isSubClassOf("Operand")) { PrintMethod = std::string(Rec->getValueAsString("PrintMethod")); OperandType = std::string(Rec->getValueAsString("OperandType")); OperandNamespace = std::string(Rec->getValueAsString("OperandNamespace")); // If there is an explicit encoder method, use it. EncoderMethod = std::string(Rec->getValueAsString("EncoderMethod")); MIOpInfo = Rec->getValueAsDag("MIOperandInfo"); // Verify that MIOpInfo has an 'ops' root value. if (!isa(MIOpInfo->getOperator()) || cast(MIOpInfo->getOperator())->getDef()->getName() != "ops") PrintFatalError(R->getLoc(), "Bad value for MIOperandInfo in operand '" + Rec->getName() + "'\n"); // If we have MIOpInfo, then we have #operands equal to number of entries // in MIOperandInfo. if (unsigned NumArgs = MIOpInfo->getNumArgs()) NumOps = NumArgs; if (Rec->isSubClassOf("PredicateOp")) isPredicable = true; else if (Rec->isSubClassOf("OptionalDefOperand")) hasOptionalDef = true; } else if (Rec->getName() == "variable_ops") { if (i < NumDefs) VariadicOuts = true; isVariadic = true; continue; } else if (Rec->isSubClassOf("RegisterClass")) { OperandType = "OPERAND_REGISTER"; } else if (!Rec->isSubClassOf("PointerLikeRegClass") && !Rec->isSubClassOf("unknown_class")) PrintFatalError(R->getLoc(), "Unknown operand class '" + Rec->getName() + "' in '" + R->getName() + "' instruction!"); // Check that the operand has a name and that it's unique. if (ArgName.empty()) PrintFatalError(R->getLoc(), "In instruction '" + R->getName() + "', operand #" + Twine(i) + " has no name!"); if (!OperandNames.insert(std::string(ArgName)).second) PrintFatalError(R->getLoc(), "In instruction '" + R->getName() + "', operand #" + Twine(i) + " has the same name as a previous operand!"); OperandList.emplace_back( Rec, std::string(ArgName), std::string(PrintMethod), std::string(EncoderMethod), OperandNamespace + "::" + OperandType, MIOperandNo, NumOps, MIOpInfo); MIOperandNo += NumOps; } if (VariadicOuts) --NumDefs; // Make sure the constraints list for each operand is large enough to hold // constraint info, even if none is present. for (OperandInfo &OpInfo : OperandList) OpInfo.Constraints.resize(OpInfo.MINumOperands); } /// getOperandNamed - Return the index of the operand with the specified /// non-empty name. If the instruction does not have an operand with the /// specified name, abort. /// unsigned CGIOperandList::getOperandNamed(StringRef Name) const { unsigned OpIdx; if (hasOperandNamed(Name, OpIdx)) return OpIdx; PrintFatalError(TheDef->getLoc(), "'" + TheDef->getName() + "' does not have an operand named '$" + Name + "'!"); } /// hasOperandNamed - Query whether the instruction has an operand of the /// given name. If so, return true and set OpIdx to the index of the /// operand. Otherwise, return false. bool CGIOperandList::hasOperandNamed(StringRef Name, unsigned &OpIdx) const { assert(!Name.empty() && "Cannot search for operand with no name!"); for (unsigned i = 0, e = OperandList.size(); i != e; ++i) if (OperandList[i].Name == Name) { OpIdx = i; return true; } return false; } std::pair CGIOperandList::ParseOperandName(const std::string &Op, bool AllowWholeOp) { if (Op.empty() || Op[0] != '$') PrintFatalError(TheDef->getLoc(), TheDef->getName() + ": Illegal operand name: '" + Op + "'"); std::string OpName = Op.substr(1); std::string SubOpName; // Check to see if this is $foo.bar. std::string::size_type DotIdx = OpName.find_first_of('.'); if (DotIdx != std::string::npos) { SubOpName = OpName.substr(DotIdx+1); if (SubOpName.empty()) PrintFatalError(TheDef->getLoc(), TheDef->getName() + ": illegal empty suboperand name in '" + Op + "'"); OpName = OpName.substr(0, DotIdx); } unsigned OpIdx = getOperandNamed(OpName); if (SubOpName.empty()) { // If no suboperand name was specified: // If one was needed, throw. if (OperandList[OpIdx].MINumOperands > 1 && !AllowWholeOp && SubOpName.empty()) PrintFatalError(TheDef->getLoc(), TheDef->getName() + ": Illegal to refer to" " whole operand part of complex operand '" + Op + "'"); // Otherwise, return the operand. return std::make_pair(OpIdx, 0U); } // Find the suboperand number involved. DagInit *MIOpInfo = OperandList[OpIdx].MIOperandInfo; if (!MIOpInfo) PrintFatalError(TheDef->getLoc(), TheDef->getName() + ": unknown suboperand name in '" + Op + "'"); // Find the operand with the right name. for (unsigned i = 0, e = MIOpInfo->getNumArgs(); i != e; ++i) if (MIOpInfo->getArgNameStr(i) == SubOpName) return std::make_pair(OpIdx, i); // Otherwise, didn't find it! PrintFatalError(TheDef->getLoc(), TheDef->getName() + ": unknown suboperand name in '" + Op + "'"); return std::make_pair(0U, 0U); } static void ParseConstraint(const std::string &CStr, CGIOperandList &Ops, Record *Rec) { // EARLY_CLOBBER: @early $reg std::string::size_type wpos = CStr.find_first_of(" \t"); std::string::size_type start = CStr.find_first_not_of(" \t"); std::string Tok = CStr.substr(start, wpos - start); if (Tok == "@earlyclobber") { std::string Name = CStr.substr(wpos+1); wpos = Name.find_first_not_of(" \t"); if (wpos == std::string::npos) PrintFatalError( Rec->getLoc(), "Illegal format for @earlyclobber constraint in '" + Rec->getName() + "': '" + CStr + "'"); Name = Name.substr(wpos); std::pair Op = Ops.ParseOperandName(Name, false); // Build the string for the operand if (!Ops[Op.first].Constraints[Op.second].isNone()) PrintFatalError( Rec->getLoc(), "Operand '" + Name + "' of '" + Rec->getName() + "' cannot have multiple constraints!"); Ops[Op.first].Constraints[Op.second] = CGIOperandList::ConstraintInfo::getEarlyClobber(); return; } // Only other constraint is "TIED_TO" for now. std::string::size_type pos = CStr.find_first_of('='); if (pos == std::string::npos) PrintFatalError( Rec->getLoc(), "Unrecognized constraint '" + CStr + "' in '" + Rec->getName() + "'"); start = CStr.find_first_not_of(" \t"); // TIED_TO: $src1 = $dst wpos = CStr.find_first_of(" \t", start); if (wpos == std::string::npos || wpos > pos) PrintFatalError( Rec->getLoc(), "Illegal format for tied-to constraint in '" + Rec->getName() + "': '" + CStr + "'"); std::string LHSOpName = std::string(StringRef(CStr).substr(start, wpos - start)); std::pair LHSOp = Ops.ParseOperandName(LHSOpName, false); wpos = CStr.find_first_not_of(" \t", pos + 1); if (wpos == std::string::npos) PrintFatalError( Rec->getLoc(), "Illegal format for tied-to constraint: '" + CStr + "'"); std::string RHSOpName = std::string(StringRef(CStr).substr(wpos)); std::pair RHSOp = Ops.ParseOperandName(RHSOpName, false); // Sort the operands into order, which should put the output one // first. But keep the original order, for use in diagnostics. bool FirstIsDest = (LHSOp < RHSOp); std::pair DestOp = (FirstIsDest ? LHSOp : RHSOp); StringRef DestOpName = (FirstIsDest ? LHSOpName : RHSOpName); std::pair SrcOp = (FirstIsDest ? RHSOp : LHSOp); StringRef SrcOpName = (FirstIsDest ? RHSOpName : LHSOpName); // Ensure one operand is a def and the other is a use. if (DestOp.first >= Ops.NumDefs) PrintFatalError( Rec->getLoc(), "Input operands '" + LHSOpName + "' and '" + RHSOpName + "' of '" + Rec->getName() + "' cannot be tied!"); if (SrcOp.first < Ops.NumDefs) PrintFatalError( Rec->getLoc(), "Output operands '" + LHSOpName + "' and '" + RHSOpName + "' of '" + Rec->getName() + "' cannot be tied!"); // The constraint has to go on the operand with higher index, i.e. // the source one. Check there isn't another constraint there // already. if (!Ops[SrcOp.first].Constraints[SrcOp.second].isNone()) PrintFatalError( Rec->getLoc(), "Operand '" + SrcOpName + "' of '" + Rec->getName() + "' cannot have multiple constraints!"); unsigned DestFlatOpNo = Ops.getFlattenedOperandNumber(DestOp); auto NewConstraint = CGIOperandList::ConstraintInfo::getTied(DestFlatOpNo); // Check that the earlier operand is not the target of another tie // before making it the target of this one. for (const CGIOperandList::OperandInfo &Op : Ops) { for (unsigned i = 0; i < Op.MINumOperands; i++) if (Op.Constraints[i] == NewConstraint) PrintFatalError( Rec->getLoc(), "Operand '" + DestOpName + "' of '" + Rec->getName() + "' cannot have multiple operands tied to it!"); } Ops[SrcOp.first].Constraints[SrcOp.second] = NewConstraint; } static void ParseConstraints(const std::string &CStr, CGIOperandList &Ops, Record *Rec) { if (CStr.empty()) return; const std::string delims(","); std::string::size_type bidx, eidx; bidx = CStr.find_first_not_of(delims); while (bidx != std::string::npos) { eidx = CStr.find_first_of(delims, bidx); if (eidx == std::string::npos) eidx = CStr.length(); ParseConstraint(CStr.substr(bidx, eidx - bidx), Ops, Rec); bidx = CStr.find_first_not_of(delims, eidx); } } void CGIOperandList::ProcessDisableEncoding(std::string DisableEncoding) { while (1) { std::pair P = getToken(DisableEncoding, " ,\t"); std::string OpName = std::string(P.first); DisableEncoding = std::string(P.second); if (OpName.empty()) break; // Figure out which operand this is. std::pair Op = ParseOperandName(OpName, false); // Mark the operand as not-to-be encoded. if (Op.second >= OperandList[Op.first].DoNotEncode.size()) OperandList[Op.first].DoNotEncode.resize(Op.second+1); OperandList[Op.first].DoNotEncode[Op.second] = true; } } //===----------------------------------------------------------------------===// // CodeGenInstruction Implementation //===----------------------------------------------------------------------===// CodeGenInstruction::CodeGenInstruction(Record *R) : TheDef(R), Operands(R), InferredFrom(nullptr) { Namespace = R->getValueAsString("Namespace"); AsmString = std::string(R->getValueAsString("AsmString")); isPreISelOpcode = R->getValueAsBit("isPreISelOpcode"); isReturn = R->getValueAsBit("isReturn"); isEHScopeReturn = R->getValueAsBit("isEHScopeReturn"); isBranch = R->getValueAsBit("isBranch"); isIndirectBranch = R->getValueAsBit("isIndirectBranch"); isCompare = R->getValueAsBit("isCompare"); isMoveImm = R->getValueAsBit("isMoveImm"); isMoveReg = R->getValueAsBit("isMoveReg"); isBitcast = R->getValueAsBit("isBitcast"); isSelect = R->getValueAsBit("isSelect"); isBarrier = R->getValueAsBit("isBarrier"); isCall = R->getValueAsBit("isCall"); isAdd = R->getValueAsBit("isAdd"); isTrap = R->getValueAsBit("isTrap"); canFoldAsLoad = R->getValueAsBit("canFoldAsLoad"); isPredicable = !R->getValueAsBit("isUnpredicable") && ( Operands.isPredicable || R->getValueAsBit("isPredicable")); isConvertibleToThreeAddress = R->getValueAsBit("isConvertibleToThreeAddress"); isCommutable = R->getValueAsBit("isCommutable"); isTerminator = R->getValueAsBit("isTerminator"); isReMaterializable = R->getValueAsBit("isReMaterializable"); hasDelaySlot = R->getValueAsBit("hasDelaySlot"); usesCustomInserter = R->getValueAsBit("usesCustomInserter"); hasPostISelHook = R->getValueAsBit("hasPostISelHook"); hasCtrlDep = R->getValueAsBit("hasCtrlDep"); isNotDuplicable = R->getValueAsBit("isNotDuplicable"); isRegSequence = R->getValueAsBit("isRegSequence"); isExtractSubreg = R->getValueAsBit("isExtractSubreg"); isInsertSubreg = R->getValueAsBit("isInsertSubreg"); isConvergent = R->getValueAsBit("isConvergent"); hasNoSchedulingInfo = R->getValueAsBit("hasNoSchedulingInfo"); FastISelShouldIgnore = R->getValueAsBit("FastISelShouldIgnore"); variadicOpsAreDefs = R->getValueAsBit("variadicOpsAreDefs"); isAuthenticated = R->getValueAsBit("isAuthenticated"); bool Unset; mayLoad = R->getValueAsBitOrUnset("mayLoad", Unset); mayLoad_Unset = Unset; mayStore = R->getValueAsBitOrUnset("mayStore", Unset); mayStore_Unset = Unset; mayRaiseFPException = R->getValueAsBit("mayRaiseFPException"); hasSideEffects = R->getValueAsBitOrUnset("hasSideEffects", Unset); hasSideEffects_Unset = Unset; isAsCheapAsAMove = R->getValueAsBit("isAsCheapAsAMove"); hasExtraSrcRegAllocReq = R->getValueAsBit("hasExtraSrcRegAllocReq"); hasExtraDefRegAllocReq = R->getValueAsBit("hasExtraDefRegAllocReq"); isCodeGenOnly = R->getValueAsBit("isCodeGenOnly"); isPseudo = R->getValueAsBit("isPseudo"); ImplicitDefs = R->getValueAsListOfDefs("Defs"); ImplicitUses = R->getValueAsListOfDefs("Uses"); // This flag is only inferred from the pattern. hasChain = false; hasChain_Inferred = false; // Parse Constraints. ParseConstraints(std::string(R->getValueAsString("Constraints")), Operands, R); // Parse the DisableEncoding field. Operands.ProcessDisableEncoding( std::string(R->getValueAsString("DisableEncoding"))); // First check for a ComplexDeprecationPredicate. if (R->getValue("ComplexDeprecationPredicate")) { HasComplexDeprecationPredicate = true; DeprecatedReason = std::string(R->getValueAsString("ComplexDeprecationPredicate")); } else if (RecordVal *Dep = R->getValue("DeprecatedFeatureMask")) { // Check if we have a Subtarget feature mask. HasComplexDeprecationPredicate = false; DeprecatedReason = Dep->getValue()->getAsString(); } else { // This instruction isn't deprecated. HasComplexDeprecationPredicate = false; DeprecatedReason = ""; } } /// HasOneImplicitDefWithKnownVT - If the instruction has at least one /// implicit def and it has a known VT, return the VT, otherwise return /// MVT::Other. MVT::SimpleValueType CodeGenInstruction:: HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const { if (ImplicitDefs.empty()) return MVT::Other; // Check to see if the first implicit def has a resolvable type. Record *FirstImplicitDef = ImplicitDefs[0]; assert(FirstImplicitDef->isSubClassOf("Register")); const std::vector &RegVTs = TargetInfo.getRegisterVTs(FirstImplicitDef); if (RegVTs.size() == 1 && RegVTs[0].isSimple()) return RegVTs[0].getSimple().SimpleTy; return MVT::Other; } /// FlattenAsmStringVariants - Flatten the specified AsmString to only /// include text from the specified variant, returning the new string. std::string CodeGenInstruction:: FlattenAsmStringVariants(StringRef Cur, unsigned Variant) { - std::string Res = ""; + std::string Res; for (;;) { // Find the start of the next variant string. size_t VariantsStart = 0; for (size_t e = Cur.size(); VariantsStart != e; ++VariantsStart) if (Cur[VariantsStart] == '{' && (VariantsStart == 0 || (Cur[VariantsStart-1] != '$' && Cur[VariantsStart-1] != '\\'))) break; // Add the prefix to the result. Res += Cur.slice(0, VariantsStart); if (VariantsStart == Cur.size()) break; ++VariantsStart; // Skip the '{'. // Scan to the end of the variants string. size_t VariantsEnd = VariantsStart; unsigned NestedBraces = 1; for (size_t e = Cur.size(); VariantsEnd != e; ++VariantsEnd) { if (Cur[VariantsEnd] == '}' && Cur[VariantsEnd-1] != '\\') { if (--NestedBraces == 0) break; } else if (Cur[VariantsEnd] == '{') ++NestedBraces; } // Select the Nth variant (or empty). StringRef Selection = Cur.slice(VariantsStart, VariantsEnd); for (unsigned i = 0; i != Variant; ++i) Selection = Selection.split('|').second; Res += Selection.split('|').first; assert(VariantsEnd != Cur.size() && "Unterminated variants in assembly string!"); Cur = Cur.substr(VariantsEnd + 1); } return Res; } bool CodeGenInstruction::isOperandImpl(unsigned i, StringRef PropertyName) const { DagInit *ConstraintList = TheDef->getValueAsDag("InOperandList"); if (!ConstraintList || i >= ConstraintList->getNumArgs()) return false; DefInit *Constraint = dyn_cast(ConstraintList->getArg(i)); if (!Constraint) return false; return Constraint->getDef()->isSubClassOf("TypedOperand") && Constraint->getDef()->getValueAsBit(PropertyName); } //===----------------------------------------------------------------------===// /// CodeGenInstAlias Implementation //===----------------------------------------------------------------------===// /// tryAliasOpMatch - This is a helper function for the CodeGenInstAlias /// constructor. It checks if an argument in an InstAlias pattern matches /// the corresponding operand of the instruction. It returns true on a /// successful match, with ResOp set to the result operand to be used. bool CodeGenInstAlias::tryAliasOpMatch(DagInit *Result, unsigned AliasOpNo, Record *InstOpRec, bool hasSubOps, ArrayRef Loc, CodeGenTarget &T, ResultOperand &ResOp) { Init *Arg = Result->getArg(AliasOpNo); DefInit *ADI = dyn_cast(Arg); Record *ResultRecord = ADI ? ADI->getDef() : nullptr; if (ADI && ADI->getDef() == InstOpRec) { // If the operand is a record, it must have a name, and the record type // must match up with the instruction's argument type. if (!Result->getArgName(AliasOpNo)) PrintFatalError(Loc, "result argument #" + Twine(AliasOpNo) + " must have a name!"); ResOp = ResultOperand(std::string(Result->getArgNameStr(AliasOpNo)), ResultRecord); return true; } // For register operands, the source register class can be a subclass // of the instruction register class, not just an exact match. if (InstOpRec->isSubClassOf("RegisterOperand")) InstOpRec = InstOpRec->getValueAsDef("RegClass"); if (ADI && ADI->getDef()->isSubClassOf("RegisterOperand")) ADI = ADI->getDef()->getValueAsDef("RegClass")->getDefInit(); if (ADI && ADI->getDef()->isSubClassOf("RegisterClass")) { if (!InstOpRec->isSubClassOf("RegisterClass")) return false; if (!T.getRegisterClass(InstOpRec) .hasSubClass(&T.getRegisterClass(ADI->getDef()))) return false; ResOp = ResultOperand(std::string(Result->getArgNameStr(AliasOpNo)), ResultRecord); return true; } // Handle explicit registers. if (ADI && ADI->getDef()->isSubClassOf("Register")) { if (InstOpRec->isSubClassOf("OptionalDefOperand")) { DagInit *DI = InstOpRec->getValueAsDag("MIOperandInfo"); // The operand info should only have a single (register) entry. We // want the register class of it. InstOpRec = cast(DI->getArg(0))->getDef(); } if (!InstOpRec->isSubClassOf("RegisterClass")) return false; if (!T.getRegisterClass(InstOpRec) .contains(T.getRegBank().getReg(ADI->getDef()))) PrintFatalError(Loc, "fixed register " + ADI->getDef()->getName() + " is not a member of the " + InstOpRec->getName() + " register class!"); if (Result->getArgName(AliasOpNo)) PrintFatalError(Loc, "result fixed register argument must " "not have a name!"); ResOp = ResultOperand(ResultRecord); return true; } // Handle "zero_reg" for optional def operands. if (ADI && ADI->getDef()->getName() == "zero_reg") { // Check if this is an optional def. // Tied operands where the source is a sub-operand of a complex operand // need to represent both operands in the alias destination instruction. // Allow zero_reg for the tied portion. This can and should go away once // the MC representation of things doesn't use tied operands at all. //if (!InstOpRec->isSubClassOf("OptionalDefOperand")) // throw TGError(Loc, "reg0 used for result that is not an " // "OptionalDefOperand!"); ResOp = ResultOperand(static_cast(nullptr)); return true; } // Literal integers. if (IntInit *II = dyn_cast(Arg)) { if (hasSubOps || !InstOpRec->isSubClassOf("Operand")) return false; // Integer arguments can't have names. if (Result->getArgName(AliasOpNo)) PrintFatalError(Loc, "result argument #" + Twine(AliasOpNo) + " must not have a name!"); ResOp = ResultOperand(II->getValue()); return true; } // Bits (also used for 0bxx literals) if (BitsInit *BI = dyn_cast(Arg)) { if (hasSubOps || !InstOpRec->isSubClassOf("Operand")) return false; if (!BI->isComplete()) return false; // Convert the bits init to an integer and use that for the result. IntInit *II = dyn_cast_or_null(BI->convertInitializerTo(IntRecTy::get())); if (!II) return false; ResOp = ResultOperand(II->getValue()); return true; } // If both are Operands with the same MVT, allow the conversion. It's // up to the user to make sure the values are appropriate, just like // for isel Pat's. if (InstOpRec->isSubClassOf("Operand") && ADI && ADI->getDef()->isSubClassOf("Operand")) { // FIXME: What other attributes should we check here? Identical // MIOperandInfo perhaps? if (InstOpRec->getValueInit("Type") != ADI->getDef()->getValueInit("Type")) return false; ResOp = ResultOperand(std::string(Result->getArgNameStr(AliasOpNo)), ADI->getDef()); return true; } return false; } unsigned CodeGenInstAlias::ResultOperand::getMINumOperands() const { if (!isRecord()) return 1; Record *Rec = getRecord(); if (!Rec->isSubClassOf("Operand")) return 1; DagInit *MIOpInfo = Rec->getValueAsDag("MIOperandInfo"); if (MIOpInfo->getNumArgs() == 0) { // Unspecified, so it defaults to 1 return 1; } return MIOpInfo->getNumArgs(); } CodeGenInstAlias::CodeGenInstAlias(Record *R, CodeGenTarget &T) : TheDef(R) { Result = R->getValueAsDag("ResultInst"); AsmString = std::string(R->getValueAsString("AsmString")); // Verify that the root of the result is an instruction. DefInit *DI = dyn_cast(Result->getOperator()); if (!DI || !DI->getDef()->isSubClassOf("Instruction")) PrintFatalError(R->getLoc(), "result of inst alias should be an instruction"); ResultInst = &T.getInstruction(DI->getDef()); // NameClass - If argument names are repeated, we need to verify they have // the same class. StringMap NameClass; for (unsigned i = 0, e = Result->getNumArgs(); i != e; ++i) { DefInit *ADI = dyn_cast(Result->getArg(i)); if (!ADI || !Result->getArgName(i)) continue; // Verify we don't have something like: (someinst GR16:$foo, GR32:$foo) // $foo can exist multiple times in the result list, but it must have the // same type. Record *&Entry = NameClass[Result->getArgNameStr(i)]; if (Entry && Entry != ADI->getDef()) PrintFatalError(R->getLoc(), "result value $" + Result->getArgNameStr(i) + " is both " + Entry->getName() + " and " + ADI->getDef()->getName() + "!"); Entry = ADI->getDef(); } // Decode and validate the arguments of the result. unsigned AliasOpNo = 0; for (unsigned i = 0, e = ResultInst->Operands.size(); i != e; ++i) { // Tied registers don't have an entry in the result dag unless they're part // of a complex operand, in which case we include them anyways, as we // don't have any other way to specify the whole operand. if (ResultInst->Operands[i].MINumOperands == 1 && ResultInst->Operands[i].getTiedRegister() != -1) { // Tied operands of different RegisterClass should be explicit within an // instruction's syntax and so cannot be skipped. int TiedOpNum = ResultInst->Operands[i].getTiedRegister(); if (ResultInst->Operands[i].Rec->getName() == ResultInst->Operands[TiedOpNum].Rec->getName()) continue; } if (AliasOpNo >= Result->getNumArgs()) PrintFatalError(R->getLoc(), "not enough arguments for instruction!"); Record *InstOpRec = ResultInst->Operands[i].Rec; unsigned NumSubOps = ResultInst->Operands[i].MINumOperands; ResultOperand ResOp(static_cast(0)); if (tryAliasOpMatch(Result, AliasOpNo, InstOpRec, (NumSubOps > 1), R->getLoc(), T, ResOp)) { // If this is a simple operand, or a complex operand with a custom match // class, then we can match is verbatim. if (NumSubOps == 1 || (InstOpRec->getValue("ParserMatchClass") && InstOpRec->getValueAsDef("ParserMatchClass") ->getValueAsString("Name") != "Imm")) { ResultOperands.push_back(ResOp); ResultInstOperandIndex.push_back(std::make_pair(i, -1)); ++AliasOpNo; // Otherwise, we need to match each of the suboperands individually. } else { DagInit *MIOI = ResultInst->Operands[i].MIOperandInfo; for (unsigned SubOp = 0; SubOp != NumSubOps; ++SubOp) { Record *SubRec = cast(MIOI->getArg(SubOp))->getDef(); // Take care to instantiate each of the suboperands with the correct // nomenclature: $foo.bar ResultOperands.emplace_back( Result->getArgName(AliasOpNo)->getAsUnquotedString() + "." + MIOI->getArgName(SubOp)->getAsUnquotedString(), SubRec); ResultInstOperandIndex.push_back(std::make_pair(i, SubOp)); } ++AliasOpNo; } continue; } // If the argument did not match the instruction operand, and the operand // is composed of multiple suboperands, try matching the suboperands. if (NumSubOps > 1) { DagInit *MIOI = ResultInst->Operands[i].MIOperandInfo; for (unsigned SubOp = 0; SubOp != NumSubOps; ++SubOp) { if (AliasOpNo >= Result->getNumArgs()) PrintFatalError(R->getLoc(), "not enough arguments for instruction!"); Record *SubRec = cast(MIOI->getArg(SubOp))->getDef(); if (tryAliasOpMatch(Result, AliasOpNo, SubRec, false, R->getLoc(), T, ResOp)) { ResultOperands.push_back(ResOp); ResultInstOperandIndex.push_back(std::make_pair(i, SubOp)); ++AliasOpNo; } else { PrintFatalError(R->getLoc(), "result argument #" + Twine(AliasOpNo) + " does not match instruction operand class " + (SubOp == 0 ? InstOpRec->getName() :SubRec->getName())); } } continue; } PrintFatalError(R->getLoc(), "result argument #" + Twine(AliasOpNo) + " does not match instruction operand class " + InstOpRec->getName()); } if (AliasOpNo != Result->getNumArgs()) PrintFatalError(R->getLoc(), "too many operands for instruction!"); } diff --git a/llvm/utils/TableGen/CodeGenMapTable.cpp b/llvm/utils/TableGen/CodeGenMapTable.cpp index ea53a2d3eee6..289a20a96f02 100644 --- a/llvm/utils/TableGen/CodeGenMapTable.cpp +++ b/llvm/utils/TableGen/CodeGenMapTable.cpp @@ -1,602 +1,602 @@ //===- CodeGenMapTable.cpp - Instruction Mapping Table Generator ----------===// // // 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 // //===----------------------------------------------------------------------===// // CodeGenMapTable provides functionality for the TabelGen to create // relation mapping between instructions. Relation models are defined using // InstrMapping as a base class. This file implements the functionality which // parses these definitions and generates relation maps using the information // specified there. These maps are emitted as tables in the XXXGenInstrInfo.inc // file along with the functions to query them. // // A relationship model to relate non-predicate instructions with their // predicated true/false forms can be defined as follows: // // def getPredOpcode : InstrMapping { // let FilterClass = "PredRel"; // let RowFields = ["BaseOpcode"]; // let ColFields = ["PredSense"]; // let KeyCol = ["none"]; // let ValueCols = [["true"], ["false"]]; } // // CodeGenMapTable parses this map and generates a table in XXXGenInstrInfo.inc // file that contains the instructions modeling this relationship. This table // is defined in the function // "int getPredOpcode(uint16_t Opcode, enum PredSense inPredSense)" // that can be used to retrieve the predicated form of the instruction by // passing its opcode value and the predicate sense (true/false) of the desired // instruction as arguments. // // Short description of the algorithm: // // 1) Iterate through all the records that derive from "InstrMapping" class. // 2) For each record, filter out instructions based on the FilterClass value. // 3) Iterate through this set of instructions and insert them into // RowInstrMap map based on their RowFields values. RowInstrMap is keyed by the // vector of RowFields values and contains vectors of Records (instructions) as // values. RowFields is a list of fields that are required to have the same // values for all the instructions appearing in the same row of the relation // table. All the instructions in a given row of the relation table have some // sort of relationship with the key instruction defined by the corresponding // relationship model. // // Ex: RowInstrMap(RowVal1, RowVal2, ...) -> [Instr1, Instr2, Instr3, ... ] // Here Instr1, Instr2, Instr3 have same values (RowVal1, RowVal2) for // RowFields. These groups of instructions are later matched against ValueCols // to determine the column they belong to, if any. // // While building the RowInstrMap map, collect all the key instructions in // KeyInstrVec. These are the instructions having the same values as KeyCol // for all the fields listed in ColFields. // // For Example: // // Relate non-predicate instructions with their predicated true/false forms. // // def getPredOpcode : InstrMapping { // let FilterClass = "PredRel"; // let RowFields = ["BaseOpcode"]; // let ColFields = ["PredSense"]; // let KeyCol = ["none"]; // let ValueCols = [["true"], ["false"]]; } // // Here, only instructions that have "none" as PredSense will be selected as key // instructions. // // 4) For each key instruction, get the group of instructions that share the // same key-value as the key instruction from RowInstrMap. Iterate over the list // of columns in ValueCols (it is defined as a list >. Therefore, // it can specify multi-column relationships). For each column, find the // instruction from the group that matches all the values for the column. // Multiple matches are not allowed. // //===----------------------------------------------------------------------===// #include "CodeGenTarget.h" #include "llvm/Support/Format.h" #include "llvm/TableGen/Error.h" using namespace llvm; typedef std::map > InstrRelMapTy; typedef std::map, std::vector > RowInstrMapTy; namespace { //===----------------------------------------------------------------------===// // This class is used to represent InstrMapping class defined in Target.td file. class InstrMap { private: std::string Name; std::string FilterClass; ListInit *RowFields; ListInit *ColFields; ListInit *KeyCol; std::vector ValueCols; public: InstrMap(Record* MapRec) { Name = std::string(MapRec->getName()); // FilterClass - It's used to reduce the search space only to the // instructions that define the kind of relationship modeled by // this InstrMapping object/record. const RecordVal *Filter = MapRec->getValue("FilterClass"); FilterClass = Filter->getValue()->getAsUnquotedString(); // List of fields/attributes that need to be same across all the // instructions in a row of the relation table. RowFields = MapRec->getValueAsListInit("RowFields"); // List of fields/attributes that are constant across all the instruction // in a column of the relation table. Ex: ColFields = 'predSense' ColFields = MapRec->getValueAsListInit("ColFields"); // Values for the fields/attributes listed in 'ColFields'. // Ex: KeyCol = 'noPred' -- key instruction is non-predicated KeyCol = MapRec->getValueAsListInit("KeyCol"); // List of values for the fields/attributes listed in 'ColFields', one for // each column in the relation table. // // Ex: ValueCols = [['true'],['false']] -- it results two columns in the // table. First column requires all the instructions to have predSense // set to 'true' and second column requires it to be 'false'. ListInit *ColValList = MapRec->getValueAsListInit("ValueCols"); // Each instruction map must specify at least one column for it to be valid. if (ColValList->empty()) PrintFatalError(MapRec->getLoc(), "InstrMapping record `" + MapRec->getName() + "' has empty " + "`ValueCols' field!"); for (Init *I : ColValList->getValues()) { auto *ColI = cast(I); // Make sure that all the sub-lists in 'ValueCols' have same number of // elements as the fields in 'ColFields'. if (ColI->size() != ColFields->size()) PrintFatalError(MapRec->getLoc(), "Record `" + MapRec->getName() + "', field `ValueCols' entries don't match with " + " the entries in 'ColFields'!"); ValueCols.push_back(ColI); } } const std::string &getName() const { return Name; } const std::string &getFilterClass() const { return FilterClass; } ListInit *getRowFields() const { return RowFields; } ListInit *getColFields() const { return ColFields; } ListInit *getKeyCol() const { return KeyCol; } const std::vector &getValueCols() const { return ValueCols; } }; } // end anonymous namespace //===----------------------------------------------------------------------===// // class MapTableEmitter : It builds the instruction relation maps using // the information provided in InstrMapping records. It outputs these // relationship maps as tables into XXXGenInstrInfo.inc file along with the // functions to query them. namespace { class MapTableEmitter { private: // std::string TargetName; const CodeGenTarget &Target; // InstrMapDesc - InstrMapping record to be processed. InstrMap InstrMapDesc; // InstrDefs - list of instructions filtered using FilterClass defined // in InstrMapDesc. std::vector InstrDefs; // RowInstrMap - maps RowFields values to the instructions. It's keyed by the // values of the row fields and contains vector of records as values. RowInstrMapTy RowInstrMap; // KeyInstrVec - list of key instructions. std::vector KeyInstrVec; DenseMap > MapTable; public: MapTableEmitter(CodeGenTarget &Target, RecordKeeper &Records, Record *IMRec): Target(Target), InstrMapDesc(IMRec) { const std::string &FilterClass = InstrMapDesc.getFilterClass(); InstrDefs = Records.getAllDerivedDefinitions(FilterClass); } void buildRowInstrMap(); // Returns true if an instruction is a key instruction, i.e., its ColFields // have same values as KeyCol. bool isKeyColInstr(Record* CurInstr); // Find column instruction corresponding to a key instruction based on the // constraints for that column. Record *getInstrForColumn(Record *KeyInstr, ListInit *CurValueCol); // Find column instructions for each key instruction based // on ValueCols and store them into MapTable. void buildMapTable(); void emitBinSearch(raw_ostream &OS, unsigned TableSize); void emitTablesWithFunc(raw_ostream &OS); unsigned emitBinSearchTable(raw_ostream &OS); // Lookup functions to query binary search tables. void emitMapFuncBody(raw_ostream &OS, unsigned TableSize); }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Process all the instructions that model this relation (alreday present in // InstrDefs) and insert them into RowInstrMap which is keyed by the values of // the fields listed as RowFields. It stores vectors of records as values. // All the related instructions have the same values for the RowFields thus are // part of the same key-value pair. //===----------------------------------------------------------------------===// void MapTableEmitter::buildRowInstrMap() { for (Record *CurInstr : InstrDefs) { std::vector KeyValue; ListInit *RowFields = InstrMapDesc.getRowFields(); for (Init *RowField : RowFields->getValues()) { RecordVal *RecVal = CurInstr->getValue(RowField); if (RecVal == nullptr) PrintFatalError(CurInstr->getLoc(), "No value " + RowField->getAsString() + " found in \"" + CurInstr->getName() + "\" instruction description."); Init *CurInstrVal = RecVal->getValue(); KeyValue.push_back(CurInstrVal); } // Collect key instructions into KeyInstrVec. Later, these instructions are // processed to assign column position to the instructions sharing // their KeyValue in RowInstrMap. if (isKeyColInstr(CurInstr)) KeyInstrVec.push_back(CurInstr); RowInstrMap[KeyValue].push_back(CurInstr); } } //===----------------------------------------------------------------------===// // Return true if an instruction is a KeyCol instruction. //===----------------------------------------------------------------------===// bool MapTableEmitter::isKeyColInstr(Record* CurInstr) { ListInit *ColFields = InstrMapDesc.getColFields(); ListInit *KeyCol = InstrMapDesc.getKeyCol(); // Check if the instruction is a KeyCol instruction. bool MatchFound = true; for (unsigned j = 0, endCF = ColFields->size(); (j < endCF) && MatchFound; j++) { RecordVal *ColFieldName = CurInstr->getValue(ColFields->getElement(j)); std::string CurInstrVal = ColFieldName->getValue()->getAsUnquotedString(); std::string KeyColValue = KeyCol->getElement(j)->getAsUnquotedString(); MatchFound = (CurInstrVal == KeyColValue); } return MatchFound; } //===----------------------------------------------------------------------===// // Build a map to link key instructions with the column instructions arranged // according to their column positions. //===----------------------------------------------------------------------===// void MapTableEmitter::buildMapTable() { // Find column instructions for a given key based on the ColField // constraints. const std::vector &ValueCols = InstrMapDesc.getValueCols(); unsigned NumOfCols = ValueCols.size(); for (Record *CurKeyInstr : KeyInstrVec) { std::vector ColInstrVec(NumOfCols); // Find the column instruction based on the constraints for the column. for (unsigned ColIdx = 0; ColIdx < NumOfCols; ColIdx++) { ListInit *CurValueCol = ValueCols[ColIdx]; Record *ColInstr = getInstrForColumn(CurKeyInstr, CurValueCol); ColInstrVec[ColIdx] = ColInstr; } MapTable[CurKeyInstr] = ColInstrVec; } } //===----------------------------------------------------------------------===// // Find column instruction based on the constraints for that column. //===----------------------------------------------------------------------===// Record *MapTableEmitter::getInstrForColumn(Record *KeyInstr, ListInit *CurValueCol) { ListInit *RowFields = InstrMapDesc.getRowFields(); std::vector KeyValue; // Construct KeyValue using KeyInstr's values for RowFields. for (Init *RowField : RowFields->getValues()) { Init *KeyInstrVal = KeyInstr->getValue(RowField)->getValue(); KeyValue.push_back(KeyInstrVal); } // Get all the instructions that share the same KeyValue as the KeyInstr // in RowInstrMap. We search through these instructions to find a match // for the current column, i.e., the instruction which has the same values // as CurValueCol for all the fields in ColFields. const std::vector &RelatedInstrVec = RowInstrMap[KeyValue]; ListInit *ColFields = InstrMapDesc.getColFields(); Record *MatchInstr = nullptr; for (unsigned i = 0, e = RelatedInstrVec.size(); i < e; i++) { bool MatchFound = true; Record *CurInstr = RelatedInstrVec[i]; for (unsigned j = 0, endCF = ColFields->size(); (j < endCF) && MatchFound; j++) { Init *ColFieldJ = ColFields->getElement(j); Init *CurInstrInit = CurInstr->getValue(ColFieldJ)->getValue(); std::string CurInstrVal = CurInstrInit->getAsUnquotedString(); Init *ColFieldJVallue = CurValueCol->getElement(j); MatchFound = (CurInstrVal == ColFieldJVallue->getAsUnquotedString()); } if (MatchFound) { if (MatchInstr) { // Already had a match // Error if multiple matches are found for a column. std::string KeyValueStr; for (Init *Value : KeyValue) { if (!KeyValueStr.empty()) KeyValueStr += ", "; KeyValueStr += Value->getAsString(); } PrintFatalError("Multiple matches found for `" + KeyInstr->getName() + "', for the relation `" + InstrMapDesc.getName() + "', row fields [" + KeyValueStr + "], column `" + CurValueCol->getAsString() + "'"); } MatchInstr = CurInstr; } } return MatchInstr; } //===----------------------------------------------------------------------===// // Emit one table per relation. Only instructions with a valid relation of a // given type are included in the table sorted by their enum values (opcodes). // Binary search is used for locating instructions in the table. //===----------------------------------------------------------------------===// unsigned MapTableEmitter::emitBinSearchTable(raw_ostream &OS) { ArrayRef NumberedInstructions = Target.getInstructionsByEnumValue(); StringRef Namespace = Target.getInstNamespace(); const std::vector &ValueCols = InstrMapDesc.getValueCols(); unsigned NumCol = ValueCols.size(); unsigned TotalNumInstr = NumberedInstructions.size(); unsigned TableSize = 0; OS << "static const uint16_t "<TheDef; std::vector ColInstrs = MapTable[CurInstr]; - std::string OutStr(""); + std::string OutStr; unsigned RelExists = 0; if (!ColInstrs.empty()) { for (unsigned j = 0; j < NumCol; j++) { if (ColInstrs[j] != nullptr) { RelExists = 1; OutStr += ", "; OutStr += Namespace; OutStr += "::"; OutStr += ColInstrs[j]->getName(); } else { OutStr += ", (uint16_t)-1U";} } if (RelExists) { OS << " { " << Namespace << "::" << CurInstr->getName(); OS << OutStr <<" },\n"; TableSize++; } } } if (!TableSize) { OS << " { " << Namespace << "::" << "INSTRUCTION_LIST_END, "; OS << Namespace << "::" << "INSTRUCTION_LIST_END }"; } OS << "}; // End of " << InstrMapDesc.getName() << "Table\n\n"; return TableSize; } //===----------------------------------------------------------------------===// // Emit binary search algorithm as part of the functions used to query // relation tables. //===----------------------------------------------------------------------===// void MapTableEmitter::emitBinSearch(raw_ostream &OS, unsigned TableSize) { OS << " unsigned mid;\n"; OS << " unsigned start = 0;\n"; OS << " unsigned end = " << TableSize << ";\n"; OS << " while (start < end) {\n"; OS << " mid = start + (end - start) / 2;\n"; OS << " if (Opcode == " << InstrMapDesc.getName() << "Table[mid][0]) {\n"; OS << " break;\n"; OS << " }\n"; OS << " if (Opcode < " << InstrMapDesc.getName() << "Table[mid][0])\n"; OS << " end = mid;\n"; OS << " else\n"; OS << " start = mid + 1;\n"; OS << " }\n"; OS << " if (start == end)\n"; OS << " return -1; // Instruction doesn't exist in this table.\n\n"; } //===----------------------------------------------------------------------===// // Emit functions to query relation tables. //===----------------------------------------------------------------------===// void MapTableEmitter::emitMapFuncBody(raw_ostream &OS, unsigned TableSize) { ListInit *ColFields = InstrMapDesc.getColFields(); const std::vector &ValueCols = InstrMapDesc.getValueCols(); // Emit binary search algorithm to locate instructions in the // relation table. If found, return opcode value from the appropriate column // of the table. emitBinSearch(OS, TableSize); if (ValueCols.size() > 1) { for (unsigned i = 0, e = ValueCols.size(); i < e; i++) { ListInit *ColumnI = ValueCols[i]; for (unsigned j = 0, ColSize = ColumnI->size(); j < ColSize; ++j) { std::string ColName = ColFields->getElement(j)->getAsUnquotedString(); OS << " if (in" << ColName; OS << " == "; OS << ColName << "_" << ColumnI->getElement(j)->getAsUnquotedString(); if (j < ColumnI->size() - 1) OS << " && "; else OS << ")\n"; } OS << " return " << InstrMapDesc.getName(); OS << "Table[mid]["< &ValueCols = InstrMapDesc.getValueCols(); OS << "// "<< InstrMapDesc.getName() << "\nLLVM_READONLY\n"; OS << "int "<< InstrMapDesc.getName() << "(uint16_t Opcode"; if (ValueCols.size() > 1) { for (Init *CF : ColFields->getValues()) { std::string ColName = CF->getAsUnquotedString(); OS << ", enum " << ColName << " in" << ColName << ") {\n"; } } else { OS << ") {\n"; } // Emit map table. unsigned TableSize = emitBinSearchTable(OS); // Emit rest of the function body. emitMapFuncBody(OS, TableSize); } //===----------------------------------------------------------------------===// // Emit enums for the column fields across all the instruction maps. //===----------------------------------------------------------------------===// static void emitEnums(raw_ostream &OS, RecordKeeper &Records) { std::vector InstrMapVec; InstrMapVec = Records.getAllDerivedDefinitions("InstrMapping"); std::map > ColFieldValueMap; // Iterate over all InstrMapping records and create a map between column // fields and their possible values across all records. for (Record *CurMap : InstrMapVec) { ListInit *ColFields; ColFields = CurMap->getValueAsListInit("ColFields"); ListInit *List = CurMap->getValueAsListInit("ValueCols"); std::vector ValueCols; unsigned ListSize = List->size(); for (unsigned j = 0; j < ListSize; j++) { auto *ListJ = cast(List->getElement(j)); if (ListJ->size() != ColFields->size()) PrintFatalError("Record `" + CurMap->getName() + "', field " "`ValueCols' entries don't match with the entries in 'ColFields' !"); ValueCols.push_back(ListJ); } for (unsigned j = 0, endCF = ColFields->size(); j < endCF; j++) { for (unsigned k = 0; k < ListSize; k++){ std::string ColName = ColFields->getElement(j)->getAsUnquotedString(); ColFieldValueMap[ColName].push_back((ValueCols[k])->getElement(j)); } } } for (auto &Entry : ColFieldValueMap) { std::vector FieldValues = Entry.second; // Delete duplicate entries from ColFieldValueMap for (unsigned i = 0; i < FieldValues.size() - 1; i++) { Init *CurVal = FieldValues[i]; for (unsigned j = i+1; j < FieldValues.size(); j++) { if (CurVal == FieldValues[j]) { FieldValues.erase(FieldValues.begin()+j); --j; } } } // Emit enumerated values for the column fields. OS << "enum " << Entry.first << " {\n"; for (unsigned i = 0, endFV = FieldValues.size(); i < endFV; i++) { OS << "\t" << Entry.first << "_" << FieldValues[i]->getAsUnquotedString(); if (i != endFV - 1) OS << ",\n"; else OS << "\n};\n\n"; } } } namespace llvm { //===----------------------------------------------------------------------===// // Parse 'InstrMapping' records and use the information to form relationship // between instructions. These relations are emitted as a tables along with the // functions to query them. //===----------------------------------------------------------------------===// void EmitMapTable(RecordKeeper &Records, raw_ostream &OS) { CodeGenTarget Target(Records); StringRef NameSpace = Target.getInstNamespace(); std::vector InstrMapVec; InstrMapVec = Records.getAllDerivedDefinitions("InstrMapping"); if (InstrMapVec.empty()) return; OS << "#ifdef GET_INSTRMAP_INFO\n"; OS << "#undef GET_INSTRMAP_INFO\n"; OS << "namespace llvm {\n\n"; OS << "namespace " << NameSpace << " {\n\n"; // Emit coulumn field names and their values as enums. emitEnums(OS, Records); // Iterate over all instruction mapping records and construct relationship // maps based on the information specified there. // for (Record *CurMap : InstrMapVec) { MapTableEmitter IMap(Target, Records, CurMap); // Build RowInstrMap to group instructions based on their values for // RowFields. In the process, also collect key instructions into // KeyInstrVec. IMap.buildRowInstrMap(); // Build MapTable to map key instructions with the corresponding column // instructions. IMap.buildMapTable(); // Emit map tables and the functions to query them. IMap.emitTablesWithFunc(OS); } OS << "} // end namespace " << NameSpace << "\n"; OS << "} // end namespace llvm\n"; OS << "#endif // GET_INSTRMAP_INFO\n\n"; } } // End llvm namespace diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 4be53930fc56..b238573be494 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -1,6173 +1,6173 @@ //===- GlobalISelEmitter.cpp - Generate an instruction selector -----------===// // // 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 // //===----------------------------------------------------------------------===// // /// \file /// This tablegen backend emits code for use by the GlobalISel instruction /// selector. See include/llvm/CodeGen/TargetGlobalISel.td. /// /// This file analyzes the patterns recognized by the SelectionDAGISel tablegen /// backend, filters out the ones that are unsupported, maps /// SelectionDAG-specific constructs to their GlobalISel counterpart /// (when applicable: MVT to LLT; SDNode to generic Instruction). /// /// Not all patterns are supported: pass the tablegen invocation /// "-warn-on-skipped-patterns" to emit a warning when a pattern is skipped, /// as well as why. /// /// The generated file defines a single method: /// bool InstructionSelector::selectImpl(MachineInstr &I) const; /// intended to be used in InstructionSelector::select as the first-step /// selector for the patterns that don't require complex C++. /// /// FIXME: We'll probably want to eventually define a base /// "TargetGenInstructionSelector" class. /// //===----------------------------------------------------------------------===// #include "CodeGenDAGPatterns.h" #include "SubtargetFeatureInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/CodeGenCoverage.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/LowLevelTypeImpl.h" #include "llvm/Support/MachineValueType.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include #include using namespace llvm; #define DEBUG_TYPE "gisel-emitter" STATISTIC(NumPatternTotal, "Total number of patterns"); STATISTIC(NumPatternImported, "Number of patterns imported from SelectionDAG"); STATISTIC(NumPatternImportsSkipped, "Number of SelectionDAG imports skipped"); STATISTIC(NumPatternsTested, "Number of patterns executed according to coverage information"); STATISTIC(NumPatternEmitted, "Number of patterns emitted"); cl::OptionCategory GlobalISelEmitterCat("Options for -gen-global-isel"); static cl::opt WarnOnSkippedPatterns( "warn-on-skipped-patterns", cl::desc("Explain why a pattern was skipped for inclusion " "in the GlobalISel selector"), cl::init(false), cl::cat(GlobalISelEmitterCat)); static cl::opt GenerateCoverage( "instrument-gisel-coverage", cl::desc("Generate coverage instrumentation for GlobalISel"), cl::init(false), cl::cat(GlobalISelEmitterCat)); static cl::opt UseCoverageFile( "gisel-coverage-file", cl::init(""), cl::desc("Specify file to retrieve coverage information from"), cl::cat(GlobalISelEmitterCat)); static cl::opt OptimizeMatchTable( "optimize-match-table", cl::desc("Generate an optimized version of the match table"), cl::init(true), cl::cat(GlobalISelEmitterCat)); namespace { //===- Helper functions ---------------------------------------------------===// /// Get the name of the enum value used to number the predicate function. std::string getEnumNameForPredicate(const TreePredicateFn &Predicate) { if (Predicate.hasGISelPredicateCode()) return "GIPFP_MI_" + Predicate.getFnName(); return "GIPFP_" + Predicate.getImmTypeIdentifier().str() + "_" + Predicate.getFnName(); } /// Get the opcode used to check this predicate. std::string getMatchOpcodeForPredicate(const TreePredicateFn &Predicate) { return "GIM_Check" + Predicate.getImmTypeIdentifier().str() + "ImmPredicate"; } /// This class stands in for LLT wherever we want to tablegen-erate an /// equivalent at compiler run-time. class LLTCodeGen { private: LLT Ty; public: LLTCodeGen() = default; LLTCodeGen(const LLT &Ty) : Ty(Ty) {} std::string getCxxEnumValue() const { std::string Str; raw_string_ostream OS(Str); emitCxxEnumValue(OS); return OS.str(); } void emitCxxEnumValue(raw_ostream &OS) const { if (Ty.isScalar()) { OS << "GILLT_s" << Ty.getSizeInBits(); return; } if (Ty.isVector()) { OS << "GILLT_v" << Ty.getNumElements() << "s" << Ty.getScalarSizeInBits(); return; } if (Ty.isPointer()) { OS << "GILLT_p" << Ty.getAddressSpace(); if (Ty.getSizeInBits() > 0) OS << "s" << Ty.getSizeInBits(); return; } llvm_unreachable("Unhandled LLT"); } void emitCxxConstructorCall(raw_ostream &OS) const { if (Ty.isScalar()) { OS << "LLT::scalar(" << Ty.getSizeInBits() << ")"; return; } if (Ty.isVector()) { OS << "LLT::vector(" << Ty.getNumElements() << ", " << Ty.getScalarSizeInBits() << ")"; return; } if (Ty.isPointer() && Ty.getSizeInBits() > 0) { OS << "LLT::pointer(" << Ty.getAddressSpace() << ", " << Ty.getSizeInBits() << ")"; return; } llvm_unreachable("Unhandled LLT"); } const LLT &get() const { return Ty; } /// This ordering is used for std::unique() and llvm::sort(). There's no /// particular logic behind the order but either A < B or B < A must be /// true if A != B. bool operator<(const LLTCodeGen &Other) const { if (Ty.isValid() != Other.Ty.isValid()) return Ty.isValid() < Other.Ty.isValid(); if (!Ty.isValid()) return false; if (Ty.isVector() != Other.Ty.isVector()) return Ty.isVector() < Other.Ty.isVector(); if (Ty.isScalar() != Other.Ty.isScalar()) return Ty.isScalar() < Other.Ty.isScalar(); if (Ty.isPointer() != Other.Ty.isPointer()) return Ty.isPointer() < Other.Ty.isPointer(); if (Ty.isPointer() && Ty.getAddressSpace() != Other.Ty.getAddressSpace()) return Ty.getAddressSpace() < Other.Ty.getAddressSpace(); if (Ty.isVector() && Ty.getNumElements() != Other.Ty.getNumElements()) return Ty.getNumElements() < Other.Ty.getNumElements(); return Ty.getSizeInBits() < Other.Ty.getSizeInBits(); } bool operator==(const LLTCodeGen &B) const { return Ty == B.Ty; } }; // Track all types that are used so we can emit the corresponding enum. std::set KnownTypes; class InstructionMatcher; /// Convert an MVT to an equivalent LLT if possible, or the invalid LLT() for /// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...). static Optional MVTToLLT(MVT::SimpleValueType SVT) { MVT VT(SVT); if (VT.isScalableVector()) return None; if (VT.isFixedLengthVector() && VT.getVectorNumElements() != 1) return LLTCodeGen( LLT::vector(VT.getVectorNumElements(), VT.getScalarSizeInBits())); if (VT.isInteger() || VT.isFloatingPoint()) return LLTCodeGen(LLT::scalar(VT.getSizeInBits())); return None; } static std::string explainPredicates(const TreePatternNode *N) { - std::string Explanation = ""; + std::string Explanation; StringRef Separator = ""; for (const TreePredicateCall &Call : N->getPredicateCalls()) { const TreePredicateFn &P = Call.Fn; Explanation += (Separator + P.getOrigPatFragRecord()->getRecord()->getName()).str(); Separator = ", "; if (P.isAlwaysTrue()) Explanation += " always-true"; if (P.isImmediatePattern()) Explanation += " immediate"; if (P.isUnindexed()) Explanation += " unindexed"; if (P.isNonExtLoad()) Explanation += " non-extload"; if (P.isAnyExtLoad()) Explanation += " extload"; if (P.isSignExtLoad()) Explanation += " sextload"; if (P.isZeroExtLoad()) Explanation += " zextload"; if (P.isNonTruncStore()) Explanation += " non-truncstore"; if (P.isTruncStore()) Explanation += " truncstore"; if (Record *VT = P.getMemoryVT()) Explanation += (" MemVT=" + VT->getName()).str(); if (Record *VT = P.getScalarMemoryVT()) Explanation += (" ScalarVT(MemVT)=" + VT->getName()).str(); if (ListInit *AddrSpaces = P.getAddressSpaces()) { raw_string_ostream OS(Explanation); OS << " AddressSpaces=["; StringRef AddrSpaceSeparator; for (Init *Val : AddrSpaces->getValues()) { IntInit *IntVal = dyn_cast(Val); if (!IntVal) continue; OS << AddrSpaceSeparator << IntVal->getValue(); AddrSpaceSeparator = ", "; } OS << ']'; } int64_t MinAlign = P.getMinAlignment(); if (MinAlign > 0) Explanation += " MinAlign=" + utostr(MinAlign); if (P.isAtomicOrderingMonotonic()) Explanation += " monotonic"; if (P.isAtomicOrderingAcquire()) Explanation += " acquire"; if (P.isAtomicOrderingRelease()) Explanation += " release"; if (P.isAtomicOrderingAcquireRelease()) Explanation += " acq_rel"; if (P.isAtomicOrderingSequentiallyConsistent()) Explanation += " seq_cst"; if (P.isAtomicOrderingAcquireOrStronger()) Explanation += " >=acquire"; if (P.isAtomicOrderingWeakerThanAcquire()) Explanation += " isSubClassOf("SDNode")) return (" (" + Operator->getValueAsString("Opcode") + ")").str(); if (Operator->isSubClassOf("Intrinsic")) return (" (Operator is an Intrinsic, " + Operator->getName() + ")").str(); if (Operator->isSubClassOf("ComplexPattern")) return (" (Operator is an unmapped ComplexPattern, " + Operator->getName() + ")") .str(); if (Operator->isSubClassOf("SDNodeXForm")) return (" (Operator is an unmapped SDNodeXForm, " + Operator->getName() + ")") .str(); return (" (Operator " + Operator->getName() + " not understood)").str(); } /// Helper function to let the emitter report skip reason error messages. static Error failedImport(const Twine &Reason) { return make_error(Reason, inconvertibleErrorCode()); } static Error isTrivialOperatorNode(const TreePatternNode *N) { - std::string Explanation = ""; - std::string Separator = ""; + std::string Explanation; + std::string Separator; bool HasUnsupportedPredicate = false; for (const TreePredicateCall &Call : N->getPredicateCalls()) { const TreePredicateFn &Predicate = Call.Fn; if (Predicate.isAlwaysTrue()) continue; if (Predicate.isImmediatePattern()) continue; if (Predicate.isNonExtLoad() || Predicate.isAnyExtLoad() || Predicate.isSignExtLoad() || Predicate.isZeroExtLoad()) continue; if (Predicate.isNonTruncStore() || Predicate.isTruncStore()) continue; if (Predicate.isLoad() && Predicate.getMemoryVT()) continue; if (Predicate.isLoad() || Predicate.isStore()) { if (Predicate.isUnindexed()) continue; } if (Predicate.isLoad() || Predicate.isStore() || Predicate.isAtomic()) { const ListInit *AddrSpaces = Predicate.getAddressSpaces(); if (AddrSpaces && !AddrSpaces->empty()) continue; if (Predicate.getMinAlignment() > 0) continue; } if (Predicate.isAtomic() && Predicate.getMemoryVT()) continue; if (Predicate.isAtomic() && (Predicate.isAtomicOrderingMonotonic() || Predicate.isAtomicOrderingAcquire() || Predicate.isAtomicOrderingRelease() || Predicate.isAtomicOrderingAcquireRelease() || Predicate.isAtomicOrderingSequentiallyConsistent() || Predicate.isAtomicOrderingAcquireOrStronger() || Predicate.isAtomicOrderingWeakerThanAcquire() || Predicate.isAtomicOrderingReleaseOrStronger() || Predicate.isAtomicOrderingWeakerThanRelease())) continue; if (Predicate.hasGISelPredicateCode()) continue; HasUnsupportedPredicate = true; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")"; Separator = ", "; Explanation += (Separator + "first-failing:" + Predicate.getOrigPatFragRecord()->getRecord()->getName()) .str(); break; } if (!HasUnsupportedPredicate) return Error::success(); return failedImport(Explanation); } static Record *getInitValueAsRegClass(Init *V) { if (DefInit *VDefInit =