LLVM 编译器学习笔记之三十六-- 指令调度Instruction scheduling_llvm编译器指令-程序员宅基地

技术标签: LLVM  后端  技术文章  

1、参考Instruction scheduling in LLVM - 知乎,在中、后端均存在指令调度

     https://www.youtube.com/watch?v=cWLW0aQwFg0&t=922s

GenericScheduler:: 做寄存器压力感知的指令调度

PostGenericScheduler:: 寄存器分配后的指令调度,基于BB的指令调度

 2、在llvm12->llvm14 中Machine Instruction Scheduler有个patch af342f7240增强了load/store指令的合并,也就是指令调度不仅仅只是改变指令的顺序,该优化类似gcc中的store-merge优化

注意:后端isPairableLdStInst接口可以指示是否允许寄存器对的优化,一般来说这个合并是有利的,也不能排除个别硬件存在bug

3、指令调度*.P指Pend, *.A为ready的指令,调度时从Bot和Top两端选取,最后将在某个中间位置相遇时结束调度;另外BotQ.A中的指令会被优先考虑发射(P 代表Pending, A 代表Available, 参考SchedBoundary::pickOnlyChoice)

4、指令中隐式的定义需要被列在let Defs = []中,参考 D123578 [RISCV] Add sched to pseudo function call instructions

let isCall = 1, Defs = [LR, X0, X1], hasSideEffects = 1, Size = 16,
    isCodeGenOnly = 1 in
def TLSDESC_CALLSEQ
    : Pseudo<(outs), (ins i64imm:$sym),
             [(AArch64tlsdesc_callseq tglobaltlsaddr:$sym)]>,
      Sched<[WriteI, WriteLD, WriteI, WriteBrReg]>;

AArch64::TLSDESC_CALLSEQ lower to

adrp  x0, :tlsdesc:var

ldr   x1, [x0, #:tlsdesc_lo12:var]

add   x0, x0, #:tlsdesc_lo12:var

blr   x1 # bl 指令会将程序的返回地址放到LR寄存器中 

5、 乱序执行OOO也需要进行指令调度优化,不是任意顺序性能都一样的

硬件有一个instruction window, 假如这个window无限大,compiler不管怎么写指令都无所谓。但实际上这个window只有80-100个指令,所以当有很深的parallel loop时,compiler如何写指令就会对性能有影响了

6、实现指令调度,确保stp指令按照升序排列输出(寄存器分配后的指令调度简单从ReadyQueue队列中获取一个指令,然后和当前Cand进行比较,如果需要调整即更新Cand, 因此pickNodeFromQueue函数返回后Cand值即为最优先需要发射的指令)

D125377 [AArch64] Order STP Q's by ascending address (llvm.org)

注意:指令调度的模型SchedModel=mf.getSubtarget()和后端架构配置相关

7、 在-mattr=+slow-paired-128选项说明128位的loadstore不好可能是一个共性问题,commit 7784cacd9,涉及AArch64InstrInfo::isCandidateToMergeOrPair函数

 203 def FeatureSlowPaired128 : SubtargetFeature<"slow-paired-128",
 204     "Paired128IsSlow", "true", "Paired 128 bit loads and stores are slow">;

usage:clang  -Xclang -target-feature -Xclang +slow-paired-128 或者llc -mattr=+slow-paired-128

代码中选项使用方法,参考MF.getSubtarget<AArch64Subtarget>().hardenSlsBlr() (注:需要头文件#include "AArch64Subtarget.h")

8、指令调度模型在 llvm/lib/Target/AArch64/AArch64.td中设置,参考D89972 (之后在D120906中进行了重构),也就是当前指定使用ProcTSV110对应的模型配置

当仅仅使用 -march=armv8.2-a 未指定tsv110时使用默认"generic"对应的CortexA55Model

:def : ProcessorModel<"generic", CortexA55Model, ProcessorFeatures.Generic
+def : ProcessorModel<"tsv110", TSV110Model, [ProcTSV110]>;

9、GenericSchedulerBase::shouldReduceLatency  确认指令的时序是否要减少?(已经大于关键路径)

10、in-order和out-of-order的关键配置(相关处理CodeGen/MachineScheduler.cpp)

Target/RISCV/RISCVSchedSiFive7.td:13:  let MicroOpBufferSize = 0; // Explicitly set to zero since SiFive7 is in-order

11、将循环中的LoopMicroOpBufferSize值调小一点,参考72a799a6

12、Load/Store的时延分析特别处理,参考D8705,load->store的时延只有1个cycle?

13、指令调度中每个指令被匹配为一个SUnit结点,根据top-down topological order计算两个指令的顺序,也就是A<B代表无B->A的边(即A可以在B前发射),参考ScheduleDAGInstrs::initSUnits

SU->Latency = SchedModel.computeInstrLatency(SU->getInstr())

14、通过ARMTargetLowering::getSchedulingPreference设置特殊指令的调度模式(用于寄存器分配前的指令调度)

enum Preference {
  None,        // No preference
  Source,      // Follow source order.
  RegPressure, // Scheduling for lowest register pressure.
  Hybrid,      // Scheduling for both latency and register pressure.
  ILP,         // Scheduling for ILP in low register pressure mode.
  VLIW,        // Scheduling for VLIW targets.
  Fast,        // Fast suboptimal list scheduling
  Linearize    // Linearize DAG, no scheduling
};

15、实现模型的定义,比如将 WriteV进一步区分WriteVd和WriteVq的描述,参考D108766

16、使用llvm-mca -mtriple=aarch64 -mcpu=tsv110 -instruction-tables < test.s可以显示编译器定义的时序信息,方便和文档做比较,参考D128631 (Gcc 侧的定义commit 25095d1ef8)

llvm/utils/update_mca_test_checks.py --llvm-mca-binary=build/bin/llvm-mca xx.s

Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
 2      5     1.00    *                   ldp    s7, s16, [x24, #-8]
 1      1     0.50                        add    x13, sp, #272

Resources:
[0]   - A57UnitB
[1.0] - A57UnitI
[1.1] - A57UnitI
[2]   - A57UnitL
[3]   - A57UnitM
[4]   - A57UnitS
[5]   - A57UnitW
[6]   - A57UnitX


Resource pressure per iteration:
[0]    [1.0]  [1.1]  [2]    [3]    [4]    [5]    [6]    
 -     3.00   3.00   7.00    -      -     20.50  3.50   

Resource pressure by instruction:
[0]    [1.0]  [1.1]  [2]    [3]    [4]    [5]    [6]    Instructions:
 -      -      -     1.00    -      -      -      -     ldp    s7, s16, [x24, #-8]
 -     0.50   0.50    -      -      -      -      -     add    x13, sp, #272

17、调试 --misched-only-func=foo --misched-only-block=9 指定函数foo的第9个BB不调度

18、 throughput是根據資源數量和指令時延算出來,参考M5WriteVSTI,如果没有定义则默认根据指令类型获取默认值(MCSchedule.h

SDIV的時延是 6-12 cycles, ProcResource 數量只有1, 所以throughput就該是 1/12 - 1/6
注意:mca可以通过如下方式设置分数,参考 https://lists.llvm.org/pipermail/llvm-dev/2015-April/084470.html

19、利用SchedWriteVariant来区分ADD/SUB指令针对是否有shift操作的时延不一致的问题,参考D8043

def : InstRW<[A57WriteISReg], (instregex ".*rs$")>; 也可以用

 def : SchedAlias<WriteISReg, A57WriteISReg>; 因为这些指令默认使用WriteISReg描述

注:默认值WriteISReg只能关联一次

注意:SchedAlias和WriteRes的区别参考https://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20150406/270546.html

21、在SchedVariant中可以使用正则表达式,注RegShiftedPred是非随意的,rs$匹配的对应WriteISReg时序,未匹配的WriteI时序?疑问:未匹配的比如store指令为何没有用WriteI时序

此定义 llvm-tblgen AArch64.td -class=Instruction -I../../../include/ &> test.dump 对应的record

22、SchedWrite和SchedWriteRes的区别,参考TargetSchedule.td中的介绍

SchedWrite can be used inside AArch64InstrFormats.td for generalizing over a class of instructions.
SchedWriteRes allows you to fine-tune and override the pipeline and latency for a given instruction, or instructions matching a regex.(即只能用于InstRW或ItinRW的匹配)

23、shouldScheduleAdjacent定义两个指令需要被调度排布在一起,不定义是指令调度结束后分裂,参考D120104

24、D110480上有AMD等指令时序的描述

25、指令的Lantency在addVRegDefDeps中调用SchedModel.computeOperandLatency计算

26、finalizeBundle->prepend->..->bundleWithSucc设置了hasUnmodeledSideEffects(),从而访存指令不能跨过bundle( 可以为一类指令设置默认属性默认设置的属性仅对单指令的的tablegen pattern生效,参考D37097/D140680)

参考LLVM笔记(7) - 指令的side effect - Five100Miles - 博客园,对应side effect属性,指令默认存在副作用,需要let hasSideEffects = 0显示的调整),也可以参考 D139637 [AArch64][SVE][ISel] Combine dup of load to replicating load

utils/TableGen/InstrInfoEmitter.cpp:993:  if (Inst.hasSideEffects)     OS << "|(1ULL<<MCID::UnmodeledSideEffects)";

class SME_Switch_Intrinsic  --> 也可以pattern中设置
    : DefaultAttrsIntrinsic<[],[ llvm_i32_ty],[IntrNoMem, IntrHasSideEffects]>;

27、是否out of order模型的接口函数isOutOfOrder ==> 关键:MicroOpBufferSize

注意:在out-of-order 模型中,也可以通过let BufferSize = 0来设置确保Out Latency=1, 详见computeOutputLatency

def AnyLdSt : ProcResGroup<[UnitLdSt1, UnitLdSt2]> { let BufferSize = 0; }

28、Depth +  Height 在寄存器分配的调度后是一个常数?在寄存器分配前的调度中非常数;指令调度默认是misched-bottomup(因此无依赖的指令先调度,但实际是后发射),需要-mllvm -misched-topdown=true调整方向, 参考GenericScheduler::pickNodetryLatency

SU(0):   renamable $q1, renamable $q2 = LDPQi renamable $x9, -1 :: (load (s128) from %ir.scevgep10, align 8), (load (s128) from %ir.lsr.iv79, align 8)
  # preds left       : 0
  # succs left       : 4
  # rdefs left       : 0
  Latency            : 6  -> 
getCriticalCount(), SchedBoundary::countResource
  Depth              : 0  -> 
SU->getDepth(), tryLatency
  Height             : 17 -> 
SU->getHeight(), tryLatency

29、in-order时,Pending指已经消除依赖,但是时序未达ReadyCycle的指令?而Out-of-order时不需要考虑时序,仅考虑依赖?参考SchedBoundary::releaseNode

SchedBoundary::pickOnlyChoice中,如果checkHazard中检查发现指令发射数目超过uops, 也会重新将相关指令从Ready list中删除,放到Pend中==> 即使乱序模式下,也有Pend (dump中SU(1) uops=3

30、指令调度中dump信息解析,参考GenericScheduler::tryCandidate

** ScheduleDAGMILive::schedule picking next node  --> 开始应该新的group调度
Queue BotQ.P: 
Queue BotQ.A: 29 30 
  Cand SU(29) ORDER          --> 优选29,原因order, 即原来的指令顺序                    
  Cand SU(30) ORDER          --> 更新为30更优,同样原因是order                 
Pick Bot ORDER     
Scheduling SU(30) %52:zpr = LD1RD_IMM %16:ppr_3b,%51:gpr64common, 0

31、检查确认是否资源是瓶颈,参考SchedBoundary::countResource

  A57UnitL +2x6u  --> 前面2是Cycles,后面6是Factor

*** Critical resource A57UnitL: 2c

def : InstRW<[A57Write_6cyc_2L, WriteLDHi], (instrs LDPQi)>; ==> uops = 2+1=3,参考resolveSchedClass->getSchedClassDesc

34、 典型的SchedWriteRes描述,参考AArch64SchedA64FX.td

def A64FXWrite_ST2_WD_RI : SchedWriteRes<[A64FXGI0, A64FXGI56]> {
  let Latency = 11;
  let NumMicroOps = 3;         // 
  let ResourceCycles = [2, 2]; // 分别对应A64FXGI0A64FXGI56
}

A64FX中的注释 LDP only breaks into *one* LS micro-op

每个时钟周期同时占用一个A64FXGI0和一个A64FXGI56执行单元,等价描述

def A64FXWrite_ST2_WD_RI : SchedWriteRes<[A64FXGI0, A64FXGI0, A64FXGI56, A64FXGI56]> {
  let Latency = 11;
  let NumMicroOps = 3;
}

35、伪指令也是有定义的,比如LOADgot则关注后端对WriteAdrWriteLD的定义

def LOADgot : Pseudo<(outs GPR64common:$dst), (ins i64imm:$addr),
                     [(set GPR64common:$dst, (AArch64LOADgot tglobaladdr:$addr))]>,
              Sched<[WriteLDAdr]>;

def WriteLDAdr : WriteSequence<[WriteAdr, WriteLD]>;

也可以进行显示的定义,参考 AArch64SchedFalkorDetails.td

def : InstRW<[WriteSequence<[FalkorWr_1LD_3cyc, FalkorWr_1XYZ_1cyc]>],
                                      (instrs LOADgot)>;

36、InstRW定义中的顺序对指令的Lantency有影响,参考 D159254 [AArch64] Fix schedmodel pre/post-index loads and stores for Neoverse V2

def : InstRW<[WriteAdr, V2Write_5cyc_1I_3L, WriteLDHi],  -- 将地址更新更早发射

37、使用 llvm-mca -mtriple=aarch64 -mcpu=tsv110 --instruction-info=0 --resource-pressure=0 --timeline --timeline-max-iterations=1 可以展示指令流水线调度情况,参考gh #68854

   static const char Dispatched = 'D';
    static const char Executed = 'E';
    static const char Retired = 'R';
    static const char Waiting = '='; // Instruction is waiting in the scheduler.
    static const char Executing = 'e';
    static const char RetireLag = '-'; // The instruction is waiting to retire.

38、 基于PR80178可以看到Latency短的指令发射的优先级一般更低。

39、The main purpose of ReadAdvance is pipeline forwarding,可以通过-debug-only=subtarget-emitter调试?参考https://groups.google.com/g/llvm-dev/c/gbPGlDdT1Pc/m/lhHNPcMRBgAJ

bypass/forward指前后有两个指令,第一个指令的dst在执行完后还没有把结果写到寄存器之前,可以提前给到依赖于它的指令。

def ReadAdr : SchedReadAdvance<3, [WriteLD]> 代表来给写操作使用load的提前3拍发射

通过llvm自带脚本schedcover.py tblGenSubtarget.dbg 'default|NeoverseV1Model' 可以查看每个指令定义后的状态

注:上述tblGenSubtarget.dbg需要在cmake的命令中找到相关的生成命令,类似

   llvm-tblgen -DBSPRIV_AARCH64 -DBSPRIV_AARCH64 ... -o lib/Target/AArch64/AArch64GenSubtargetInfo.inc -d lib/Target/AArch64/AArch64GenSubtargetInfo.inc.d >$cwd/tblGenSubtarget.dbg 2>&1

40、指令调度处理融合情况 ,eg: ST.hasCmpBccFusion()

41、可以使用组合来定义Write_6c_1LD_2S

def Write_6c_1LD : SchedWriteRes<[UnitLD]> {
def Write_6c_1LD_2S : SchedWriteRes<[UnitLD, UnitS]> 则使用Write_6c_1LD_2S的地方可以使用def : InstRW<[Write_6c_1LD, WriteAdr]类似的组合替代

42、 commit 4a5b5bf展示对shift不同的常数定义不同Latency的方法,借助IsCheapLSL

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhongyunde/article/details/121652419

智能推荐

1、串口(UART/COM/TTL/RS232/RS485)-程序员宅基地

文章浏览阅读2.1w次,点赞30次,收藏344次。嵌入式MCU串口通信的基础知识_串口

Linux Commands O– Q_linux openvt-程序员宅基地

文章浏览阅读253次。有时间就学习一下jimmy师兄的笔记,收获很多,但是呢,年纪大了,脑子真的容易忘记事情,今天看了,没几台又给忘记了,分享的这些linux命令可以打印出来贴寝室墙上,每天回来都看看:Command DescriptionobjcopyCopy and translate object files.objdumpDisplay information from object files.o..._linux openvt

s-function简介和示例-程序员宅基地

文章浏览阅读776次。simulink模型。_s-function

http隧道 java_使用java语言实现http隧道技术-程序员宅基地

文章浏览阅读119次。该楼层疑似违规已被系统折叠隐藏此楼查看此楼/***Getaparametervalue**@paramkeyString*@paramdefString*@returnString*/publicStringgetParameter(Stringkey,Stringdef){returnisStandalone?System.getProperty(ke..._java http隧道

Keepalived高可用+邮件告警_keepalived sendmail-程序员宅基地

文章浏览阅读913次。IP主机名备注192.168.117.14keepalived-master主节点192.168.117.15keepalived-slaver备节点192.168.117.100VIP1.主备节点均安装keepalived# yum install -y keepalived httpd2.主备节点均修改keepalived日志存放路径..._keepalived sendmail

SPFILE 错误导致数据库无法启动(ORA-01565)_ora01565 ora27046-程序员宅基地

文章浏览阅读469次。--==========================================--SPFILE错误导致数据库无法启动(ORA-01565)--========================================== SPFILE错误导致数据库无法启动 SQL> startup ORA-01078: failurein proce_ora01565 ora27046

随便推点

ring0下的 fs:[124]_nsfs124-程序员宅基地

文章浏览阅读930次。 反汇编内核函数的时候经常会看到mov eax, fs:[124].一直没弄清楚fs寄存器在ring0存放的是什么。今天查了下资料。 fs寄存器在Ring0中指向一个称为KPCR的数据结构,即FS段的起点与KPCR结构对齐。而在Ring0中fs寄存器一般为0x30。 这样看KPCR的数据结构:nt!_KPCR +0x000 NtTib : ..._nsfs124

高德地图打包后不能使用,高德导航View不显示,高德地图导航组件黑屏的问题;_amap.amapwx打包成安卓后无法使用-程序员宅基地

文章浏览阅读6.2k次。在现在的APP中地图真的很常见,我们在去使用是一般都会选择一些三方的API,像百度、高德等等。。。在集成的过程中,难免会遇到一些问题,大部分问题我们仔细的阅读开发文档都能解决的;但是!!!问题出现后就没心情去细细的看文档了,总想着去搜索问题看有没有类似的;主要问题:打包后地图出现问题,绝大部分都是因为KEY的原因,我们使用正式签名文件中的SHA1去创建一个KEY替换到代码中的那个就行了;而我的问题..._amap.amapwx打包成安卓后无法使用

HTML5与微信开发(2)-视频播放事件及API属性_微信开发者工具视频快进-程序员宅基地

文章浏览阅读8.6k次,点赞2次,收藏2次。HTML5 的视频播放事件想必大家已经期待很久了吧,在HTML4.1、4.0之前我们如果在网页上播放视频无外乎两种方法: 第一种:安装FLASH插件或者微软发布的插件 第二种:在本地安装播放器,在线播放组件之类的 因为并不是所有的浏览器都安装了FLASH插件,就算安装也不一定所有的都能安装成功。像苹果系统就是默认禁用FLASH的,安卓虽然一开始的时候支持FLASH,但是在安卓4.0以后也开始不_微信开发者工具视频快进

JedisConnectionException Connection Reset_jedisconnectionexception: java.net.socketexception-程序员宅基地

文章浏览阅读5.4k次,点赞3次,收藏4次。在使用redis的过程常见错误总结1.JedisConnectionException Connection Reset参考这边文章:Connection reset原因分析和解决方案https://blog.csdn.net/cwclw/article/details/527971311.1问题描述Exception in thread "main" redis.clients...._jedisconnectionexception: java.net.socketexception: connection reset

Lua5.3版GC机制理解_lua5.3 gc-程序员宅基地

文章浏览阅读8.3k次,点赞8次,收藏42次。目录1.Lua垃圾回收算法原理简述2.Lua垃圾回收中的三种颜色3.Lua垃圾回收详细过程4.步骤源码详解4.1新建对象阶段4.2触发条件4.3 GC函数状态机4.4标记阶段4.5清除阶段5.总结参考资料lua垃圾回收(Garbage Collect)是lua中一个比较重要的部分。由于lua源码版本变迁,目前大多数有关这个方面的文章都还是基于lua5.1版本,有一定的滞后性。因此本文通过参考当前..._lua5.3 gc

手机能打开的表白代码_能远程打开,各种手机电脑进行监控操作,最新黑科技...-程序员宅基地

文章浏览阅读511次。最近家中的潮人,老妈闲着没事干,开始学玩电脑,引起他的各种好奇心。如看看新闻,上上微信或做做其他的事情。但意料之中的是电脑上会莫名出现各种问题?不翼而飞的图标?照片又不见了?文件被删了,卡机或者黑屏,无声音了,等等问题。常常让她束手无策,求助于我,可惜在电话中说不清,往往只能苦等我回家后才能解决,那种开心乐趣一下子消失了。想想,这样也不是办法啊, 于是,我潜心寻找了两款优秀的远程控制软件。两款软件...

推荐文章

热门文章

相关标签