LLVM:Writing an LLVM Backend

寫一個LLVM後端 :轉換LLVM IR爲目標平臺的指令(彙編碼/機器碼-JIT)

https://releases.llvm.org/8.0.0/docs/WritingAnLLVMBackend.html#id33

 

  • 建立新後端的七大步驟
    1. 描述特定目標平臺的屬性
      1. TargetMachine.cpp
    2. 描述特定目標平臺的寄存器
      1. TargetRegisterInfo.td:寄存器定義+寄存器別名+寄存器分類
      2. TargetRegisterInfo.cpp:寄存器分配和交互,include ”TargetGenRegisterInfo.inc”
    3. 描述特定目標平臺的指令集
      1. TargetInstrFormats.td:指令格式
      2. TargetInstrInfo.td:指令定義
      3. TargetInstrInfo.cpp:
    4. LLVM IR從DAG到目標平臺指令的選擇和轉換:指令選擇
      1. TargetISelDAGToDAG.cpp:模式匹配+DAGToDAG指令選擇
      2. TargetISelLowering.cpp:替換或刪除目標平臺不支持的數據和操作類型,即數據和操作合法化
    5. LLVM IR->GAS格式的彙編
      1. TargetAsmPrinter.cpp
    6. (可選)支持子目標平臺,允許使用-mcpu=和-mattr=命令行選項
      1. TargetSubtarget.cpp
    7. (可選)JIT即時編譯
      1. TargetJITInfo.cpp
  • 目標機器
    1. getInstrInfo()
    2. getRegisterInfo()
    3. getFrameInfo():棧幀佈局
    4. getDataLayout():數據佈局(大小端序+數據類型+ABI對齊+首選對齊+數據大小)
    5. getSubtargetImpl()
    6. getTargetLowering()
    7. getJITInfo()
    8. Target Registration:目標登記註冊
  • 寄存器
    1. 定義單個寄存器
      1. def F0 : Rf< 0, "F0">, DwarfRegNum<[32]>;
      2. 編碼,名稱,調試信息序號
    2. 定義寄存器類
      1. def FPRegs : RegisterClass<"SP", [f32], 32, (sequence "F%u", 0, 31)>;
        • 命名空間,ValueType.td裏面的數據類型,數據對齊方式/大小 ,所屬寄存器F0~F31
      2. def DFPRegs : RegisterClass<"SP", [f64], 64, (add D0, D15)>;
      3. 實現TargetRegisterInfo的子類
        • getCalleeSavedRegs—按所需的調用保存堆棧幀偏移量的順序返回調用保存的寄存器列表。
        • getReservedRegs—返回一個按物理寄存器編號索引的位集,指示某個寄存器是否不可用。
        • hasFP—返回一個布爾值,指示一個函數是否應該有一個專用的幀指針寄存器。
        • eliminateCallFramePseudoInstr—如果使用了調用幀設置或銷燬僞指令,可以調用它來消除它們。
        • eliminateFrameIndex—從可能使用它們的指令中刪除抽象的幀索引。
        • emitPrologue—在函數中插入序言代碼。
        • emitEpilogue—在函數中插入epilogue代碼。
    3. 定義指令集
      1. Target.td :指令、操作數、InstrInfo和其他基本類
      2. TargetSelectionDAG.td
        • SelectionDAG節點(SDNode)
        • Pattern, Pat, PatFrag, PatLeaf, ComplexPattern
      3. TargetInstrFormats.td
        • 指令模板分類一般依據
          1. 單/雙/三/四寄存器類型
          2. 標量/矢量類型
          3. 分支/普通/Intrinsic/僞指令
        • 指令操作數映射到指令中未綁定字段
          1. 定義操作數:bits<6>op1;bits<4>op2;bits<6>rd;bits<6>rs1;bits<6>rs2;
          2. 綁定字段:let Inst{31-26}=op1; let Inst{3-0}=op2;
          3. 指令操作數序號獲取支持
            1. getNamedOperandIdx獲取對應操作數的序號,從0開始
            2. 需要在TargetInstrInfo.cpp及其頭文件加上一些預處理宏
      4. TargetInstrInfo.td
        • 一條指令應該包含的基本描述
          1. 操作碼:可能多段,比如[31:26]+[3:0]
          2. 操作數:輸入和輸出dag,類型爲寄存器類或者操作數類
          3. 彙編字符串:
          4. DAG Pattern:模式匹配,SDNode,類型爲寄存器類或者PatLeaf類等
          5. Predicates:條件判斷是否選擇匹配
          6. 槽分配碼
          7. 額外屬性:是否可交換/是否訪存/是否爲僞指令/各種標誌位設定
        • 相似指令描述技巧:multiclass + defm
        • 指令間的關聯映射:InstrMapping類

https://releases.llvm.org/8.0.0/docs/HowToUseInstrMappings.html

        • 操作數類型def xxx:Operand<i32/i16/f64/OtherVT>;
      • Schedule.td
        • def ALU32_S_SLOT0 :InstrItinClass;
        • def SLOT0       : FuncUnit;
        • def TargetGenericItineraries :ProcessorItineraries<[SLOT0, SLOT1, SLOT2, SLOT3],[],[InstrItinData<ALU32_S_SLOT0, [InstrStage<1, [SLOT0]>]>,...
      • 實現TargetInstrInfo的子類:TargeInstrInfo.cpp
        • isLoadFromStackSlot—如果指定的機器指令是從堆棧插槽直接加載的,則返回目標的寄存器編號和堆棧插槽的幀索引。
        • isStoreToStackSlot—如果指定的機器指令是直接存儲到堆棧槽的,則返回目標的寄存器編號和堆棧槽的幀索引。
        • copyPhysReg—在一對物理寄存器之間拷貝值。
        • storeRegToStackSlot—將寄存器值存儲到堆棧槽中。
        • loadRegFromStackSlot—從堆棧槽加載寄存器值。
        • storeRegToAddr—將寄存器值存儲到內存中。
        • loadRegFromAddr—從內存中加載寄存器值。
        • foldMemoryOperand—嘗試爲指定的操作數組合任何裝載或存儲指令的指令。
      • 分支摺疊和條件轉換:TargetInstrInfo中的AnalyzeBranch方法可以用來檢查條件指令並刪除不必要的指令
    • 指令選擇器
      1. TargetISelDAGToDAG.cpp
        • 將非本機DAG指令轉換爲本機特定目標指令+匹配模式
        • TargetInstrInfo.td->TargetGenDAGISel.inc(SelectCode方法)
        • TargetCallingConv.td(函數調用和返回值約定)->TargetGenCallingConv.inc
        • DAG各個階段可視化輔助:llc
      2. TargetISelLowering.cpp:替換或刪除目標平臺不支持的數據和操作類型,即數據和操作合法化
        • 指定支持哪些類型:addRegisterClass(MVT::i32, SP::IntRegsRegisterClass);
        • 對於本地/目標平臺不支持的類型的處理函數如下
          1. setOperationAction—一般操作。
          2. setLoadExtAction -加載擴展。
          3. setTruncStoreAction—截斷存儲。
          4. setIndexedLoadAction -索引加載。
          5. setIndexedStoreAction -索引存儲。
          6. setConvertAction -類型轉換,比如i1->i32
          7. setCondCodeAction—支持給定的條件代碼。
        • 處理函數三個參數(ISD節點,MVT數據類型,處理方式4選1)
          1. Promote:i1->i8->i16->i32->i64;f32->f64
          2. Expand:
            1. 處理不支持的數據類型:i64->i32->i16->i8->i1
            2. 處理不支持的操作類型:用系統其他的操作替換
          3. Custom:自定義合法化處理函數,LowerFP_TO_SINT(Op, DAG)
          4. Legal:默認表示本地/目標平臺支持該類型或操作,很少用
      3. TargetCallingConv.td調用約定
        • CCIfType<[f32,f64], CCAssignToReg<[R0, R1]>>
        • CCIfType<[i1,i8,i16],CCPromoteToType<i32>>
        • CCIfType<[i32],CCAssignToStack<16,4>>:首選棧大小16,首選對齊方式4Bytes=32bits,爲零的話則使用ABI大小和對齊方式
        • CCDelegateTo<TargetCC>:如果TargetCC這個調用約定存在的話則調用/應用這個約定
        • CCIf <謂詞,動作> -如果謂詞匹配,應用動作。
        • CCIfInReg <action> -如果參數被標記爲“inreg”屬性,則應用該操作。
        • CCIfNest <action>—如果參數被標記爲“nest”屬性,則應用該操作。
        • CCIfNotVarArg <action>—如果當前函數沒有采用可變數量的參數,則應用該操作。類似於CCAssignToReg,但是有一個寄存器的影子列表。
        • CCPassByVal <size, align>—將值賦給指定的最小大小和對齊方式的堆棧槽。
        • CallingConv <[actions]> -定義支持的每個調用約定。
    • 彙編打印器
      1. TargetInstPrinter.cpp:TargetInstrInfo.td->TargetGenAsmWriter.inc(printInstruction方法)
        • printOperand
        • printMemOperand
        • printCCOperand (for conditional statements)
        • printDataDirective
        • printDeclare
        • printImplicitDef
        • printInlineAsm
      2. TargetAsmInfo.cpp+TargetAsmInfo.h
      3. TargetAsmPrinter.cpp:llvm IR->彙編代碼
        • SetupMachineFunction
        • EmitInstruction
        • EmitConstantPool
        • EmitJumpTableInfo
        • EmitFunctionBodyStart
        • EmitFunctionBodyEnd
        • doFinalization
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章