7 Step One: Load a.ll
file and parse it into Module
After done some intitializations for LLVM backend, the main
function calls compileModule
to compile the input file into assembly. Fisrtly, it calls llvm::parseIRFile() to load input
file into MemoryBuffer
, see How to use LLVM to read files into Memory in details,
then llvm::parseAssembly() into Module
because the input file is not in Bitcode
mode
but in IR Assembly
mode.
/// llvm-project/llvm/include/llvm/IR/Module.h /// A Module instance is used to store all the information related to an /// LLVM module. Modules are the top level container of all other LLVM /// Intermediate Representation (IR) objects. Each module directly contains a /// list of globals variables, a list of functions, a list of libraries (or /// other modules) this module depends on, a symbol table, and various data /// about the target's characteristics. /// A module maintains a GlobalValRefMap object that is used to hold all /// constant references to global variables in the module. When a global /// variable is destroyed, it should have no entries in the GlobalValueRefMap. /// The main container class for the LLVM Intermediate Representation. class Module { LLVMContext &Context; ///< The LLVMContext from which types and ///< constants are allocated. GlobalListType GlobalList; ///< The Global Variables in the module FunctionListType FunctionList; ///< The Functions in the module AliasListType AliasList; ///< The Aliases in the module IFuncListType IFuncList; ///< The IFuncs in the module NamedMDListType NamedMDList; ///< The named metadata in the module std::string GlobalScopeAsm; ///< Inline Asm at global scope. std::unique_ptr<ValueSymbolTable> ValSymTab; ///< Symbol table for values ComdatSymTabType ComdatSymTab; ///< Symbol table for COMDATs std::unique_ptr<MemoryBuffer> OwnedMemoryBuffer; ///< Memory buffer directly owned by this ///< module, for legacy clients only. std::unique_ptr<GVMaterializer> Materializer; ///< Used to materialize GlobalValues std::string ModuleID; ///< Human readable identifier for the module std::string SourceFileName; ///< Original source file name for module, ///< recorded in bitcode. std::string TargetTriple; ///< Platform target triple Module compiled on ///< Format: (arch)(sub)-(vendor)-(sys0-(abi) NamedMDSymTabType NamedMDSymTab; ///< NamedMDNode names. DataLayout DL; ///< DataLayout associated with the module }
According to LLVM IR BNF, it LLParser::ParseTargetDefinitions(),
bool LLParser::ParseTargetDefinitions() { while (true) { switch (Lex.getKind()) { case lltok::kw_target: if (ParseTargetDefinition()) return true; break; case lltok::kw_source_filename:if (ParseSourceFileName()) return true; break; default: return false;}}} /// ::= 'target' 'triple' '=' STRINGCONSTANT /// ::= 'target' 'datalayout' '=' STRINGCONSTANT bool LLParser::ParseTargetDefinition(); /// ::= 'source_filename' '=' STRINGCONSTANT bool LLParser::ParseSourceFileName();
then LLParser::ParseTopLevelEntities(),
bool LLParser::ParseTopLevelEntities() { while (true) { switch (Lex.getKind()) { default: return TokError("expected top-level entity"); case lltok::Eof: return false; case lltok::kw_declare: if (ParseDeclare()) return true; break; case lltok::kw_define: if (ParseDefine()) return true; break; case lltok::kw_module: if (ParseModuleAsm()) return true; break; case lltok::kw_deplibs: if (ParseDepLibs()) return true; break; case lltok::LocalVarID: if (ParseUnnamedType()) return true; break; case lltok::LocalVar: if (ParseNamedType()) return true; break; case lltok::GlobalID: if (ParseUnnamedGlobal()) return true; break; case lltok::GlobalVar: if (ParseNamedGlobal()) return true; break; case lltok::ComdatVar: if (parseComdat()) return true; break; case lltok::exclaim: if (ParseStandaloneMetadata()) return true; break; case lltok::SummaryID: if (ParseSummaryEntry()) return true; break; case lltok::MetadataVar:if (ParseNamedMetadata()) return true; break; case lltok::kw_attributes: if (ParseUnnamedAttrGrp()) return true; break; case lltok::kw_uselistorder: if (ParseUseListOrder()) return true; break; case lltok::kw_uselistorder_bb: if (ParseUseListOrderBB()) return true; break;}}} /// ::= 'declare' FunctionHeader bool LLParser::ParseDeclare(); /// ::= 'define' FunctionHeader (!dbg !56)* '{' ... bool LLParser::ParseDefine(); /// ::= 'module' 'asm' STRINGCONSTANT bool LLParser::ParseModuleAsm(); /// ::= 'deplibs' '=' '[' ']' /// ::= 'deplibs' '=' '[' STRINGCONSTANT (',' STRINGCONSTANT)* ']' /// FIXME: Remove in 4.0. Currently parse, but ignore. bool LLParser::ParseDepLibs(); /// ::= LocalVarID '=' 'type' type bool LLParser::ParseUnnamedType(); /// ::= LocalVar '=' 'type' type bool LLParser::ParseNamedType(); /// OptionalVisibility (ALIAS | IFUNC) ... /// OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility /// OptionalDLLStorageClass /// ... -> global variable /// GlobalID '=' OptionalVisibility (ALIAS | IFUNC) ... /// GlobalID '=' OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility /// OptionalDLLStorageClass /// ... -> global variable bool LLParser::ParseUnnamedGlobal() ; /// GlobalVar '=' OptionalVisibility (ALIAS | IFUNC) ... /// GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier /// OptionalVisibility OptionalDLLStorageClass /// ... -> global variable bool LLParser::ParseNamedGlobal(); bool LLParser::parseComdat() ; /// !42 = !{...} bool LLParser::ParseStandaloneMetadata(); /// ::= SummaryID '=' GVEntry | ModuleEntry | TypeIdEntry bool LLParser::ParseSummaryEntry() ; /// !foo = !{ !1, !2 } bool LLParser::ParseNamedMetadata() ; /// ::= 'attributes' AttrGrpID '=' '{' AttrValPair+ '}' bool LLParser::ParseUnnamedAttrGrp(); /// ::= 'uselistorder' Type Value ',' UseListOrderIndexes bool LLParser::ParseUseListOrder(PerFunctionState *PFS) ; /// ::= 'uselistorder_bb' @foo ',' %bar ',' UseListOrderIndexes bool LLParser::ParseUseListOrderBB() ;
and do some validation.
Afterward, we get Module like this, using M->dump()
in lldb
to see the parsed result.
(lldb) b llc.cpp:524 (lldb) p M->dump() ; ModuleID = '/Users/k/llvm-project/llvm/lib/Target/RISCV/LLDB/localvar.ll' source_filename = "/Users/k/llvm-project/llvm/lib/Target/RISCV/LLDB/localvar.ll" target datalayout = "e-m:e-p:32:32-i64:64-n32-S128" define i32 @main(i32 signext %argc, i8** %argv) { %1 = alloca i32, align 4 store i32 1, i32* %1, align 4 ret i32 0 }
After we got the Module, llc
builds up all of the passes that we want to do to the module.
Those transformations are taken effect in its passes.
More information about Pass and PassManager.
Because we are to emit an assembly file from a.ll
,
llc
will call LLVMTargetMachine::addPassesToEmitFile to add related passes.
It calls
- addPassesToGenerateCode()
add needed passes to codegen process which transform
IR
toMachineInstr
, - LLVMTargetMachine::addAsmPrinter()
add needed passes to output Asm which transform
MachineInstr
toMCInstr
then toAssembly
, - llvm::createFreeMachineFunctionPass() This pass frees the MachineFunction object associated with a Function, in turn.
Notes that, in addPassesToGenerateCode()
, llc
calls LLVMTargetMachine::createPassConfig() of LLVMTargetMachine
,
which RISCV has its derivation RISCVTargetMachine
,
and overrides the RISCVTargetMachine::createPassConfig(),
to return an instance of RISCVPassConfig ,
to customize passes with TargetPassConfig::addPass() for
- RISCVPassConfig::addIRPass(), called in
TargetPassConfig::addISelPasses()
, to perform LLVM IR to IR transforms following machine independent optimization. - RISCVPassConfig::addInstSelector() This method should install an instruction selector pass, which converts from LLVM code to machine instructions
- RISCVPassConfig::addIRTranslator() This method should install an IR translator pass, which converts from LLVM code to machine instructions with possibly generic opcodes.
- RISCVPassConfig::addLegalizeMachineIR() This method should install a legalize pass, which converts the instruction sequence into one that can be selected by the target.
- RISCVPassConfig::addRegBankSelect() This method should install a register bank selector pass, which assigns register banks to virtual registers without a register class or register banks.
- RISCVPassConfig::addGlobalInstructionSelect() This method should install a (global) instruction selector pass, which converts possibly generic instructions to fully target-specific instructions, thereby constraining all generic virtual registers to register classes.
- RISCVPassConfig::addPreEmitPass() This pass may be implemented by targets that want to run passes immediately before machine code is emitted.
- RISCVPassConfig::addPreEmitPass2() Targets may add passes immediately before machine code is emitted in this callback. This is called even later than `addPreEmitPass`.
- RISCVPassConfig::addPreSched2() This method may be implemented by targets that want to run passes after prolog-epilog insertion and before the second instruction scheduling pass.
RISCVPassConfig::addPreRegAlloc() This method may be implemented by targets that want to run passes immediately before register allocation.
After those passes set up, we can see what are those passes inside.
(lldb) p PM.PM->dumpPasses() Target Library Information Target Pass Configuration Machine Module Information Target Transform Information Type-Based Alias Analysis Scoped NoAlias Alias Analysis Assumption Cache Tracker Profile summary info Create Garbage Collector Module Metadata Machine Branch Probability Analysis --ModulePass Manager ----Pre-ISel Intrinsic Lowering ----FunctionPass Manager ------Expand Atomic instructions ------Module Verifier ------Dominator Tree Construction ------Basic Alias Analysis (stateless AA impl) ------Natural Loop Information ------Canonicalize natural loops ------Scalar Evolution Analysis ------Loop Pass Manager --------Canonicalize Freeze Instructions in Loops --------Induction Variable Users --------Loop Strength Reduction ------Basic Alias Analysis (stateless AA impl) ------Function Alias Analysis Results ------Merge contiguous icmps into a memcmp ------Natural Loop Information ------Lazy Branch Probability Analysis ------Lazy Block Frequency Analysis ------Expand memcmp() to load/stores ------Lower Garbage Collection Instructions ------Shadow Stack GC Lowering ------Lower constant intrinsics ------Remove unreachable blocks from the CFG ------Dominator Tree Construction ------Natural Loop Information ------Post-Dominator Tree Construction ------Branch Probability Analysis ------Block Frequency Analysis ------Constant Hoisting ------Partially inline calls to library functions ------Instrument function entry/exit with calls to e.g. mcount() (post inlining) ------Scalarize Masked Memory Intrinsics ------Expand reduction intrinsics ------Dominator Tree Construction ------Natural Loop Information ------CodeGen Prepare ----Rewrite Symbols ----FunctionPass Manager ------Dominator Tree Construction ------Exception handling preparation ------Safe Stack instrumentation pass ------Insert stack protectors ------Module Verifier ------Dominator Tree Construction ------Basic Alias Analysis (stateless AA impl) ------Function Alias Analysis Results ------Natural Loop Information ------Post-Dominator Tree Construction ------Branch Probability Analysis ------Lazy Branch Probability Analysis ------Lazy Block Frequency Analysis ------RISCV DAG->DAG Pattern Instruction Selection ------Finalize ISel and expand pseudo-instructions ------Lazy Machine Block Frequency Analysis ------Early Tail Duplication ------Optimize machine instruction PHIs ------Slot index numbering ------Merge disjoint stack slots ------Local Stack Slot Allocation ------Remove dead machine instructions ------MachineDominator Tree Construction ------Machine Natural Loop Construction ------Early Machine Loop Invariant Code Motion ------MachineDominator Tree Construction ------Machine Block Frequency Analysis ------Machine Common Subexpression Elimination ------MachinePostDominator Tree Construction ------Machine code sinking ------Peephole Optimizations ------Remove dead machine instructions ------RISCV Merge Base Offset ------Detect Dead Lanes ------Process Implicit Definitions ------Remove unreachable machine basic blocks ------Live Variable Analysis ------MachineDominator Tree Construction ------Machine Natural Loop Construction ------Eliminate PHI nodes for register allocation ------Two-Address instruction pass ------Slot index numbering ------Live Interval Analysis ------Simple Register Coalescing ------Rename Disconnected Subregister Components ------Machine Instruction Scheduler ------Machine Block Frequency Analysis ------Debug Variable Analysis ------Live Stack Slot Analysis ------Virtual Register Map ------Live Register Matrix ------Bundle Machine CFG Edges ------Spill Code Placement Analysis ------Lazy Machine Block Frequency Analysis ------Machine Optimization Remark Emitter ------Greedy Register Allocator ------Virtual Register Rewriter ------Stack Slot Coloring ------Machine Copy Propagation Pass ------Machine Loop Invariant Code Motion ------Fixup Statepoint Caller Saved ------PostRA Machine Sink ------Machine Block Frequency Analysis ------MachineDominator Tree Construction ------MachinePostDominator Tree Construction ------Lazy Machine Block Frequency Analysis ------Machine Optimization Remark Emitter ------Shrink Wrapping analysis ------Prologue/Epilogue Insertion & Frame Finalization ------Control Flow Optimizer ------Lazy Machine Block Frequency Analysis ------Tail Duplication ------Machine Copy Propagation Pass ------Post-RA pseudo instruction expansion pass ------MachineDominator Tree Construction ------Machine Natural Loop Construction ------Post RA top-down list latency scheduler ------Analyze Machine Code For Garbage Collection ------Machine Block Frequency Analysis ------MachinePostDominator Tree Construction ------Branch Probability Basic Block Placement ------Insert fentry calls ------Insert XRay ops ------Implement the 'patchable-function' attribute ------Branch relaxation pass ------Contiguously Lay Out Funclets ------StackMap Liveness Analysis ------Live DEBUG_VALUE analysis ------RISCV pseudo instruction expansion pass ------RISCV atomic pseudo instruction expansion pass ------Lazy Machine Block Frequency Analysis ------Machine Optimization Remark Emitter ------RISCV Assembly Printer ------Free MachineFunction
Then
llc
will callsPM.run(*M)
to run all the added passed onModule
to output Assembly if everything is OK.