<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>悲催的袜子</title>
<link>https://sadsock.github.io/</link>
<atom:link href="https://sadsock.github.io/index.xml" rel="self" type="application/rss+xml"/>
<description></description>
<generator>quarto-1.8.27</generator>
<lastBuildDate>Tue, 03 Mar 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Analyzing Modern NVIDIA GPU cores</title>
  <link>https://sadsock.github.io/posts/2026-03-03-modern-nvidia-gpu-core-analysis.html</link>
  <description><![CDATA[ 




<section id="sec-introduction" class="level1">
<h1>引言</h1>
<p>近年来，GPU 已不仅用于图形渲染，也被广泛用于通用计算负载<span class="citation" data-cites="usageOfGPUs">[1]</span>。GPU 架构提供的大规模并行性可被许多现代应用充分利用，例如生物信息学<span class="citation" data-cites="cudaInGPUS markovGPUBioinformatics">[2], [3]</span>、物理模拟<span class="citation" data-cites="molecularPhysicsCuda fdtdPhysicsCuda">[4], [5]</span>、化学计算<span class="citation" data-cites="detailedChemistry cheminformaticsGPU">[6], [7]</span>等。如今，GPU 是加速现代机器学习工作负载的核心平台，这些工作负载对内存带宽与计算能力的需求极高<span class="citation" data-cites="cuDNNLibraryIA">[8]</span>。近年来 GPU 微架构、互连技术（如 NVLink<span class="citation" data-cites="nvlink">[9]</span>）与通信框架（如 NCCL<span class="citation" data-cites="nccl">[10]</span>）持续演进，这些进步使得需要数千块 GPU 的大语言模型训练与推理成为可能<span class="citation" data-cites="articuloWebMSNumGPUsIA">[11]</span>。</p>
<p>然而，现代商业 GPU 的微架构细节信息十分稀缺，学术研究中常用的模型仍以 2006 年发布的 Tesla 架构为基线<span class="citation" data-cites="gpgpusim3 accelsim teslaHotchips">[12], [13], [14]</span>。现代 GPU 相比 Tesla 已发生了显著变化，基于旧架构的模型可能导致结论偏差。本文旨在揭示现代 NVIDIA GPU 架构中的关键特性与组件细节，以提升学术界 GPU 微架构模型的准确性，从而帮助研究者更好地识别未来 GPU 的挑战与机会。本文的主要贡献如下：</p>
<ul>
<li>描述发射阶段的运行机制，包括依赖处理、warp 就绪条件以及发射调度策略。</li>
<li>描述一种可信的取指阶段与其调度器，并说明其与发射阶段的协调方式。</li>
<li>给出寄存器文件的重要细节并解释寄存器文件缓存的行为，表明现代 NVIDIA GPU 不使用 operand collection 阶段或 collector units。</li>
<li>揭示内存流水线的多项细节。</li>
<li>重新设计 Accel-Sim 模拟器中的 SM/Core 模型，并整合本文揭示的全部细节。</li>
<li>将新模型与真实硬件进行验证并与 Accel-Sim 对比。对 NVIDIA RTX A6000（Ampere）而言，我们的新模型在执行周期上的 MAPE 为 13.98%，比旧模型提升 18.24%。</li>
<li>证明基于流缓冲的朴素指令预取在性能精度上优于预期，效果接近“完美指令缓存”。</li>
<li>分析寄存器文件缓存和寄存器文件读端口数量对仿真精度与性能的影响。</li>
<li>比较本文揭示的依赖管理机制与传统记分牌方法在性能、面积与仿真精度上的差异，表明这种软硬协同设计更高效。</li>
<li>证明模型对 NVIDIA 其他架构（如 Turing）的可移植性。</li>
</ul>
<p>本文其余部分结构如下：第 小节&nbsp;2 节介绍背景与动机；第 小节&nbsp;3 节说明逆向方法；第 小节&nbsp;4 节描述现代 NVIDIA GPU 的控制位（control bits）及其行为；第 小节&nbsp;5 节呈现 GPU 核心微架构；第 小节&nbsp;6 节描述仿真模型；第 小节&nbsp;7 节评估模型准确性并分析指令预取、寄存器文件缓存与依赖管理机制等因素；第 小节&nbsp;8 节回顾相关工作；最后第 小节&nbsp;9 节总结全文。</p>
</section>
<section id="sec-backgroundmotivation" class="level1">
<h1>背景与动机</h1>
<p>学术界对 GPU 微架构的研究多依赖 GPGPU-Sim 模拟器所采用的模型<span class="citation" data-cites="gpgpusim3 gpgpuBook">[12], [15]</span>。该模拟器近年来更新为包含 Volta 起引入的 sub-core（NVIDIA 术语为 Processing Blocks）方案。图&nbsp;1 展示了该模型的架构示意图，可以看到其包含四个 sub-core 以及若干共享组件，如 L1 指令缓存、L1 数据缓存、共享内存与纹理单元。</p>
<div id="fig-academia" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-academia-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://sadsock.github.io/assets/modern-nvidia-gpu/Rev_literature.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-academia-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
图&nbsp;1: 学术界常用的 SM/Core 设计示意。
</figcaption>
</figure>
</div>
<p>在该 GPU 流水线的取指阶段，轮转调度器会选择一个 warp，其下一条指令位于 L1 指令缓存且其指令缓冲区有空位。每个 warp 维护一个独占的指令缓冲区，用于存放取指与译码后的连续指令，直到这些指令就绪并被发射。</p>
<p>在发射阶段，采用 GTO（Greedy Then Oldest）调度器<span class="citation" data-cites="GTO">[16]</span>，只要 warp 不在等待 barrier 且其最老指令与流水线中的更早指令无数据依赖，就会被发射。以往工作通常假设每个 warp 有两个记分牌用于依赖检测：第一个记录寄存器待写入以追踪 WAW 与 RAW，只有当所有源操作数在该记分牌中清零时指令才可发射；第二个记分牌统计寄存器的在途消费者数量，用于防止 WAR 冒险<span class="citation" data-cites="warHazards">[17]</span>。第二个记分牌的必要性在于：虽然指令按序发射，但由于可变延迟指令（如内存指令）可能在发射后排队，其源操作数的读取可能晚于更年轻指令的写回，导致 WAR 冒险。</p>
<p>指令发射后进入 Collector Unit（CU），等待源寄存器操作数就绪。每个 sub-core 的私有寄存器文件由多个 bank 组成，每个 bank 有若干端口以在单周期内支持多次访问；仲裁器负责处理同一 bank 的并发访问冲突。当指令的所有源操作数都在 CU 中就绪后，进入 Dispatch 阶段并派发到对应的执行单元（如内存、单精度、特殊函数单元）。不同单元的延迟各不相同，写回阶段将结果写入寄存器文件。</p>
<p>Accel-Sim 所建模的 GPU 架构类似基于 Tesla 的 NVIDIA GPU<span class="citation" data-cites="teslaHotchips">[14]</span>，该架构发布于 2006 年，并仅做了少量现代化更新（例如 sub-core 模型与类似 Volta 的分区缓存与 IPOLY 索引<span class="citation" data-cites="accelsim IPOLYPaper">[13], [18]</span>）。但该模型缺少现代 NVIDIA GPU 中存在的关键组件，例如 L0 指令缓存<span class="citation" data-cites="voltaPaper turingPaper amperePaper hopperPaper adaPaper voltaHotChips turingHotChips">[19], [20], [21], [22], [23], [24], [25]</span>与统一寄存器文件<span class="citation" data-cites="turingHotChips">[25]</span>。此外，sub-core 中的多个关键组件（如发射逻辑、寄存器文件、寄存器文件缓存）并未更新以反映当前设计。</p>
<p>本文旨在逆向现代 NVIDIA GPU 的核心微架构，并更新 Accel-Sim 以纳入这些新特性，从而让该模拟器的基线更贴近工业界已验证的商业设计。</p>
</section>
<section id="sec-researchmethodology" class="level1">
<h1>逆向方法</h1>
<p>本节阐述我们用于发现 NVIDIA Ampere GPU 核心（SM）微架构的研究方法。</p>
<p>我们的做法是编写由少量指令组成的 microbenchmarks，通过测量一段特定指令序列的执行时间来推断微架构特性。具体做法是：在代码段前后插入读取 GPU <code>CLOCK</code> 计数器的指令，将计数器保存到寄存器并写入主存，便于后续分析。被测指令序列通常由手写 SASS 指令及其控制位构成。根据测试需求，我们对记录的周期进行可视化，以验证或否定某一控制位语义或微架构特性。以下是两个示例：</p>
<ul>
<li>使用下面代码揭示多 bank 寄存器文件的读冲突（见 小节&nbsp;5.3）。若将 <code>R_X</code> 与 <code>R_Y</code> 都设为奇数（例如 <code>R19</code> 和 <code>R21</code>），耗时为 5 个周期（每 sub-core 每周期可发射一条指令的理论最小值）。若将 <code>R_X</code> 改为偶数（如 <code>R18</code>）而 <code>R_Y</code> 仍为奇数（如 <code>R21</code>），耗时变为 6 个周期；若两个操作数均为偶数（如 <code>R18</code> 与 <code>R20</code>），耗时为 7 个周期。也就是说，两条连续指令之间可能出现 0 到 2 个气泡（bubble），取决于寄存器选择。</li>
<li>图&nbsp;4 展示了多次记录的时间标记在图形上的体现，用于揭示 warp 发射策略（详见 小节&nbsp;5.1.2）。</li>
</ul>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 用于检查寄存器文件读冲突的代码</span></span>
<span id="cb1-2">CLOCK</span>
<span id="cb1-3">NOP</span>
<span id="cb1-4">FFMA R11, R10, R12, R14</span>
<span id="cb1-5">FFMA R13, R16, R_X, R_Y</span>
<span id="cb1-6">NOP</span>
<span id="cb1-7">CLOCK</span></code></pre></div></div>
<p>尽管 NVIDIA 没有官方工具可直接编写 SASS（NVIDIA 汇编语言），但已有多种第三方工具支持重排与修改汇编指令（包括控制位），常用于优化关键内核。MaxAS<span class="citation" data-cites="maxas">[26]</span> 是最早的 SASS 修改工具；随后 KeplerAS<span class="citation" data-cites="KeplerASRepo KeplerASPaper">[27], [28]</span> 针对 Kepler 架构出现；之后 TuringAS<span class="citation" data-cites="turingas">[29]</span> 与 CUAssembler<span class="citation" data-cites="CuAssembler">[30]</span> 支持更新架构。我们选择 CUAssembler，因为它具有更高的灵活性、可扩展性，并支持最新硬件。</p>
</section>
<section id="sec-moderngpuarch" class="level1">
<h1>现代 NVIDIA GPU 架构中的控制位</h1>
<p>现代 NVIDIA GPU 的 ISA 包含控制位，编译器借此保证正确性并优化性能。与以往在运行时追踪寄存器读写以处理数据依赖的架构不同（见 小节&nbsp;2），现代 NVIDIA GPU 依赖编译器管理寄存器数据依赖<span class="citation" data-cites="gtx680WhitePaper">[31]</span>。因此，每条汇编指令都携带控制位用于依赖管理、性能与能耗优化。</p>
<p>下文描述每条指令中的控制位行为。我们的解释参考了多份文档<span class="citation" data-cites="dissectingVolta dissectingTuring CuAssembler maxas">[26], [30], [32], [33]</span>，但这些文档常不完整或存在歧义，因此我们结合 小节&nbsp;3 的方法来验证这些控制位的语义。</p>
<p>sub-core 每周期最多发射一条指令。默认情况下，发射调度器倾向于从同一 warp 发射指令，只要该 warp 中程序顺序最老的指令已就绪。编译器通过控制位标记指令何时可发射；若上一周期被选中的 warp 的最老指令未就绪，则发射逻辑按照 小节&nbsp;5.1 描述的策略从其他 warp 中选择。</p>
<p>对于固定延迟指令的生产者—消费者依赖，每个 warp 有一个称为“停顿计数器（Stall counter）”的计数器。若该计数器非零，则该 warp 不可发射。编译器将“生产者指令的延迟 - 生产者与首个消费者之间的指令数”写入停顿计数器。所有 warp 的停顿计数器每周期减 1，直到为 0；发射逻辑仅需检查该计数器即可判断是否可从同一 warp 继续发射。</p>
<p>例如，一条延迟为 4 周期的加法指令，且其首个消费者是下一条指令，则停顿计数器被设为 4。我们验证了如果停顿计数器设置不正确，程序结果会出错，因为硬件不检查 RAW 冒险，而完全依赖编译器设置的计数器。该机制也节省面积与布线能耗，因为固定延迟单元到依赖处理逻辑的连线不再需要，而传统记分牌方案则需要这些连线。</p>
<p>另一个控制位是“Yield”，用于告知硬件下一周期不要从同一 warp 发射指令。如果其他 warp 也未就绪，则该周期不会发射任何指令。</p>
<p>每条指令都会设置停顿计数器与 Yield 位。当停顿计数器大于 1 时，warp 至少停顿一个周期，此时无论 Yield 是否置位均无影响。</p>
<p>另一方面，部分指令（如内存或特殊函数）具有可变延迟，编译器无法预知其执行时间，因此不能仅靠停顿计数器处理。这类冒险通过“依赖计数器（Dependence counters）”解决。每个 warp 有 6 个专用寄存器（<code>SB0</code> 到 <code>SB5</code>），每个计数器可计数至 63。</p>
<p>这些计数器在 warp 启动时置零。对于生产者—消费者依赖，生产者在发射后增加某计数器，并在写回时减小；消费者指令会等待该计数器归零后才能发射。</p>
<p>对于 WAR 冒险，机制类似，但计数器在读取源操作数后减小，而非在写回时减小。</p>
<p>每条指令的控制位可以指明最多两个在发射时递增的计数器，其中一个在写回时递减（用于 RAW/WAW），另一个在寄存器读取时递减（用于 WAR）。为此，每条指令有两个 3 位字段用于指示这两个计数器，并有一个 6 位掩码，用于指定该指令在发射前需要检查哪些依赖计数器（最多可检查 6 个）。</p>
<p>如果一条指令有多个源操作数且其生产者均为可变延迟指令，那么这些生产者可共用同一个依赖计数器而不降低并行性。但当存在超过 6 个不同可变延迟生产者时，会受到并行度限制，编译器需在“合并计数器”与“重排指令”之间权衡。</p>
<p>依赖计数器的自增发生在生产者发射后的下一周期，因此不会在当周期生效。如果消费者紧随其后，则生产者需要将停顿计数器设为 2，以避免下一周期错误发射消费者指令。</p>
<div id="fig-control-bits-example" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-control-bits-example-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://sadsock.github.io/assets/modern-nvidia-gpu/Rev_ControlBitsExample.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-control-bits-example-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
图&nbsp;2: 使用依赖计数器处理依赖的示例。
</figcaption>
</figure>
</div>
<p>如 图&nbsp;2 所示，该代码包含四条指令（3 条 load 与 1 条 add）及其编码。由于 add 依赖于可变延迟的 load，需依赖计数器避免数据冒险。PC 为 <code>0x80</code> 的指令对 <code>0x50</code> 与 <code>0x60</code> 存在 RAW 依赖，因此 <code>SB3</code> 在 <code>0x50</code> 与 <code>0x60</code> 发射时增加，在写回时减少。另一方面，add 对 <code>0x60</code> 与 <code>0x70</code> 有 WAR 依赖，于是 <code>SB0</code> 在 <code>0x60</code> 与 <code>0x70</code> 发射时增加，在读取源操作数后减少。add 的依赖计数器掩码要求 <code>SB0</code> 与 <code>SB3</code> 在发射前均为 0。注意 <code>0x70</code> 还使用 <code>SB4</code> 控制与后续指令的 RAW/WAR 冒险，但 <code>0x80</code> 与该 load 无依赖，因此不等待 <code>SB4</code>。在读取源操作数后清除 WAR 依赖是重要的优化，因为源操作数往往在结果写回之前很早就已读取，尤其在内存指令中更为明显。</p>
<p>另一种检查计数器就绪性的方式是使用 <code>DEPBAR.LE</code> 指令。例如，<code>DEPBAR.LE SB1, 0x3, {4,3,2}</code> 要求依赖计数器 <code>SB1</code> 的值小于等于 3 才能继续执行；最后一个参数是可选项，如果使用，则该指令还要求指定的依赖计数器（例中为 4、3、2）全部为 0 才能发射。</p>
<p><code>DEPBAR.LE</code> 在某些场景非常有用。例如，当有一串 <code>N</code> 条可变延迟指令按序写回（如带 <code>STRONG.SM</code> 的内存指令），而消费者只需等待前 <code>M</code> 条指令完成时，可设置 <code>DEPBAR.LE</code> 的参数为 <code>N-M</code> 来等待前 <code>M</code> 条。另一个例子是复用同一依赖计数器来同时保护 RAW/WAW 与 WAR：由于 WAR 先于 RAW/WAW 解决，后续的 <code>DEPBAR.LE SBx, 0x1</code> 可等待 WAR 解除并继续执行，而真正的消费者则等待计数器归零以确保结果写回。</p>
<p>此外，GPU 还使用寄存器文件缓存（RFC）以节能并减少寄存器文件读端口竞争。该结构由软件管理：每个源操作数都有一个 <code>reuse</code> 控制位，指示硬件是否缓存该寄存器内容。RFC 的组织细节见 小节&nbsp;5.3.1。</p>
<p>最后，虽然本文聚焦 NVIDIA 架构，但 AMD GPU ISA 的公开文档<span class="citation" data-cites="amdCDNA1 amdCDNA2 amdCDNA3 amdRDNA1 amdRDNA2 amdRDNA35 amdRDNA3 amdVega7nm amdGCN3 amdVega">[34], [35], [36], [37], [38], [39], [40], [41], [42], [43]</span> 显示 AMD 同样依赖软硬协同机制管理依赖、提升性能。类似 NVIDIA 的 <code>DEPBAR.LE</code>，AMD 使用 <code>waitcnt</code> 指令；根据架构，每个 wavefront（warp）有 3 或 4 个计数器，且每个计数器绑定特定指令类型并必须使用以避免相关冒险。AMD 不允许普通指令通过控制位等待计数器归零，必须显式插入 <code>waitcnt</code>，这增加了指令数量。该设计降低了解码开销，但提高了指令数。相比之下，NVIDIA 提供更多计数器且不与指令类型绑定，因此可在同一指令类型内部保持更多并发依赖链。虽然 AMD 的 ALU 指令不需要软件或编译器干预即可避免冒险，但在 RDNA 3/3.5 中引入了 <code>DELAY_ALU</code> 以缓解依赖导致的流水线停顿<span class="citation" data-cites="amdRDNA35 amdRDNA3">[39], [40]</span>。而 NVIDIA 则依赖编译器为固定延迟指令正确设置停顿计数器，以较少的指令数换取更高的解码开销。</p>
</section>
<section id="sec-design" class="level1">
<h1>GPU 核心微架构</h1>
<p>本节基于 小节&nbsp;3 所述的方法，介绍我们对现代 NVIDIA 商用 GPU 核心微架构的发现。图&nbsp;3 展示了 GPU 核心的关键组件。下文分别展开介绍发射调度器、前端、寄存器文件以及内存流水线。</p>
<div id="fig-ourcore" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-ourcore-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://sadsock.github.io/assets/modern-nvidia-gpu/Rev_ourCore.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-ourcore-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
图&nbsp;3: 现代 NVIDIA GPU 的 SM/Core 设计。
</figcaption>
</figure>
</div>
<section id="sec-warpscheduler" class="level2">
<h2 class="anchored" data-anchor-id="sec-warpscheduler">发射调度器</h2>
<p>本小节解析现代 NVIDIA GPU 的发射调度器。我们先在 小节&nbsp;5.1.1 中描述每周期哪些 warp 会被视为“可发射”，再在 小节&nbsp;5.1.2 中说明选择策略。</p>
<div id="fig-issue-scheduling-timeline" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-issue-scheduling-timeline-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://sadsock.github.io/assets/modern-nvidia-gpu/Rev_issueTimeline.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-issue-scheduling-timeline-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
图&nbsp;4: 四个 warp 发射时间线示例。
</figcaption>
</figure>
</div>
<section id="sec-readinesschecking" class="level3">
<h3 class="anchored" data-anchor-id="sec-readinesschecking">Warp 就绪条件</h3>
<p>若一个 warp 在某周期要被视为其最老指令的候选发射者，需要满足若干条件。这些条件既依赖该 warp 的历史指令状态，也依赖核心的全局状态。</p>
<p>显然，需要指令缓冲区中存在有效指令。另一个条件是该 warp 的最老指令不能与同一 warp 中尚未完成的更老指令存在数据依赖。依赖由控制位进行软件处理，如 小节&nbsp;4 所述。</p>
<p>此外，对于固定延迟指令，只有在能够保证其发射后执行所需资源可用时，该 warp 才能在该周期发射最老指令。</p>
<p>其中一类资源是执行单元。执行单元存在输入锁存器，指令到达执行阶段时该锁存器必须为空。当执行单元宽度为半个 warp 时，锁存器会被占用两个周期；宽度为一个 warp 时则占用一个周期。</p>
<p>对于源操作数位于常量缓存（Constant Cache）的指令，标签查找在发射阶段进行。若所选 warp 的最老指令需要常量缓存而发生 miss，则调度器不会发射任何指令直到 miss 被服务；如果 4 个周期后 miss 仍未完成，则调度器切换到另一条指令就绪的 warp（选择最年轻的）。</p>
<p>关于寄存器文件读端口的可用性，我们观察到发射调度器并不会考虑“未来若干周期是否会发生端口冲突”。我们的实验表明，即使移除最后一个 <code>FFMA</code> 与 <code>CLOCK</code> 之间的 <code>NOP</code>，寄存器读冲突也不会阻止第二个 <code>CLOCK</code> 的发射。我们进行了大量实验以推断从发射到执行之间的流水线结构，虽然无法完全拟合所有案例，但以下模型对绝大多数实验有效，因此采用该模型：固定延迟指令在发射阶段与读操作数阶段之间有两个中间阶段。第一个阶段称为 Control，适用于固定和可变延迟指令，用于递增依赖计数器或读取时钟计数器等。由此可以解释：如果一条指令递增依赖计数器，紧随其后的指令又需要等待该计数器归零，则两者之间至少需要插入一个周期，否则递增在下一周期才生效，因此连续指令无法依赖计数器直接避免冒险，除非前一条指令设置了 Yield 或设置了大于 1 的停顿计数器。</p>
<p>第二个阶段仅用于固定延迟指令，我们称之为 Allocate。在该阶段检查寄存器文件读端口是否可用，若无法保证在后续周期无冲突读取操作数，则在该阶段停顿，直到可以保证读取。寄存器文件读写流水线及其缓存细节见 小节&nbsp;5.3。</p>
<p>可变延迟指令（如内存指令）在通过 Control 阶段后直接进入队列，不经过 Allocate。队列中的指令只有在确认不会发生读端口冲突时才进入寄存器文件读流水线。固定延迟指令优先于可变延迟指令获得读端口分配，因为固定延迟指令必须在固定周期内完成以保证依赖正确性。</p>
</section>
<section id="sec-schedulingpolicy" class="level3">
<h3 class="anchored" data-anchor-id="sec-schedulingpolicy">调度策略</h3>
<p>为揭示发射调度策略，我们设计了多种涉及多个 warp 的测试，记录每周期调度器选择哪个 warp 发射。该信息通过保存 GPU <code>CLOCK</code> 计数器的指令获取，但硬件不允许连续发射两条此类指令，因此我们在中间插入固定数量的其他指令（通常为 <code>NOP</code>），并变化 <code>Yield</code> 与停顿计数器控制位的取值。</p>
<p>实验结果表明，warp 调度器采用贪心策略：若同一 warp 满足就绪条件，则继续从该 warp 发射；当需要切换 warp 时，选择满足就绪条件的“最年轻”warp。</p>
<p>图&nbsp;4 展示了四个 warp 在同一 sub-core 中执行时的发射时间线。每个 warp 执行相同的代码，包含 32 条彼此独立的指令，理论上每周期可发射 1 条。</p>
<p>在情况 (a) 中，所有停顿计数器、依赖掩码与 Yield 均为 0。调度器从最年轻的 warp（W3）开始发射，直到其 ICache miss；此时 W3 没有有效指令，调度器切换到 W2。由于 W2 复用了 W3 带入的指令，因此其 ICache 命中；当 W2 执行到 W3 miss 的位置时，miss 已被服务，后续指令命中，于是调度器继续贪心地完成 W2。随后调度器继续从 W3（最年轻）发射至结束，再转到 W1、W0。</p>
<p>情况 (b) 中，每个 warp 的第二条指令将停顿计数器设为 4。调度器在 W3 发射两条后切换至 W2，再两条后切换至 W1，然后回到 W3（其停顿计数器已归零）。当 W3、W2、W1 结束后，调度器开始发射 W0。W0 发射第二条指令后，由于没有其他 warp 可隐藏停顿，产生 4 个气泡。</p>
<p>情况 (c) 中，每个 warp 的第二条指令设置 Yield。调度器在发射第二条指令后切换到其他 warp 中最年轻的一个，例如 W3 切换到 W2，W2 再切回 W3。我们还测试了“Yield 置位且无其他 warp 可选”的情况（图中未示），此时调度器产生 1 个周期的气泡。</p>
<p>我们将该策略称为“编译器引导的贪心后选最年轻（CGGTY）”，因为调度器由控制位（停顿计数器、Yield 与依赖计数器）引导。</p>
<p>需要说明的是，我们仅验证了同一 CTA 内 warp 的行为；尚未有可靠方法分析不同 CTA 之间 warp 的交互。</p>
</section>
</section>
<section id="sec-frontend" class="level2">
<h2 class="anchored" data-anchor-id="sec-frontend">前端</h2>
<p>根据 NVIDIA 多份文档中的 SM 结构图<span class="citation" data-cites="voltaPaper turingPaper amperePaper hopperPaper adaPaper voltaHotChips turingHotChips">[19], [20], [21], [22], [23], [24], [25]</span>，SM 包含四个 sub-core，warp 以轮转方式分配到 sub-core（即 warp ID <code>mod 4</code>）<span class="citation" data-cites="dissectingVolta dissectingTuring">[32], [33]</span>。每个 sub-core 有私有 L0 指令缓存，并连接到全 SM 共享的 L1 指令缓存。我们假设存在仲裁器协调多个 sub-core 的请求。</p>
<p>每个 L0 ICache 都有指令预取器<span class="citation" data-cites="nvidiaInstPrefeching">[44]</span>。我们的实验印证了 Cao 等人关于 GPU 指令预取有效性的结论<span class="citation" data-cites="GPUinstPrefeching">[45]</span>。尽管我们无法确认 NVIDIA 的具体实现，但推测为类似流缓冲的简单方案<span class="citation" data-cites="streamBufferPrefetching">[46]</span>，即在 miss 发生时预取后续连续的缓存行。基于分析，我们假设流缓冲大小为 16（见 小节&nbsp;7.3）。</p>
<p>我们无法通过实验确认精确的取指策略，但若其与发射策略差异过大，将频繁出现“指令缓冲区无有效指令”的情况，而我们并未观察到这一现象。因此，我们假设每个 sub-core 每周期可取指并译码 1 条指令。取指调度器优先从上次（或最近一次）发射指令的 warp 取指，除非该 warp 的指令缓冲区中已存在的指令数加上在途取指数达到缓冲区容量。此时调度器切换到指令缓冲区有空位的最年轻 warp。我们假设每个 warp 有 3 个指令缓冲区条目，足以支撑“贪心发射”策略，因为从取指到发射有 2 个流水阶段。如果缓冲区仅有 2 项，贪心策略将被破坏：例如在所有请求 ICache 命中、所有 warp 缓冲区均满的情况下，若第 1 周期发射 W1 同时取指 W0，第 2 周期将发射 W1 的第 2 条并取指第 3 条；第 3 周期 W1 的第 3 条仍在译码中，缓冲区无有效指令，贪心策略失败，被迫切换 warp。3 项缓冲区可避免该问题，这与我们的实验一致。多数文献假设取指/译码宽度为 2 且缓冲区为 2 项，并且只有当缓冲区为空才取指，这会导致每两个连续指令后就切换 warp，与我们的观察不符。</p>
</section>
<section id="sec-regfile" class="level2">
<h2 class="anchored" data-anchor-id="sec-regfile">寄存器文件</h2>
<p>我们通过大量实验（不同 SASS 指令组合、不同寄存器文件端口压力、是否使用寄存器文件缓存）来揭示寄存器文件组织。</p>
<p>现代 NVIDIA GPU 具有多种寄存器文件：</p>
<ul>
<li><strong>常规寄存器（Regular）</strong>：最近架构每个 SM 有 65536 个 32 位寄存器<span class="citation" data-cites="voltaPaper turingPaper amperePaper hopperPaper adaPaper">[19], [20], [21], [22], [23]</span>，用于保存线程计算值。寄存器按 32 个为一组，对应 warp 中 32 个线程，形成 2048 个“warp 寄存器”。寄存器在 sub-core 间均匀分布，每个 sub-core 的寄存器文件分成两个 bank<span class="citation" data-cites="dissectingVolta dissectingTuring">[32], [33]</span>。每个 warp 可使用 1 至 256 个寄存器，编译时确定。每 warp 使用寄存器越多，SM 并行 warp 数越少。</li>
<li><strong>统一寄存器（Uniform）</strong>：每个 warp 有 64 个私有 32 位寄存器，用于保存该 warp 所有线程共享的值<span class="citation" data-cites="dissectingTuring">[33]</span>。</li>
<li><strong>谓词寄存器（Predicate）</strong>：每个 warp 有 8 个 32 位寄存器，每个 bit 对应 warp 中一个线程，用于指示是否执行指令及分支走向。</li>
<li><strong>统一谓词寄存器（Uniform Predicate）</strong>：每个 warp 有 8 个 1-bit 寄存器，用于全 warp 共享的谓词。</li>
<li><strong>SB 寄存器</strong>：每个 warp 有 6 个“依赖计数器”寄存器，用于追踪可变延迟依赖（见 小节&nbsp;4）。</li>
<li><strong>B 寄存器</strong>：每个 warp 至少有 16 个 <code>B</code> 寄存器用于控制流重汇合（re-convergence）<span class="citation" data-cites="mojtabaControlFlow">[47]</span>。</li>
<li><strong>特殊寄存器</strong>：用于存放线程 ID、block ID 等特殊值。</li>
</ul>
<p>与以往假设存在 operand collector 以处理寄存器端口冲突的工作不同<span class="citation" data-cites="GPU2OcusSubcore accelsim gpgpuBook">[13], [15], [48]</span>，现代 NVIDIA GPU 并未使用 operand collector。若使用 collector，会引入发射到写回的可变延迟，使得 NVIDIA ISA 的固定延迟无法在编译期确定，无法正确依赖管理（见 小节&nbsp;4）。我们通过多组生产者—消费者指令序列的实验验证了 collector 的不存在：无论寄存器端口冲突数量如何，停顿计数器所需值与指令执行时间均保持不变。</p>
<p>实验显示，每个寄存器文件 bank 有一个 1024-bit 专用写端口。此外，当一条 load 指令与一条固定延迟指令在同周期完成时，被延迟的是 load 指令；而当两个固定延迟指令（如 <code>IADD3</code> 与 <code>IMAD</code>）写同一 bank 时，两者都不延迟。这表明固定延迟指令使用了类似 Fermi 中引入的结果队列<span class="citation" data-cites="fermiWhitePaper">[49]</span>。其消费者不被延迟，说明存在旁路（bypass）以在写回前转发结果。</p>
<p>在读方面，我们观察到每 bank 1024-bit 带宽。通过连续 <code>FADD</code>、<code>FMUL</code>、<code>FFMA</code> 指令测得这一结果。<sup>1</sup></p>
<p>例如，当 <code>FMUL</code> 的两个源操作数来自同一 bank 时会产生 1 个周期气泡；若来自不同 bank 则无气泡。<code>FFMA</code> 的三个源操作数都在同一 bank 时会产生 2 个周期气泡。</p>
<p>我们未能找到一个完全匹配所有案例的读端口仲裁策略，因为气泡生成还依赖指令类型与操作数角色。最接近实验结果的模型为：固定延迟指令在发射与读操作数之间有两个中间阶段 Control 与 Allocate（见 小节&nbsp;5.1.1）。Allocate 负责保留寄存器文件读端口；每 bank 有一个 1024-bit 读端口，并通过寄存器文件缓存缓解读冲突。实验显示所有固定延迟指令读取操作数会持续 3 个周期，即便某些周期空闲（例如仅有两个源操作数），因为 <code>FADD</code> 与 <code>FMUL</code> 的延迟与 <code>FFMA</code> 相同，而 <code>FFMA</code> 不论三操作数是否在同一 bank 延迟都相同。若指令在 Allocate 阶段发现无法在后续三周期内读取完所有操作数，则其被阻塞在该阶段并向上游施加气泡，直到能够预留所需端口。</p>
<section id="sec-reg-file-cache" class="level3">
<h3 class="anchored" data-anchor-id="sec-reg-file-cache">寄存器文件缓存</h3>
<p>寄存器文件缓存（RFC）可缓解寄存器文件端口争用并节能，这一思路已被多项研究探讨<span class="citation" data-cites="registerFileCacheFirst registerFileCacheSimilar BOW ltrf mojtabaIsca">[50], [51], [52], [53], [54]</span>。</p>
<p>我们的实验表明 NVIDIA 的设计与 Gebhart 等人的方案类似<span class="citation" data-cites="registerFileCacheSimilar">[51]</span>。RFC 由编译器控制，仅用于常规寄存器操作数。关于 Last Result File 结构，我们称之为结果队列，其行为类似，但并未采用两级发射调度器（见 小节&nbsp;5.1.2）。</p>
<p>RFC 组织结构如下：每个 sub-core 的两个寄存器 bank 各有一个入口，每个入口存放三个 1024-bit 值，对应三类常规寄存器源操作数。总容量为 6 个 1024-bit 操作数值（子条目）。某些指令的一个操作数可能需要连续两个寄存器（如张量核心指令），此时两个寄存器分别来自不同 bank，并缓存到对应入口。</p>
<p>编译器控制分配策略。当指令发射并读取操作数时，若该操作数设置了 <code>reuse</code>，其值将被写入 RFC。若后续指令来自同一 warp，且寄存器 ID 与 RFC 中一致、并且该操作数在指令中的位置一致，则可从 RFC 命中。若有新的读请求到达同一 bank 和同一操作数位置，则该缓存值将失效（无论命中与否）。这一行为在下面示例 2 中展示：为了让第三条指令命中 <code>R2</code>，第二条指令必须再次为 <code>R2</code> 设置 reuse，即便 <code>R2</code> 已经缓存。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 寄存器文件缓存行为示例</span></span>
<span id="cb2-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example 1</span></span>
<span id="cb2-3">IADD3 R1, R2.reuse, R3, R4 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Allocates R2</span></span>
<span id="cb2-4">FFMA R5, R2, R7, R8 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 hits and becomes unavailable</span></span>
<span id="cb2-5">IADD3 R10, R2, R12, R13 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 misses</span></span>
<span id="cb2-6"></span>
<span id="cb2-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example 2</span></span>
<span id="cb2-8">IADD3 R1, R2.reuse, R3, R4 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Allocates R2</span></span>
<span id="cb2-9">FFMA R5, R2.reuse, R7, R8 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 hits and is retained</span></span>
<span id="cb2-10">IADD3 R10, R2, R12, R13 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 hits</span></span>
<span id="cb2-11"></span>
<span id="cb2-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example 3. R2 misses in the second instruction since it is</span></span>
<span id="cb2-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># cached in another slot. R2 remains available in the first</span></span>
<span id="cb2-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># slot since R7 uses a different bank</span></span>
<span id="cb2-15">IADD3 R1, R2.reuse, R3, R4 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Allocates R2</span></span>
<span id="cb2-16">FFMA R5, R7, R2, R8 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 misses</span></span>
<span id="cb2-17">IADD3 R10, R2, R12, R13 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 hits</span></span>
<span id="cb2-18"></span>
<span id="cb2-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Example 4. R2 misses in the third instruction since the</span></span>
<span id="cb2-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># second instruction uses a different register that goes to</span></span>
<span id="cb2-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># the same bank in the same operand slot</span></span>
<span id="cb2-22">IADD3 R1, R2.reuse, R3, R4 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Allocates R2</span></span>
<span id="cb2-23">FFMA R5, R4, R7, R8 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R4 misses and R2 becomes unavailable</span></span>
<span id="cb2-24">IADD3 R10, R2, R12, R13 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># R2 misses</span></span></code></pre></div></div>
</section>
</section>
<section id="sec-memory-pipeline" class="level2">
<h2 class="anchored" data-anchor-id="sec-memory-pipeline">内存流水线</h2>
<p>现代 NVIDIA GPU 的内存流水线包含每个 sub-core 本地的初始阶段，而实际访存的后续阶段由四个 sub-core 共享，因为数据缓存与共享内存为 SM 共享结构<span class="citation" data-cites="voltaHotChips turingHotChips">[24], [25]</span>。本节揭示每个 sub-core 的 load/store 队列大小、sub-core 向共享结构发送请求的速率，以及不同内存指令的延迟。</p>
<p>注意内存访问分为两大类：共享内存访问（SM 内部、线程块共享）与全局内存访问（GPU 主存）。</p>
<p>为了确定队列大小与内存带宽，我们进行了一系列实验，每个 sub-core 要么执行一个 warp，要么空闲。每个 warp 执行一串相互独立的 load/store 指令，这些指令始终命中数据缓存或共享内存，并使用常规寄存器。表&nbsp;1 展示实验结果。第一列为指令序号，其余四列为不同活跃 sub-core 数量下该指令的发射周期（每格按周期升序列出所有活跃 sub-core）。</p>
<div id="tbl-memoryconsecutive" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-memoryconsecutive-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;1: 每条内存指令的发射周期（每格为所有活跃 sub-core 按周期升序的列表）。
</figcaption>
<div aria-describedby="tbl-memoryconsecutive-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<colgroup>
<col style="width: 20%">
<col style="width: 20%">
<col style="width: 20%">
<col style="width: 20%">
<col style="width: 20%">
</colgroup>
<thead>
<tr class="header">
<th>指令编号</th>
<th>活跃 sub-core 数 = 1</th>
<th>活跃 sub-core 数 = 2</th>
<th>活跃 sub-core 数 = 3</th>
<th>活跃 sub-core 数 = 4</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>1</td>
<td>2</td>
<td>2/2</td>
<td>2/2/2</td>
<td>2/2/2/2</td>
</tr>
<tr class="even">
<td>2</td>
<td>3</td>
<td>3/3</td>
<td>3/3/3</td>
<td>3/3/3/3</td>
</tr>
<tr class="odd">
<td>3</td>
<td>4</td>
<td>4/4</td>
<td>4/4/4</td>
<td>4/4/4/4</td>
</tr>
<tr class="even">
<td>4</td>
<td>5</td>
<td>5/5</td>
<td>5/5/5</td>
<td>5/5/5/5</td>
</tr>
<tr class="odd">
<td>5</td>
<td>6</td>
<td>6/6</td>
<td>6/6/6</td>
<td>6/6/6/6</td>
</tr>
<tr class="even">
<td>6</td>
<td>13</td>
<td>13/15</td>
<td>13/15/17</td>
<td>13/15/17/19</td>
</tr>
<tr class="odd">
<td>7</td>
<td>17</td>
<td>17/19</td>
<td>19/21/23</td>
<td>21/23/25/27</td>
</tr>
<tr class="even">
<td>8</td>
<td>21</td>
<td>21/23</td>
<td>25/27/29</td>
<td>29/31/33/35</td>
</tr>
<tr class="odd">
<td><img src="https://latex.codecogs.com/png.latex?i%3E8"></td>
<td><img src="https://latex.codecogs.com/png.latex?(i-1)+4"></td>
<td><img src="https://latex.codecogs.com/png.latex?(i-1)+4"></td>
<td><img src="https://latex.codecogs.com/png.latex?(i-1)+6"></td>
<td><img src="https://latex.codecogs.com/png.latex?(i-1)+8"></td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>可以看到，在 Ampere 中每个 sub-core 可连续 5 条内存指令按每周期一条发射，第 6 条指令的发射会产生停顿，且停顿周期取决于活跃 sub-core 数量。这表明每个 sub-core 可无阻塞地缓冲 5 条指令，而全局共享结构可每两周期从任一 sub-core 接收一个内存请求。观察多 sub-core 情形可知：第 6 条及之后的指令在每个 sub-core 上以两周期间隔发射。</p>
<p>我们还可推断 sub-core 中的地址计算吞吐率为每 4 周期一条指令：当只有一个 sub-core 活跃时，第 6 条指令之后出现 4 周期间隔。两 sub-core 活跃时，由于共享结构吞吐率为每 2 周期一条，故每个 sub-core 可每 4 周期发射一条；更多 sub-core 活跃时共享结构成为瓶颈。例如四 sub-core 活跃时，每个 sub-core 只能每 8 周期发射一条，因为共享结构最大吞吐为每 2 周期一条。</p>
<p>关于每个 sub-core 内存队列大小，我们估计为 4，尽管每个 sub-core 可缓冲 5 条连续指令。指令进入单元时占用槽位，离开单元时释放槽位。</p>
<p>我们测量了不同内存指令在缓存命中且单线程执行时的两类延迟。第一类是从 load 发射到消费者或覆盖同一目的寄存器的指令可以发射的最早时间（RAW/WAW 延迟；store 不产生寄存器 RAW/WAW）。第二类是从 load/store 发射到能够写入其源寄存器的指令可发射的最早时间（WAR 延迟）。结果见 表&nbsp;2。</p>
<div id="tbl-mem-latencies" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-mem-latencies-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;2: 内存指令延迟（周期）。带 <code>*</code> 的值为近似值。
</figcaption>
<div aria-describedby="tbl-mem-latencies-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>指令</th>
<th>地址寄存器类型</th>
<th style="text-align: right;">WAR</th>
<th style="text-align: right;">RAW/WAW</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Load Global 32 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">29</td>
</tr>
<tr class="even">
<td>Load Global 64 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">31</td>
</tr>
<tr class="odd">
<td>Load Global 128 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">35</td>
</tr>
<tr class="even">
<td>Load Global 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">11</td>
<td style="text-align: right;">32</td>
</tr>
<tr class="odd">
<td>Load Global 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">11</td>
<td style="text-align: right;">34</td>
</tr>
<tr class="even">
<td>Load Global 128 bit</td>
<td>Regular</td>
<td style="text-align: right;">11</td>
<td style="text-align: right;">38</td>
</tr>
<tr class="odd">
<td>Store Global 32 bit</td>
<td>Uniform</td>
<td style="text-align: right;">10</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Global 64 bit</td>
<td>Uniform</td>
<td style="text-align: right;">12*</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Store Global 128 bit</td>
<td>Uniform</td>
<td style="text-align: right;">16*</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Global 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">14</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Store Global 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">16</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Global 128 bit</td>
<td>Regular</td>
<td style="text-align: right;">20</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Load Shared 32 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">23</td>
</tr>
<tr class="even">
<td>Load Shared 64 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">23</td>
</tr>
<tr class="odd">
<td>Load Shared 128 bit</td>
<td>Uniform</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">25</td>
</tr>
<tr class="even">
<td>Load Shared 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">24</td>
</tr>
<tr class="odd">
<td>Load Shared 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">24</td>
</tr>
<tr class="even">
<td>Load Shared 128 bit</td>
<td>Regular</td>
<td style="text-align: right;">9</td>
<td style="text-align: right;">26</td>
</tr>
<tr class="odd">
<td>Store Shared 32 bit</td>
<td>Uniform</td>
<td style="text-align: right;">10</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Shared 64 bit</td>
<td>Uniform</td>
<td style="text-align: right;">12</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Store Shared 128 bit</td>
<td>Uniform</td>
<td style="text-align: right;">16</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Shared 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">12</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Store Shared 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">14</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="even">
<td>Store Shared 128 bit</td>
<td>Regular</td>
<td style="text-align: right;">18</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>Load Constant 32 bit</td>
<td>Immediate</td>
<td style="text-align: right;">10</td>
<td style="text-align: right;">26</td>
</tr>
<tr class="even">
<td>Load Constant 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">29</td>
<td style="text-align: right;">29</td>
</tr>
<tr class="odd">
<td>Load Constant 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">29</td>
<td style="text-align: right;">29</td>
</tr>
<tr class="even">
<td><code>LDGSTS</code> 32 bit</td>
<td>Regular</td>
<td style="text-align: right;">13</td>
<td style="text-align: right;">39</td>
</tr>
<tr class="odd">
<td><code>LDGSTS</code> 64 bit</td>
<td>Regular</td>
<td style="text-align: right;">13</td>
<td style="text-align: right;">39</td>
</tr>
<tr class="even">
<td><code>LDGSTS</code> 128 bit</td>
<td>Regular</td>
<td style="text-align: right;">13</td>
<td style="text-align: right;">39</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>我们观察到：使用统一寄存器计算地址的全局内存访问比使用常规寄存器更快，因为前者所有线程共享同一地址，仅需计算一个地址，而后者每线程可能不同。</p>
<p>共享内存 load 的延迟低于全局内存。其 WAR 延迟对常规与统一寄存器相同，而 RAW/WAW 延迟在统一寄存器时少 1 个周期。这表明共享内存的地址计算在共享结构中完成，WAR 依赖在源寄存器读取后即可解除。</p>
<p>如预期，延迟也受访问粒度影响。对于 WAR 依赖，load 延迟不随数据大小变化，因为源操作数仅用于地址计算且大小固定；对于 store，写入数据也是源操作数，数据越大 WAR 延迟越高。RAW/WAW 依赖（仅适用于 load）随数据大小增大而增大，因为需要更多数据传输到寄存器文件。我们测得该传输带宽为每周期 512 bit。</p>
<p>此外，常量缓存的 WAR 延迟显著高于全局内存 load，而 RAW/WAW 延迟略低。我们尚无法给出合理解释。但我们发现，固定延迟指令访问常量空间使用不同的缓存层级：通过 <code>LDC</code> 预加载常量地址并等待完成后，再发射使用同一常量地址的固定延迟指令，会出现约 79 周期的 miss 延迟，而非命中。这表明固定延迟指令访问常量空间使用 L0 FL（固定延迟）常量缓存，而 <code>LDC</code> 指令使用 L0 VL（可变延迟）常量缓存。</p>
<p>最后，我们分析 <code>LDGSTS</code> 指令，该指令用于减少寄存器文件压力并提升数据传输效率<span class="citation" data-cites="patentLDGSTS">[55]</span>。它从全局内存加载数据并直接写入共享内存，绕过寄存器文件，节省指令与寄存器。其延迟与指令粒度无关。WAR 依赖在地址计算完成后解除；RAW/WAW 依赖在读阶段完成后解除。</p>
</section>
</section>
<section id="sec-modeling" class="level1">
<h1>建模</h1>
<p>我们重新设计 Accel-Sim 框架的 SM/core 模型<span class="citation" data-cites="accelsim">[13]</span>，通过修改流水线实现 小节&nbsp;4 与 小节&nbsp;5 解释的所有细节（见 图&nbsp;3）。主要新增组件如下：</p>
<p>首先，我们为每个 sub-core 添加了 L0 指令缓存与流缓冲预取器。L0 指令与常量缓存通过可参数化延迟连接到 L1 指令/常量缓存。缓存容量、层级与延迟依据既有测量与 Jia 等人关于 Ampere 的研究确定<span class="citation" data-cites="dissectingAmpere">[56]</span>。</p>
<p>我们修改了发射阶段以支持控制位、固定延迟指令对新 L0 常量缓存的标签查询，以及新的 CGGTY 发射调度器。我们加入了 Control 阶段（递增依赖计数器）与 Allocate 阶段（固定延迟指令检查寄存器文件与 RFC 冲突）。</p>
<p>针对内存指令，我们为每个 sub-core 建模了新的本地单元，并为 sub-core 共享部分建模了共享单元，其延迟与上一节一致。</p>
<p>此外，Abdelkhalik 等人表明张量核心指令延迟依赖其操作数数值类型与尺寸<span class="citation" data-cites="demystifyingAmpere">[57]</span>，因此我们在模型中为不同操作数类型与尺寸设置了对应延迟。</p>
<p>我们还建模了以下细节：在缺乏每 sub-core 双精度执行单元的架构中，双精度指令使用全 sub-core 共享的执行流水线；对使用多个寄存器的操作数，精确建模读写时序（此前模型常将其简化为一个寄存器）；修正此前工作中指令地址报告的部分不准确之处<span class="citation" data-cites="cams2023paper">[58]</span>。</p>
<p>除模拟器的 SM/core 模型更新外，我们还扩展了 tracer 工具：导出所有操作数类型的 ID（常规、统一、谓词、立即数等）；并增加获取控制位的能力（NVBit 不提供），通过 CUDA 二进制工具<span class="citation" data-cites="cudaBinaryUtils">[59]</span>在编译时提取 SASS。为此，我们修改应用编译流程，使其在编译期生成与微架构相关的代码而非 JIT。遗憾的是，少数内核（均来自 Deepbench）无法获取 SASS，导致控制位无法提取。对此我们采用“混合依赖模式”：无法获取控制位的内核使用传统记分牌，其他内核使用控制位。</p>
<p>我们还扩展工具以捕获通过描述符访问常量缓存或全局内存的指令。尽管该访问方式被认为在 Hopper 才引入<span class="citation" data-cites="cudaBinaryUtils">[59]</span>，我们发现 Ampere 已使用。描述符是编码内存访问的新方式，包含两个操作数：第一个为统一寄存器，用于编码指令语义；第二个编码地址。我们扩展 tracer 捕获地址，但未能追踪统一寄存器中编码的语义。</p>
<p>我们计划公开 Accel-Sim 框架中的所有模拟器与 tracer 改动。</p>
</section>
<section id="sec-validation" class="level1">
<h1>验证</h1>
<p>本节评估所提出 GPU 核心微架构的准确性。首先在 小节&nbsp;7.1 描述方法；随后在 小节&nbsp;7.2 验证设计；在 小节&nbsp;7.4 中研究寄存器文件缓存与读端口数量的影响；在 小节&nbsp;7.3 中分析指令预取器，在 小节&nbsp;7.5 中分析依赖检查机制；最后在 小节&nbsp;7.6 讨论模型向其他 NVIDIA 架构的迁移。</p>
<section id="sec-methodoloy" class="level2">
<h2 class="anchored" data-anchor-id="sec-methodoloy">方法</h2>
<p>我们通过将模拟器结果与真实 GPU 的硬件计数器指标进行对比来验证准确性。使用了四种 Ampere GPU<span class="citation" data-cites="amperePaper">[21]</span>（规格见 表&nbsp;4），CUDA 版本为 11.4，NVBit 为 1.5.5。我们也将模型与原始 Accel-Sim 进行对比，因为新模型基于其构建。</p>
<p>我们选取了来自 12 个基准套件的广泛基准。各套件、应用数量与输入集数量见 表&nbsp;3。总计 143 个基准，其中 83 个为不同应用，其余为同应用不同输入。</p>
<div id="tbl-bench-suites" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-bench-suites-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;3: 基准套件。
</figcaption>
<div aria-describedby="tbl-bench-suites-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>套件</th>
<th style="text-align: right;">应用数</th>
<th style="text-align: right;">输入集</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Cutlass<span class="citation" data-cites="cutlass">[60]</span></td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">17</td>
</tr>
<tr class="even">
<td>Deepbench<span class="citation" data-cites="deepbenchWeb">[61]</span></td>
<td style="text-align: right;">3</td>
<td style="text-align: right;">27</td>
</tr>
<tr class="odd">
<td>Dragon<span class="citation" data-cites="dragon">[62]</span></td>
<td style="text-align: right;">4</td>
<td style="text-align: right;">6</td>
</tr>
<tr class="even">
<td>GPU Microbenchmark<span class="citation" data-cites="accelsim">[13]</span></td>
<td style="text-align: right;">15</td>
<td style="text-align: right;">15</td>
</tr>
<tr class="odd">
<td>ISPASS 2009<span class="citation" data-cites="gpgpusim3">[12]</span></td>
<td style="text-align: right;">8</td>
<td style="text-align: right;">8</td>
</tr>
<tr class="even">
<td>Lonestargpu<span class="citation" data-cites="lonestar">[63]</span></td>
<td style="text-align: right;">3</td>
<td style="text-align: right;">6</td>
</tr>
<tr class="odd">
<td>Pannotia<span class="citation" data-cites="pannotia">[64]</span></td>
<td style="text-align: right;">7</td>
<td style="text-align: right;">11</td>
</tr>
<tr class="even">
<td>Parboil<span class="citation" data-cites="parboil">[65]</span></td>
<td style="text-align: right;">6</td>
<td style="text-align: right;">6</td>
</tr>
<tr class="odd">
<td>Polybench<span class="citation" data-cites="polybench">[66]</span></td>
<td style="text-align: right;">8</td>
<td style="text-align: right;">8</td>
</tr>
<tr class="even">
<td>Proxy Apps DOE<span class="citation" data-cites="proxy">[67]</span></td>
<td style="text-align: right;">3</td>
<td style="text-align: right;">4</td>
</tr>
<tr class="odd">
<td>Rodinia 2<span class="citation" data-cites="rodinia">[68]</span></td>
<td style="text-align: right;">10</td>
<td style="text-align: right;">10</td>
</tr>
<tr class="even">
<td>Rodinia 3<span class="citation" data-cites="rodinia">[68]</span></td>
<td style="text-align: right;">15</td>
<td style="text-align: right;">25</td>
</tr>
<tr class="odd">
<td><strong>合计</strong></td>
<td style="text-align: right;"><strong>83</strong></td>
<td style="text-align: right;"><strong>143</strong></td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
</section>
<section id="sec-cycle-accuracy" class="level2">
<h2 class="anchored" data-anchor-id="sec-cycle-accuracy">性能准确性</h2>
<div id="tbl-gpus-specs-and-results" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-gpus-specs-and-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;4: GPU 规格与性能准确性。
</figcaption>
<div aria-describedby="tbl-gpus-specs-and-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th></th>
<th style="text-align: right;">RTX 3080</th>
<th style="text-align: right;">RTX 3080 Ti</th>
<th style="text-align: right;">RTX 3090</th>
<th style="text-align: right;">RTX A6000</th>
<th style="text-align: right;">RTX 2080 Ti</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>规格</strong></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
</tr>
<tr class="even">
<td>核心频率</td>
<td style="text-align: right;">1710 MHz</td>
<td style="text-align: right;">1365 MHz</td>
<td style="text-align: right;">1395 MHz</td>
<td style="text-align: right;">1800 MHz</td>
<td style="text-align: right;">1350 MHz</td>
</tr>
<tr class="odd">
<td>显存频率</td>
<td style="text-align: right;">9500 MHz</td>
<td style="text-align: right;">9500 MHz</td>
<td style="text-align: right;">9750 MHz</td>
<td style="text-align: right;">8000 MHz</td>
<td style="text-align: right;">7000 MHz</td>
</tr>
<tr class="even">
<td>SM 数量</td>
<td style="text-align: right;">68</td>
<td style="text-align: right;">80</td>
<td style="text-align: right;">82</td>
<td style="text-align: right;">84</td>
<td style="text-align: right;">68</td>
</tr>
<tr class="odd">
<td>每 SM warp 数</td>
<td style="text-align: right;">48</td>
<td style="text-align: right;">48</td>
<td style="text-align: right;">48</td>
<td style="text-align: right;">48</td>
<td style="text-align: right;">32</td>
</tr>
<tr class="even">
<td>每 SM 共享内存/L1D</td>
<td style="text-align: right;">128 KB</td>
<td style="text-align: right;">128 KB</td>
<td style="text-align: right;">128 KB</td>
<td style="text-align: right;">128 KB</td>
<td style="text-align: right;">96 KB</td>
</tr>
<tr class="odd">
<td>内存分区数</td>
<td style="text-align: right;">20</td>
<td style="text-align: right;">24</td>
<td style="text-align: right;">24</td>
<td style="text-align: right;">24</td>
<td style="text-align: right;">22</td>
</tr>
<tr class="even">
<td>L2 总容量</td>
<td style="text-align: right;">5 MB</td>
<td style="text-align: right;">6 MB</td>
<td style="text-align: right;">6 MB</td>
<td style="text-align: right;">6 MB</td>
<td style="text-align: right;">5.5 MB</td>
</tr>
<tr class="odd">
<td><strong>验证</strong></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
<td style="text-align: right;"></td>
</tr>
<tr class="even">
<td>我们模型 MAPE</td>
<td style="text-align: right;">17.15%</td>
<td style="text-align: right;">18%</td>
<td style="text-align: right;">17.93%</td>
<td style="text-align: right;">13.98%</td>
<td style="text-align: right;">19.73%</td>
</tr>
<tr class="odd">
<td>Accel-Sim MAPE</td>
<td style="text-align: right;">27.95%</td>
<td style="text-align: right;">28.19%</td>
<td style="text-align: right;">28.5%</td>
<td style="text-align: right;">32.22%</td>
<td style="text-align: right;">26.67%</td>
</tr>
<tr class="even">
<td>我们模型相关系数</td>
<td style="text-align: right;">0.99</td>
<td style="text-align: right;">0.99</td>
<td style="text-align: right;">0.98</td>
<td style="text-align: right;">0.98</td>
<td style="text-align: right;">0.98</td>
</tr>
<tr class="odd">
<td>Accel-Sim 相关系数</td>
<td style="text-align: right;">0.98</td>
<td style="text-align: right;">0.98</td>
<td style="text-align: right;">0.98</td>
<td style="text-align: right;">0.97</td>
<td style="text-align: right;">0.95</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>表&nbsp;4 展示了我们模型与 Accel-Sim 相对于真实硬件的 MAPE。可以看到，我们模型在所有 GPU 上都更准确，且对最大规模的 NVIDIA RTX A6000，MAPE 不到 Accel-Sim 的一半。相关系数方面，两者接近，但我们略优。</p>
<div id="fig-cycles-results" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-cycles-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://sadsock.github.io/assets/modern-nvidia-gpu/AbsError_Cycles_general.svg" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-cycles-results-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
图&nbsp;5: NVIDIA RTX A6000 在各基准下的绝对百分比误差。基准按误差从小到大排序。
</figcaption>
</figure>
</div>
<p>图&nbsp;5 展示了 RTX A6000 上两种模型的 APE 分布（基准按误差排序）。我们模型在所有应用上 APE 更低，并且对约半数应用差距显著。Accel-Sim 在 10 个应用上 APE 大于等于 100%，最坏达到 543%，而我们的模型 APE 从未超过 62%。以 90 分位为衡量尾部准确性，Accel-Sim 为 82.64%，而我们为 31.47%。这表明我们的模型明显更准确且更稳健。</p>
</section>
<section id="sec-results-instruction-prefetching" class="level2">
<h2 class="anchored" data-anchor-id="sec-results-instruction-prefetching">指令预取的敏感性分析</h2>
<p>流缓冲指令预取器的参数对模型准确性影响很大。我们评估了多种配置：关闭预取、完美指令缓存，以及流缓冲大小为 1、2、4、8、16、32 的配置，均基于 RTX A6000。结果见 表&nbsp;5，可见最佳准确性对应流缓冲大小为 16。</p>
<div id="tbl-prefetching-analysis" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-prefetching-analysis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;5: 不同预取配置的 MAPE 与相对性能提升。
</figcaption>
<div aria-describedby="tbl-prefetching-analysis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<colgroup>
<col style="width: 8%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
<col style="width: 11%">
</colgroup>
<thead>
<tr class="header">
<th>配置</th>
<th style="text-align: right;">关闭</th>
<th style="text-align: right;">1</th>
<th style="text-align: right;">2</th>
<th style="text-align: right;">4</th>
<th style="text-align: right;">8</th>
<th style="text-align: right;">16</th>
<th style="text-align: right;">32</th>
<th style="text-align: right;">完美 ICache</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>MAPE</td>
<td style="text-align: right;">56.61%</td>
<td style="text-align: right;">43.94%</td>
<td style="text-align: right;">28.59%</td>
<td style="text-align: right;">18.55%</td>
<td style="text-align: right;">14.67%</td>
<td style="text-align: right;">13.98%</td>
<td style="text-align: right;">14.35%</td>
<td style="text-align: right;">15.2%</td>
</tr>
<tr class="even">
<td>加速比</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">1.08x</td>
<td style="text-align: right;">1.2x</td>
<td style="text-align: right;">1.33x</td>
<td style="text-align: right;">1.42x</td>
<td style="text-align: right;">1.47x</td>
<td style="text-align: right;">1.46x</td>
<td style="text-align: right;">1.58x</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>从 表&nbsp;5 的加速结果还可得出一条结论：简单的流缓冲预取器在 GPU 上的行为接近完美指令缓存。这是因为 sub-core 中的不同 warp 往往执行同一代码区域，而典型 GPGPU 应用控制流较简单，预取连续 <img src="https://latex.codecogs.com/png.latex?N"> 行通常有效。由于 GPU 不进行分支预测，采用 Fetch-Directed 预取<span class="citation" data-cites="fdpPrefetching">[69]</span> 不划算。</p>
<p>就仿真准确性而言，当不研究指令缓存增强时，完美指令缓存通常能以更高速度提供近似准确性。但对控制流较复杂的基准（如 Rodinia 的 <code>dwt2d</code>、<code>lud</code>、<code>nw</code>），完美指令缓存或不使用流缓冲会导致显著不准确（相对完美 ICache 误差超过 20%，相对无预取甚至超过 200%）。这是因为完美 ICache 无法体现频繁跳转带来的性能惩罚，而无预取则过度惩罚程序的其他部分，这也表明这些基准仍有改进空间。</p>
</section>
<section id="sec-eval-rf" class="level2">
<h2 class="anchored" data-anchor-id="sec-eval-rf">寄存器文件架构的敏感性分析</h2>
<p>表&nbsp;6 展示了寄存器文件缓存（RFC）与每 bank 读端口数量对准确性与性能的影响，并给出了理想配置（所有操作数可在一个周期取回）。总体平均性能与准确性在各配置间相近，但特定基准表现更微妙：例如计算密集型的 <code>MaxFlops</code>（Accel-Sim GPU Microbenchmark）与使用 <code>sgemm</code> 参数的 Cutlass<span class="citation" data-cites="accelsim cutlass">[13], [60]</span>。这两者高度依赖固定延迟算术指令，而这类指令通常有 3 个操作数，易受寄存器文件访问限制影响。</p>
<div id="tbl-rf-analysis" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-rf-analysis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;6: 不同寄存器文件配置的 MAPE 与相对性能。
</figcaption>
<div aria-describedby="tbl-rf-analysis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>配置</th>
<th style="text-align: right;">1R + RFC 开</th>
<th style="text-align: right;">1R + RFC 关</th>
<th style="text-align: right;">2R + RFC 关</th>
<th style="text-align: right;">理想</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>MAPE</td>
<td style="text-align: right;">13.98%</td>
<td style="text-align: right;">16.05%</td>
<td style="text-align: right;">13.38%</td>
<td style="text-align: right;">13.57%</td>
</tr>
<tr class="even">
<td>加速比</td>
<td style="text-align: right;">1x</td>
<td style="text-align: right;">0.984x</td>
<td style="text-align: right;">1.012x</td>
<td style="text-align: right;">1.013x</td>
</tr>
<tr class="odd">
<td>MaxFlops APE</td>
<td style="text-align: right;">2.82%</td>
<td style="text-align: right;">2.82%</td>
<td style="text-align: right;">28.97%</td>
<td style="text-align: right;">28.97%</td>
</tr>
<tr class="even">
<td>MaxFlops 加速比</td>
<td style="text-align: right;">1x</td>
<td style="text-align: right;">1x</td>
<td style="text-align: right;">1.44x</td>
<td style="text-align: right;">1.44x</td>
</tr>
<tr class="odd">
<td>Cutlass APE</td>
<td style="text-align: right;">9.72%</td>
<td style="text-align: right;">39.35%</td>
<td style="text-align: right;">0.97%</td>
<td style="text-align: right;">2.3%</td>
</tr>
<tr class="even">
<td>Cutlass 加速比</td>
<td style="text-align: right;">1x</td>
<td style="text-align: right;">0.79x</td>
<td style="text-align: right;">1.11x</td>
<td style="text-align: right;">1.12x</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>对 <code>MaxFlops</code> 而言，RFC 是否存在对性能影响不大，因为仅有一条静态指令使用 RFC。但当每 bank 读端口数增至 2（总共 4 个读端口）时，性能提升约 44%，因为每条指令常有 3 个操作数。相对地，从仿真准确性角度看，2 端口配置偏差明显。</p>
<p>对 Cutlass（<code>sgemm</code>）而言，单端口且无 RFC 会导致性能显著下降（0.78x），与程序中约 35.9% 的静态指令至少有一个操作数使用 RFC 的观察一致。引入每 bank 2 个读端口则提升约 12% 性能，说明寄存器文件与缓存结构仍有优化空间。</p>
<p>总之，寄存器文件架构及其缓存对个别基准影响显著，准确建模十分关键。平均而言，单端口 + 简单缓存已接近“无限端口”效果，但对某些基准差距仍很大，值得进一步研究。</p>
</section>
<section id="sec-deps-analysis" class="level2">
<h2 class="anchored" data-anchor-id="sec-deps-analysis">依赖管理机制分析</h2>
<p>本小节比较本文揭示的软硬协同依赖管理机制与传统记分牌方法在性能与面积上的影响。表&nbsp;7 给出结果。面积开销相对于一个 SM 的常规寄存器文件（256 KB）。</p>
<div id="tbl-dependence-checking-mechanisms" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-dependence-checking-mechanisms-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
表&nbsp;7: 不同依赖管理机制的速度、面积与 MAPE。
</figcaption>
<div aria-describedby="tbl-dependence-checking-mechanisms-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>指标</th>
<th style="text-align: right;">控制位方案</th>
<th style="text-align: right;">记分牌（最大消费者 63）</th>
<th style="text-align: right;">记分牌（无限制）</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>加速比</td>
<td style="text-align: right;">1</td>
<td style="text-align: right;">0.97x</td>
<td style="text-align: right;">0.97x</td>
</tr>
<tr class="even">
<td>面积开销</td>
<td style="text-align: right;">0.09%</td>
<td style="text-align: right;">5.32%</td>
<td style="text-align: right;">-</td>
</tr>
<tr class="odd">
<td>MAPE</td>
<td style="text-align: right;">13.98%</td>
<td style="text-align: right;">14.87%</td>
<td style="text-align: right;">14.87%</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>传统记分牌机制需要为所有可写寄存器提供条目，即每 warp 332 个条目（255 常规寄存器、63 统一寄存器、7 谓词寄存器、7 统一谓词寄存器）。此外需两个记分牌：一个处理 RAW/WAW，另一个处理 WAR<span class="citation" data-cites="warHazards">[17]</span>，因为虽然发射顺序严格，但可变延迟指令的读写可能乱序，导致 WAR。WAR 记分牌需要记录每条目消费者数量。若支持最多 63 个消费者，则单 warp 需要 2324 bit（<img src="https://latex.codecogs.com/png.latex?332%20+%20332%5Ctimes%20log2(63+1)">）。按 48 个 warp 的 SM 计算，总计 111,552 bit，占寄存器文件面积的 5.32%。</p>
<p>相比之下，本文的软硬协同机制仅需每 warp 6 个 6-bit 依赖计数器、4-bit 停顿计数器与 1-bit Yield，共 41 bit。每个 SM 仅 1968 bit，占寄存器文件面积的 0.09%，远小于记分牌方案。</p>
<p>综上，基于控制位的软硬协同机制在性能与面积上优于其他方案，且面积开销极小。在支持 64 个 warp/SM 的架构（如 Hopper<span class="citation" data-cites="hopperPaper">[22]</span>）中，对比更显著：控制位方案开销为 0.13%，而记分牌方案（63 消费者）为 7.09%。此外，在无法获取控制位的应用场景（如部分 Deepbench 内核）中，使用双记分牌（RAW/WAW 与 WAR）且最大消费者 63 的方案在仿真准确性上与控制位接近，因此是有效替代方案。</p>
</section>
<section id="sec-use-other-archs" class="level2">
<h2 class="anchored" data-anchor-id="sec-use-other-archs">向其他 NVIDIA 架构的可移植性</h2>
<p>本文主要聚焦 Ampere，但结论同样适用于 Turing 等架构。除四种 Ampere GPU 的结果外，表&nbsp;4 还给出 Turing GPU 结果，显示对 NVIDIA RTX 2080 Ti 的 MAPE 相比 Accel-Sim 提升 6.94%。</p>
<p>虽然我们仅在 Turing 与 Ampere 上验证了模型，但认为其结论可适用于更广泛的 NVIDIA 架构。公开资料与 SM 结构图显示，近期架构的重大变化主要集中在张量核心、光线追踪单元，以及如 TPC 内 SM 之间共享内存的分布式特性等小幅改动<span class="citation" data-cites="hopperPaper">[22]</span>。不过，为适配其他架构仍需重新估计部分指令（如内存指令）的延迟。</p>
</section>
</section>
<section id="sec-relatedwork" class="level1">
<h1>相关工作</h1>
<p>模拟器是学术界与工业界评估体系结构设计的主要工具<span class="citation" data-cites="SimSurvey">[70]</span>，因为其成本低且可用于验证设计与真实硬件的接近程度。GPGPU 亦不例外。NVIDIA 公开了其内部模拟器（NVArchSim/NVAS）的部分开发过程<span class="citation" data-cites="NVAS">[71]</span>。学术界广泛使用两类公开模拟器：MGPUSim<span class="citation" data-cites="mgpusim">[72]</span>（建模 AMD GCN 3，面向支持虚拟内存的多 GPU 系统）与 Accel-Sim<span class="citation" data-cites="accelsim">[13]</span>（基于 GPGPU-Sim 3<span class="citation" data-cites="gpgpusim3">[12]</span> 的周期级 trace 驱动模拟器，支持 CUDA 且建模类似 NVIDIA 的现代架构）。</p>
<p>在 CPU 领域，有许多逆向研究，例如 Intel 分支预测器<span class="citation" data-cites="intelBranchPredictor">[73]</span>与缓存设计<span class="citation" data-cites="intelReverseCacheAddress intelReverseCacheSlice">[74], [75]</span>。</p>
<p>针对 NVIDIA GPU，已有不少工作聚焦于特定组件：Ahn 等<span class="citation" data-cites="reverseNoc">[76]</span>与 Jin 等<span class="citation" data-cites="UncoveringNOC">[77]</span>逆向 Volta 与 Ampere 的片上网络；Lashgar 等<span class="citation" data-cites="reverseMshrPRT">[78]</span>研究 Fermi 与 Kepler 处理内存请求的能力；Jia 等<span class="citation" data-cites="dissectingVolta dissectingTuring">[32], [33]</span>揭示缓存行大小、相联度、指令延迟与寄存器文件细节；Khairy 等<span class="citation" data-cites="voltaCaches">[79]</span>研究 Volta 的 L1 与 L2 缓存；Abdelkhalik 等<span class="citation" data-cites="demystifyingAmpere">[57]</span>建立了 Ampere 的 PTX 与 SASS 关系及执行延迟；张量核心方面已有多项工作<span class="citation" data-cites="dissectingVolta dissecTensorCoresVolta1 dissecTensorCoresVolta2 dissectingTuring dissecTensorCoresTuringVolta1 dissecTensorCoresTuringVolta2 dissecTensorCoresAmpere1 dissecTensorCoresAmpere2">[32], [33], [80], [81], [82], [83], [84], [85]</span>；张等<span class="citation" data-cites="tunnelsTLB">[86]</span>逆向 Turing/Ampere TLB 以进行 Multi-Instance GPU 攻击；Shoushtary 等<span class="citation" data-cites="mojtabaControlFlow">[47]</span>用 1.03% 误差提出 Turing 控制流指令的语义；Amert 等<span class="citation" data-cites="reverseTX2">[87]</span>研究 NVIDIA Jetson TX2 中 GPU 任务调度与 ARM CPU 交互；Wong 等<span class="citation" data-cites="demystifyingGPU">[88]</span>描述 Tesla 早期架构的缓存、TLB、SIMT 控制流与 CTA barrier。</p>
<p>关于其他 GPU 厂商，Gutierrez 等<span class="citation" data-cites="amdGap1">[89]</span>指出使用 AMD GPU 机器 ISA 而非中间语言对准确评估瓶颈与优化至关重要。GAP 工具<span class="citation" data-cites="amdGap2">[90]</span>揭示真实 AMD GPU 与 gem5 模拟之间的不一致，推动了 gem5 对 AMD GPU 的改进<span class="citation" data-cites="amdGap3 amdGap4">[91], [92]</span>。Gera 等<span class="citation" data-cites="intelGPUs">[93]</span>提出 Intel 集成 GPU 的模拟框架与特性分析。</p>
<p>编译器提示（控制位）至少从 NVIDIA Kepler 架构就已使用<span class="citation" data-cites="KeplerASPaper KeplerASRepo">[27], [28]</span>，Gray 等<span class="citation" data-cites="maxas">[26]</span>公开描述了其细节。在 Kepler/Maxwell/Pascal 中，平均每 3 到 7 条指令就有一条是编译器插入的提示指令。Jia 等<span class="citation" data-cites="dissectingVolta dissectingTuring">[32], [33]</span>指出新架构将指令位宽从 64 扩展到 128 bit，使得提示位内嵌于每条指令中。这些提示位既用于优化性能，也用于避免数据冒险以保证正确性。CPU 也使用编译器提示辅助硬件资源管理<span class="citation" data-cites="HardwareAwareCompilation">[94]</span>，如旁路、分支预测、缓存等。</p>
<p>据我们所知，本文首次揭示现代 NVIDIA GPU 核心微架构，并构建高准确度的微架构仿真模型。我们发现的关键新特性包括：控制位语义及其支持的微架构改变、发射调度器行为、寄存器文件及其缓存结构、内存流水线多个重要细节。这些因素对准确建模现代 NVIDIA GPU 至关重要。</p>
</section>
<section id="sec-conclusion" class="level1">
<h1>结论</h1>
<p>本文通过在真实硬件上的逆向分析，揭示了现代 NVIDIA GPU 微架构。我们剖析了发射阶段逻辑，分析了 warp 就绪条件，并发现 warp 间发射调度遵循 CGGTY 策略；揭示了寄存器文件的端口数量与宽度，以及寄存器文件缓存的行为；分析了内存流水线的重要特性，如 load/store 队列大小、sub-core 间争用以及不同粒度访问对延迟的影响；并提出满足现代 NVIDIA GPU 需求的取指阶段设计。</p>
<p>本文还系统整理并扩展了控制位的公开信息。</p>
<p>此外，我们在模拟器中建模了这些细节，并与真实硬件对比，证明其在执行周期准确性上比以往模型提升超过 18.24%。</p>
<p>我们进一步展示，使用简单流缓冲的指令预取在准确性与性能上接近完美指令缓存；控制位驱动的依赖管理机制优于传统记分牌。</p>
<p>最后，我们分析了寄存器文件缓存与读端口数量对仿真准确性与性能的影响。</p>
<p>总体而言，GPU 是一种硬件—编译器协同设计：编译器引导硬件处理依赖，并通过提示位改进性能与能耗。</p>



</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">参考文献</h2><div id="refs" class="references csl-bib-body" data-entry-spacing="0">
<div id="ref-usageOfGPUs" class="csl-entry">
<div class="csl-left-margin">[1] </div><div class="csl-right-inline">M. Burtscher, R. Nasre, 和 K. Pingali, <span>《<span>A quantitative study of irregular programs on GPUs</span>》</span>, 收入 <em>Proceedings - 2012 IEEE International Symposium on Workload Characterization, IISWC 2012</em>, 2012, 页 141–151. doi: <a href="https://doi.org/10.1109/IISWC.2012.6402918">10.1109/IISWC.2012.6402918</a>.</div>
</div>
<div id="ref-cudaInGPUS" class="csl-entry">
<div class="csl-left-margin">[2] </div><div class="csl-right-inline">M. S. Nobile, P. Cazzaniga, A. Tangherloni, 和 D. Besozzi, <span>《<span>Graphics processing units in bioinformatics, computational biology and systems biology</span>》</span>, <em>Briefings in Bioinformatics</em>, 卷 18, 期 5, 页 870–885, 7月 2016, doi: <a href="https://doi.org/10.1093/bib/bbw058">10.1093/bib/bbw058</a>.</div>
</div>
<div id="ref-markovGPUBioinformatics" class="csl-entry">
<div class="csl-left-margin">[3] </div><div class="csl-right-inline">A. Bustamam, K. Burrage, 和 N. A. Hamilton, <span>《<span>Fast Parallel Markov Clustering in Bioinformatics Using Massively Parallel Computing on GPU with CUDA and ELLPACK-R Sparse Format</span>》</span>, <em>IEEE/ACM Transactions on Computational Biology and Bioinformatics</em>, 卷 9, 期 3, 页 679–692, 2012, doi: <a href="https://doi.org/10.1109/TCBB.2011.68">10.1109/TCBB.2011.68</a>.</div>
</div>
<div id="ref-molecularPhysicsCuda" class="csl-entry">
<div class="csl-left-margin">[4] </div><div class="csl-right-inline">W. Liu, B. Schmidt, G. Voss, 和 W. Müller-Wittig, <span>《<span>Accelerating molecular dynamics simulations using Graphics Processing Units with CUDA</span>》</span>, <em>Computer Physics Communications</em>, 卷 179, 期 9, 页 634–641, 2008, doi: <a href="https://doi.org/10.1016/j.cpc.2008.05.008">https://doi.org/10.1016/j.cpc.2008.05.008</a>.</div>
</div>
<div id="ref-fdtdPhysicsCuda" class="csl-entry">
<div class="csl-left-margin">[5] </div><div class="csl-right-inline">C. Warren <em>等</em>, <span>《<span>A CUDA-based GPU engine for gprMax: Open source FDTD electromagnetic simulation software</span>》</span>, <em>Computer Physics Communications</em>, 卷 237, 页 208–218, 2019, doi: <a href="https://doi.org/10.1016/j.cpc.2018.11.007">https://doi.org/10.1016/j.cpc.2018.11.007</a>.</div>
</div>
<div id="ref-detailedChemistry" class="csl-entry">
<div class="csl-left-margin">[6] </div><div class="csl-right-inline">F. E. Hernández Pérez <em>等</em>, <span>《<span>Direct numerical simulations of reacting flows with detailed chemistry using many-core/GPU acceleration</span>》</span>, <em>Computers &amp; Fluids</em>, 卷 173, 页 73–79, 2018, doi: <a href="https://doi.org/10.1016/j.compfluid.2018.03.074">https://doi.org/10.1016/j.compfluid.2018.03.074</a>.</div>
</div>
<div id="ref-cheminformaticsGPU" class="csl-entry">
<div class="csl-left-margin">[7] </div><div class="csl-right-inline">J. L. Xiaoxia Li Zheng Mo 和 L. Guo, <span>《´Revealing chemical reactions of coal pyrolysis with GPU-enabled ReaxFF molecular dynamics and cheminformatics analysis》</span>.</div>
</div>
<div id="ref-cuDNNLibraryIA" class="csl-entry">
<div class="csl-left-margin">[8] </div><div class="csl-right-inline">S. Chetlur <em>等</em>, <span>《<span>cuDNN: Efficient Primitives for Deep Learning</span>》</span>, 10月 2014, doi: <a href="https://doi.org/10.48550/arxiv.1410.0759">10.48550/arxiv.1410.0759</a>.</div>
</div>
<div id="ref-nvlink" class="csl-entry">
<div class="csl-left-margin">[9] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA NVLink TM High-Speed Interconnect: Application Performance</span>》</span>, Nvidia, 2014.</div>
</div>
<div id="ref-nccl" class="csl-entry">
<div class="csl-left-margin">[10] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA Collective Communications Library (NCCL)</span>》</span>. <a href="https://developer.nvidia.com/nccl" class="uri">https://developer.nvidia.com/nccl</a>, 2016年.</div>
</div>
<div id="ref-articuloWebMSNumGPUsIA" class="csl-entry">
<div class="csl-left-margin">[11] </div><div class="csl-right-inline">Microsoft, <span>《<span>How Microsoft’s bet on Azure unlocked an AI revolution.</span>》</span> <a href="https://news.microsoft.com/source/features/ai/how-microsofts-bet-on-azure-unlocked-an-ai-revolution/" class="uri">https://news.microsoft.com/source/features/ai/how-microsofts-bet-on-azure-unlocked-an-ai-revolution/</a>, 2023年.</div>
</div>
<div id="ref-gpgpusim3" class="csl-entry">
<div class="csl-left-margin">[12] </div><div class="csl-right-inline">A. Bakhoda, G. L. Yuan, W. W. L. Fung, H. Wong, 和 T. M. Aamodt, <span>《<span>Analyzing CUDA workloads using a detailed GPU simulator</span>》</span>, 收入 <em>2009 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)</em>, 2009, 页 163–174. doi: <a href="https://doi.org/10.1109/ISPASS.2009.4919648">10.1109/ISPASS.2009.4919648</a>.</div>
</div>
<div id="ref-accelsim" class="csl-entry">
<div class="csl-left-margin">[13] </div><div class="csl-right-inline">M. Khairy, Z. Shen, T. M. Aamodt, 和 T. G. Rogers, <span>《<span>Accel-Sim: An Extensible Simulation Framework for Validated GPU Modeling</span>》</span>, 收入 <em>2020 ACM/IEEE 47th Annual International Symposium on Computer Architecture (ISCA)</em>, 5月 2020, 页 473–486. doi: <a href="https://doi.org/10.1109/ISCA45697.2020.00047">10.1109/ISCA45697.2020.00047</a>.</div>
</div>
<div id="ref-teslaHotchips" class="csl-entry">
<div class="csl-left-margin">[14] </div><div class="csl-right-inline">E. Lindholm, J. Nickolls, S. Oberman, 和 J. Montrym, <span>《<span>NVIDIA Tesla: A Unified Graphics and Computing Architecture</span>》</span>, <em>IEEE Micro</em>, 卷 28, 期 2, 页 39–55, 2008, doi: <a href="https://doi.org/10.1109/MM.2008.31">10.1109/MM.2008.31</a>.</div>
</div>
<div id="ref-gpgpuBook" class="csl-entry">
<div class="csl-left-margin">[15] </div><div class="csl-right-inline">T. M. Aamodt, W. W. L. Fung, 和 T. G. Rogers, <em><span>General-purpose Graphics Processor Architectures</span></em>. Morgan &amp; Claypool Publishers, 2018.</div>
</div>
<div id="ref-GTO" class="csl-entry">
<div class="csl-left-margin">[16] </div><div class="csl-right-inline">T. G. Rogers, M. Oconnor, 和 T. M. Aamodt, <span>《<span>Cache-conscious wavefront scheduling</span>》</span>, 收入 <em>Proceedings - 2012 IEEE/ACM 45th International Symposium on Microarchitecture, MICRO 2012</em>, IEEE Computer Society, 2012, 页 72–83. doi: <a href="https://doi.org/10.1109/MICRO.2012.16">10.1109/MICRO.2012.16</a>.</div>
</div>
<div id="ref-warHazards" class="csl-entry">
<div class="csl-left-margin">[17] </div><div class="csl-right-inline">M. Mishkin, <span>《<span>Write-after-Read Hazard Prevention in GPGPUsim</span>》</span>, 2016.</div>
</div>
<div id="ref-IPOLYPaper" class="csl-entry">
<div class="csl-left-margin">[18] </div><div class="csl-right-inline">B. R. Rau, <span>《Pseudo-randomly interleaved memory》</span>, 收入 <em>Proceedings of the 18th Annual International Symposium on Computer Architecture</em>, 收入 ISCA ’91. New York, NY, USA: Association for Computing Machinery, 1991, 页 74–83. doi: <a href="https://doi.org/10.1145/115952.115961">10.1145/115952.115961</a>.</div>
</div>
<div id="ref-voltaPaper" class="csl-entry">
<div class="csl-left-margin">[19] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA Tesla V100 GPU architecture the world’s most advanced data center GPU</span>》</span>, NVIDIA, 2017.</div>
</div>
<div id="ref-turingPaper" class="csl-entry">
<div class="csl-left-margin">[20] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA TURING GPU architecture Graphics Reinvented NVIDIA Turing GPU Architecture</span>》</span>, NVIDIA, 2018.</div>
</div>
<div id="ref-amperePaper" class="csl-entry">
<div class="csl-left-margin">[21] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA AMPERE GA102 GPU architecture Second-Generation RTX NVIDIA Ampere GA102 GPU Architecture</span>》</span>, NVIDIA, 2020.</div>
</div>
<div id="ref-hopperPaper" class="csl-entry">
<div class="csl-left-margin">[22] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA H100 Tensor Core GPU Architecture</span>》</span>, NVIDIA, 2022.</div>
</div>
<div id="ref-adaPaper" class="csl-entry">
<div class="csl-left-margin">[23] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA ADA GPU architecture</span>》</span>, NVIDIA, 2022.</div>
</div>
<div id="ref-voltaHotChips" class="csl-entry">
<div class="csl-left-margin">[24] </div><div class="csl-right-inline">J. Choquette, O. Giroux, 和 D. Foley, <span>《<span>Volta: Performance and Programmability</span>》</span>, <em>IEEE Micro</em>, 卷 38, 期 2, 页 42–52, 2018, doi: <a href="https://doi.org/10.1109/MM.2018.022071134">10.1109/MM.2018.022071134</a>.</div>
</div>
<div id="ref-turingHotChips" class="csl-entry">
<div class="csl-left-margin">[25] </div><div class="csl-right-inline">J. Burgess, <span>《<span>RTX on—The NVIDIA Turing GPU</span>》</span>, <em>IEEE Micro</em>, 卷 40, 期 2, 页 36–44, 2020, doi: <a href="https://doi.org/10.1109/MM.2020.2971677">10.1109/MM.2020.2971677</a>.</div>
</div>
<div id="ref-maxas" class="csl-entry">
<div class="csl-left-margin">[26] </div><div class="csl-right-inline">S. Gray, <span>《<span>MaxAS: Assembler for NVIDIA Maxwell architecture</span>》</span>. 载于: <a href="https://github.com/NervanaSystems/maxas">https://github.com/NervanaSystems/maxas</a></div>
</div>
<div id="ref-KeplerASRepo" class="csl-entry">
<div class="csl-left-margin">[27] </div><div class="csl-right-inline">X. Zhang, <span>《<span>KeplerAs: An Open Source Kepler GPU Assembler</span>》</span>. 载于: <a href="https://github.com/xiuxiazhang/KeplerAs">https://github.com/xiuxiazhang/KeplerAs</a></div>
</div>
<div id="ref-KeplerASPaper" class="csl-entry">
<div class="csl-left-margin">[28] </div><div class="csl-right-inline">X. Zhang, G. Tan, S. Xue, J. Li, K. Zhou, 和 M. Chen, <span>《<span>Understanding the GPU Microarchitecture to Achieve Bare-Metal Performance Tuning</span>》</span>, 收入 <em>Proceedings of the 22nd ACM SIGPLAN Symposium on Principles and Practice of Parallel Programming</em>, 收入 PPoPP ’17. New York, NY, USA: Association for Computing Machinery, 2017, 页 31–43. doi: <a href="https://doi.org/10.1145/3018743.3018755">10.1145/3018743.3018755</a>.</div>
</div>
<div id="ref-turingas" class="csl-entry">
<div class="csl-left-margin">[29] </div><div class="csl-right-inline">D. Yan, <span>《<span>TuringAS: Assembler for NVIDIA Volta and Turing GPUs</span>》</span>. 载于: <a href="https://github.com/daadaada/turingas">https://github.com/daadaada/turingas</a></div>
</div>
<div id="ref-CuAssembler" class="csl-entry">
<div class="csl-left-margin">[30] </div><div class="csl-right-inline">Cloudcores, <span>《<span>CuAssembler: An unofficial cuda assembler, for all generations of SASS</span>》</span>. 载于: <a href="https://github.com/cloudcores/CuAssembler">https://github.com/cloudcores/CuAssembler</a></div>
</div>
<div id="ref-gtx680WhitePaper" class="csl-entry">
<div class="csl-left-margin">[31] </div><div class="csl-right-inline">NVIDIA, <span>《<span>Technology Overview NVIDIA GeForce GTX 680</span>》</span>, NVIDIA, 2012.</div>
</div>
<div id="ref-dissectingVolta" class="csl-entry">
<div class="csl-left-margin">[32] </div><div class="csl-right-inline">Z. Jia, M. Maggioni, B. Staiger, 和 D. P. Scarpazza, <span>《<span>Dissecting the NVIDIA Volta GPU Architecture via Microbenchmarking</span>》</span>, <em>CoRR</em>, 卷 abs/1804.06826, 2018, 载于: <a href="http://arxiv.org/abs/1804.06826">http://arxiv.org/abs/1804.06826</a></div>
</div>
<div id="ref-dissectingTuring" class="csl-entry">
<div class="csl-left-margin">[33] </div><div class="csl-right-inline">Z. Jia, M. Maggioni, J. Smith, 和 D. P. Scarpazza, <span>《<span>Dissecting the NVIDIA Turing T4 GPU via Microbenchmarking Technical Report</span>》</span>, 2019.</div>
</div>
<div id="ref-amdCDNA1" class="csl-entry">
<div class="csl-left-margin">[34] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"AMD Instinct MI100" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2020.</div>
</div>
<div id="ref-amdCDNA2" class="csl-entry">
<div class="csl-left-margin">[35] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"AMD Instinct MI200" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2022.</div>
</div>
<div id="ref-amdCDNA3" class="csl-entry">
<div class="csl-left-margin">[36] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"AMD Instinct MI300" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2024.</div>
</div>
<div id="ref-amdRDNA1" class="csl-entry">
<div class="csl-left-margin">[37] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"RDNA 1.0" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2020.</div>
</div>
<div id="ref-amdRDNA2" class="csl-entry">
<div class="csl-left-margin">[38] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"RDNA 2" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2020.</div>
</div>
<div id="ref-amdRDNA35" class="csl-entry">
<div class="csl-left-margin">[39] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"RDNA 3.5" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2024.</div>
</div>
<div id="ref-amdRDNA3" class="csl-entry">
<div class="csl-left-margin">[40] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">"RDNA 3" Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2023.</div>
</div>
<div id="ref-amdVega7nm" class="csl-entry">
<div class="csl-left-margin">[41] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">Vega 7nm Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2020.</div>
</div>
<div id="ref-amdGCN3" class="csl-entry">
<div class="csl-left-margin">[42] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">AMD Graphics Core Next Architecture, Generation 3. Reference Guide.</span></span>》</span>, AMD, 2016.</div>
</div>
<div id="ref-amdVega" class="csl-entry">
<div class="csl-left-margin">[43] </div><div class="csl-right-inline">AMD, <span>《<span><span style="color: darkerGreen">Vega Instruction Set Architecture. Reference Guide.</span></span>》</span>, AMD, 2020.</div>
</div>
<div id="ref-nvidiaInstPrefeching" class="csl-entry">
<div class="csl-left-margin">[44] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA Developer Forums: Instruction cache and instruction fetch stalls</span>》</span>. 载于: <a href="https://forums.developer.nvidia.com/t/instruction-cache-and-instruction-fetch-stalls/76883">https://forums.developer.nvidia.com/t/instruction-cache-and-instruction-fetch-stalls/76883</a></div>
</div>
<div id="ref-GPUinstPrefeching" class="csl-entry">
<div class="csl-left-margin">[45] </div><div class="csl-right-inline">J. Cao, Z. Chen, Y. Wang, H. Guo, 和 P. Wang, <span>《Instruction prefetch for improving GPGPU performance》</span>, <em>IEICE Transactions on Fundamentals of Electronics, Communications and Computer Sciences</em>, 卷 E104A, 页 773–785, 2021, doi: <a href="https://doi.org/10.1587/TRANSFUN.2020EAP1105">10.1587/TRANSFUN.2020EAP1105</a>.</div>
</div>
<div id="ref-streamBufferPrefetching" class="csl-entry">
<div class="csl-left-margin">[46] </div><div class="csl-right-inline">N. P. Jouppi, <span>《Improving direct-mapped cache performance by the addition of a small fully-associative cache and prefetch buffers》</span>, 收入 <em>1990 17th Annual International Symposium on Computer Architecture</em>, 1990, 页 364–373. doi: <a href="https://doi.org/10.1109/ISCA.1990.134547">10.1109/ISCA.1990.134547</a>.</div>
</div>
<div id="ref-mojtabaControlFlow" class="csl-entry">
<div class="csl-left-margin">[47] </div><div class="csl-right-inline">M. A. Shoushtary, J. T. Murgadas, 和 A. Gonzalez, <span>《<span>Control Flow Management in Modern GPUs</span>》</span>. 2024年. 载于: <a href="https://arxiv.org/abs/2407.02944">https://arxiv.org/abs/2407.02944</a></div>
</div>
<div id="ref-GPU2OcusSubcore" class="csl-entry">
<div class="csl-left-margin">[48] </div><div class="csl-right-inline">A. Barnes, F. Shen, 和 T. G. Rogers, <span>《<span>Mitigating GPU Core Partitioning Performance Effects</span>》</span>, 收入 <em>2023 IEEE International Symposium on High-Performance Computer Architecture (HPCA)</em>, 2023, 页 530–542. doi: <a href="https://doi.org/10.1109/HPCA56546.2023.10070957">10.1109/HPCA56546.2023.10070957</a>.</div>
</div>
<div id="ref-fermiWhitePaper" class="csl-entry">
<div class="csl-left-margin">[49] </div><div class="csl-right-inline">NVIDIA, <span>《<span>NVIDIA’s Next Generation CUDA TM Compute Architecture: Fermi</span>》</span>, NVIDIA, 2009.</div>
</div>
<div id="ref-registerFileCacheFirst" class="csl-entry">
<div class="csl-left-margin">[50] </div><div class="csl-right-inline">M. Gebhart <em>等</em>, <span>《Energy-efficient mechanisms for managing thread context in throughput processors》</span>, 收入 <em>2011 38th Annual International Symposium on Computer Architecture (ISCA)</em>, 2011, 页 235–246. doi: <a href="https://doi.org/10.1145/2000064.2000093">10.1145/2000064.2000093</a>.</div>
</div>
<div id="ref-registerFileCacheSimilar" class="csl-entry">
<div class="csl-left-margin">[51] </div><div class="csl-right-inline">M. Gebhart, S. W. Keckler, 和 W. J. Dally, <span>《<a href="">A compile-time managed multi-level register file hierarchy</a>》</span>, 收入 <em>2011 44th Annual IEEE/ACM International Symposium on Microarchitecture (MICRO)</em>, 2011, 页 465–476.</div>
</div>
<div id="ref-BOW" class="csl-entry">
<div class="csl-left-margin">[52] </div><div class="csl-right-inline">H. A. Esfeden, A. Abdolrashidi, S. Rahman, D. Wong, 和 N. Abu-Ghazaleh, <span>《<span>BOW: Breathing Operand Windows to Exploit Bypassing in GPUs</span>》</span>, 收入 <em>2020 53rd Annual IEEE/ACM International Symposium on Microarchitecture (MICRO)</em>, 2020, 页 996–1008. doi: <a href="https://doi.org/10.1109/MICRO50266.2020.00084">10.1109/MICRO50266.2020.00084</a>.</div>
</div>
<div id="ref-ltrf" class="csl-entry">
<div class="csl-left-margin">[53] </div><div class="csl-right-inline">M. Sadrosadati <em>等</em>, <span>《<span>LTRF: Enabling High-Capacity Register Files for GPUs via Hardware/Software Cooperative Register Prefetching</span>》</span>, 收入 <em>Proceedings of the Twenty-Third International Conference on Architectural Support for Programming Languages and Operating Systems</em>, 收入 ASPLOS ’18. New York, NY, USA: Association for Computing Machinery, 2018, 页 489–502. doi: <a href="https://doi.org/10.1145/3173162.3173211">10.1145/3173162.3173211</a>.</div>
</div>
<div id="ref-mojtabaIsca" class="csl-entry">
<div class="csl-left-margin">[54] </div><div class="csl-right-inline">M. Abaie Shoushtary, J. M. Arnau, J. Tubella Murgadas, 和 A. Gonzalez, <span>《<a href=""><span>Memento: An Adaptive, Compiler-Assisted Register File Cache for GPUs</span></a>》</span>, 收入 <em>2024 ACM/IEEE 51th Annual International Symposium on Computer Architecture (ISCA)</em>, 2024.</div>
</div>
<div id="ref-patentLDGSTS" class="csl-entry">
<div class="csl-left-margin">[55] </div><div class="csl-right-inline">S. J. Heinrich 和 A. L. Madison, <span>《Techniques for efficiently transferring data to a processor》</span>, 卷 417, 2019.</div>
</div>
<div id="ref-dissectingAmpere" class="csl-entry">
<div class="csl-left-margin">[56] </div><div class="csl-right-inline">Z. Jia 和 P. V. Sandt, <span>《<span>Dissecting the Ampere GPU Architecture through Microbenchmarking</span>》</span>, 收入 <em>NVIDIA GTC 2021</em>, <span>NVIDIA</span>, 2021. 载于: <a href="https://www.nvidia.com/en-us/on-demand/session/gtcspring21-s33322/">https://www.nvidia.com/en-us/on-demand/session/gtcspring21-s33322/</a></div>
</div>
<div id="ref-demystifyingAmpere" class="csl-entry">
<div class="csl-left-margin">[57] </div><div class="csl-right-inline">H. Abdelkhalik, Y. Arafa, N. Santhi, 和 A.-H. A. Badawy, <span>《<span>Demystifying the Nvidia Ampere Architecture through Microbenchmarking and Instruction-level Analysis</span>》</span>, 收入 <em>2022 IEEE High Performance Extreme Computing Conference (HPEC)</em>, 2022, 页 1–8. doi: <a href="https://doi.org/10.1109/HPEC55821.2022.9926299">10.1109/HPEC55821.2022.9926299</a>.</div>
</div>
<div id="ref-cams2023paper" class="csl-entry">
<div class="csl-left-margin">[58] </div><div class="csl-right-inline">R. Huerta, M. A. Shoushtary, 和 A. González, <span>《<span>Analyzing and Improving Hardware Modeling of Accel-Sim</span>》</span>. 2024年. 载于: <a href="https://arxiv.org/abs/2401.10082">https://arxiv.org/abs/2401.10082</a></div>
</div>
<div id="ref-cudaBinaryUtils" class="csl-entry">
<div class="csl-left-margin">[59] </div><div class="csl-right-inline">NVIDIA, <span>《<span>CUDA binary utilities documentation</span>》</span>. n.d. 载于: <a href="https://docs.nvidia.com/cuda/cuda-binary-utilities/">https://docs.nvidia.com/cuda/cuda-binary-utilities/</a></div>
</div>
<div id="ref-cutlass" class="csl-entry">
<div class="csl-left-margin">[60] </div><div class="csl-right-inline">NVIDIA, <span>《<span>CUTLASS: CUDA Templates for Linear Algebra Subroutines</span>》</span>. n.d. 载于: <a href="https://github.com/NVIDIA/cutlass">https://github.com/NVIDIA/cutlass</a></div>
</div>
<div id="ref-deepbenchWeb" class="csl-entry">
<div class="csl-left-margin">[61] </div><div class="csl-right-inline">S. Narang 和 G. Diamos, <span>《<span>DeepBench: Benchmarking Deep Learning operations on different hardware</span>》</span>. 2016年. 见于: 2022年4月21日. [在线]. 载于: <a href="https://github.com/baidu-research/DeepBench">https://github.com/baidu-research/DeepBench</a></div>
</div>
<div id="ref-dragon" class="csl-entry">
<div class="csl-left-margin">[62] </div><div class="csl-right-inline">J. Wang 和 S. Yalamanchili, <span>《<span>Characterization and analysis of dynamic parallelism in unstructured GPU applications</span>》</span>, 收入 <em>2014 IEEE International Symposium on Workload Characterization (IISWC)</em>, 2014, 页 51–60. doi: <a href="https://doi.org/10.1109/IISWC.2014.6983039">10.1109/IISWC.2014.6983039</a>.</div>
</div>
<div id="ref-lonestar" class="csl-entry">
<div class="csl-left-margin">[63] </div><div class="csl-right-inline">M. Burtscher, R. Nasre, 和 K. Pingali, <span>《<span>A quantitative study of irregular programs on GPUs</span>》</span>, 收入 <em>2012 IEEE International Symposium on Workload Characterization (IISWC)</em>, 2012, 页 141–151. doi: <a href="https://doi.org/10.1109/IISWC.2012.6402918">10.1109/IISWC.2012.6402918</a>.</div>
</div>
<div id="ref-pannotia" class="csl-entry">
<div class="csl-left-margin">[64] </div><div class="csl-right-inline">S. Che, B. M. Beckmann, S. K. Reinhardt, 和 K. Skadron, <span>《<span>Pannotia: Understanding irregular GPGPU graph applications</span>》</span>, 收入 <em>2013 IEEE International Symposium on Workload Characterization (IISWC)</em>, 2013, 页 185–195. doi: <a href="https://doi.org/10.1109/IISWC.2013.6704684">10.1109/IISWC.2013.6704684</a>.</div>
</div>
<div id="ref-parboil" class="csl-entry">
<div class="csl-left-margin">[65] </div><div class="csl-right-inline">J. A. Stratton <em>等</em>, <span>《<span>Parboil: A Revised Benchmark Suite for Scientific and Commercial Throughput Computing</span>》</span>, <em>Center for Reliable and High-Performance Computing</em>, 2012.</div>
</div>
<div id="ref-polybench" class="csl-entry">
<div class="csl-left-margin">[66] </div><div class="csl-right-inline">S. Grauer-Gray, L. Xu, R. Searles, S. Ayalasomayajula, 和 J. Cavazos, <span>《<span>Auto-tuning a high-level language targeted to GPU codes</span>》</span>, 收入 <em>2012 Innovative Parallel Computing (InPar)</em>, 2012, 页 1–10. doi: <a href="https://doi.org/10.1109/InPar.2012.6339595">10.1109/InPar.2012.6339595</a>.</div>
</div>
<div id="ref-proxy" class="csl-entry">
<div class="csl-left-margin">[67] </div><div class="csl-right-inline">O. Villa <em>等</em>, <span>《<span>Scaling the Power Wall: A Path to Exascale</span>》</span>, 收入 <em>SC ’14: Proceedings of the International Conference for High Performance Computing, Networking, Storage and Analysis</em>, 2014, 页 830–841. doi: <a href="https://doi.org/10.1109/SC.2014.73">10.1109/SC.2014.73</a>.</div>
</div>
<div id="ref-rodinia" class="csl-entry">
<div class="csl-left-margin">[68] </div><div class="csl-right-inline">S. Che <em>等</em>, <span>《<span>Rodinia: A benchmark suite for heterogeneous computing</span>》</span>, 收入 <em>Proceedings of the 2009 IEEE International Symposium on Workload Characterization, IISWC 2009</em>, 2009, 页 44–54. doi: <a href="https://doi.org/10.1109/IISWC.2009.5306797">10.1109/IISWC.2009.5306797</a>.</div>
</div>
<div id="ref-fdpPrefetching" class="csl-entry">
<div class="csl-left-margin">[69] </div><div class="csl-right-inline">G. Reinman, B. Calder, 和 T. Austin, <span>《Fetch directed instruction prefetching》</span>, 收入 <em>MICRO-32. Proceedings of the 32nd Annual ACM/IEEE International Symposium on Microarchitecture</em>, 1999, 页 16–27. doi: <a href="https://doi.org/10.1109/MICRO.1999.809439">10.1109/MICRO.1999.809439</a>.</div>
</div>
<div id="ref-SimSurvey" class="csl-entry">
<div class="csl-left-margin">[70] </div><div class="csl-right-inline">A. Akram 和 L. Sawalha, <span>《<span>A Survey of Computer Architecture Simulation Techniques and Tools</span>》</span>, <em>IEEE Access</em>, 卷 7, 页 78120–78145, 2019, doi: <a href="https://doi.org/10.1109/ACCESS.2019.2917698">10.1109/ACCESS.2019.2917698</a>.</div>
</div>
<div id="ref-NVAS" class="csl-entry">
<div class="csl-left-margin">[71] </div><div class="csl-right-inline">O. Villa <em>等</em>, <span>《<span>Need for Speed: Experiences Building a Trustworthy System-Level GPU Simulator</span>》</span>, 收入 <em>2021 IEEE International Symposium on High-Performance Computer Architecture (HPCA)</em>, 2021, 页 868–880. doi: <a href="https://doi.org/10.1109/HPCA51647.2021.00077">10.1109/HPCA51647.2021.00077</a>.</div>
</div>
<div id="ref-mgpusim" class="csl-entry">
<div class="csl-left-margin">[72] </div><div class="csl-right-inline">Y. Sun <em>等</em>, <span>《<a href=""><span>MGPUSim: Enabling Multi-GPU Performance Modeling and Optimization</span></a>》</span>, 收入 <em>2019 ACM/IEEE 46th Annual International Symposium on Computer Architecture (ISCA)</em>, 2019, 页 197–209.</div>
</div>
<div id="ref-intelBranchPredictor" class="csl-entry">
<div class="csl-left-margin">[73] </div><div class="csl-right-inline">H. Yavarzadeh, M. Taram, S. Narayan, D. Stefan, 和 D. Tullsen, <span>《<span>Half&amp;Half: Demystifying Intel’s Directional Branch Predictors for Fast, Secure Partitioned Execution</span>》</span>, 收入 <em>2023 IEEE Symposium on Security and Privacy (SP)</em>, 2023, 页 1220–1237. doi: <a href="https://doi.org/10.1109/SP46215.2023.10179415">10.1109/SP46215.2023.10179415</a>.</div>
</div>
<div id="ref-intelReverseCacheAddress" class="csl-entry">
<div class="csl-left-margin">[74] </div><div class="csl-right-inline">C. Maurice, N. Le Scouarnec, C. Neumann, O. Heen, 和 A. Francillon, <span>《<span>Reverse Engineering Intel Last-Level Cache Complex Addressing Using Performance Counters</span>》</span>, 收入 <em>Research in Attacks, Intrusions, and Defenses</em>, H. Bos, F. Monrose, 和 G. Blanc, 编, Cham: Springer International Publishing, 2015, 页 48–65.</div>
</div>
<div id="ref-intelReverseCacheSlice" class="csl-entry">
<div class="csl-left-margin">[75] </div><div class="csl-right-inline">G. Irazoqui, T. Eisenbarth, 和 B. Sunar, <span>《<span>Systematic Reverse Engineering of Cache Slice Selection in Intel Processors</span>》</span>, 收入 <em>2015 Euromicro Conference on Digital System Design</em>, 2015, 页 629–636. doi: <a href="https://doi.org/10.1109/DSD.2015.56">10.1109/DSD.2015.56</a>.</div>
</div>
<div id="ref-reverseNoc" class="csl-entry">
<div class="csl-left-margin">[76] </div><div class="csl-right-inline">J. Ahn <em>等</em>, <span>《<span>Network-on-Chip Microarchitecture-based Covert Channel in GPUs</span>》</span>, 收入 <em>MICRO-54: 54th Annual IEEE/ACM International Symposium on Microarchitecture</em>, 收入 MICRO ’21. New York, NY, USA: Association for Computing Machinery, 2021, 页 565–577. doi: <a href="https://doi.org/10.1145/3466752.3480093">10.1145/3466752.3480093</a>.</div>
</div>
<div id="ref-UncoveringNOC" class="csl-entry">
<div class="csl-left-margin">[77] </div><div class="csl-right-inline">Z. Jin <em>等</em>, <span>《´Uncovering Real GPU NoC Characteristics: Implications on Interconnect Architecture》</span>, doi: <a href="https://doi.org/10.1109/MICRO61859.2024.00070">10.1109/MICRO61859.2024.00070</a>.</div>
</div>
<div id="ref-reverseMshrPRT" class="csl-entry">
<div class="csl-left-margin">[78] </div><div class="csl-right-inline">A. Lashgar, E. Salehi, 和 A. Baniasadi, <span>《<span>A Case Study in Reverse Engineering GPGPUs: Outstanding Memory Handling Resources</span>》</span>, <em>SIGARCH Comput. Archit. News</em>, 卷 43, 期 4, 页 15–21, 4月 2016, doi: <a href="https://doi.org/10.1145/2927964.2927968">10.1145/2927964.2927968</a>.</div>
</div>
<div id="ref-voltaCaches" class="csl-entry">
<div class="csl-left-margin">[79] </div><div class="csl-right-inline">M. Khairy, J. Akshay, T. Aamodt, 和 T. G. Rogers, <span>《<span>Exploring Modern GPU Memory System Design Challenges through Accurate Modeling</span>》</span>, 2018, 载于: <a href="http://arxiv.org/abs/1810.07269 http://dx.doi.org/10.1109/ISCA45697.2020.00047">http://arxiv.org/abs/1810.07269 http://dx.doi.org/10.1109/ISCA45697.2020.00047</a></div>
</div>
<div id="ref-dissecTensorCoresVolta1" class="csl-entry">
<div class="csl-left-margin">[80] </div><div class="csl-right-inline">S. Markidis, S. Chien, E. Laure, I. Peng, 和 J. S. Vetter, <span>《<span>NVIDIA Tensor Core Programmability, Performance &amp; Precision</span>》</span>, 收入 <em>2018 IEEE International Parallel and Distributed Processing Symposium Workshops (IPDPSW)</em>, Los Alamitos, CA, USA: IEEE Computer Society, 5月 2018, 页 522–531. doi: <a href="https://doi.org/10.1109/IPDPSW.2018.00091">10.1109/IPDPSW.2018.00091</a>.</div>
</div>
<div id="ref-dissecTensorCoresVolta2" class="csl-entry">
<div class="csl-left-margin">[81] </div><div class="csl-right-inline">M. Martineau, P. Atkinson, 和 S. McIntosh-Smith, <span>《<span>Benchmarking the NVIDIA V100 GPU and Tensor Cores</span>》</span>, 收入 <em>Euro-Par 2018: Parallel Processing Workshops: Euro-Par 2018 International Workshops, Turin, Italy, August 27-28, 2018, Revised Selected Papers</em>, Berlin, Heidelberg: Springer-Verlag, 2019, 页 444–455. doi: <a href="https://doi.org/10.1007/978-3-030-10549-5_35">10.1007/978-3-030-10549-5_35</a>.</div>
</div>
<div id="ref-dissecTensorCoresTuringVolta1" class="csl-entry">
<div class="csl-left-margin">[82] </div><div class="csl-right-inline">M. A. Raihan, N. Goli, 和 T. M. Aamodt, <span>《<span>Modeling Deep Learning Accelerator Enabled GPUs</span>》</span>, 收入 <em>2019 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)</em>, 2019, 页 79–92. doi: <a href="https://doi.org/10.1109/ISPASS.2019.00016">10.1109/ISPASS.2019.00016</a>.</div>
</div>
<div id="ref-dissecTensorCoresTuringVolta2" class="csl-entry">
<div class="csl-left-margin">[83] </div><div class="csl-right-inline">D. Yan, W. Wang, 和 X. Chu, <span>《<span>Demystifying Tensor Cores to Optimize Half-Precision Matrix Multiply</span>》</span>, 收入 <em>2020 IEEE International Parallel and Distributed Processing Symposium (IPDPS)</em>, 2020, 页 634–643. doi: <a href="https://doi.org/10.1109/IPDPS47924.2020.00071">10.1109/IPDPS47924.2020.00071</a>.</div>
</div>
<div id="ref-dissecTensorCoresAmpere1" class="csl-entry">
<div class="csl-left-margin">[84] </div><div class="csl-right-inline">M. Fasi, N. J. Higham, M. Mikaitis, 和 S. Pranesh, <span>《<span>Numerical behavior of NVIDIA tensor cores</span>》</span>, <em>PeerJ Computer Science</em>, 卷 7, 页 1–19, 2月 2021, doi: <a href="https://doi.org/10.7717/PEERJ-CS.330/FIG-1">10.7717/PEERJ-CS.330/FIG-1</a>.</div>
</div>
<div id="ref-dissecTensorCoresAmpere2" class="csl-entry">
<div class="csl-left-margin">[85] </div><div class="csl-right-inline">W. Sun, A. Li, T. Geng, S. Stuijk, 和 H. Corporaal, <span>《<span>Dissecting Tensor Cores via Microbenchmarks: Latency, Throughput and Numeric Behaviors</span>》</span>, <em>IEEE Transactions on Parallel and Distributed Systems</em>, 卷 34, 期 1, 页 246–261, 2023, doi: <a href="https://doi.org/10.1109/TPDS.2022.3217824">10.1109/TPDS.2022.3217824</a>.</div>
</div>
<div id="ref-tunnelsTLB" class="csl-entry">
<div class="csl-left-margin">[86] </div><div class="csl-right-inline">Z. Zhang, T. Allen, F. Yao, X. Gao, 和 R. Ge, <span>《<span>TunneLs for Bootlegging: Fully Reverse-Engineering GPU TLBs for Challenging Isolation Guarantees of NVIDIA MIG</span>》</span>, 收入 <em>Proceedings of the 2023 ACM SIGSAC Conference on Computer and Communications Security</em>, 收入 CCS ’23. New York, NY, USA: Association for Computing Machinery, 2023, 页 960–974. doi: <a href="https://doi.org/10.1145/3576915.3616672">10.1145/3576915.3616672</a>.</div>
</div>
<div id="ref-reverseTX2" class="csl-entry">
<div class="csl-left-margin">[87] </div><div class="csl-right-inline">T. Amert, N. Otterness, M. Yang, J. H. Anderson, 和 F. D. Smith, <span>《<span>GPU Scheduling on the NVIDIA TX2: Hidden Details Revealed</span>》</span>, 收入 <em>2017 IEEE Real-Time Systems Symposium (RTSS)</em>, 2017, 页 104–115. doi: <a href="https://doi.org/10.1109/RTSS.2017.00017">10.1109/RTSS.2017.00017</a>.</div>
</div>
<div id="ref-demystifyingGPU" class="csl-entry">
<div class="csl-left-margin">[88] </div><div class="csl-right-inline">H. Wong, M.-M. Papadopoulou, M. Sadooghi-Alvandi, 和 A. Moshovos, <span>《<span>Demystifying GPU microarchitecture through microbenchmarking</span>》</span>, 收入 <em>2010 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)</em>, 2010, 页 235–246. doi: <a href="https://doi.org/10.1109/ISPASS.2010.5452013">10.1109/ISPASS.2010.5452013</a>.</div>
</div>
<div id="ref-amdGap1" class="csl-entry">
<div class="csl-left-margin">[89] </div><div class="csl-right-inline">A. Gutierrez <em>等</em>, <span>《<span><span style="color: darkerGreen">Lost in Abstraction: Pitfalls of Analyzing GPUs at the Intermediate Language Level</span></span>》</span>, 收入 <em>2018 IEEE International Symposium on High Performance Computer Architecture (HPCA)</em>, 2018, 页 608–619. doi: <a href="https://doi.org/10.1109/HPCA.2018.00058">10.1109/HPCA.2018.00058</a>.</div>
</div>
<div id="ref-amdGap2" class="csl-entry">
<div class="csl-left-margin">[90] </div><div class="csl-right-inline">C. Jamieson, A. Chandrashekar, I. McDougall, 和 M. D. Sinclair, <span>《<span><span style="color: darkerGreen">gem5 GPU Accuracy Profiler (GAP)</span></span>》</span>, 收入 <em>4th gem5 Users’ Workshop</em>, 2022.</div>
</div>
<div id="ref-amdGap3" class="csl-entry">
<div class="csl-left-margin">[91] </div><div class="csl-right-inline">V. Ramadas, D. Kouchekinia, N. Osuji, 和 M. D. Sinclair, <span>《<span><span style="color: darkerGreen">Closing the Gap: Improving the Accuracy of gem5’s GPU Models</span></span>》</span>, 收入 <em>5th gem5 Users’ Workshop</em>, 2023.</div>
</div>
<div id="ref-amdGap4" class="csl-entry">
<div class="csl-left-margin">[92] </div><div class="csl-right-inline">V. Ramadas, D. Kouchekinia, 和 M. D. Sinclair, <span>《<span><span style="color: darkerGreen">Further Closing the GAP: Improving the Accuracy of gem5’s GPU Models</span></span>》</span>, 收入 <em>6th Young Architects’ (YArch) Workshop</em>, 2024.</div>
</div>
<div id="ref-intelGPUs" class="csl-entry">
<div class="csl-left-margin">[93] </div><div class="csl-right-inline">P. Gera, H. Kim, H. Kim, S. Hong, V. George, 和 C.-K. Luk, <span>《<span><span style="color: darkerGreen">Performance Characterisation and Simulation of Intel’s Integrated GPU Architecture</span></span>》</span>, 收入 <em>2018 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)</em>, Los Alamitos, CA, USA: IEEE Computer Society, 4月 2018, 页 139–148. doi: <a href="https://doi.org/10.1109/ISPASS.2018.00027">10.1109/ISPASS.2018.00027</a>.</div>
</div>
<div id="ref-HardwareAwareCompilation" class="csl-entry">
<div class="csl-left-margin">[94] </div><div class="csl-right-inline">A. Shrivastava 和 J. Cai, <span>《Hardware-aware compilation》</span>, 收入 <em>Handbook of Hardware/Software Codesign</em>, Netherlands: Springer Netherlands, 2017, 页 795–827. doi: <a href="https://doi.org/10.1007/978-94-017-7267-9_26">10.1007/978-94-017-7267-9_26</a>.</div>
</div>
</div></section><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>Ampere 允许 FP32 操作在 FP32 与 INT32 执行单元上执行<span class="citation" data-cites="amperePaper">[21]</span>，因此两个连续 FP32 指令之间不会因执行单元冲突产生气泡。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>翻译</category>
  <category>Architecture</category>
  <guid>https://sadsock.github.io/posts/2026-03-03-modern-nvidia-gpu-core-analysis.html</guid>
  <pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>深入理解 LLVM code generator (一)</title>
  <link>https://sadsock.github.io/posts/2021-8-27-deeper-look-llvm-codegen-0.html</link>
  <description><![CDATA[ 




<p>在上一篇文章中， 我介绍了指令在 LLVM 中从源语言编译为机器代码时所采用的各种形式。 本文简要地提到了 LLVM 中的许多层，每一层都很有趣且不平凡。</p>
<p>在这里，我想重点讨论最重要和最复杂的层之一——代码生成器，特别是指令选择机制。 简单来说，代码生成器的任务是将高级的、目标无关的 LLVM IR 转换为低级的、目标相关的机器语言。 指令选择是将 IR 中的抽象操作映射到目标体系结构的具体指令的过程。</p>
<p>本文将通过一个简单的示例来展示实际的指令选择机制(LLVM 中的 ISel)。</p>
<section id="入门" class="level2">
<h2 class="anchored" data-anchor-id="入门">入门</h2>
<p>这是一个乘法 IR 的例子。</p>
<pre><code>define i64 @imul(i64 %a, i64 %b) nounwind readnone {
entry:
  %mul = mul nsw i64 %b, %a
  ret i64 %mul
}</code></pre>
<p>它是用 Clang (-emit-llvm 选项)在 x64 机器上从以下 c 代码编译而成的。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode c code-with-copy"><code class="sourceCode c"><span id="cb2-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long</span> imul<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long</span> a<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long</span> b<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> b<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>代码生成器完成的第一件事是将 IR 转换为 Selection DAG。 这是最初的 DAG。</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://sadsock.github.io/assets/a-deeper-look-into-the-llvm-code-generator-part-1/dag_imul5.png" class="img-fluid figure-img"></p>
<figcaption>Selection DAG</figcaption>
</figure>
</div>
<p>这里并没有太多值得关注的内容，所有类型对于目标架构来说都是合法的； 因此，这个 DAG 可以直接用于指令选择。</p>
</section>
<section id="指令选择-pattern" class="level2">
<h2 class="anchored" data-anchor-id="指令选择-pattern">指令选择 pattern</h2>
<p>指令选择可以说是代码生成阶段最重要的部分。 它的任务是将合法的 Selection DAG 转换为包含 target machine code 的新 DAG。 换句话说，就是输入抽象的、独立于目标的 DAG 输出具体的、依赖于目标的新 DAG。 为此，LLVM 使用由两个主要步骤组成的 pattern 匹配算法。</p>
<p>第一个步骤是离线进行的，即 发生在 LLVM 本身正在构建时，涉及 TableGen 工具，该工具根据指令的定义生成 pattern 匹配表。 TableGen 是 LLVM 生态系统的重要组成部分，在指令选择中起着特别重要的作用， 因此花几分钟讨论它是值得的(官方文档<a href="https://llvm.org/docs/TableGen/index.html">TableGen Overview</a>)。</p>
<p>TableGen 的问题在于，它的一些用途非常复杂(我们很快就会看到，指令选择是最严重的问题之一)，以至于很容易忘记其核心思想是多么简单。 LLVM 开发人员很久以前就意识到，必须为每个新目标编写大量重复代码。 比如，同一条机器指令被用于代码生成、汇编程序、反汇编程序、优化器和许多其他地方。 每一次这样的使用都会产生一个“表”，将指令映射到某些信息。 如果我们可以在一个位置定义所有指令，收集所有我们需要的关于它们的有用信息，然后自动生成所有表，这不是很好吗？ 这正是 TableGen 生来就要做的。</p>
<p>让我们检查与本文相关的指令定义（取自<code>lib/Target/X86/X86InstrArithmetic.td</code>并重新格式化）：</p>
<pre><code>def IMUL64rr : RI&lt;0xAF, MRMSrcReg, (outs GR64:$dst),
                                   (ins GR64:$src1, GR64:$src2),
                  "imul{q}\t{$src2, $dst|$dst, $src2}",
                  [(set GR64:$dst, EFLAGS,
                        (X86smul_flag GR64:$src1, GR64:$src2))],
                  IIC_IMUL64_RR&gt;,
                 TB;</code></pre>
<p>如果你觉得这像一堆垃圾，别担心，大家的第一印象都是如此。 为了充分提取通用代码，TableGen 开发了一些高级功能，如多重继承、模板等。 所有这些都使定义有些难以理解，如果要查看 <code>IMUL64rr</code> 的“裸”定义，可以从 LLVM 源代码树的根目录运行它：</p>
<pre><code>$ llvm-tblgen lib/Target/X86/X86.td -I=include -I=lib/Target/X86</code></pre>
<p>13.5 MB 的输出只包含简单的 defs 条目，TableGen 后端可以从中获取所需的内容。IMUL64rr 的 def 大约有 75 个字段，但我们只关注本文需要的内容。</p>
<p>我们讨论的最重要的字段是上面的 def 中的第六个模板参数。</p>
<pre><code>[(set GR64:$dst, EFLAGS,
      (X86smul_flag GR64:$src1, GR64:$src2))],</code></pre>
<p>这是匹配 IMUL64rr 的 pattern。 它本质上是一个描述要匹配的 DAG 的 s-expression。 在这种情况下，它大致意味着：匹配一个带有两个 64 位 GPR(通用寄存器)子节点的 X86ISD:：SMUL 节点（包含在 X86smul_flag 的定义中）， 该节点返回两个结果，一个分配给目标 GPR，另一个分配给状态寄存器。 当自动指令选择在 DAG 中看到这样的序列时，就将其与 IMUL64rr 指令相匹配。</p>
<p>细心的读者会注意到我在这里有点说谎。 如果此模式匹配的节点是 X86ISD::SMUL，那么它如何匹配上面所示的具有 ISD::MUL 节点的 DAG 呢? 事实上,它并不能匹配。 稍后我将展示与 DAG 实际匹配的模式，但我认为演示指令定义很重要，以便稍后讨论如何将所有模式组合在一起。</p>
<p>那么 ISD::MUL 和 X86ISD::SMUL 之间有什么区别？ 前者不关心受乘法影响的标志位，而后者关心。 C 语言中的乘法通常不关心标志位，因此选择了 ISD::MUL。 但是 LLVM 提供了一些特殊的内在函数，例如 llvm.smul.with.overflow，其中可以从操作中返回溢出标志。 对于这些（可能还有其他用途），LLVM 定义了 X86ISD::SMUL 节点。</p>
<p>那么，实际匹配 ISD::MUL 节点的是什么呢? 这个模式来自<code>lib/Target/X86/X86InstrCompiler.td</code>。</p>
<pre><code>def : Pat&lt;(mul GR64:$src1, GR64:$src2),
          (IMUL64rr GR64:$src1, GR64:$src2)&gt;;</code></pre>
<p>这是一个匿名 TableGen 记录，它定义了一个独立于特定指令的 pattern。 该 pattern 定义了从 输入 DAG 到输出 DAG 的映射，后者包含选定的指令。 我们不关心如何手动调用此 pattern，因此 TableGen 允许我们定义匿名 pattern。 下面是 <code>include/llvm/Target/TargetSelectionDAG.td</code> 中一个有趣的片段，其中定义了 pattern 类（及其特化的 Pat 类）：</p>
<pre><code>// Selection DAG Pattern Support.
//
// Patterns are what are actually matched against by the target-flavored
// instruction selection DAG.  Instructions defined by the target implicitly
// define patterns in most cases, but patterns can also be explicitly added when
// an operation is defined by a sequence of instructions (e.g. loading a large
// immediate value on RISC targets that do not support immediates as large as
// their GPRs).
//

class Pattern&lt;dag patternToMatch, list&lt;dag&gt; resultInstrs&gt; {
  dag             PatternToMatch  = patternToMatch;
  list&lt;dag&gt;       ResultInstrs    = resultInstrs;
  list&lt;Predicate&gt; Predicates      = [];  // See class Instruction in Target.td.
  int             AddedComplexity = 0;   // See class Instruction in Target.td.
}

// Pat - A simple (but common) form of a pattern, which produces a simple result
// not needing a full list.
class Pat&lt;dag pattern, dag result&gt; : Pattern&lt;pattern, [result]&gt;;</code></pre>
<p>这段代码顶部的大注释很有帮助，它描述了与 IMUL64rr 完全相反的情况。 在我们的例子中，在指令内定义的模式实际上更复杂，而基本模式是在指令外用 pattern 定义的。</p>
</section>
<section id="pattern-匹配机制" class="level2">
<h2 class="anchored" data-anchor-id="pattern-匹配机制">pattern 匹配机制</h2>
<p>TableGen 支持多种类型的 pattern 来描述目标机器支持的指令。 我们已经学习了在指令定义中隐式定义的 pattern 和显式定义的独立 pattern。 此外，还存在指定 C++函数的 complex pattern，以及包含任意 C++代码片段的 pattern fragments。 如果您感兴趣，<code>include/llvm/Target/TargetSelectionDAG.td</code> 中的注释对这些类型提供了描述。</p>
<p>之所以在 TableGen 中混合 c++ 代码是可行的， 是因为 TableGen 最终被翻译成一个 c++方法(面向特定 DAG ISel 后端)，并嵌入到目标对 <code>SelectionDAGISel</code> 接口的实现中。</p>
<p>更具体地说，顺序是：</p>
<ol type="1">
<li><p>通用的<code>SelectionDAGISel::DoInstructionSelection</code>方法针对每个 DAG 节点调用 <code>Select</code> 函数。</p></li>
<li><p><code>Select</code>是一个抽象方法，由具体的后端实现。例如<code>X86DAGToDAGISel:：Select</code>。</p></li>
<li><p><code>X86DAGToDAGISel::Select</code>会拦截一些节点进行手动匹配，但将大部分工作委托给<code>X86DAGToDAGISel:：SelectCode</code>。</p></li>
<li><p><code>X86DAGToDAGISel::SelectCode</code> 由 TableGen [4] 自动生成，包含匹配表， 然后调用通用 <code>SelectionDAGISel::SelectCodeCommon</code>在匹配表中查找匹配项。</p></li>
</ol>
<p>什么是匹配器表?从本质上说，它是一个“程序”，以某种特定于指令选择的“字节码”编写。 为了在保持效率的同时兼顾灵活性，TableGen 将后端定义的所有 pattern 混合在一起，并生成一个程序，给定任意的 DAG，该程序将找出与之匹配的 pattern。 <code>SelectionDAGISel::SelectCodeCommon</code> 充当这个字节码的解释器。</p>
<p>不幸的是，用于 pattern 匹配的字节码在任何地方都没有文档。 要了解它是如何工作的，除了查看解释器代码和为某些后端生成的字节码之外，别无他法。</p>
</section>
<section id="示例" class="level2">
<h2 class="anchored" data-anchor-id="示例">示例</h2>
<p>让我们学习一下示例 DAG 中的 <code>ISD::MUL</code> 节点是如何匹配的。 为此，将 <code>-debug</code> 选项传递给 <code>llc</code>， 这使其转储整个 code generate 过程的详细调试信息， 每个 DAG 节点的选择过程都可以被追踪。 下面是与 <code>ISD::MUL</code> 节点有关部分：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Selecting:</span> 0x38c4ee0: i64 = mul 0x38c4de0, 0x38c4be0 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ORD=1</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ID=7</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ISEL:</span> Starting pattern match on root node: 0x38c4ee0: i64 = mul 0x38c4de0, 0x38c4be0 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ORD=1</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ID=7</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb8-4"></span>
<span id="cb8-5">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Initial</span> Opcode index to 57917</span>
<span id="cb8-6">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 57922</span>
<span id="cb8-7">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58133</span>
<span id="cb8-8">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58137</span>
<span id="cb8-9">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58246</span>
<span id="cb8-10">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58249</span>
<span id="cb8-11">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58335</span>
<span id="cb8-12">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">TypeSwitch</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>i64<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">from</span> 58337 to 58380</span>
<span id="cb8-13"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">MatchAddress:</span> X86ISelAddressMode 0x7fff447ca040</span>
<span id="cb8-14"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Base_Reg</span> nul Base.FrameIndex 0</span>
<span id="cb8-15"> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Scale1</span></span>
<span id="cb8-16"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">IndexReg</span> nul Disp 0</span>
<span id="cb8-17"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">GV</span> nul CP nul</span>
<span id="cb8-18"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ES</span> nul JT-1 Align0</span>
<span id="cb8-19">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58380</span>
<span id="cb8-20">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58396</span>
<span id="cb8-21">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58407</span>
<span id="cb8-22">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58516</span>
<span id="cb8-23">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58517</span>
<span id="cb8-24">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58531</span>
<span id="cb8-25">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58532</span>
<span id="cb8-26">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58544</span>
<span id="cb8-27">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 58545</span>
<span id="cb8-28">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58557</span>
<span id="cb8-29">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Morphed</span> node: 0x38c4ee0: i64,i32 = IMUL64rr 0x38c4de0, 0x38c4be0 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ORD=1</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb8-30"></span>
<span id="cb8-31"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ISEL:</span> Match complete!</span>
<span id="cb8-32"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">=</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> 0x38c4ee0: i64,i32 = IMUL64rr 0x38c4de0, 0x38c4be0 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">ORD=1</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span></span></code></pre></div></div>
<p>这里提到的索引指向匹配表的某一项。 您可以在生成的 X86GenDAGISel.inc 文件的每一行开头的注释中看到它们。 下面是表格的开头部分:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb9-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> The main instruction selector code.</span>
<span id="cb9-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">SDNode</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>SelectCode<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">SDNode</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>N<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span></span>
<span id="cb9-3">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> Some target values are emitted as 2 bytes, TARGET_VAL handles</span>
<span id="cb9-4">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> this.</span>
<span id="cb9-5">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#define TARGET_VAL(X) X &amp; 255, unsigned(X) &gt;&gt; 8</span></span>
<span id="cb9-6">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">static</span> const unsigned char MatcherTable[] = {</span>
<span id="cb9-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*0*/</span>     OPC_SwitchOpcode /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>221 cases <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/, 73<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">128,103/*13257*/,</span>  TARGET_VAL<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ISD::STORE</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">,//</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>13262</span>
<span id="cb9-8"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*5*/</span>       OPC_RecordMemRef,</span>
<span id="cb9-9"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*6*/</span>       OPC_RecordNode,   // <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#0 = 'st' chained node</span></span>
<span id="cb9-10"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*7*/</span>       OPC_Scope, 5<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">128,2/*261*/,</span> /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>-<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>271<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/ // 7 children in Scope</span></code></pre></div></div>
<p>在位置 0 有一个 OPC_SwitchOpcode 操作， 这是关于 Opcode 的一个巨大的 switch 表，接下来是一系列 case， 每个 case 都从它的 size 开始(如果匹配失败，匹配器就知道要去哪里开始新的匹配)，然后是 Opcode。 例如，如上面的清单所示，表中的第一个 case 的 Opcode 是<code>ISD::STORE</code>， 其 size 为 13257(由于表是基于字节的，所以 size 采用特殊的变长编码方式编码)。</p>
<p>查看 llc 的输出，MUL 节点的匹配从偏移量 57917 开始。 这是表的相关部分：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb10-1">          <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*SwitchOpcode*/</span> 53<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">128,8/*1077*/,</span>  TARGET_VAL<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ISD::MUL</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">,//</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>58994</span>
<span id="cb10-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*57917*/</span>   OPC_Scope, 85<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">128,1/*213*/,</span> /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>-<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>58133<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/ // 7 children in Scope</span></code></pre></div></div>
<p>正如预期的那样，这是 <code>ISD::MUL</code> 的 case。 这个 case 的匹配从 OPC_Scope 开始，它是一个指令，指示解释器保存当前状态。 如果在该 Scope 内匹配失败，则可以恢复状态以继续匹配下一个 case。 在上面的片段中，如果在该 Scope 内匹配失败，它将在偏移量 58133 处继续匹配。</p>
<p>你可以在 llc 输出中看到:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb11-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Initial</span> Opcode index to 57917</span>
<span id="cb11-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Match</span> failed at index 57922</span>
<span id="cb11-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Continuing</span> at 58133</span></code></pre></div></div>
<p>在 57922 时，解释器尝试将节点的子节点匹配到 <code>ISD::LOAD</code>， 但失败了，并跳转到作用域指示的 58133。 同样，可以跟踪匹配过程的其余部分 - 按照 llc 输出和匹配表作为参考。 不过，在偏移量 58337 处发生了一些有趣的事情。 这是相关的表格部分：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb12-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58337*/</span>     OPC_SwitchType /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>2 cases <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/, 38,  MVT::i32,// <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>58378</span>
<span id="cb12-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58340*/</span>       OPC_Scope, 17, /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>-<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>58359<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/ // 2 children in Scope</span>
<span id="cb12-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58342*/</span>         OPC_CheckPatternPredicate, 4, // <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">!Subtarget-</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>is64Bit<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">))</span></span>
<span id="cb12-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58344*/</span>         OPC_CheckComplexPat, /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>CP<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/3, /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>#<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/0, // SelectLEAAddr:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#1 #2 #3 #4 #5</span></span>
<span id="cb12-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58347*/</span>         OPC_MorphNodeTo, TARGET_VAL<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">X86::LEA32r</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">,</span> 0,</span>
<span id="cb12-6">                      <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">1/*#VTs*/,</span> MVT::i32, 5/<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>#Ops<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/, 1, 2, 3, 4, 5,</span>
<span id="cb12-7">                  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> Src: lea32addr:i32:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> Complexity = 18</span>
<span id="cb12-8">                  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> Dst: <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LEA32r:i32</span> lea32addr:i32:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span></code></pre></div></div>
<p>这是上面描述的 complex pattern 的对应的 match 表。 SelectLEAAddr 是一个 c++方法(由 X86 backend 的 ISel 的实现定义)， 它尝试将节点的操作数与 LEA 匹配。 下面的输出来自该方法，正如我们所看到的，最终会匹配失败。</p>
<p>最后，当解释器到达偏移量 58557 时，匹配成功。 以下是相关的表格部分:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb13-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58557*/</span>       /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>Scope<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/ 12, /<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>-<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>58570<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/</span>
<span id="cb13-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58558*/</span>         OPC_CheckType, MVT::i64,</span>
<span id="cb13-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">/*58560*/</span>         OPC_MorphNodeTo, TARGET_VAL<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">X86::IMUL64rr</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">,</span> 0,</span>
<span id="cb13-4">                      <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">2/*#VTs*/,</span> MVT::i64, MVT::i32, 2/<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>#Ops<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>/, 0, 1,</span>
<span id="cb13-5">                  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> Src: <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">mul:i64</span> GR64:i64:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src1</span>, GR64:i64:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src2</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">-</span> Complexity = 3</span>
<span id="cb13-6">                  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">//</span> Dst: <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">IMUL64rr:i64:i32</span> GR64:i64:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src1</span>, GR64:i64:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">$src2</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span></code></pre></div></div>
<p>简单地说，当匹配一系列优化和特殊情况失败后， 匹配器最终选择了一个通用的 64 位寄存器之间的整数乘法， 这个整数乘法与 <code>IMUL64rr</code> 指令相匹配。</p>
<p>debug 信息显示指令选择器寻找合适指令的过程非常复杂。 为了生成好的代码，必须先尝试匹配各种优化的指令序列，然后再回到通用指令序列。 在本文的下一部分，我将展示一些更高级的优化指令选择案例。</p>
</section>
<section id="最终代码" class="level2">
<h2 class="anchored" data-anchor-id="最终代码">最终代码</h2>
<p>下面的 DAG 是在指令选择后的样子：</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://sadsock.github.io/assets/a-deeper-look-into-the-llvm-code-generator-part-1/dag_imul_postisel.png" class="img-fluid figure-img"></p>
<figcaption>Selection DAG</figcaption>
</figure>
</div>
<p>由于 DAG 的入口是基本项，所以这一部分都很相似； 主要区别在于乘法和返回节点被选择为实际指令。</p>
<p>如果您还记得文章 the life of an instruction in LLVM 的内容， 那么在指令选择器选择指令之后，指令还将经历两个额外的版本。 发出的最终代码是:</p>
<pre class="assembly"><code>imul:                                   # @imul
      imulq   %rsi, %rdi
      movq    %rdi, %rax
      ret</code></pre>
<p>imulq 是<code>X86::IMUL64rr</code>的汇编(GAS 风格)表示。 它将函数的参数相乘(根据 AMD64 ABI，前两个整数是%rsi 和%rdi); 然后将结果移动到返回寄存器%rax。</p>
</section>
<section id="结论" class="level2">
<h2 class="anchored" data-anchor-id="结论">结论</h2>
<p>本文深入介绍了指令选择过程 - LLVM 代码生成器的关键部分。 虽然它使用了一个相对简单的例子， 但它应该包含足够的信息来初步了解所涉及的机制。 在本文的下一部分中，我将研究几个额外的示例， 通过这些示例，代码生成过程的其他方面应该会变得更加清晰。</p>
</section>
<section id="参考文献" class="level2">
<h2 class="anchored" data-anchor-id="参考文献">参考文献</h2>
<p>本文翻译自<a href="https://eli.thegreenplace.net/2013/02/25/a-deeper-look-into-the-llvm-code-generator-part-1/">A deeper look into the LLVM code generator, Part 1</a>， 如有侵权，请联系博主。</p>


</section>

 ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2021-8-27-deeper-look-llvm-codegen-0.html</guid>
  <pubDate>Fri, 27 Aug 2021 11:20:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(五)：算术指令</title>
  <link>https://sadsock.github.io/posts/2020-12-03-RISCW-5.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="算术指令" class="level2">
<h2 class="anchored" data-anchor-id="算术指令">算术指令</h2>
<p>现在，我们将把上一篇文章讨论的关于LLVM后端中的指令选择付诸实践。 我们将通过查看RISCW后端的算术指令(比如加法、减法和乘法)的具体实现来做到这一点。</p>
<p><strong>注意：</strong>这篇文章中讨论的代码可以在<a href="https://github.com/andresag01/llvm-project/commit/38ac00579c9e84cb4374d34da917a37142f072ca">这里</a>找到。</p>
<p>RISCW的算术指令相对简单， 在指令选择过程中仅需要配置指令选择器和合法化器，这主要需编写三部分代码。 首先，使用TableGen描述目标平台支持的指令集。 其次，通知LLVM需要合法化的算术运算和类型。 最后，添加自定义C++代码来增强指令选择器的功能。</p>
<p><strong>注意：</strong>在为不同体系结构实现指令选择时，总体思路大致相似，但是具体情况将有所不同。 例如，您的指令集可能具有有趣的/复杂的算术指令，例如长乘或多次累加，可能需要额外的代码来正确地支持这些指令。 另外，我们不会自定义优化，但是您可以查看其他后端有关自定义优化的内容，例如XCore和ARM有很多很好的例子。</p>
<p><strong>注意：</strong>这里显示的大部分代码最初来自LLVM现有的RISCV后端，但是为了照顾本教程的读者，它已经被大大简化了。</p>
<section id="tablegen" class="level3">
<h3 class="anchored" data-anchor-id="tablegen">TableGen</h3>
<p>TableGen是描述体系结构（包括寄存器，指令集，调用约定等）的专用编程语言， 其目标是创建可维护、易于阅读的代码来描述体系结构。 在构建LLVM时，TableGen工具将TableGen代码翻译为C++并将其与手工编码的C++文件一起编译。</p>
<p><strong>注意：</strong>实际上，我发现TableGen代码很难阅读，更难编码！</p>
<p>简而言之，TableGen是具有以下功能的声明性编程语言：</p>
<ul>
<li><p>有两个主要组件： <code>record</code>和<code>class</code>。 <code>record</code>是<code>class</code>的实例，包含了名称和相关字段的值。 <code>class</code>是<code>record</code>的抽象，可用于生成具体的<code>record</code>。 两者相互配合可以很好地提取公共代码并减少重复。</p></li>
<li><p><code>record</code>可以从一个或多个<code>class</code>继承，还可以定义自己的字段。</p></li>
<li><p>字段具有名称和值（或值列表），值具有特定的类型（如bit或int）。<a href="https://llvm.org/docs/TableGen/ProgRef.html#types">这是</a>类型列表。</p></li>
<li><p><code>include</code>指令用于将TableGen代码从一个文件包含到另一个文件中，就像C的<code>#include</code>一样。</p></li>
</ul>
<p>以下是从LLVM<a href="https://llvm.org/docs/TableGen/ProgRef.html#examples-classes-and-records">文档</a>中获取的示例，其中显示了一个非常简单的TableGen文件。 它包含一个<code>class</code>C，该<code>class</code>定义了值为1的bit类型的字段V， 还声明了一个从类C派生的记录X。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb1-1">class C {</span>
<span id="cb1-2">  bit V = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-3">}</span>
<span id="cb1-4">def X : C<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>后端需要定义TableGen的<code>record</code>，用以声明指令、寄存器等。 这些<code>record</code>必须从内部类继承，这样TableGen工具才能魔术地为这些<code>record</code>生成适当的C++代码。 例如，声明寄存器的<code>record</code>必须从内部的Register类继承。 在这篇文章中，我们将查看声明寄存器和指令的TableGen代码。</p>
<p><strong>注意:</strong>TableGen文档在解释它是什么和语法方面做得很好。 但是，介绍后端应该重用的内部类和预定义记录的文档却很少；要了解这些内容您必须查看源代码。</p>
<p><strong>注意:</strong>回想一下之前的文章， 我们的CMake文件使用带各种参数的TableGen命令把扩展名为<code>.td</code>的TableGen代码文件转化为扩展名为<code>.inc</code>的C++文件。 在构建系统发出TableGen命令之后，可以在<code>build/lib/Target/RISCW</code>的构建目录中找到生成的文件。 Tablegen的参数可以<a href="https://llvm.org/docs/CommandGuide/tblgen.html">这里</a>(非常简短地)查看。</p>
<p><strong>注意:</strong>TableGen还可以配置指令选择过程中的调度和形成阶段，但是我不会在本教程中讨论这一点。 如果你感兴趣，可以看看<a href="https://www.youtube.com/watch?v=brpomKUynEA&amp;ab_channel=LLVM">这个视频</a>。</p>
</section>
<section id="定义寄存器" class="level3">
<h3 class="anchored" data-anchor-id="定义寄存器">定义寄存器</h3>
<p>RISCW的寄存器定义非常简单， 可以在<code>llvm/lib/Target/RISCW/RISCWRegisterInfo.td</code>中找到。 让我们对其进行剖析，以了解TableGen的工作方式。</p>
<p>在文件的顶部，我们找到以下类。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb2-1">let Namespace = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RISCW"</span> in {</span>
<span id="cb2-2">class RISCWReg&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; Enc, string n, list&lt;string&gt; alt = []&gt; : Register&lt;n&gt; {</span>
<span id="cb2-3">  let HWEncoding{<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4-0</span>} = Enc<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-4">  let AltNames = alt<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-5">}</span>
<span id="cb2-6">} // <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span> Namespace</span></code></pre></div></div>
<p>该代码声明了一个<code>RISCWReg</code>类，该类继承自在<code>include/llvm/Target/Target.td</code>中定义的内部类Register。 该代码还告诉我们，从此类继承时，我们必须提供最多三个参数：</p>
<ul>
<li><p>寄存器编码<code>Enc</code>，它是类型为<code>bits&lt;5&gt;</code>的5位整数。</p></li>
<li><p>字符串<code>n</code>，表示人类可读的寄存器名称，如RISCV中的<code>x0</code>、<code>x1</code>等。</p></li>
<li><p>寄存器的别名列表，该列表是可选的。 例如，RISCV中的x2也可以称为sp，即堆栈指针。 但请注意，此列表是可选的，因为可以声明没有别名的寄存器，如果寄存器没有别名，则把<code>alt</code>设置<code>[]</code>。</p></li>
</ul>
<p>另外，请注意Register类接受一个参数：类型为字符串的寄存器名称。 RISCWReg的其余两个参数（<code>Enc</code>和<code>alt</code>）被<code>let</code>语句用于覆盖Register类定义的<code>HWEncoding</code>和<code>AltNames</code>域。 您可以在<a href="https://github.com/andresag01/llvm-project/blob/38ac00579c9e84cb4374d34da917a37142f072ca/llvm/include/llvm/Target/Target.td#L180">这里</a>和<a href="https://github.com/andresag01/llvm-project/blob/38ac00579c9e84cb4374d34da917a37142f072ca/llvm/include/llvm/Target/Target.td#L136">这里</a>阅读Register的代码。</p>
<p>接下来，我们在该文件中找到以下Register定义。 每一行代码都是一个<code>record</code>，它定义了一个寄存器。 这些<code>record</code>继承自两个类:<code>RISCWReg</code>(我们已经讨论过了)和<code>DwarfRegNum</code>。 <code>DwarfRegNum</code>是这里定义的另一个内部类，用于为GCC和GDB提供调试信息。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb3-1">def X0  : RISCWReg&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x0"</span>, [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"zero"</span>]&gt;, DwarfRegNum&lt;[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb3-2">...</span>
<span id="cb3-3">def X31 : RISCWReg&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x31"</span>, [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"t6"</span>]&gt;, DwarfRegNum&lt;[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31</span>]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>文件的最后几行定义寄存器组， RISCW定义了两种寄存器组：<code>GPR</code>通用寄存器和堆栈指针<code>SP</code>寄存器。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb4-1">def GPR : RegisterClass&lt;<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RISCW"</span>, [<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span></span>
<span id="cb4-2">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">17</span>),</span>
<span id="cb4-3">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>),</span>
<span id="cb4-4">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">28</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31</span>),</span>
<span id="cb4-5">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>),</span>
<span id="cb4-6">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">18</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">27</span>),</span>
<span id="cb4-7">    (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>)</span>
<span id="cb4-8">  )&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-9"></span>
<span id="cb4-10">def SP : RegisterClass&lt;<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RISCW"</span>, [<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span> X2)&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>定义Register组的<code>record</code>继承自RegisterClass类，后者具有四个参数（还有一个可选的第五个参数，我们将不讨论）：</p>
<ul>
<li><p>命名空间在我们的例子中是RISCW，这与我们在上面的RISCWReg类中重写的名称空间字段相匹配。</p></li>
<li><p>此组寄存器支持的数据类型列表。 这是一个列表，因为某些体系结构中的寄存器可以支持多种数据类型。 例如，一些64位计算机的寄存器可以在32位和64位模式下工作。 RISCW只适用于32位机器，因此寄存器总是32位，它们的类型就是i32。</p></li>
<li><p>从内存中存储或加载寄存器时寄存器的对齐方式。</p></li>
<li><p>一个DAG，指示此寄存器组包含的寄存器。还有…</p>
<ul>
<li><p>我是说DAG！注意，<code>GPR</code>的DAG有一个<code>ADD</code>模式，该模式有6个<code>sequence</code>节点。 <code>sequence</code>是一种操作，它接受字符串格式参数以及起始值和结束值。 序列中的每个元素都是TableGen工具根据格式参数指定的格式生成。 所以，这个DAG的另一种写法是(add X10, X17,…, X3, X4)。</p></li>
<li><p>此DAG还指定了寄存器分配器使用寄存器的顺序。 例如，如果两者都可用，分配器将优先使用来自GPR的<code>x10</code>而不是<code>x4</code>。</p></li>
</ul></li>
</ul>
<p>寄存器组稍后用于配置合法化器。</p>
</section>
<section id="定义指令" class="level3">
<h3 class="anchored" data-anchor-id="定义指令">定义指令</h3>
<p>计算机体系结构使用一组格式对指令进行编码。 例如，RISCV体系结构使用32位编码和4种基本指令格式来编码指令<sup>3</sup>。 此外，每种格式都有一组唯一的操作码(或操作码)。 然后使用格式和操作码对特定指令进行编码，这使处理器能够识别指令并确定它们的操作数。</p>
<p>在LLVM中，定义指令的方式与定义指令集编码的方式类似。 定义指令的代码通常由两部分组成: 格式定义和指令定义。</p>
<section id="定义格式" class="level4">
<h4 class="anchored" data-anchor-id="定义格式">定义格式</h4>
<p>定义格式的同时也定义了唯一的标识符， 这些标识符指示指令的格式， C++代码使用它来正确地发出指令编码。 RISCW后端定义格式的代码，如下所示:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb5-1">class InstFormat&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; val&gt; {</span>
<span id="cb5-2">  bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; Value = val<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-3">}</span>
<span id="cb5-4">def InstFormatPseudo : InstFormat&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-5">def InstFormatR      : InstFormat&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-6">...</span>
<span id="cb5-7">def InstFormatOther  : InstFormat&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">17</span>&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p><strong>注意：</strong> 在LLVM后端中，定义指令格式的代码通常在文件<code>llvm/lib/Target/&lt;BACKEND&gt;/&lt;BACKEND&gt;InstrFormats.td</code>中， 而实际定义指令的代码在文件<code>llvm/lib/Target/&lt;BACKEND&gt;/&lt;BACKEND&gt;InstrInfo.td</code>中。 但是在复杂的后端中， 指令定义可以拆分为多个文件， 例如，RISCV的每个扩展都对应一个不同的文件。 这些文件称为<code>llvm/lib/Target/RISCV/RISCVInstrInfo&lt;EXT&gt;.td</code>， 其中<code>&lt;EXT&gt;</code>是扩展字母，如M，A等。 RISCW后端相对简单， 用于定义格式和指令的TableGen代码分别存储在<code>llvm/lib/Target/RISCW/RISCWInstrFormats.td</code>和<code>llvm/lib/Target/RISCWInstrInfo.td</code>中。</p>
<p><strong>注意:</strong>RISCW后端基于RISCV后端。 两者都试图严格地根据RISCV的参考手册定义指令， 但这不是必须的。 你也可以针对你的体系结构和编译器构造只属于你的TableGen代码。</p>
<p>RISCW后端为操作码定义了如下<code>record</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb6-1">class RISCWOpcode&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>&gt; val&gt; {</span>
<span id="cb6-2">  bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>&gt; Value = val<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb6-3">}</span>
<span id="cb6-4">def OPC_LOAD      : RISCWOpcode&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b0000011&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb6-5">def OPC_LOAD_FP   : RISCWOpcode&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b0000111&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb6-6">...</span>
<span id="cb6-7">def OPC_SYSTEM    : RISCWOpcode&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b1110011&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>所有指令必须继承自LLVM内部的<code>Instruction</code>类。 方便起见，RISCW后端还定义了<code>Instruction</code>的子类<code>RWInst</code>， 它覆盖了许多字段，例如<code>Size</code>和<code>TSFlags</code>，同时添加了额外的字段。 <code>Instruction</code>类实际上非常庞大， 需要配置许多选项， 我建议您通读LLVM的源代码以了解什么样的配置可以满足你的需求。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb7-1">class RWInst&lt;dag outs, dag ins, string opcodestr, string argstr,</span>
<span id="cb7-2">             list&lt;dag&gt; pattern, InstFormat format&gt;</span>
<span id="cb7-3">    : Instruction {</span>
<span id="cb7-4">  field bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>&gt; Inst<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-5">  field bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>&gt; SoftFail = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-6">  let Size = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-7">  ...</span>
<span id="cb7-8">  let TSFlags{<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4-0</span>} = format.Value<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-9">}</span></code></pre></div></div>
<p><strong>注意：</strong><code>Instruction</code>类中的许多字段仅对特定任务有用。 例如，仅当发出目标代码时才使用<code>Inst</code>字段和<code>opcode</code>。 因此，不要理会您不需要的东西！</p>
<p>再次为方便起见， RISCW后端为每种格式定义了<code>RWInst</code>类的子类。 我们实际的指令将继承这些“低级的”格式，以避免代码重复。</p>
<p>格式类根据相关格式的编码覆盖<code>Inst</code>字段的值。 另外，还有一个特殊的<code>Pseudo</code>类，用于设置<code>Instruction</code>类中的<code>isPseudo</code>字段。 这些pseudo指令通常是栈调整和函数返回等操作的占位符， 我们将在后面的文章中进行探讨。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb8-1">class Pseudo&lt;dag outs, dag ins, list&lt;dag&gt; pattern, string opcodestr = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>,</span>
<span id="cb8-2">             string argstr = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>&gt;</span>
<span id="cb8-3">    : RWInst&lt;outs, ins, opcodestr, argstr, pattern, InstFormatPseudo&gt;,</span>
<span id="cb8-4">      Sched&lt;[]&gt; {</span>
<span id="cb8-5">  let isPseudo = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-6">  let isCodeGenOnly = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-7">}</span>
<span id="cb8-8"></span>
<span id="cb8-9">class RWInstR&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>&gt; funct7, bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>&gt; funct3, RISCWOpcode opcode, dag outs,</span>
<span id="cb8-10">              dag ins, string opcodestr, string argstr&gt;</span>
<span id="cb8-11">    : RWInst&lt;outs, ins, opcodestr, argstr, [], InstFormatR&gt; {</span>
<span id="cb8-12">  bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; rs2<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-13">  bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; rs1<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-14">  bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>&gt; rd<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-15"></span>
<span id="cb8-16">  let Inst{<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31-25</span>} = funct7<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-17">  ...</span>
<span id="cb8-18">  let Opcode = opcode.Value<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb8-19">}</span>
<span id="cb8-20">...</span></code></pre></div></div>
<p>关于这些TableGen类，有几点需要强调：</p>
<ul>
<li><p><code>funct *</code>和<code>opcode</code>参数用于形成唯一的操作码，该操作码用于将指令编码为二进制。</p></li>
<li><p><code>outs</code>和<code>ins</code>参数是DAG，分别指定指令的输出和输入操作数。 操作数通常是寄存器或立即数，但也可以是堆栈帧位置，全局地址等。</p></li>
<li><p><code>opcodestr</code>是指令的助记符，例如<code>ADD</code>用于加法，<code>SUB</code>用于减法，等等。</p></li>
<li><p><code>argstr</code>参数是一种格式字符串，用于告诉LLVM如何在汇编中打印指令的操作数。 例如，如果<code>outs</code>参数说有一个<code>$r1</code>寄存器操作数， 而<code>ins</code>参数说有一个<code>$r2</code>寄存器操作数并且<code>argstr</code>的格式为<code>"$r1,$r2"</code>， 则该指令的汇编将首先显示输出操作数（即紧随助记符之后）然后显示<code>,</code>，然后显示输入操作数。</p></li>
<li><p>在<code>Pseudo</code>中，还有一个<code>pattern</code>操作数， 它告诉LLVM在目标指令选择阶段可以用该指令替换DAG中的哪些节点。 更详细的细节以后再讨论！</p></li>
</ul>
</section>
<section id="定义指令-1" class="level4">
<h4 class="anchored" data-anchor-id="定义指令-1">定义指令</h4>
<p>为了方便起见，还定义了另一个类， 该类可以根据操作数以及操作是否涉及ALU、内存等来简化指令的定义。 例如，我们有下面的<code>ALU_rr</code>类来定义使用ALU（算数逻辑单元）的指令， 该类有三个GPR类型的操作数:两个是输入，一个是输出。 显然，所有<code>ALU_rr</code>指令都是R格式的，因为该类继承自 RWInstR。 另外，<code>ALU_rr</code>的定义位于let块中， 它覆盖了<code>Instruction</code>中值为0的<code>hasSideEffects</code>、<code>mayLoad</code>和<code>mayStore</code>字段； 这些字段中的大多数都是不言自明的，不过我鼓励您阅读代码以获得更多信息。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb9-1">let hasSideEffects = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayLoad = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayStore = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> in</span>
<span id="cb9-2">class ALU_rr&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>&gt; funct7, bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>&gt; funct3, string opcodestr&gt;</span>
<span id="cb9-3">    : RWInstR&lt;funct7, funct3, OPC_OP, (outs <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rd), (ins <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs2),</span>
<span id="cb9-4">              opcodestr, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$rd, $rs1, $rs2"</span>&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>通过继承<code>ALU_rr</code>类来定义指令实际上非常简单。 例如，以下是定义<code>ADD</code>指令的代码：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb10-1">def ADD : ALU_rr&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b0000000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b000, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"add"</span>&gt;, Sched&lt;[WriteIALU, ReadIALU, ReadIALU]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>TableGen代码很难理解，因为大多数后端都使用深层继承来减少代码重复。 您可能需要拿出笔和纸来手动展开一些记录，以了解其工作原理。 例如，展开<code>ADD</code>的第一级看起来如下所示。 这个较长版本的<code>ADD</code>也是有效的TableGen代码， 将以前版本的<code>ADD</code>替换为这个较长的版本， LLVM仍然可以毫无问题地构建！</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb11-1">let hasSideEffects = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayLoad = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayStore = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> in</span>
<span id="cb11-2">def ADD : RWInstR&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b0000000, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>b000, OPC_OP, (outs <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rd),</span>
<span id="cb11-3">              (ins <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs2), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"add"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$rd, $rs1, $rs2"</span>&gt;,</span>
<span id="cb11-4">          Sched&lt;[WriteIALU, ReadIALU, ReadIALU]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>定义带有立即数(而不是寄存器)的指令的方法大致相同， 但必须调整输入操作数。 例如，下面显示的<code>ALU_ri</code>类的第二个输入是名为<code>$imm12</code>的12位立即数(类型为simm12)。 simm12的定义证实该类的类型为i32还指明了编译器后端处理该操作数时， 解码器和编码器使用的C++类。 最后，simm12继承自ImmLeaf类， 该类带有一个必须满足的条件， 指令选择期间， DAG中的叶节点必须满足该条件才能匹配simm12类(稍后将详细介绍!)。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb12-1">def simm12 : Operand&lt;<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>&gt;, ImmLeaf&lt;<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>, [{return isInt&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>&gt;(Imm)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;}]&gt; {</span></span>
<span id="cb12-2">  let ParserMatchClass = SImmAsmOperand&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-3">  let EncoderMethod = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"getImmOpValue"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-4">  let DecoderMethod = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"decodeSImmOperand&lt;12&gt;"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-5">  let MCOperandPredicate = [{</span>
<span id="cb12-6">    int64_t Imm<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-7">    if (MCOp.evaluateAsConstantImm(Imm))</span>
<span id="cb12-8">      return isInt&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>&gt;(Imm)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-9">    return MCOp.isBareSymbolRef()<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-10">  }]<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-11">  let OperandType = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"OPERAND_SIMM12"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-12">  let OperandNamespace = <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RISCWOp"</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-13">}</span>
<span id="cb12-14"></span>
<span id="cb12-15">...</span>
<span id="cb12-16"></span>
<span id="cb12-17">let hasSideEffects = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayLoad = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, mayStore = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> in</span>
<span id="cb12-18">class ALU_ri&lt;bits&lt;<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>&gt; funct3, string opcodestr&gt;</span>
<span id="cb12-19">    : RWInstI&lt;funct3, OPC_OP_IMM, (outs <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rd), (ins <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simm12:</span>$imm12)</span>
<span id="cb12-20">              opcodestr, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$rd, $rs1, $imm12"</span>&gt;,</span>
<span id="cb12-21">      Sched&lt;[WriteIALU, ReadIALU]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>在一些计算机体系结构中（如RISCV）有一些指令是其他指令的别名。 通常，这样做只是为了方便或提高汇编代码的可读性。 例如，RISCV没有专门的寄存器-寄存器移动指令，只能使用不带立即数的<code>ADDI</code>指令代替。 我们可以通过<code>InstAlias</code>和<code>MnemonicAlias</code>类告诉LLVM我们的别名:</p>
</section>
</section>
<section id="合法化" class="level3">
<h3 class="anchored" data-anchor-id="合法化">合法化</h3>
<p>类型和操作的合法化阶段均在后端的<code>TargetLowering</code>子类中使用C++代码进行配置。 在RISCW中，此类为<code>RISCWTargetLowering</code>，其代码在<code>lib/Target/RISCW/RISCWISelLowering.cpp</code>和<code>lib/Target/RISCW/RISCWISelLowering.h</code>中。 我们为支持算术指令所做的大多数更改实际上都在构造函数中。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RISCWTargetLowering:</span>:RISCWTargetLowering(const TargetMachine &amp;TM,</span>
<span id="cb13-2">                                         const RISCWSubtarget &amp;STI)</span>
<span id="cb13-3">    : TargetLowering(TM), Subtarget(STI)</span>
<span id="cb13-4">{</span>
<span id="cb13-5">  addRegisterClass(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">MVT:</span>:<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>, &amp;<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RISCW:</span>:GPRRegClass)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-6">  ...</span>
<span id="cb13-7"></span>
<span id="cb13-8">  setSchedulingPreference(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sched:</span>:RegPressure)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-9">  ...</span>
<span id="cb13-10"></span>
<span id="cb13-11">  setOperationAction(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ISD:</span>:SRA_PARTS, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">MVT:</span>:<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>, Custom)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-12">  ...</span>
<span id="cb13-13"></span>
<span id="cb13-14">  setOperationAction(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ISD:</span>:ROTL,  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">MVT:</span>:<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>, Expand)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-15">  ...</span>
<span id="cb13-16">}</span></code></pre></div></div>
<p>有几件事值得注意：</p>
<ul>
<li><p><code>addRegisterClass</code>用于配置LLVM寄存器组和对应的寄存器类型，此信息用于类型合法化。</p></li>
<li><p><code>setSchedulingPreference</code>用于配置调度和形成阶段。这里我们告诉LLVM使用一个最小化寄存器压力的算法。</p></li>
<li><p><code>setOperationAction</code>用于配置LLVM如何使操作合法化，即合法化Section DAG中的节点。 回想一下，有三个选项：扩展、提升和自定义。 LLVM会自动处理前两个操作， 例如，RISCV没有位旋转指令，因此我们要求编译器将该操作扩展为其他操作，如移位和<code>ors</code>以模拟旋转。 标记为自定义的操作在<code>TargetLowering</code>子类中由C++代码处理。</p></li>
</ul>
<p>每当LLVM在合法化过程中遇到具有custom操作的DAG节点时， 都会生成对<code>TargetLoweing</code>类的<code>LowerOperation</code>方法的回调。 例如，<code>SHL_PARTS</code>操作用于将64位整数左移。 我们通过构造函数中的<code>setOperationAction(ISD:：SHL_PARTS，MVT:：i32，Custom)</code>来告诉LLVM， 我们实现了一个<code>LowerShlParts</code>函数来来合法化此操作。 <code>LowerShlParts</code>用32位移位和<code>ors</code>等其他合法操作来代替<code>SHL_PARTS</code>节点。 我鼓励您看看代码，看看这是如何工作的！</p>
<p><strong>注意</strong>：需要合法化的操作完全取决于后端实现的指令集， 以及TableGen代码中DAG节点与指令匹配的程度（下一节将对此进行详细介绍！）。 如果使某些操作合法化太复杂或发出的代码效率低下， 则说明您的指令集可能缺少某些指令！</p>
</section>
<section id="目标指令选择" class="level3">
<h3 class="anchored" data-anchor-id="目标指令选择">目标指令选择</h3>
<p>目标指令选择阶段将后端TableGen文件中提供的DAG模式匹配到Section DAG中的节点。 匹配成功时，Section DAG中的节点将被替换为具体机器(或伪)指令的节点。 因此，TableGen定义的模式的质量对于发出好的代码至关重要， 您应该多花费些时间来调整这些模式!</p>
<p><strong>注意：</strong>TableGen模式通常定义在<code>llvm/lib/Target/&lt;BACKEND&gt;/&lt;BACKEND&gt;InstrInfo.td</code>文件的末尾。</p>
<p>模式records继承自LLVM的<code>Pat</code>类， 该类有两个参数， 第一个参数是一个包含要匹配的模式DAG，第二个参数是一个带有机器指令的DAG。 当模式匹配DAG中的某个内容时，匹配的节点被第二个参数中的DAG替换。</p>
<p>不出所料，我们将使用一些类来帮助我们简化模式的定义。 这里有两个这样的类：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb14-1">class PatGprGpr&lt;SDPatternOperator OpNode, RWInst Inst&gt;</span>
<span id="cb14-2">    : Pat&lt;(OpNode <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs2), (Inst <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs2)&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb14-3">class PatGprSimm12&lt;SDPatternOperator OpNode, RWInstI Inst&gt;</span>
<span id="cb14-4">    : Pat&lt;(OpNode <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simm12:</span>$imm12), (Inst <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GPR:</span>$rs1, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simm12:</span>$imm12)&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p><code>PatGprGpr</code>用带有两个通用寄存器操作数作为输入的指令替换DAG中的节点。 例如，下面是<code>ADD</code>指令的模式声明:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb15-1">def : PatGprGpr&lt;<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span>, ADD&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p><code>PatGprSimm12</code>将DAG中的节点替换为具有一个通用输入操作数和一个12位立即数的指令。 为了实现这一点，用<code>simm12</code>取代了<code>Pat</code>中第一个和第二个参数中的<code>GPR</code>。 回想一下，<code>simm12</code>是我们在本文前一节中讨论过的一个<code>record</code>。 它继承自<code>Operand</code>类，因此可以在指令的定义中使用。 <code>simm12</code>也继承自<code>ImmLeaf</code>类，因此也可以在模式定义中使用。 下面是<code>ADDI</code>指令的定义，该指令接受一个立即数:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb16-1">def : PatGprSimm12&lt;<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span>, ADDI&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p><strong>注意：</strong><code>ImmLeaf</code>类是用于匹配立即数的模式。 它的参数是即时类型，例如<code>I32</code>、<code>I16</code>以及谓词等。 谓词即C++代码块， 它检查指定模式必须满足的条件。 例如，<code>simm12</code>有一个谓词<code>return isInt&lt;12&gt;(Imm)</code>来检查整数是否在12位范围内。</p>
<p><strong>注意：</strong>有时，同一条指令可以与Selection DAG中的多个模式匹配。 在这种情况下，使用LLVM的<code>PatFrag</code>类和<code>Pat</code>来避免代码重复， 如RISCW的<code>bit-shift</code>模式。</p>
<p><strong>注意:</strong>RISCW和RISCV后端首先声明指令， 并分别提供模式record。 然而，其他后端，如ARM或XCore，以不同的方式创建Instruction的子类，以便它们接受一个模式参数。 这种方法减少了代码行数，因为模式可以在定义指令的同一record中提供， 但在我看来，这使得TableGen代码更难阅读(也更难解释!)</p>
<p>当在DAG中找到匹配时， Pat的第二个参数也可以转换操作数(通常是立即数)。 为此，我们首先需要声明一个继承自<code>SDNodeXForm</code>的操作节点。 <code>SDNodeXForm</code>以实现所需转换的一段c++代码作为参数。 然后，我们在模式中使用新声明的节点，如下面的示例所示。 在本例中，HI20节点提取立即数的20个最重要的比特位。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb17-1">def HI20 : SDNodeXForm&lt;imm, [{</span>
<span id="cb17-2">  return CurDAG-&gt;getTargetConstant(((N-&gt;getZExtValue()+<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x800</span>) &gt;&gt; <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>) &amp; <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0xfffff</span>,</span>
<span id="cb17-3">                                   SDLoc(N), N-&gt;getValueType(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>))<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb17-4">}]&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb17-5"></span>
<span id="cb17-6">def : Pat&lt;(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">simm32hi20:</span>$imm), (LUI (HI20 <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">imm:</span>$imm))&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p><strong>注意：</strong><code>SDNodeXForm</code>节点不插入实际指令。 它们通常用于转换即时数据，因此可以在代码发出之前就完全解析。</p>
<p>随着指令选择的进行，LLVM每个<code>Selection DAG</code>节点调用<code>SelectionDAGISel</code>子类的<code>Select</code>方法。 因此，后端可以在<code>Select</code>方法中提供代码， 实现复杂的模式匹配， 而这种匹配在TableGen中很难表达。 例如，RISCW的<code>Select</code>方法包括用<code>x0</code>寄存器来替换常量0的代码。</p>
<p><strong>注意:</strong><code>SelectionDAGISel</code>子类通常在文件<code>llvm/lib/Target/&lt;backend&gt;/&lt;backend&gt;ISelDAGToDAG.h</code>和<code>llvm/lib/Target/&lt;backend&gt;/&lt;backend&gt;ISelDAGToDAG.cpp</code>中。 例如，RISCW后端相关代码在<code>llvm/lib/Target/RISCW/RISCWISelDAGToDAG.h</code>和<code>llvm/lib/Target/RISCW/RISCWISelDAGToDAG.cpp</code>中。</p>
</section>
</section>
<section id="结束语" class="level2">
<h2 class="anchored" data-anchor-id="结束语">结束语</h2>
<p>实际上有很多方法可以实现一个体系结构的LLVM后端， 所以在使用任何一种方法之前，请确保评估最适合您的需求的方法。 另外，请记住下面的TableGen类的简介，在编写后端时几乎肯定需要这些类。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode c++ code-with-copy"><code class="sourceCode cpp"><span id="cb18-1">Register        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Register declarations</span></span>
<span id="cb18-2"></span>
<span id="cb18-3">RegisterClass   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Register Class declarations</span></span>
<span id="cb18-4"></span>
<span id="cb18-5">Instruction     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Instruction declarations</span></span>
<span id="cb18-6"></span>
<span id="cb18-7">Operand         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Instruction operands</span></span>
<span id="cb18-8"></span>
<span id="cb18-9">ImmLeaf         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Pattern-matching immediates</span></span>
<span id="cb18-10"></span>
<span id="cb18-11">SDNodeXForm     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Transforming immediates after a match</span></span>
<span id="cb18-12"></span>
<span id="cb18-13">Pat             <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Providing a pattern to match in the Selection DAG</span></span>
<span id="cb18-14"></span>
<span id="cb18-15">PatFrag         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//Providing a pattern fragment with multiple patterns to match in the Selection DAG</span></span></code></pre></div></div>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
<li id="fn3"><p>关于RISCV指令格式的完整描述见参考手册的第2章↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-03-RISCW-5.html</guid>
  <pubDate>Fri, 11 Dec 2020 03:30:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(四)：指令选择</title>
  <link>https://sadsock.github.io/posts/2020-12-08-RISCW-4.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="指令选择" class="level2">
<h2 class="anchored" data-anchor-id="指令选择">指令选择</h2>
<p>我们先前简要介绍了LLVM后端的工作过程。 在这篇文章中，我们将更深入地了解这个过程的第一个阶段：指令选择。 我们的目的是在了解RISCW后端的具体实现之前，了解它的工作原理以及如何配置它。</p>
<p><strong>注意：</strong><a href="https://llvm.org/docs/CodeGenerator.html#instruction-selection-section">LLVM文档</a>对指令选择的工作原理给出了简短而清晰的描述， 这篇文章通过示例来重新说明这一点。</p>
<p><strong>注意：</strong>本文中显示的示例是使用我们的RISCW后端框架构建的。可以在<a href="https://github.com/andresag01/llvm-project/commit/274cfea0f9662f0ed49f6132b0424323d0b11750">这里</a>找到它的来源。</p>
<p>指令选择过程将LLVM IR转化为指令序列，该指令序列使用了无穷数量的寄存器。 该过程分为以下几个阶段： 1. 构建初始DAG 2. 优化 3. 类型合法化 4. 优化 5. 操作合法化 6. 优化 7. 目标指令选择 8. 调度和形成</p>
<p>我觉得通过例子来了解发生的事情会比较容易，我们将考虑指令选择如何转换下面的C程序。 该代码包含一个MUL函数，该函数接受64位参数x和32位参数y。 参数相乘，并返回32位整数结果。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode c++ code-with-copy"><code class="sourceCode cpp"><span id="cb1-1"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> MUL<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">long</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-2"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb1-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-4"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<section id="构建dag" class="level3">
<h3 class="anchored" data-anchor-id="构建dag">构建DAG</h3>
<p>这是指令选择的第一阶段。 它接受LLVM IR作为输入， 并产生Selection DAG(有向无环图)作为输出。 指令选择过程中的每个其他阶段都在DAG上执行，直到产生输出指令序列。 正如前面几篇文章所讨论的，LLVM IR是前端工具(如Clang)根据C代码生成，随后由LLVM优化器进行优化。 下面是C程序的LLVM IR:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">define</span> dso_local <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">@MUL</span>(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i64</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%x</span>, <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%y</span>) local_unnamed_addr #<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> {</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">entry:</span></span>
<span id="cb2-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%0</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">trunc</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i64</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%x</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">to</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span></span>
<span id="cb2-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%conv1</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">mul</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%0</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%y</span></span>
<span id="cb2-5">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">ret</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%conv1</span></span>
<span id="cb2-6">}</span></code></pre></div></div>
<p>Selection DAG实际上是一种精巧的树型数据结构，它表示LLVM IR中的基本块。 基本块是不包含分支目的地（入口除外）和分支指令（出口除外）的指令序列。 示例MUL函数非常简单，它只有一个称为入口的基本块，其他函数通常具有多个基本块。 例如，下面的<code>hello</code>函数具有四个基本块：<code>entry</code>，<code>if.then</code>，<code>if.else</code>和<code>return</code>。 每个基本块都将被转换为单独的DAG。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">define</span> dso_local <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">@hello</span>(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%x</span>) local_unnamed_addr #<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> {</span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">entry:</span></span>
<span id="cb3-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%cmp</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">icmp</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">eq</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%x</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span></span>
<span id="cb3-4">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">br</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i1</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%cmp</span>, <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">label</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%if.then</span>, <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">label</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%if.else</span></span>
<span id="cb3-5"></span>
<span id="cb3-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if.then:</span>                                          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; preds = %entry</span></span>
<span id="cb3-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%call</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">tail</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">call</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">bitcast</span> (<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> (...)* <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">@hello100</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">to</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> (<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>)*)(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>) #<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb3-8">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">br</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">label</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%return</span></span>
<span id="cb3-9"></span>
<span id="cb3-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">if.else:</span>                                          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; preds = %entry</span></span>
<span id="cb3-11">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%call1</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">tail</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">call</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">bitcast</span> (<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> (...)* <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">@helloOther</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">to</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> (<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>)*)(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%x</span>) #<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb3-12">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">br</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">label</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%return</span></span>
<span id="cb3-13"></span>
<span id="cb3-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">return:</span>                                           <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; preds = %if.else, %if.then</span></span>
<span id="cb3-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%retval.0</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">phi</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> [ <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%call</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%if.then</span> ], [ <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%call1</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%if.else</span> ]</span>
<span id="cb3-16">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">ret</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%retval.0</span></span>
<span id="cb3-17">}</span></code></pre></div></div>
<p>Selection DAG具有以下属性： + 每个节点都是<code>SDNode</code>类的实例，代表一个操作，如加、减、乘等。 操作类型都定义在文件<code>include/llvm/CodeGen/ISDOpcodes.h</code>中。 + 每个节点都有0个或多个操作数，操作数由其他节点定义，用指向该节点的边表示，边是<code>SDValue</code>类的实例。 + 操作产生的值的类型为<code>MTV</code>（Machine Value Type），比如<code>i1</code>和<code>i8</code>，它们分别表示1位和8位整数。 + 具有副作用的节点会强制对操作进行排序，例如<code>return</code>和<code>loads</code>语句，它们具有类型为<code>MVT::Other</code>的特殊chain值， 既作为输入操作数又作为输出操作数。 + <code>ISD::EntryToken</code>类型的叶节点是代码的入口。 + DAG的根节点是带有链操作数的最终副作用节点。这是的代码块的最后一个操作，例如函数结尾处的返回。</p>
<p><strong>警告！</strong>LLVM后端的类型系统非常有限。 当输入的LLVM IR被转换为DAG时，许多有用的类型信息被丢弃。 最值得注意的丢弃是指针类型，<code>MVT</code>类的类型列表完全没有包含指针类型。 因此，指针在DAG中使用整数类型表示，所以很难判断一个节点(如<code>add</code>)的操作数是指针还是整数。</p>
<p>这是我从MUL函数获得的初始Selection DAG。 <img src="https://sadsock.github.io/assets/riscw/llvm-4-dag-before-combine1.png" class="img-fluid" alt="Selection DAG"></p>
<p>入口节点位于图的顶部，而根节点位于底部。 还有一个由蓝色边连接的节点链，从根节点开始，到入口节点结束。 这些蓝边就是前面讨论过的链操作数。 黑色边显示的是值的流动，如整数和浮点数。 此Selection DAG的等效文本表示如下所示：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t0:</span> ch = EntryToken</span>
<span id="cb4-2">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t2:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>,ch = CopyFromReg t0, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Register:</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%0</span></span>
<span id="cb4-3">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t4:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>,ch = CopyFromReg t0, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Register:</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%1</span></span>
<span id="cb4-4">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t7:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i64</span> = build_pair t2, t4</span>
<span id="cb4-5">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t8:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> = truncate t7</span>
<span id="cb4-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t6:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>,ch = CopyFromReg t0, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Register:</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%2</span></span>
<span id="cb4-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t9:</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> = <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">mul</span> t8, t6</span>
<span id="cb4-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t11:</span> ch,glue = CopyToReg t0, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Register:</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> $x0, t9</span>
<span id="cb4-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t12:</span> ch = <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">RISCWISD:</span>:Ret t11, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Register:</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> $x0, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">t11:</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span></code></pre></div></div>
<p><strong>注意：</strong>Selection DAG可以同时包含目标独立和目标相关的节点。 目标独立的操作定义在文件<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/include/llvm/CodeGen/ISDOpcodes.h#L40">ISDOpcodes.h</a>中。 目标相关的操作由每个后端定义，通常定义在声明TargetLowering子类的同一文件中。 您可以在<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/lib/Target/RISCW/RISCWISelLowering.h#L25">llvm/lib/Target/RISCW/RISCWISelLowering.h</a>中找到目标相关节点的定义。 上面显示的用于MUL的Selection DAG具有节点t12，该节点代表目标相关操作<code>RISCWISD::Ret</code>，该函数表示从函数返回。</p>
<p><strong>注意：</strong>在指令选择过程的各个阶段，您可以通过向<code>llc</code>命令传递<code>-view-dag-combine1-dags</code>，<code>-view-legalize-dags</code>，<code>-view-dag-combine2-dags</code>参数告诉LLVM生成Selection DAG的可视化表示，还可以通过向<code>llc</code>命令传递<code>-debug</code>参数告诉LLVM生成Selection DAG的文本表示。</p>
</section>
<section id="优化" class="level3">
<h3 class="anchored" data-anchor-id="优化">优化</h3>
<p>指令选择阶段会有三次DAG优化过程，第一次优化发生在LLVM IR构建Selection DAG之后。 其余两次将在合法化阶段后执行。 根据LLVM文档，这些优化旨在简化可能由其他阶段(如合法化)生成的不必要的复杂Selection DAGs。</p>
<p>就我所知，LLVM的优化过程就是通过运行一个编译pass将一组节点组合成更简洁的节点。 为了实现这一点，LLVM用这个巨大的C++文件(&gt;20,000行)遍历DAG，并通过模式匹配来寻找优化机会。 例如，合并器可以将Selection DAG的<code>(add (add x, y) z)</code>节点，转换为<code>(add x，y + z)</code>节点，从而消除了<code>add</code>节点。</p>
<p>下图显示了MUL示例函数第一遍优化后的Selection DAG。 与上一节中的Selection DAG相比，优化的DAG用节点t13代替了节点t3和t4。 该优化将丢弃64位整数x的高32位，因为函数MUL的返回值仅依赖x的低32位， 因此，可以通过消除t3和t4简化DAG。 <img src="https://sadsock.github.io/assets/riscw/llvm-4-dag-before-legalize-types.png" class="img-fluid" alt="Selection DAG"></p>
<p>LLVM的DAG合成器只优化目标独立的操作，即<code>ISDOpcodes.h</code>中定义的<code>add</code>、<code>sub</code>、<code>load</code>、<code>store</code>等。 通过覆盖<code>targetlower</code>子类中的<code>PerformDAGCombine</code>函数， 后端可以为合成器提供额外的优化功能，这些功能可以是目标独立的也可以是目标依赖的。 此外，后端必须通知优化器受支持的目标独立的节点， 这通过在<code>targetlower</code>类的子类的构造函数中调用<code>setTargetDAGCombine</code>函数完成。</p>
<p><strong>注意：</strong>令人困惑的是，LLVM后端的<code>TargetLowering</code>子类通常在<code>&lt;TARGET_NAME&gt;ISelLowering.cpp</code>文件中实现。 您可以在<code>llvm/lib/Target/RISCW/RISCWISelLowering.cpp</code>中找到RISCW的实现。</p>
<p><strong>注意：</strong>XCore后端有一个很好的<code>PerformDAGCombine</code>和<code>setTargetDAGCombine</code>例子（参见<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/lib/Target/XCore/XCoreISelLowering.cpp#L1590">这里</a>和<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/lib/Target/XCore/XCoreISelLowering.cpp#L170">这里</a>） ——我的意思是很容易阅读。 另外，看看<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/lib/Target/XCore/XCoreISelLowering.cpp#L1739">这个后端</a>是如何将<code>add(add(mul(x，y)，a)，b)</code>节点组合到更简单的目标依赖的<code>lmul(x，y，a，b)</code>节点， 因为XCore架构有一个乘法累加指令<sup>3</sup>。</p>
</section>
<section id="类型和操作合法化" class="level3">
<h3 class="anchored" data-anchor-id="类型和操作合法化">类型和操作合法化</h3>
<p>上面显示的优化的DAG包含一个节点t7， 它会产生一个i64类型的值， 但是RISCW机器只支持32位。 合法化阶段会解决这些问题。</p>
<p>首先执行的是类型合法化， 它将DAG转换为仅使用本地计算机原生支持的数据类型。 为了实现这一点，它将小类型转换或提升为较大的类型； 例如，<code>i1</code>类型的1位整数转换为<code>i32</code>类型的32位整数。 此外，编译器将大整数分解或展开为较小的整数，例如在32位计算机中，<code>i64</code>类型的64位整数被转换为<code>i32</code>类型的32位整数。 下面的DAG是前面展示的DAG的合法化版本。 在本例中，编译器消除了t3和t7节点，以确保新的DAG只使用<code>i32</code>整数。</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://sadsock.github.io/assets/riscw/llvm-4-dag-before-combine2.png" class="img-fluid figure-img"></p>
<figcaption>Selection DAG</figcaption>
</figure>
</div>
<p>操作合法化在第二个优化阶段之后执行。 它将DAG转换为仅使用本地计算机原生支持的操作。 例如，DAG可能包含bit-rotate left（rotl）节点，但目标指令集可能不支持该指令， 因此，操作合法化把该操作转化为移位和或运算的联合操作。</p>
<p>有三种使操作合法化的策略。 首先，将不受支持的操作扩展为一组受支持的操作来模拟原不受支持的操作。 其次，把类型提升为更大的类型，以支持缺失的操作。 第三，使用TargetLowering类的子类中的钩子在C++中实现自定义的合法化。</p>
<p><strong>注意：</strong>在以后的文章中，我将解释后端如何配置合法化阶段。 这可以在TargetLowering类的子类的构造函数中完成。</p>
</section>
<section id="指令选择-1" class="level3">
<h3 class="anchored" data-anchor-id="指令选择-1">指令选择</h3>
<p>此时，DAG包含大部分目标独立（如加和减）节点，以及一小部分目标依赖节点（如<code>RISCWISD::Ret</code>）。 下一步，需要将这些抽象操作映射到目标架构的具体机器指令， LLVM在指令选择阶段会对此进行处理。 处理方法很简单：编译器通过模式匹配将DAG中的节点映射成机器指令。 指令的模式和描述由编译器后端的开发人员通过<a href="https://llvm.org/docs/TableGen/ProgRef.html">TableGen</a>代码提供。 另外，可以使用C++直接编写难以使用TableGen描述的复杂模式。</p>
<p>我们将在以后的文章中更仔细地研究TableGen。 现在，让我们考虑一下LLVM如何将合法且优化的DAG映射成下面显示的新DAG。 在这种情况下，只有两个节点需要更改。 t9用指令<code>MUL</code>替换了mul操作，而t12用指令<code>PseudoRet</code>替换了<code>RISCWISD::Ret</code>。</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://sadsock.github.io/assets/riscw/llvm-4-dag-before-scheduler.png" class="img-fluid figure-img"></p>
<figcaption>Selection DAG</figcaption>
</figure>
</div>
</section>
<section id="调度和形成" class="level3">
<h3 class="anchored" data-anchor-id="调度和形成">调度和形成</h3>
<p>谢天谢地，这里没什么好说的，因为帖子已经很长了！ 此阶段根据某些约束将DAG转换为指令列表。 例如，后端可以通过<code>TargetLowering</code>子类的构造函数调用<a href="https://github.com/andresag01/llvm-project/blob/274cfea0f9662f0ed49f6132b0424323d0b11750/llvm/include/llvm/CodeGen/TargetLowering.h#L1939">setSchedulingPreference</a>来指定调度选项。</p>
<p><strong>警告！</strong> 一定要尝试不同的调度首选项！ 如果调度策略与体系结构和处理器的特性不匹配，那么调度策略会显著降低所发出的代码的质量。 <a href="">这里</a>列出了各种调度选项。</p>
<p>下面是我们MUL函数的指令清单。 有几个重要的事情要注意。 首先，与实际机器的寄存器相反，生成的代码仍然使用一组无限多的虚拟寄存器； 编译器稍后会通过寄存器分配器来处理这个问题。 第二，已经存在一些对寄存器分配过程有用的生命周期信息；例如，有两个寄存器，即<code>x0</code>和<code>x2</code>， 它们在基本块的开始处就处于活动状态。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">bb.0.entry:</span></span>
<span id="cb5-2">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">liveins:</span> $x0, $x2</span>
<span id="cb5-3">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%2</span>:gpr = COPY $x2</span>
<span id="cb5-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%0</span>:gpr = COPY $x0</span>
<span id="cb5-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%3</span>:gpr = MUL <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%0</span>:gpr, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%2</span>:gpr</span>
<span id="cb5-6">  $x0 = COPY <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">%3</span>:gpr</span>
<span id="cb5-7">  PseudoRET implicit $x0</span></code></pre></div></div>
</section>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
<li id="fn3"><p>查看XCore XS1体系结构<a href="https://www.xmos.ai/download/The-XMOS-XS1-Architecture(X7879A).pdf">参考手册</a>，了解乘法累加（LMUL）指令的详细说明。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-08-RISCW-4.html</guid>
  <pubDate>Tue, 08 Dec 2020 11:30:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(三)：配置构建系统</title>
  <link>https://sadsock.github.io/posts/2020-12-06-RISCW-3.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="配置构建系统" class="level2">
<h2 class="anchored" data-anchor-id="配置构建系统">配置构建系统</h2>
<p>在前面，我们提到每个LLVM后端都有一个单独的目录<code>LLVM/lib/Target</code>，后端的大部分代码都在其中。 此外，LLVM依赖CMake为实际的构建系统(如Make、Ninja等)生成构建文件。 在这篇文章中，我们将仔细研究其中的一些CMake配置文件。</p>
<p>注意:RISCW后端代码可以在<a href="https://github.com/andresag01/llvm-project/commit/274cfea0f9662f0ed49f6132b0424323d0b11750">这里</a>找到。</p>
<section id="cmake配置文件" class="level3">
<h3 class="anchored" data-anchor-id="cmake配置文件">CMake配置文件</h3>
<p>回想一下，RISCW后端存放在<code>llvm/lib/Target/RISCW</code>目录下。 该目录及其子目录都有两个构建文件(<code>CMakeLists.txt</code>和<code>LLVMBuild.txt</code>)，描述后端模块的结构，提供链接信息等。 RISCW后端目前非常简单，目录结构是这样的:</p>
<pre><code>llvm/lib/Target/RISCW
|- CMakeLists.txt
|- LLVMBuild.txt
|- Other C++, TableGen, etc files
|- TargetInfo/
|  |- CMakeLists.txt
|  |- LLVMBuild.txt
|  |- Other C++, TableGen, etc files
|- MCTargetDesc/
   |- CMakeLists.txt
   |- LLVMBuild.txt
   |- Other C++, TableGen, etc files</code></pre>
<p>每个使用CMake的项目都会包含<code>CMakeLists.txt</code>文件<sup>3</sup>，RISCW也不例外。 这些文件包含一系列CMake指令，这些指令用于驱动构建文件的生成。 在LLVM后端中，CMake会根据这些指令执行一些操作，例如指示要编译的C++源文件或生成TableGen指令。 LLVMBuild.txt文件对当前目录中包含的组件提供了描述。</p>
</section>
<section id="llvmbuild.txt文件" class="level3">
<h3 class="anchored" data-anchor-id="llvmbuild.txt文件">LLVMBuild.txt文件</h3>
<p>让我们看一下<code>llvm/lib/Target/RISCW/LLVMBuild.txt</code>的内容。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode json code-with-copy"><code class="sourceCode json"><span id="cb2-1"><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">common</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb2-2"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">subdirectories</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">MCTargetDesc</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">TargetInfo</span></span>
<span id="cb2-3"></span>
<span id="cb2-4"><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">component_</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb2-5"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">type</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">TargetGroup</span></span>
<span id="cb2-6"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">name</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCW</span></span>
<span id="cb2-7"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">parent</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">Target</span></span>
<span id="cb2-8"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">has_asmprinter</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb2-9"></span>
<span id="cb2-10"><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">component_</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb2-11"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">type</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">Library</span></span>
<span id="cb2-12"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">name</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCWCodeGen</span></span>
<span id="cb2-13"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">parent</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCW</span></span>
<span id="cb2-14"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">required_libraries</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">AsmPrinter</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">CodeGen</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">Core</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">MC</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCWDesc</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCWInfo</span></span>
<span id="cb2-15">  <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">SelectionDAG</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">Support</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">Target</span></span>
<span id="cb2-16"></span>
<span id="cb2-17"><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">add_to_library_groups</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">=</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">RISCW</span></span></code></pre></div></div>
<p>该文件告诉构建系统RISCW后端具有两个组件。 其中一个是顶级组件RISCW，其类型为TargetGroup，该类型表明RISCW是一个后端，构建系统对该类型有一些特殊处理。 Target是RISCW的父组件。 请注意，此命名与LLVM后端的目录结构匹配，即<code>llvm/lib/Target/RISCW</code>。 RISCW的名称也不是任意的，它必须与后端的TableGen文件中的定义匹配。</p>
<p>LLVM后端提供了一系列可选功能， 例如assembly printing，assembly parsing等， LLVMBuild.txt文件表明了后端支持哪些可选功能。 比如，最后一行告诉构建系统RISCW后端实现了assembly printing功能。</p>
<p><strong>注意：</strong>查看现有后端（如ARM或RISCV）中的代码，以了解它们除了has_asmprinter之外还启用了哪些功能。</p>
<p>LLVMBuild.txt文件还定义了第二个名为RISCWCodeGen的组件，其类型为Library，其父级为RISCW。 另外，文件还指明了RISCWCodeGen需要的依赖库，例如AsmPrinter，CodeGen等，构建LLVM时，缺少库会导致链接错误。</p>
<p><code>llvm/lib/Target/RISCW</code>的每个子目录也会包含一个LLVMBuild.txt文件，该文件使用RISCW作为父级来定义自己的组件。</p>
</section>
<section id="cmakelists.txt文件" class="level3">
<h3 class="anchored" data-anchor-id="cmakelists.txt文件">CMakeLists.txt文件</h3>
<p>让我们看一下<code>llvm/lib/Target/RISCW/CMakeLists.txt</code>文件的内容。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">set</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LLVM_TARGET_DEFINITIONS</span> RISCW.td<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tablegen</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LLVM</span> RISCWGenRegisterInfo.inc <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-gen-register-info</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">tablegen</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LLVM</span> RISCWGenInstrInfo.inc <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-gen-instr-info</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other TableGen commands</span></span>
<span id="cb3-6"></span>
<span id="cb3-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">add_public_tablegen_target</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RISCWCommonTableGen</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-8"></span>
<span id="cb3-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># RISCWCodeGen should match with LLVMBuild.txt RISCWCodeGen</span></span>
<span id="cb3-10"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">add_llvm_target</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RISCWCodeGen</span></span>
<span id="cb3-11">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RISCWAsmPrinter.cpp</span></span>
<span id="cb3-12">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Other files</span></span>
<span id="cb3-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-14"></span>
<span id="cb3-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Should match with "subdirectories =  MCTargetDesc TargetInfo" in LLVMBuild.txt</span></span>
<span id="cb3-16"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">add_subdirectory</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">TargetInfo</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-17"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">add_subdirectory</span><span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">MCTargetDesc</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span></code></pre></div></div>
<p>文件顶部的<code>set</code>命令将<code>LLVM_TARGET_DEFINITION</code>定义为`RISCW.td**。 这个文件通常包含一些顶级定义， 并通过包含其他文件的方式来引入其他TableGen定义——我们将在后面的文章中更仔细地研究TableGen， 但是请随时查看代码库中的RISCW.td。</p>
<p>然后我们在CMake文件中看到一些TableGen命令。 它们命令构建系统使TableGen工具根据<code>*.td</code>文件生成<code>RISCWGen*.inc</code>。 这些<code>*.inc</code>文件实际上就是传统的C++代码，在编译完LLVM之后，你可以在<code>build/lib/Target/RISCW</code>的构建目录中找到它们；它们可以用于调试，但是不容易阅读。</p>
<p>接下来的命令，也就是<code>add_llvm_target</code>， 指定了当前目录中要构建的C++文件，被指定的文件不能位于子目录中。 <code>add_llvm_target</code>命令的第一个参数是目标的名称，并且应该与<code>LLVMBuild.txt</code>中定义名称匹配。</p>
<p>最后，CMakeLists.txt指定了CMake应该查看的子目录，在后端RISCW中，只有两个:<code>TargetInfo</code>和<code>MCTargetDesc</code>。 子目录中的CMakeLists.txt与此类似，但要简单得多!</p>
<p><strong>注意:</strong>后端文件的命名约定通常很重要，显然应该与构建文件的内容相匹配。 例如，<code>set(LLVM_TARGET_DEFINITIONS RISCW.td)</code>命令要求<code>RISCW.td</code>存在! 一定要仔细检查这些错误，因为构建错误可能有点含糊不清。</p>
</section>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
<li id="fn3"><p>您可以在<a href="https://cmake.org/overview/">此处</a>找到有关CMake的更多信息。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-06-RISCW-3.html</guid>
  <pubDate>Tue, 08 Dec 2020 04:30:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(二)：创建后端</title>
  <link>https://sadsock.github.io/posts/2020-12-03-RISCW-2.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="创建后端" class="level2">
<h2 class="anchored" data-anchor-id="创建后端">创建后端</h2>
<p>开发LLVM后端并不是一件特别吸引人的事情。 您很快就会意识到，这项工作在很大程度上就是从其他现有后端复制代码。 在线论坛上，LLVM开发者建议从“复制一个现有的后端，重命名并修改它以适应您的需要”开。 但是即使是相对较小的后端，比如Lanai或XCore，也相当复杂，而且代码也不容易理解！</p>
<p>在本系列文章中，将采取略有不同的方法。 我们将使用现有的LLVM后端作为起点，但我已经删除了大部分代码，并将其减少到编译一个(很小的)程序所需的最低限度。 精简的后端，称为RISCW，非常简单，可以帮助理解LLVM目标独立代码生成器，而不必纠缠于细节。 在这篇文章的其余部分，我将使用RISCW后端来展示如何创建一个新的LLVM后端。 我们还将看到如何用一个实验性的后端构建LLVM，甚至编译一个(非常简单的) C程序到汇编。</p>
<section id="triple和elf配置" class="level3">
<h3 class="anchored" data-anchor-id="triple和elf配置">Triple和ELF配置</h3>
<p>我们首先为后端配置一个新的目标描述Triple。 由于历史原因，Triple编码了目标平台的重要信息（如体系结构、供应商和操作系统）。 以下是配置一个新的Triple的步骤:</p>
<ol type="1">
<li>在llvm/include/llvm/ADT/Triple.h用Triple声明一个新的体系结构 (见这里)。</li>
<li>提供字符串和Triple之间的类型转换(参见这里,这里及这里)。</li>
<li>指出后端支持的目标文件类型，例如ELF、COFF等，ricw只支持ELF(参见这里)。</li>
<li>指出目标平台的字平台，例如32位或64位，以及指针的大小(请参阅这里,这里及这里)。</li>
</ol>
<p><strong>注意:</strong>你可以在这里和这里找到更多关于Triple的信息。</p>
<p><strong>注意:</strong>指令集并不一定意味着指针的大小。 例如，在为RV64编译时，指针并不总是64位的。 指针大小通常由ABI给出，在64位机器中，它可以是ilp32(即int、long和指针为32位)。</p>
<p>下面的参数用于配置ELF:</p>
<ol type="1">
<li><p>创建一个枚举值作为RISCW的体系结构的标识(见此处)。 这个值被编码在ELF文件头的e_machine字段中。 这个值不是随意设置的; 它必须取得授权，例如:0xF3 for RISCV。 但是我们现在将它设置为一个未使用的值。</p></li>
<li><p>声明ELF重定位类型(见这里和这里)。 同样，这些是依赖于架构的，这里列出了用于RISCV的类型。 在这个阶段，我们将简单地为RISCW放置了占位符。</p></li>
<li><p>文件格式名称(见此处)。</p></li>
<li><p>指示给定类的目标描述Triple(见此处)。目前，ELF头中的类是一个字节，用于对格式是32位还是64位进行编码。</p></li>
</ol>
<p><strong>注意:</strong>查看wikipedia获取更多关于ELF文件的信息。</p>
</section>
<section id="配置驱动器" class="level3">
<h3 class="anchored" data-anchor-id="配置驱动器">配置驱动器</h3>
<p>回想一下，我们使用clang将输入的C代码编译成LLVM IR。 但是clang不仅仅是我们的编译器前端，它也是一个驱动器，类似GCC，驱动编译流水线将输入的C程序转换为另一个表示，比如把C转换为汇编或目标代码。 因此，我们需要告诉clang</p>
<ol type="1">
<li><p>新后端的支持特性。例如，clang需要知道RISCW是32位还是64位。</p></li>
<li><p>新后端的编译流程。例如，它应该使用什么汇编程序? 什么连接器? 有哪些包括路径等等。</p></li>
</ol>
<p>我们可以通过添加一个新的target类<code>RISCWTargetInfo</code>来告诉clang有关RISCW的信息，该类与LLVM已有的target类一起被实例化，如这里所示。 该类在这里和这里分别被声明和定义。 在这段代码中有一些重要的事情需要强调:</p>
<ul>
<li><code>RISCWTargetInfo</code>通过字符串描述数据布局。这个字符串编码许多重要信息，比如指针中每一位、堆栈对齐要求等。</li>
<li>基本C数据类型的大小。</li>
<li>函数<code>RISCWTargetInfo::getTargetDefines(**</code>指示编译时定义的C预处理器宏，例如，这些宏是在使用RISCV后端编译代码时定义的。 宏通常描述后端支持的体系结构、ABI、启用/禁用任何特性等</li>
</ul>
<p><strong>注意:</strong>一个后端可能支持多个指令集和ABI，因此驱动器的配置必须根据选定的目标Triple进行更改。 例如，<code>RISCWTargetInfo</code>根据Triple包含riscv32还是riscv64来更改数据布局字符串。</p>
<p><strong>注意:</strong>这里可以查看RISCWTargetInfo的父类TargetInfo的声明。 它包含了更多的可以配置的选项。</p>
<p>配置工具链相对简单。 我们只需要实现一个从Toolchain继承的RISCWToolChain类，如下所示。 代码基本上是不言自明的，通过覆盖ToolChain类的成员，您可以修改更多的选项(见此处)。</p>
</section>
<section id="创建新target" class="level3">
<h3 class="anchored" data-anchor-id="创建新target">创建新Target</h3>
<p>每个后端在llvm/lib/Target下都有一个单独的目录，其中包含后端的大部分代码。 我们不会在这篇文章中深入讨论代码的细节(稍后我们会这样做) ，因为即使是一个很小的后端，比如RISCW，也有很多文件。 目前，我们可以将这些文件大致分为三类:</p>
<ul>
<li><p><strong>TableGen文件</strong>LLVM目标无关代码生成框架实现了一个精心设计的模式匹配算法，用于为输入的程序选择指令。 待匹配的模式使用TableGen语法描述。 此外，TableGen文件还描述了target在体系结构方面的重要特性，如寄存器的数量和调用约定等。</p></li>
<li><p><strong>Build文件</strong>后端的每个目录都必须被声明，否则它将不会被构建。 此外，我们的后端的顶部目录(<code>llvm/lib/Target/RISCW</code>) ，以及每个子目录必须包含两个构建文件:<code>CMakeLists.txt</code>和<code>LLVMBuild.txt</code>， 前者将源文件和任何子目录添加为生成目标，而后者为生成目标设置简单的生成参数，参数包括生成目标的名称、链接所需的库等。</p></li>
<li><p><strong>C++文件</strong>包含了大量的后端代码，实现了从简单的配置选项到更复杂的指令选择功能(TableGen没有实现或不能实现)的所有功能。</p></li>
</ul>
</section>
<section id="建立实验性后端" class="level3">
<h3 class="anchored" data-anchor-id="建立实验性后端">建立实验性后端</h3>
<p>现在，一切都已经建立，我们可以构建带有RISCW后端的LLVM。 但是我们不能简单地根据上一章的内容修改CMake的<code>-DLLVM_TARGETS_TO_BUILD</code>选项，以包含RISCW，因为后端仍处于试验阶段。 相反，我们使用<code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD</code>选项，如下:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cmake</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-G</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ninja"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_ENABLE_PROJECTS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"clang"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_TARGETS_TO_BUILD</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ARM;Lanai;RISCV"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"RISCW"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DCMAKE_BUILD_TYPE</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Debug"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_ENABLE_ASSERTIONS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>On ../llvm</span>
<span id="cb1-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ninja</span></span></code></pre></div></div>
<p>当构建完成后，你可以检查RISCW现在是否是一个可用的后端，如下所示:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">$</span> ./build/bin/llc <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--version</span></span>
<span id="cb2-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LLVM</span> <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">http://llvm.org/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb2-3">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">LLVM</span> version 10.0.1</span>
<span id="cb2-4">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">DEBUG</span> build with assertions.</span>
<span id="cb2-5">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Default</span> target: x86_64-unknown-linux-gnu</span>
<span id="cb2-6">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Host</span> CPU: znver2</span>
<span id="cb2-7"></span>
<span id="cb2-8">  <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Registered</span> Targets:</span>
<span id="cb2-9">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">arm</span>     <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> ARM</span>
<span id="cb2-10">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">armeb</span>   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> ARM <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">big</span> endian<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb2-11">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">lanai</span>   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> Lanai</span>
<span id="cb2-12">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">riscv32</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> 32-bit RISC-V</span>
<span id="cb2-13">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">riscv64</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> 64-bit RISC-V</span>
<span id="cb2-14">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">riscw</span>   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> 32-bit RISC-V         <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>== YAY!!</span>
<span id="cb2-15">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">thumb</span>   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> Thumb</span>
<span id="cb2-16">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">thumbeb</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-</span> Thumb <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">big</span> endian<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span></code></pre></div></div>
</section>
<section id="编译c程序" class="level3">
<h3 class="anchored" data-anchor-id="编译c程序">编译C程序</h3>
<p>我们的RISCW后端只能发出两条add和ret指令，而且它不能正确处理函数调用、堆栈和几乎所有其他的东西！ 因此，我们将约束自己，只编译这个小函数:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">int</span> test<span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">(</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">int</span> a, int b<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">)</span></span>
<span id="cb3-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span></span>
<span id="cb3-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">a</span> + b<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span></span>
<span id="cb3-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>就这样，我们得到了这样一个代码:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb4-1">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.text</span></span>
<span id="cb4-2">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.file</span>   <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"test.c"</span></span>
<span id="cb4-3">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.globl</span>  test                    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">--</span> Begin function test</span>
<span id="cb4-4">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.type</span>   test,@function</span>
<span id="cb4-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">test:</span>                                   <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">@test</span></span>
<span id="cb4-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">%bb.0:</span>                                <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">%entry</span></span>
<span id="cb4-7">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">add</span> x0, x1, x0</span>
<span id="cb4-8">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ret</span></span>
<span id="cb4-9"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.Lfunc_end0:</span></span>
<span id="cb4-10">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.size</span>   test, .Lfunc_end0-test</span>
<span id="cb4-11">                                        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">--</span> End function</span>
<span id="cb4-12">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.ident</span>  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"clang version 10.0.1 (https://github.com/llvm/llvm-project 89f2d2cc3bba7cb12cee346b3205cb0335e758cd)"</span></span>
<span id="cb4-13">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">.section</span>    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".note.GNU-stack"</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>,@progbits</span></code></pre></div></div>
<p>有很多东西缺失了，代码实际上是不正确的，在RISCV中的x0是一个硬编码为0的只读寄存器。 但是我认为我们已经达到了目标: 建立了一个最小的LLVM后端，可以很容易地用更多的特性进行扩展。</p>
<p><strong>注意:</strong>如果您使用上一篇文章中的命令来编译上面的测试函数，请确保为clang设置了<code>-target riscw</code>和为llc设置了<code>-march=riscw</code>。</p>
<p><strong>注意:</strong>试图编译更复杂的程序将导致<code>cannot select...</code>错误。如果你感兴趣，就试一试。</p>
<p><strong>注意:</strong>您可以通过将<code>-debug</code>选项传递给<code>llc</code>来指示编译器打印调试信息。</p>
</section>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-03-RISCW-2.html</guid>
  <pubDate>Sun, 06 Dec 2020 05:30:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(一)：入门</title>
  <link>https://sadsock.github.io/posts/2020-12-03-RISCW-1.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="入门" class="level2">
<h2 class="anchored" data-anchor-id="入门">入门</h2>
<p>在为新项目编写代码之前，我通常会配置环境，并对查看经存在的代码，这就是这一节要做的。在这一节中，我将展示如何下载编译LLVM和其他对调试有用的工具。我们还将了解如何使用现有的LLVM后端和GNU工具链来编译、汇编、链接和运行程序。</p>
<section id="环境" class="level3">
<h3 class="anchored" data-anchor-id="环境">环境</h3>
<p>我正在使用Ubuntu，但是你应该能够在其他系统中重复这些步骤，而且(相对来说)几乎没有什么不同。您将需要以下工具来构建软件。</p>
<ul>
<li>Makefile</li>
<li>C/C++ Compiler – 我用 GCC 9.2.1</li>
<li>autotools</li>
<li>CMake</li>
<li>Ninja</li>
<li>Git</li>
<li>大量耐心</li>
</ul>
<p><strong>注意：</strong>我可能忘记了一些东西，但是构建系统会通过一个错误告诉您；</p>
</section>
<section id="编译llvm" class="level3">
<h3 class="anchored" data-anchor-id="编译llvm">编译LLVM</h3>
<p>LLVM维护者已经建立了这个方便的repo，它包含LLVM和工具链的其他部分，比如Clang。</p>
<pre><code>git clone https://github.com/llvm/llvm-project</code></pre>
<p>在本系列文章中，我们将使用llvm 10.0.1，我建议您也使用该版本的LLVM。 因为LLVM的变化非常快，这里显示的一些代码在旧/新版本中可能无法工作。 不过，原理应该大致相同。</p>
<p>LLVM使用CMake为构建系统生成构建文件，LLVM支持的构建系有：Ninja，Makefiles，Visual Studio和XCode。 我通常使用Ninja，因为我认为它在我的系统中速度最快（我没有证据支持该判断！）。 您可以通过cmake命令的<code>-G</code>参数来更改构建系统。</p>
<p>CMake有很多选项，我鼓励您对其进行研究，因为有些选项对调试非常有帮助。 您可以在<a href="https://llvm.org/docs/CMake.html">这里</a>阅读所有构建选项。 在本教程中，我将使用以下选项:</p>
<ol type="1">
<li><p><code>-DLLVM_ENABLE_PROJECTS</code> 构建编译器的其余部分，比如Clang。</p></li>
<li><p><code>-DLLVM_TARGETS_TO_BUILD</code> 指定要构建的后端。查看其他后端的输出对调试很有帮助，但是如果添加太多，构建会花费很长时间。</p></li>
<li><p><code>-DCMAKE_BUILD_TYPE</code> 构建Debug版本。</p></li>
<li><p><code>-DLLVM_ENABLE_ASSERTIONS=On</code> 启用断言，对调试很有帮助。</p></li>
</ol>
<p>以下是在克隆repo之后构建LLVM的方法。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> llvm-project</span>
<span id="cb2-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> checkout llvmorg-10.0.1</span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mkdir</span> build</span>
<span id="cb2-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> build</span>
<span id="cb2-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cmake</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-G</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ninja"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_ENABLE_PROJECTS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"clang"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_TARGETS_TO_BUILD</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ARM;Lanai;RISCV"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DCMAKE_BUILD_TYPE</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Debug"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-DLLVM_ENABLE_ASSERTIONS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>On ../llvm</span>
<span id="cb2-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ninja</span></span></code></pre></div></div>
<p><strong>注意：</strong>您可以在<a href="https://llvm.org/docs/GettingStarted.html">这里</a>和<a href="https://llvm.org/docs/CMake.html">这里</a>找到更多有关构建LLVM的信息。</p>
<p><strong>注意：</strong>您可以为Ninja传递<code>-j &lt;NUM_JOBS&gt;</code>选项，以指示要并行的作业数。 过高的<code>&lt;NUM_JOBS&gt;</code>会导致构建崩溃，并产生<code>collect2：ld ...</code>错误消息。</p>
</section>
<section id="编译risc-v的gnu工具链" class="level3">
<h3 class="anchored" data-anchor-id="编译risc-v的gnu工具链">编译RISC V的GNU工具链</h3>
<p>你可能有点困惑，为什么我建议构建GCC的RISC V后端？ 难道我们不是要自己编写编译器后端吗？</p>
<p>我们构建GCC的RISC V后端，是因为我们希望在初始阶段使用GCC的汇编器和链接器来测试LLVM后端生成的代码。 编译过程分为很多阶段，在初始阶段，我们已经有以下结构:</p>
<ul>
<li>Clang 编译C代码到LLVM IR</li>
<li>LLVM 优化IR</li>
<li>LLVM后端 编译IR到汇编</li>
<li>GCC 汇编和链接可执行文件</li>
</ul>
<p>使用以下命令下载，构建和安装GCC for RISCV。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">git</span> clone https://github.com/riscv/riscv-gnu-toolchain</span>
<span id="cb3-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> riscv-gnu-toolchain</span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mkdir</span> build</span>
<span id="cb3-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> build</span>
<span id="cb3-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">../configure</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--with-arch</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>rv32gc <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--with-abi</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ilp32</span>
<span id="cb3-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">make</span></span>
<span id="cb3-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">make</span> install</span></code></pre></div></div>
<p><strong>注意：</strong>请确保为指令集的正确变体（即RV32）构建GCC工具链，因为构建系统的默认值为RV64！</p>
<p><strong>注意：</strong>GNU工具链支持RISC V的多个ABI，例如<code>ilp32</code>，<code>ilp32d</code>和<code>ilp32f</code>，这取决于您是否需要软浮点，硬浮点。</p>
</section>
<section id="编译c程序" class="level3">
<h3 class="anchored" data-anchor-id="编译c程序">编译C程序</h3>
<p>现在，构建和运行C代码的环境已经配置好了，尽管我们还没自己的后端（还！）。让我们从一个简单的C程序开始：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode c++ code-with-copy"><code class="sourceCode cpp"><span id="cb4-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">#include </span><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">&lt;stdio.h&gt;</span></span>
<span id="cb4-2"></span>
<span id="cb4-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> main<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">void</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-4"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb4-5">    printf<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hello world!</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb4-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-7"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>首先，使用Clang将C代码编译为LLVM IR。 我们的计划是使用标准库中来自头文件stdio.h的函数printf，如果不能找到头文件，编译器会提示出错。 为了使用GCC自带的RISC V标准C库，我们使用了<code>-isystem</code>参数。 这会将包含所需头文件的目录添加到Clang预处理器的搜索目录列表中。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">clang</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-O2</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-emit-llvm</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-target</span> riscv64 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-isystem</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>PATH_TO_GCC<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>/riscv64-unknown-elf/include <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-c</span> test.c <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-o</span> test.bc</span></code></pre></div></div>
<p>上面的命令把C语言文件test.c编译到LLVM IR文件test.bc，这是专门为机器设计的语言人类很难直接阅读。 我们可以使用以下命令反汇编该文件：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">llvm-dis</span> test.bc</span></code></pre></div></div>
<p>现在，使用包含以下内容的后端将IR编译为程序集，而无需使用以下命令下载LLVM： 现在，使用LLVM自带的后端将IR编译为程汇编：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">llc</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-march</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>riscv64 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-O2</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-filetype</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>asm test.bc <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-o</span> test.S</span></code></pre></div></div>
<p>GCC可以直接生成程序的二进制文件。 我将其分为两个步骤，但是您可以根据需要使用单个命令。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">riscv64-unknown-elf-gcc</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-c</span> test.S <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-o</span> test.o</span>
<span id="cb8-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">riscv64-unknown-elf-gcc</span> test.o <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-o</span> test</span></code></pre></div></div>
<p>最后，我们可以使用模拟器或真实硬件运行程序。</p>
</section>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-03-RISCW-1.html</guid>
  <pubDate>Fri, 04 Dec 2020 11:30:38 GMT</pubDate>
</item>
<item>
  <title>为LLVM添加简易RISCV后端(零)：简介</title>
  <link>https://sadsock.github.io/posts/2020-12-03-RISCW-0.html</link>
  <description><![CDATA[ 




<p>为一个新的指令集编写编译器是一件复杂的事情，尽管LLVM的出现使这个过程比之前简单多了！ 一个匪夷所思的困难是缺乏一个简单的、循序渐进的教程<sup>1</sup><sup>2</sup>。 因此，本系列博客试图提供一个从头开始编写LLVM后端的简易教程来解决（部分）问题。</p>
<section id="简介" class="level2">
<h2 class="anchored" data-anchor-id="简介">简介</h2>
<p>在本教程中，我将为RISC V指令集的基本32位版本（即RV32IM）开发一个后端。希望这能帮助那些不熟悉LLVM的人开始使用这个工具，并将其扩展到自己的项目中。看懂本教程不需要前置知识，但是如果你熟悉C++和RISC V，学习本教程会更容易。</p>
<p>在本文剩下的部分中，我将简要描述LLVM的体系结构和后端结构。不过，我不会在这里详细说明，因为，如果你像我一样，在5分钟（或5秒）之后，就会忘记读过的任何繁琐文档。LLVM的细节将在以后的帖子中根据需要提供。</p>
<p><strong>注意:</strong>如果你想要更详细的版本，你可以自娱自乐地阅读<a href="http://llvm.org/docs/UserGuides.html">LLVM User Guides</a>。</p>
<section id="llvm架构" class="level3">
<h3 class="anchored" data-anchor-id="llvm架构">LLVM架构</h3>
<p>传统上，编译过程分为三个阶段。 首先，编译器的前端将源代码转换为某种中间表示（IR）； 然后，优化IR； 最后，编译器的后端将IR转换为机器代码。 传统的编译器通常仅支持一种编程语言和一种目标指令集，编译器的源代码很难重用，例如添加新的目标指令集。</p>
<p>LLVM模块化地实现了三个编译过程，可以解决重用问题。 其思想是LLVM的核心（即IR和优化器）是固定的，但是前端和后端可以被替换，以使编译器可以支持多种编程语言和指令集。 例如，我们可以使用Clang（LLVM的前端）和x86后端把C/C++代码编译成X86指令集上的可执行程序。 我们也可以用ARM后端替换X86后端，从而得到ARM指令集上的可执行程序。</p>
<p><strong>注意：</strong>LLVM的设计师Chris Lattner撰写了这篇<a href="http://www.aosabook.org/en/llvm.html">文章</a>介绍LLVM的体系结构及其设计动机。</p>
<p><strong>注意：</strong>LLVM的这三个阶段的每个阶段在都有一个专用的可执行文件。 clang是C/C++的前端（显然针对不同的编程语言有不同的前端），opt是优化程序，llc用于调用后端。 通常，我们使用clang作为驱动程序来执行前端，使用llc和opt配合适当的参数来生成IR，汇编，可执行文件等。</p>
</section>
<section id="代码生成" class="level3">
<h3 class="anchored" data-anchor-id="代码生成">代码生成</h3>
<p>LLVM后端将IR编译为目标代码或汇编代码。 每个后端都只支持单一平台，但可以支持多个指令集。 例如，LLVM只有一个ARM后端，但该后端可以为ARMv6和ARMv7等指令集生成代码。 每个后端都建立在LLVM的目标无关代码生成器之上。 目标无关代码生成器是一个框架，可实现诸如寄存器分配之类的关键算法。 从广义上讲，后端的任务是配置该框架并使之适应其目标指令集的特定需求。</p>
<p>代码生成具有以下阶段：</p>
<ol type="1">
<li><p><strong>指令选择</strong> 映射LLVM IR到目标指令集中的指令。 此阶段使用无限数量的虚拟寄存器和函数调用堆栈的抽象引用。</p></li>
<li><p><strong>计划和编排</strong> 确定指令的顺序。 需要明确的是，在指令选择阶段已经对指令进行了排序，但这里可以根据寄存器分配策略或指令等待时间来对其中的一些指令的排序进行优化。</p></li>
<li><p><strong>基于SSA的机器代码优化</strong> 执行诸如<a href="https://en.wikipedia.org/wiki/Peephole_optimization">peephole</a>优化之类的工作。</p></li>
<li><p><strong>寄存器分配</strong> 将虚拟寄存器映射到物理寄存器。</p></li>
<li><p><strong>Prolog / Epilog插入</strong> 在每个函数的开头（或prolog）和结尾（或epilog）插入机器指令。 这些通常是在进入或退出函数时扩展堆栈的指令。 由于当前已经知道堆栈大小，因此也可以解析抽象堆栈引用。</p></li>
<li><p><strong>后期机器代码优化</strong> 可能不言自明。</p></li>
<li><p><strong>代码发射</strong> 发出目标代码或汇编代码。</p></li>
</ol>
<p>接下来，我将看一下构建LLVM以及如何设置开发/调试环境…</p>
<p><strong>注意：</strong>您可以阅读<a href="http://llvm.org/docs/CodeGenerator.html">这篇文章</a>了解LLVM目标无关代码生成器的更多信息。</p>
</section>
</section>
<section id="注释" class="level2">
<h2 class="anchored" data-anchor-id="注释">注释</h2>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p>公平地说，有不少关于LLVM的书籍和网站，但大多数都是对这个工具的一般性描述，还有是关于如何编写新前端的实践教程，但后端的教程非常少。↩︎</p></li>
<li id="fn2"><p><a href="https://jonathan2251.github.io/lbd/">这个教程</a>描述了如何开发LLVM后端，但我发现很难理解。↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-12-03-RISCW-0.html</guid>
  <pubDate>Thu, 03 Dec 2020 11:30:38 GMT</pubDate>
</item>
<item>
  <title>TableGen 简介</title>
  <link>https://sadsock.github.io/posts/2020-11-01-TableGen-Overview.html</link>
  <description><![CDATA[ 




<section id="简介" class="level1">
<h1>简介</h1>
<p>TableGen的目的是帮助开发者开发和维护特定领域的记录。这些记录的数量可能很大，所以它是专门来编写灵活的描述，并提取这些记录的共同特征。这减少了描述的重复量，减少了出错的机会，使构建特定领域的信息更容易。</p>
<p>TableGen前端解析文件，实例化声明，并将结果交给特定领域的<a href="http://llvm.org/docs/TableGen/index.html#backend">backend</a>进行处理。有关TableGen的详细描述，请参阅 <a href="http://llvm.org/docs/TableGen/ProgRef.html">TableGen Programmer’s Reference</a> 。有关调用TableGen的各种xxx-tblgen命令的详细信息，请参阅<a href="http://llvm.org/docs/CommandGuide/tblgen.html">xxx-tblgen - Target Description to C++ Code</a> 。</p>
<p>目前TableGen的主要用户有 <a href="http://llvm.org/docs/CodeGenerator.html">The LLVM Target-Independent Code Generator</a>和<a href="https://clang.llvm.org/docs/UsersManual.html#controlling-errors-and-warnings">Clang diagnostics and attributes</a>。</p>
<p>注意，如果您经常使用TableGen，并且使用emacs或vim，那么您可以在llvm发行版的<code>llvm/utils/emacs</code>和<code>llvm/utils/vim</code>目录中分别找到一个emacs“TableGen mode”和一个vim语言文件。</p>
</section>
<section id="tablegen程序" class="level1">
<h1>TableGen程序</h1>
<p>TableGen文件由TableGen程序解释：llvm-tblgen在bin下构建目录中。 它没有安装到系统（或您的sysroot设置的位置）中，因为它在LLVM的构建过程之外没有任何用处。</p>
<section id="运行tablegen" class="level2">
<h2 class="anchored" data-anchor-id="运行tablegen">运行TableGen</h2>
<p>TableGen的运行就像其他任何LLVM工具一样。 第一个（可选）参数指定要读取的文件。 如果未指定文件名，则llvm-tblgen从标准输入读取。</p>
<p>要使用TableGen就必须指定后端，这些后端可以在命令行中选择（输入“ <code>llvm-tblgen -help</code>”获取列表）。例如，要获取特定类型的子类的所有定义的列表(这对于构建这些记录的枚举很有用)，请使用<code>print-enum</code>选项：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">$</span> llvm-tblgen X86.td <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-print-enums</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Register</span>
<span id="cb1-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">AH,</span> AL, AX, BH, BL, BP, BPL, BX, CH, CL, CX, DH, DI, DIL, DL, DX, EAX, EBP, EBX,</span>
<span id="cb1-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ECX,</span> EDI, EDX, EFLAGS, EIP, ESI, ESP, FP0, FP1, FP2, FP3, FP4, FP5, FP6, IP,</span>
<span id="cb1-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">MM0,</span> MM1, MM2, MM3, MM4, MM5, MM6, MM7, R10, R10B, R10D, R10W, R11, R11B, R11D,</span>
<span id="cb1-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">R11W,</span> R12, R12B, R12D, R12W, R13, R13B, R13D, R13W, R14, R14B, R14D, R14W, R15,</span>
<span id="cb1-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">R15B,</span> R15D, R15W, R8, R8B, R8D, R8W, R9, R9B, R9D, R9W, RAX, RBP, RBX, RCX, RDI,</span>
<span id="cb1-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RDX,</span> RIP, RSI, RSP, SI, SIL, SP, SPL, ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7,</span>
<span id="cb1-8"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">XMM0,</span> XMM1, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5,</span>
<span id="cb1-9"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">XMM6,</span> XMM7, XMM8, XMM9,</span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">$</span> llvm-tblgen X86.td <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-print-enums</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>Instruction</span>
<span id="cb1-12"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ABS_F,</span> ABS_Fp32, ABS_Fp64, ABS_Fp80, ADC32mi, ADC32mi8, ADC32mr, ADC32ri,</span>
<span id="cb1-13"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ADC32ri8,</span> ADC32rm, ADC32rr, ADC64mi32, ADC64mi8, ADC64mr, ADC64ri32, ADC64ri8,</span>
<span id="cb1-14"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ADC64rm,</span> ADC64rr, ADD16mi, ADD16mi8, ADD16mr, ADD16ri, ADD16ri8, ADD16rm,</span>
<span id="cb1-15"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ADD16rr,</span> ADD32mi, ADD32mi8, ADD32mr, ADD32ri, ADD32ri8, ADD32rm, ADD32rr,</span>
<span id="cb1-16"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ADD64mi32,</span> ADD64mi8, ADD64mr, ADD64ri32, ...</span></code></pre></div></div>
<p>默认后端打印出所有记录。 还有一个通用后端，它将所有记录输出为JSON数据结构，并使用-dump-json选项启用。</p>
<p>如果您计划使用TableGen，那么您很可能必须编写一个后端来提取您需要的信息并以适当的方式格式化它。可以通过用C++扩展TableGen本身，或者用任何可以处理JSON的语言编写脚本来实现这一点。</p>
</section>
<section id="例子" class="level2">
<h2 class="anchored" data-anchor-id="例子">例子</h2>
<p>在没有其他参数的情况下，llvm-tblgen 解析指定的文件并输出所有的类，然后输出所有的定义。这是查看各种定义扩展到何种程度的好方法。在 X86.td 文件上运行这个命令会输出以下命令(在撰写本文时) :</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode rust code-with-copy"><code class="sourceCode rust"><span id="cb2-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb2-2">def ADD32rr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Instruction X86Inst I</span></span>
<span id="cb2-3">  string Namespace <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"X86"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-4">  dag OutOperandList <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (outs GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>dst)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-5">  dag InOperandList <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (ins GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src2)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-6">  string AsmString <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"add{l}</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\t</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">{$src2, $dst|$dst, $src2}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-7">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>dag<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Pattern</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(set GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>dst<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> (add GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src2))]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-8">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Uses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-9">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Defs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [EFLAGS]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-10">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Predicate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Predicates <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-11">  int CodeSize <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-12">  int AddedComplexity <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-13">  bit isReturn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-14">  bit isBranch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-15">  bit isIndirectBranch <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-16">  bit isBarrier <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-17">  bit isCall <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-18">  bit canFoldAsLoad <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-19">  bit mayLoad <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-20">  bit mayStore <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-21">  bit isImplicitDef <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-22">  bit isConvertibleToThreeAddress <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-23">  bit isCommutable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-24">  bit isTerminator <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-25">  bit isReMaterializable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-26">  bit isPredicable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-27">  bit hasDelaySlot <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-28">  bit usesCustomInserter <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-29">  bit hasCtrlDep <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-30">  bit isNotDuplicable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-31">  bit hasSideEffects <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-32">  InstrItinClass Itinerary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> NoItinerary<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-33">  string Constraints <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-34">  string DisableEncoding <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-35">  bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Opcode <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb2-36">  Format Form <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> MRMDestReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-37">  bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> FormBits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb2-38">  ImmType ImmT <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> NoImm<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-39">  bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> ImmTypeBits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb2-40">  bit hasOpSizePrefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-41">  bit hasAdSizePrefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-42">  bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb2-43">  bit hasREX_WPrefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb2-44">  FPFormat FPForm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?;</span></span>
<span id="cb2-45">  bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> FPFormBits <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb2-46"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb2-47"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span></code></pre></div></div>
<p>该定义对应于x86体系结构的32位寄存器-寄存器相加指令。 Def ADD32rr定义了一个名为ADD32rr的记录，行尾的注释表示该定义的父类。 记录体包含TableGen为记录汇编的所有数据，指示该指令是“x86”命名空间的一部分，指示代码生成器如何选择指令的模式，它是双地址指令，具有特定的编码等。记录中信息的内容和语义专用于X86后端，这里仅作为示例。</p>
<p>正如您所看到的，代码生成器支持的每条指令都需要大量信息，手动指定所有指令将不可维护，容易出错，而且首先要做的事情很累人。因为我们正在使用 TableGen，所有的信息都来自以下定义:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode rust code-with-copy"><code class="sourceCode rust"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> Defs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [EFLAGS]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-2">    isCommutable <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span>                  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// X = ADD Y,Z --&gt; X = ADD Z,Y</span></span>
<span id="cb3-3">    isConvertibleToThreeAddress <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Can transform into LEA.</span></span>
<span id="cb3-4">def ADD32rr  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> I<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0x01</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> MRMDestReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> (outs GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>dst)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-5">                                   (ins GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src2)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-6">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"add{l}</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\t</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">{$src2, $dst|$dst, $src2}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-7">                 [(set GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>dst<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> (add GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> GR32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:$</span>src2))]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span></code></pre></div></div>
<p>这个定义使用定制类 I (从定制类 X86Inst 扩展而来) ，这个定制类是在 x86特定的 TableGen 文件中定义的，可以提取了指令共享的公共特性。TableGen 的一个关键特性是，它允许最终用户定义他们在描述其信息时喜欢使用的抽象。</p>
</section>
</section>
<section id="语法" class="level1">
<h1>语法</h1>
<p>TableGen的语法类似c++模板，带有内置的类型和约束。此外，TableGen的语法引入了一些自动化概念，如multiclass、foreach、let等。</p>
<section id="基本概念" class="level2">
<h2 class="anchored" data-anchor-id="基本概念">基本概念</h2>
<p>TableGen文件由两个关键部分组成:“类”和“定义”，这两个部分都被认为是“记录”。</p>
<p><strong>记录</strong> 有一个唯一的名称、一个值列表和一个父类列表。 值列表是TableGen为每条记录构建的主要数据；正是该列表保存了应用程序的特定信息。 此数据的解释留给特定的后端，但结构和格式规则由TableGen负责。</p>
<p><strong>定义</strong> 是“记录”的具体形式。它通常不包含任何未定义的值，并用“ def”关键字进行标记。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode rust code-with-copy"><code class="sourceCode rust"><span id="cb4-1">def FeatureFPARMv8 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> SubtargetFeature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fp-armv8"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HasFPARMv8"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"true"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-2">                                      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Enable ARMv8 FP"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span></code></pre></div></div>
<p>在此示例中，FeatureFPARMv8是使用某些值初始化的SubtargetFeature记录。 这些类通过关键字class定义在同一文件或其他文件中的。 对大多数平台来说，包含通用类的TableGen文件保存在<code>include/llvm/Target</code>中。</p>
<p><strong>class</strong> 是用于构建和描述其他记录的抽象记录。这些类使最终用户可以为他们所针对的领域（例如LLVM代码生成器中的“ Register”，“ RegisterClass”和“ Instruction”）构建抽象，提取改领域的公共属性（例如“ FPInst”，用于表示X86后端中的浮点指令）。TableGen会跟踪用于建立定义的所有类，因此后端可以找到特定类的所有定义，例如“instruction”。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode rust code-with-copy"><code class="sourceCode rust"><span id="cb5-1">class ProcNoItin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>string Name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>SubtargetFeature<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Features<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-2">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Processor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> NoItineraries<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Features<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span></code></pre></div></div>
<p>在这里，类ProcNoItin通过类型为string的参数名、目标特性列表和硬编码的NoItineraries来特化Processor类。</p>
<p><strong>multiclass</strong> 一次实例化一组抽象记录。每个实例化都会产生多个TableGen定义。如果一个multiclass继承了另一个multiclass，那么子multiclass中的定义将成为当前multiclass的一部分，就像它们是在当前multiclass中声明的一样。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode rust code-with-copy"><code class="sourceCode rust"><span id="cb6-1">multiclass ro_signed_pats<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>string T<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> string Rm<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dag Base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dag Offset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dag <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Extend</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-2">                        dag address<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> ValueType sty<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb6-3">def <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Pat<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>cast<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>SDNode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sextload"</span> # sty) address))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-4">          (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>cast<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Instruction<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"LDRS"</span> # T # <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"w_"</span> # Rm # <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_RegOffset"</span>)</span>
<span id="cb6-5">            Base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Offset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Extend</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb6-6"></span>
<span id="cb6-7">def <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Pat<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>(<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i64</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>cast<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>SDNode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sextload"</span> # sty) address))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-8">          (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>cast<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Instruction<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"LDRS"</span> # T # <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x_"</span> # Rm # <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_RegOffset"</span>)</span>
<span id="cb6-9">            Base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Offset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Extend</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb6-10"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-11"></span>
<span id="cb6-12">defm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ro_signed_pats<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Rm<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Offset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Extend</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-13">                      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>foreach(decls<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>pattern<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> address<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-14">                               <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>subst(SHIFT<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> imm_eq0<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> decls<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>pattern))<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-15">                      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i8</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span></code></pre></div></div>
<p>有关TableGen的深入描述，请参阅TableGen的<a href="http://llvm.org/docs/TableGen/ProgRef.html">TableGen Programmer’s Reference</a>。</p>
</section>
</section>
<section id="tablegen后端" class="level1">
<h1>TableGen后端</h1>
<p>没有后端，TableGen文件没有实际含义。 运行xxx-tblgen时的默认操作是以文本格式打印信息，但这仅对调试TableGen文件本身有用。 但是，TableGen的功能是将源文件解释为内部表示形式，可以将其生成为所需的任何内容。</p>
<p>目前TableGen的用法是创建包含表的大型include的文件，您可以直接包含这些文件(如果输出是您正在编码的语言) ，也可以通过包裹include文件的宏进行预处理。</p>
<p>如果后端已经以C格式打印了表，或者输出仅仅是一列字符串(用于错误和警告消息) ，则可以使用直接输出。如果需要在不同的上下文中使用相同的信息(如指令名) ，则应该使用预处理的输出，因此后端应该打印一个元信息列表，该列表可以形成不同的编译时格式。</p>
<p>可用后端的列表，请参阅<a href="http://llvm.org/docs/TableGen/BackEnds.html">TableGen BackEnds</a>；有关如何编写和调试新后端的信息，请参阅<a href="http://llvm.org/docs/TableGen/BackGuide.html">TableGen Backend Developer’s Guide</a>。</p>
</section>
<section id="tablegen缺陷" class="level1">
<h1>TableGen缺陷</h1>
<p>尽管TableGen非常通用，但它有一些已经被多次指出的缺陷。 共同点是，虽然TableGen允许您构建特定于领域的语言，但您创建的最终语言缺乏其他DSL的功能，这反过来又会显著增加TableGen文件的大小和复杂性。</p>
<p>同时，TableGen允许您通过定制的后端创建基本概念的任何含义，这会扭曲原始设计，使新手很难理解TableGen文件。</p>
<p>有些人赞成进一步扩展语义，但要确保后端遵守严格的规则。 其他人则建议我们应该改用功能更强大的DSL，这些DSL是为特定目的而设计的，甚至应该重用现有DSL。</p>


</section>

 ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-11-01-TableGen-Overview.html</guid>
  <pubDate>Tue, 03 Nov 2020 11:30:38 GMT</pubDate>
</item>
<item>
  <title>编写LLVM后端</title>
  <link>https://sadsock.github.io/posts/2020-11-01-write-an-LLVM-backend.html</link>
  <description><![CDATA[ 




<section id="一-简介" class="level1">
<h1>一 简介</h1>
<p>本文档描述了编写编译器后端的技术， 这些后端将LLVM中间表示（IR）转换为特定机器或其他语言的代码， 这些代码可以是汇编代码也可以是二进制代码（可用于JIT编译器）。</p>
<p>LLVM的后端是一个平台独立的代码生成器， 它可以为不同类型的目标CPU生成代码， 包括X86、PowerPC、ARM和SPARC。 后端还可以为GPU或Cell处理器的SPU生成代码， 以支持计算内核的执行。</p>
<p>本文档主要关注<code>llvm/lib/Target</code>目录中的现有示例， 特别是为SPARC平台创建静态编译器(也就是发射汇编码)， 因为SPARC具有相当典型的特征， 比如RISC指令集和常见的调用约定。</p>
<section id="目标读者" class="level2">
<h2 class="anchored" data-anchor-id="目标读者">目标读者</h2>
<p>本文档的受众是需要编写LLVM后端来为特定的硬件或软件平台生成代码的开发者。</p>
</section>
<section id="前置知识" class="level2">
<h2 class="anchored" data-anchor-id="前置知识">前置知识</h2>
<p>在阅读本文档之前，必须先阅读以下重要文档：</p>
<ul>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/LangRef.html">LLVM Language Reference Manual</a>–LLVM汇编语言的参考手册。</p></li>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/CodeGenerator.html">The LLVM Target-Independent Code Generator</a>– 用于将LLVM内部表示转换为指定平台的机器码的组件（类和代码生成算法）的指南。 特别注意代码生成阶段：指令选择、调度和形成、基于SSA的优化、寄存器分配、Prolog/Epilog代码插入、后期机器代码优化和代码发射。</p></li>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/TableGen/index.html">TableGen</a>–TableGen（tblgen）应用程序的描述文档， 该程序管理LLVM代码生成特定于域的信息。 TableGen处理来自目标描述文件（后缀.td）的输入，并生成可用于代码生成的C++代码。</p></li>
</ul>
</section>
<section id="基本步骤" class="level2">
<h2 class="anchored" data-anchor-id="基本步骤">基本步骤</h2>
<p>编写将LLVM IR转换为指定平台的机器码或其他语言的代码的编译器后端，需要以下步骤：</p>
<ul>
<li>创建TargetMachine类的子类， 该类描述目标计算机的特征， 这一步可以参考已有后端的TargetMachine类及其头文件； 例如，直接复制SparcTargetMachine.cpp和SparcTargetMachine.h但更改其文件名， 并且更改引用“Sparc”的代码以引用您的代码。</li>
<li>描述目标平台的寄存器。 使用TableGen从RegisterInfo.td文件生成定义寄存器、寄存器别名和寄存器组的代码。 还可能需要为TargetRegisterInfo的子类编写代码，这些代码代用于支持寄存器的分配以及描述寄存器间的约束。</li>
<li>描述目标平台的指令集。 使用TableGen从TargetInstrFormats.td和TargetInstrInfo.td文件生成描述目标平台的指令集的代码。 您可能需要手动为TargetInstrInfo的子类编写代码，以描述目标平台支持的某些特殊指令。</li>
<li>描述指令选择规则，该过程将LLVM IR的有向无环图（DAG）表示转换到目标平台原生指令表示。 使用TableGen根据TargetInstrInfo.td文件从定义的模式来生成支持指令选择的代码， 有时需要手动为XXXISelDAGToDAG.cpp编写代码来完成DAG-to-DAG的转换， 有时还需要手动为XXXISelLowering.cpp文件编写代码来替换不被SelectionDAG原生支持的操作和数据类型。</li>
<li>编写汇编生成器，汇编生成器将LLVM IR转换为目标计算机的GAS格式。 你需要在TargetInstrInfo.td文件中增加assembly strings。 同时还需要实现AsmPrinter的子类以及TargetAsmInfo的子类， 来实现LLVM IR到汇编的转换。</li>
<li>（可选）添加对子平台(具有不同功能的变体)的支持。 实现TargetSubtarget的子类， 该类允许您使用<code>-mcpu=</code>和<code>-mattr=</code>命令行选项。</li>
<li>（可选）添加JIT支持并创建机器码发射器(TargetJITInfo的子类)， 它用于直接将二进制代码发送到内存中。</li>
</ul>
<p>在.cpp和.h文件中，首先为这些方法建立占位，然后在以后实现它们。 最初，您可能不知道这些类需要哪些私有成员，哪些类需要子类化。</p>
</section>
<section id="准备工作" class="level2">
<h2 class="anchored" data-anchor-id="准备工作">准备工作</h2>
<p>要创建实际的编译器后端， 您需要创建和修改一些文件。 这里只讨论必须的操作。 但是要实际使用LLVM的目标独立代码生成器， 您必须执行<a href="https://releases.llvm.org/11.0.0/docs/CodeGenerator.html">LLVM Target-Independent Code Generator</a>中描述的步骤。</p>
<p>首先，您应该在<code>lib/Target</code>目录下创建一个子目录来保存与您的目标相关的所有文件。 如果目标名为“Dummy”，则创建<code>lib/target/Dummy</code>目录。</p>
<p>在这个新目录中，创建文件<code>CMakeLists.txt</code>。 最简单的方法是复制另一个目标的<code>CMakeLists.txt</code>文件并对其进行修改。 它至少应该指定<code>LLVM_TARGET_DEFINITIONS</code>变量。 这个库可以作为一个整体命名为<code>LLVMDummy</code>(参见MIPS后端)。 也可以将库拆分为<code>LLVMDummyAsmPrinter</code>和<code>LLVMDummyAsmPrinter</code>， 后者应该在<code>lib/Target/Dummy</code>的子目录中实现(参见PowerPC后端)。</p>
<p>注意，这两个命名方案都被硬编码到<code>llvm-config</code>中。 使用任何其他命名方案都会迷惑<code>llvm-config</code>， 并在链接<code>llc</code>时产生许多(看起来不相关的)链接器错误。</p>
<p>要使您的后端能执行实际的工作， 您需要实现<code>TargetMachine</code>的子类。 这个实现通常位于文件<code>lib/Target/DummyTargetMachine.cpp</code>中， <code>lib/Target</code>目录中的其它文件也应该被正确实现。 要使用LLVM的目标独立代码生成器， 您应该像所有机器后端一样： 创建<code>LLVMTargetMachine</code>的子类。（要从头开始创建目标，请创建TargetMachine的子类。）</p>
<p>要让LLVM真正构建并链接你的后端， 你需要用<code>-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=Dummy</code>命令运行cmake。 这将构建您的后端，而不需要将其添加到后端的列表中。</p>
<p>后端到达稳定版后，可以将其添加到位于主<code>CMakeLists.txt</code>文件的<code>LLVM_ALL_TARGETS</code>变量中。</p>
</section>
</section>
<section id="二-目标机器" class="level1">
<h1>二 目标机器</h1>
<p><code>LLVMTargetMachine</code>被设计成实现了目标无关代码生成器的目标的基类。 <code>LLVMTargetMachine</code>类特化为实现了各种虚拟方法的具体目标类。 <code>LLVMTargetMachine</code>在<code>include/llvm/target/TargetMachine.h</code>中定义为<code>TargetMachine</code>的子类。 <code>TargetMachine</code>类(TargetMachine.cpp)还负责处理许多命令行选项。</p>
<p>要为特定的目标创建<code>LLVMTargetMachine</code>的子类， 首先要复制现有的<code>TargetMachine</code>类的类文件和头文件。 您应该修改您创建的文件的文件名以反映能该目标。 例如，对于<code>SPARC</code>，将文件命名为<code>SparcTargetMachine.h</code>和<code>SparcTargetMachine.cpp</code>。</p>
<p>对于目标机器XXX， <code>XXXTargetMachine</code>必须实现一系列用于获取后端组件的对象的方法。 这些方法被命名为<code>get * Info</code>， 这些方法可以获取指令集(<code>getInstrInfo</code>)、寄存器(<code>getRegisterInfo</code>)、堆栈布局(<code>getFrameInfo</code>)等信息。 <code>XXXTargetMachine</code>还必须实现<code>getDataLayout</code>方法， 以访问数据特征(如数据类型大小和对齐要求)对象。</p>
<p>例如，对于SPARC目标， 头文件<code>SparcTargetMachine.h</code>声明了<code>get*Info</code>和<code>getDataLayout</code>等方法的原型， 这些方法的返回值都是<code>SparcTargetMachine</code>类的成员变量。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb1-1">namespace llvm {</span>
<span id="cb1-2"></span>
<span id="cb1-3">class Module<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-4"></span>
<span id="cb1-5">class SparcTargetMachine : public LLVMTargetMachine {</span>
<span id="cb1-6">  const DataLayout DataLayout<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;       // Calculates type size &amp; alignment</span></span>
<span id="cb1-7">  SparcSubtarget Subtarget<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-8">  SparcInstrInfo InstrInfo<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-9">  TargetFrameInfo FrameInfo<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-10"></span>
<span id="cb1-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">protected:</span></span>
<span id="cb1-12">  virtual const TargetAsmInfo *createTargetAsmInfo() const<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-13"></span>
<span id="cb1-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">public:</span></span>
<span id="cb1-15">  SparcTargetMachine(const Module &amp;M, const <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">std:</span>:string &amp;FS)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-16"></span>
<span id="cb1-17">  virtual const SparcInstrInfo *getInstrInfo() const {return &amp;InstrInfo<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; }</span></span>
<span id="cb1-18">  virtual const TargetFrameInfo *getFrameInfo() const {return &amp;FrameInfo<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; }</span></span>
<span id="cb1-19">  virtual const TargetSubtarget *getSubtargetImpl() const{return &amp;Subtarget<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; }</span></span>
<span id="cb1-20">  virtual const TargetRegisterInfo *getRegisterInfo() const {</span>
<span id="cb1-21">    return &amp;InstrInfo.getRegisterInfo()<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-22">  }</span>
<span id="cb1-23">  virtual const DataLayout *getDataLayout() const { return &amp;DataLayout<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">; }</span></span>
<span id="cb1-24">  static unsigned getModuleMatchQuality(const Module &amp;M)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-25"></span>
<span id="cb1-26">  // Pass Pipeline Configuration</span>
<span id="cb1-27">  virtual bool addInstSelector(PassManagerBase &amp;PM, bool Fast)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-28">  virtual bool addPreEmitPass(PassManagerBase &amp;PM, bool Fast)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-29">}<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-30"></span>
<span id="cb1-31">} // <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span> namespace llvm</span></code></pre></div></div>
<ul>
<li><p>getInstrInfo()</p></li>
<li><p>getRegisterInfo()</p></li>
<li><p>getFrameInfo()</p></li>
<li><p>getDataLayout()</p></li>
<li><p>getSubtargetImpl()</p></li>
</ul>
<p>对于某些目标，还需要支持以下方法：</p>
<ul>
<li><p>getTargetLowering()</p></li>
<li><p>getJITInfo()</p></li>
</ul>
<p>有些体系结构（如gpu）不支持跳转到任意位置， 使用屏蔽执行来实现分支， 使用包裹循环体的特殊指令来实现循环。 为了避免CFG修改引入不可还原的控制流， 而这些控制流又不被硬件支持， 目标必须在初始化时调用<code>setRequiresStructuredCFG(true)</code>。</p>
<p>此外，<code>XXXTargetMachine</code>构造函数应该指定一个<code>TargetDescription</code>字符串， 该字符串确定目标机器的数据布局， 包括指针大小、对齐方式和端序等特征。 例如，<code>SparcTargetMachine</code>的构造函数包含以下内容:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">SparcTargetMachine:</span>:SparcTargetMachine(const Module &amp;M, const <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">std:</span>:string &amp;FS)</span>
<span id="cb2-2">  : DataLayout(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"E-p:32:32-f128:128:128"</span>),</span>
<span id="cb2-3">    Subtarget(M, FS), InstrInfo(Subtarget),</span>
<span id="cb2-4">    FrameInfo(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">TargetFrameInfo:</span>:StackGrowsDown, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) {</span>
<span id="cb2-5">}</span></code></pre></div></div>
<p>连字符分隔TargetDescription字符串的各个部分。</p>
<ul>
<li><p>字符串中的大写’e’表示目标数据模型是big-endian，小写’e’表示little-endian。</p></li>
<li><p>“p:”后面跟着指针信息:大小、ABI对齐和首选对齐。如果”p:“后面只有两个数字，那么第一个值是指针大小，第二个值是ABI和首选对齐方式。</p></li>
<li><p>然后是表示数字类型对齐的字母：“i”、“f”、“v”或“a”（对应于整数、浮点、向量或 聚合）。“i”、“v”或“a”后跟ABI对齐和首选 对齐。“f”后跟三个值：第一个值表示长双精度的大小，然后是ABI对齐，然后是ABI首选对齐。</p></li>
</ul>
</section>
<section id="三-目标注册" class="level1">
<h1>三 目标注册</h1>
<p>您还必须向<code>TargetRegistry</code>注册您的目标， 这是其他LLVM工具在运行时查找和使用您的目标的工具。 <code>TargetRegistry</code>可以直接使用， 但是对于大多数目标来说， 有一些辅助模板可以帮助您完成工作。</p>
<p>所有目标应该声明一个全局<code>Target</code>对象， 用于在注册期间表示目标。 然后，在目标的<code>TargetInfo</code>库中， 目标应该定义该对象并使用<code>RegisterTarget</code>模板注册目标。 例如，Sparc注册代码如下:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb3-1">Target <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">llvm:</span>:getTheSparcTarget()<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb3-2"></span>
<span id="cb3-3">extern <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">void</span> LLVMInitializeSparcTargetInfo() {</span>
<span id="cb3-4">  RegisterTarget&lt;<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Triple:</span>:sparc, /*HasJIT=*/<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span>&gt;</span>
<span id="cb3-5">    X(getTheSparcTarget(), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sparc"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Sparc"</span>)<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb3-6">}</span></code></pre></div></div>
<p>这允许<code>TargetRegistry</code>按名称或按目标三元组查找目标。 此外，大多数目标还将注册在单独的库中可用的其他特性。 这些注册步骤是分开的，因为有些客户可能希望只链接目标的某些部分–例如， JIT代码生成器不需要使用汇编打印机。 下面是一个注册Sparc汇编输出器的例子:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb4-1">extern <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">void</span> LLVMInitializeSparcAsmPrinter() {</span>
<span id="cb4-2">  RegisterAsmPrinter&lt;SparcAsmPrinter&gt; X(getTheSparcTarget())<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-3">}</span></code></pre></div></div>
<p>更多信息, 请参照“<a href="https://releases.llvm.org/doxygen/TargetRegistry_8h-source.html">llvm/Target/TargetRegistry.h</a>”.</p>
</section>
<section id="四-寄存器和寄存器组" class="level1">
<h1>四 寄存器和寄存器组</h1>
<p>（译注：本节及后文将原文中Register Set译为寄存器集合，将Register Class根据原文的含义译为寄存器组或Register类。）</p>
<p>您需要创建一个具体的寄存器描述类，这个类称为XXXRegisterInfo(其中XXX是平台标识符)，它描述了寄存器间的约束并为寄存器分配器提供必要的信息。</p>
<p>您还需要定义寄存器组来对相关寄存器进行分类。同一寄存器组的寄存器可以被某些指令以相同的方式使用。典型的例子是用于整数、浮点或向量的寄存器组。寄存器分配器允许指令以类似的方式使用同一寄存器组中的任何寄存器。寄存器分配器先给指令分配虚拟寄存器，然后会在寄存器分配阶段分配物理寄存器。</p>
<p>描述寄存器的大部分代码，包括寄存器定义、寄存器别名和寄存器组，都可以由TableGen工具自动生成。TableGen会根据开发者编写的xxxRegisterinfo.td文件，生成XXXGenRegisterInfo.h.inc和XXXGenRegisterInfo.inc文件。XXXRegisterInfo的实现过程中的一些代码需要手工编码。</p>
<section id="定义寄存器" class="level2">
<h2 class="anchored" data-anchor-id="定义寄存器">4.1 定义寄存器</h2>
<p>XXXRegisterinfo.td文件通常以目标机器的寄存器定义开始。Register类(在Target.td中定义)用于为每个寄存器定义一个对象。字符串n就是寄存器的名称。基本的Register对象不包含子寄存器，也没有指定别名。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb5-2">  string Namespace <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-3">  string AsmName <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-4">  string Name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> SpillSize <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-6">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span> SpillAlignment <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb5-7">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Aliases <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[];</span></span>
<span id="cb5-8">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> SubRegs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[];</span></span>
<span id="cb5-9">  list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">int</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> DwarfNumbers <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[];</span></span>
<span id="cb5-10"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>例如，X86RegisterInfo.td文件使用Register类定义寄存器。比如：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb6-1">def AL <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"AL"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;,</span> DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;[</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]&gt;;</span></span></code></pre></div></div>
<p>这行代码定义了寄存器AL并使用DwarfRegNum为其赋值，gcc，gdb或其他调试信息工具用该值来识别寄存器。对于AL寄存器来说，DwarfRegNum使用了一个由3个值组成的数组，用来表示3 种不同的模式：第一个值是用于X86-64，第二个值用于X86-32中的异常处理（exception handling），第三个是通用值。-1表示gcc的值未定义，-2表示寄存器在该模式下是非法的。</p>
<p>根据X86RegisterInfo.td文件的描述，TableGen会在X86GenRegisterInfo.inc文件中生成以下代码：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb7-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> GR8<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> X86<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>AL<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb7-2"></span>
<span id="cb7-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> AL_AliasSet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> X86<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>AX<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> X86<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>EAX<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> X86<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>RAX<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb7-4"></span>
<span id="cb7-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> TargetRegisterDesc RegisterDescriptors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb7-6">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb7-7"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"AL"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"AL"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> AL_AliasSet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Empty_SubRegsSet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> Empty_SubRegsSet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> AL_SuperRegsSet <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span></code></pre></div></div>
<p>根据register info文件，TableGen为每个寄存器生成一个TargetRegisterDesc对象。TargetRegisterDesc在<code>include/llvm/Target/Target.h</code>中被定义，包含以下字段：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">struct</span> TargetRegisterDesc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb8-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">char</span>     <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>AsmName<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Assembly language name for the register</span></span>
<span id="cb8-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">char</span>     <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>Name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>         <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Printable name for the reg (for debugging)</span></span>
<span id="cb8-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>AliasSet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Register Alias Set</span></span>
<span id="cb8-5">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>SubRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Sub-register set</span></span>
<span id="cb8-6">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>ImmSubRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Immediate sub-register set</span></span>
<span id="cb8-7">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>SuperRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span>    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Super-register set</span></span>
<span id="cb8-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span></code></pre></div></div>
<p>TableGen使用名称(TargetRegisterDesc的AsmName和Name字段)以及寄存器间的关系(TargetRegisterDesc的其他字段)来定义寄存器。在这个示例中，寄存器“AX”、“ EAX”和“RAX”为彼此的别名，TableGen为这个寄存器别名集生成一个以null结尾的数组(AL_aliasset)。</p>
<p>Register类通常用作更复杂类的基类。在Target.td中，Register类是RegisterWithSubRegs类的基类，该类用于定义需要在SubRegs列表中指定子寄存器的寄存器，如下所示</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> RegisterWithSubRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> subregs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-2">  let SubRegs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> subregs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb9-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>SparcRegisterInfo.td为SPARC定义了额外的寄存器类：register类的子类SparcReg和其进一步的子类：Ri、Rf和Rd。SPARC的寄存器由5位ID号标识，这是这些子类的一个共同特性。“let”表达式可以覆盖最初在父类中定义的值（例如Rd类中的subgros字段）。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb10-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> SparcReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-2">  field bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> Num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-3">  let Namespace <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SP"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-4"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Ri - 32-bit integer registers</span></span>
<span id="cb10-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Ri<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb10-7">SparcReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-8">  let Num <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-9"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Rf - 32-bit floating-point registers</span></span>
<span id="cb10-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Rf<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span></span>
<span id="cb10-12">SparcReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-13">  let Num <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Rd - Slots in the FP register file for 64-bit floating-point values.</span></span>
<span id="cb10-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> Rd<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>bits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> string n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> list<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>Register<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> subregs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> SparcReg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-17">  let Num <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> num<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-18">  let SubRegs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> subregs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb10-19"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>SparcRegisterInfo.td文件利用Register类的子类来定义寄存器，例如</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> G0 : Ri<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"G0"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb11-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> G1 : Ri<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"G1"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb11-3">...</span>
<span id="cb11-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> F0 : Rf<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F0"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb11-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> F1 : Rf<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F1"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">33</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb11-6">...</span>
<span id="cb11-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> D0 : Rd<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F0"</span>, [F0, F1]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span>
<span id="cb11-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> D1 : Rd<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F2"</span>, [F2, F3]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span>, DwarfRegNum<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span>[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">34</span>]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;;</span></span></code></pre></div></div>
<p>上面显示的最后两个寄存器(D0和D1)是双精度浮点寄存器，它们是单精度浮点子寄存器对的别名。除了别名之外，子寄存器和父寄存器的关系也定义在TargetRegisterDesc的某些字段中。</p>
</section>
<section id="定义寄存器组" class="level2">
<h2 class="anchored" data-anchor-id="定义寄存器组">4.2 定义寄存器组</h2>
<p><code>RegisterClass</code>类（在Target.td中指定）用于定义一个对象， 该对象表示一组相关的寄存器， 还定义了寄存器的默认分配顺序。 使用<code>Target.td</code>的目标描述文件<code>XXXRegisterInfo.td</code>可以使用以下类构造寄存器组：</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb12-1">class RegisterClass&lt;string namespace,</span>
<span id="cb12-2">list&lt;ValueType&gt; regTypes, int alignment, dag regList&gt; {</span>
<span id="cb12-3">  string Namespace = namespace<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-4">  list&lt;ValueType&gt; RegTypes = regTypes<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-5">  int Size = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;  // spill size, in bits; zero lets tblgen pick the size</span></span>
<span id="cb12-6">  int Alignment = alignment<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-7"></span>
<span id="cb12-8">  // CopyCost is the cost of copying a value between two registers</span>
<span id="cb12-9">  // <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">default</span> value <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> means a single instruction</span>
<span id="cb12-10">  // A negative value means copying is extremely expensive <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> impossible</span>
<span id="cb12-11">  int CopyCost = <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-12">  dag MemberList = regList<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-13"></span>
<span id="cb12-14">  // for register classes that are subregisters of this class</span>
<span id="cb12-15">  list&lt;RegisterClass&gt; SubRegClassList = []<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-16"></span>
<span id="cb12-17">  code MethodProtos = [{}]<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;  // to insert arbitrary code</span></span>
<span id="cb12-18">  code MethodBodies = [{}]<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb12-19">}</span></code></pre></div></div>
<p>要定义<code>RegisterClass</code>，请使用以下4个参数:</p>
<ul>
<li><p>第一个参数定义了命名空间的名称。</p></li>
<li><p>第二个参数是寄存器类型的列表，寄存器的类型定义在文件<code>include/llvm/CodeGen/ValueTypes.td</code>中。 已定义的值包括整数类型(<code>i16</code>、<code>i32</code>和<code>i1</code>(布尔值))、浮点类型(<code>f32</code>、<code>f64</code>)和向量类型(例如，<code>v8i16</code>表示<code>8xi16</code>向量)。 <code>RegisterClass</code>中的所有寄存器必须具有相同的<code>ValueType</code>， 但有些寄存器可以不同的配置存储向量数据。 例如，一个能够处理128位向量的寄存器也能处理16个8位整数元素，8个16位整数，4个32位整数，等等。</p></li>
<li><p>第三个参数指定寄存器数据在<code>load</code>或<code>save</code>时所需的对齐方式。</p></li>
<li><p>最后一个参数<code>regList</code>指定这个集合包含的寄存器。 如果没有指定寄存器的分配顺序， 那么<code>regList</code>还暗含了寄存器的分配顺序。 除了简单地用<code>(add R0，R1，...)</code>列出寄存器之外， 还可以用更高级的集合操作符。 更多信息，请参见<code>include/llvm/Target/Target.td</code>。</p></li>
</ul>
<p>在<code>SparcRegisterInfo.td</code>中， 定义了三个<code>RegisterClass</code>对象：<code>FPReg</code>，<code>DFPReg</code>和<code>IntReg</code>。 对于所有三个寄存器类，第一个参数都是使用字符串“ SP”定义名称空间。 <code>FPRegs</code>定义了一组32个单精度浮点寄存器（F0至F31）。 <code>DFPRegs</code>定义了一组16个双精度寄存器（D0-D15）。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode llvm code-with-copy"><code class="sourceCode llvm"><span id="cb13-1">// F0, F1, F2, ..., F31</span>
<span id="cb13-2">def FPRegs : RegisterClass&lt;<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SP"</span>, [f32], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>, (sequence <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"F%u"</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31</span>)&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-3"></span>
<span id="cb13-4">def DFPRegs : RegisterClass&lt;<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SP"</span>, [f64], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">64</span>,</span>
<span id="cb13-5">                            (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span> D0, D1, D2, D3, D4, D5, D6, D7, D8,</span>
<span id="cb13-6">                                 D9, D10, D11, D12, D13, D14, D15)&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb13-7"></span>
<span id="cb13-8">def IntRegs : RegisterClass&lt;<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"SP"</span>, [<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">i32</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>,</span>
<span id="cb13-9">    (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">add</span> L0, L1, L2, L3, L4, L5, L6, L7,</span>
<span id="cb13-10">         I0, I1, I2, I3, I4, I5,</span>
<span id="cb13-11">         O0, O1, O2, O3, O4, O5, O7,</span>
<span id="cb13-12">         G1,</span>
<span id="cb13-13">         // Non-allocatable <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">regs:</span></span>
<span id="cb13-14">         G2, G3, G4,</span>
<span id="cb13-15">         O6,        // stack ptr</span>
<span id="cb13-16">         I6,        // frame ptr</span>
<span id="cb13-17">         I7,        // return address</span>
<span id="cb13-18">         G0,        // <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">constant</span> zero</span>
<span id="cb13-19">         G5, G6, G7 // reserved for kernel</span>
<span id="cb13-20">    )&gt;<span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>TableGen将<code>SparcRegisterInfo.td</code>编译成多个输出文件， 这些输出文件将会被包含在您编写的其他源代码中。 <code>SparcRegisterInfo.td</code>被编译成<code>SparcGenRegisterInfo.h.inc</code>， 这个文件将被包含在实现SPARC寄存器的头文件<code>SparcRegisterInfo.h</code>中。 <code>SparcGenRegisterInfo.h.inc</code>定义了一个名为<code>SparcGenRegisterInfo</code>的新结构， 该结构继承<code>TargetRegisterInfo</code>， 还根据预定义的寄存器集（<code>DFPRegsClass</code>，<code>FPRegsClass</code>和<code>IntRegsClass</code>）来指定类型。</p>
<p><code>Sparcregisterinfo.td</code>还会生成<code>SparcGenRegisterInfo.inc</code>文件， 它被包含在文件<code>SparcRegisterInfo.cpp</code>的底部， 该文件用于实现Sparc的寄存器。 下面只显示生成的整数寄存器和关联的寄存器集， <code>IntRegs</code>中寄存器的顺序同目标描述文件中<code>IntRegs</code>定义的顺序一致。</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode cpp code-with-copy"><code class="sourceCode cpp"><span id="cb14-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegs Register Class...</span></span>
<span id="cb14-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">unsigned</span> IntRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-3">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L0<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L3<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L4<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L5<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-4">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L6<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>L7<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I0<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I3<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-5">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I4<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I5<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O0<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O3<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-6">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O4<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O5<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O7<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G1<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G2<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G3<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-7">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G4<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>O6<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I6<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>I7<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G0<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G5<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-8">  SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G6<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> SP<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>G7<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-9"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-10"></span>
<span id="cb14-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegsVTs Register Class Value Types...</span></span>
<span id="cb14-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> MVT<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>ValueType IntRegsVTs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-13">  MVT<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>i32<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> MVT<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>Other</span>
<span id="cb14-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-15"></span>
<span id="cb14-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">namespace</span> SP <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Register class instances</span></span>
<span id="cb14-17">  DFPRegsClass    DFPRegsRegClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb14-18">  FPRegsClass     FPRegsRegClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb14-19">  IntRegsClass    IntRegsRegClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb14-20"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb14-21">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegs Sub-register Classes...</span></span>
<span id="cb14-22">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> TargetRegisterClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> IntRegsSubRegClasses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-23">    NULL</span>
<span id="cb14-24">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-25"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb14-26">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegs Super-register Classes..</span></span>
<span id="cb14-27">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> TargetRegisterClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> IntRegsSuperRegClasses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-28">    NULL</span>
<span id="cb14-29">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-30"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb14-31">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegs Register Class sub-classes...</span></span>
<span id="cb14-32">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> TargetRegisterClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> IntRegsSubclasses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-33">    NULL</span>
<span id="cb14-34">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-35"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span></span>
<span id="cb14-36">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// IntRegs Register Class super-classes...</span></span>
<span id="cb14-37">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">static</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> TargetRegisterClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">const</span> IntRegsSuperclasses <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb14-38">    NULL</span>
<span id="cb14-39">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb14-40"></span>
<span id="cb14-41">  IntRegsClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span>IntRegsClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> TargetRegisterClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>IntRegsRegClassID<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-42">    IntRegsVTs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> IntRegsSubclasses<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> IntRegsSuperclasses<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> IntRegsSubRegClasses<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb14-43">    IntRegsSuperRegClasses<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> IntRegs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> IntRegs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="cb14-44"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>寄存器分配器将避免使用保留寄存器， 并且被调用方保存的寄存器在所有易失性寄存器被使用之前都不会被使用。 这通常已经足够好了， 但在某些情况下， 可能需要提供自定义分配命令。</p>
</section>
<section id="实现targetregisterinfo的子类" class="level2">
<h2 class="anchored" data-anchor-id="实现targetregisterinfo的子类">4.3 实现TargetRegisterInfo的子类</h2>
<p>最后一步是手工编写<code>XXXRegisterInfo</code>的部分代码， 它实现了文件<code>TargetRegisterInfo.h</code>描述的接口（请参见<code>TargetRegisterInfo</code>类）。 如果不实现这些接口，这些接口将返回<code>0</code>、<code>NULL</code>或<code>false</code>。 下面是为实现SPARC而在文件<code>SparcRegisterInfo.cpp</code>中手工编写函数列表:</p>
<ul>
<li><p><code>getCalleeSavedRegs</code> —- 返回被叫方保存的寄存器列表，按被叫方所需的堆栈帧偏移量顺序。</p></li>
<li><p><code>getReservedRegs</code> —- 返回物理寄存器索引的集合，指示特定寄存器是否不可用。</p></li>
<li><p><code>hasFP</code> —- 返回一个布尔值，指示函数是否应具有专用的帧指针寄存器。</p></li>
<li><p><code>eliminateCallFramePseudoInstr</code> —- 如果使用调用帧设置或销毁伪指令，则可以调用此命令来消除它们。</p></li>
<li><p><code>excludeFrameIndex</code> – 从能使用抽象帧索引的指令中删除抽象帧索引。</p></li>
<li><p><code>emitPrologue</code> – 在函数中插入Prologue代码。</p></li>
<li><p><code>emitEpilogue</code> – 在函数中插入Epilogue代码。</p></li>
</ul>
</section>
</section>
<section id="五-指令集" class="level1">
<h1>五 指令集</h1>
<p>在代码生成的早期阶段， LLVM IR代码被转换为Selection DAG， 节点是<code>SDNode</code>类的实例， <code>SDNode</code>类包含目标指令， 具有操作码、操作数、类型要求和操作属性。 例如，操作是否是可交换的，是否需要从内存加载数据。 文件<code>include/llvm/CodeGen/SelectionDAGNodes.h</code>（ISD命名空间中的NodeType枚举）描述了节点的各种类型。</p>
<section id="指令操作数映射" class="level2">
<h2 class="anchored" data-anchor-id="指令操作数映射">指令操作数映射</h2>
<section id="指令操作数名称映射" class="level3">
<h3 class="anchored" data-anchor-id="指令操作数名称映射">指令操作数名称映射</h3>
</section>
<section id="指令操作数类型" class="level3">
<h3 class="anchored" data-anchor-id="指令操作数类型">指令操作数类型</h3>
</section>
</section>
<section id="指令调度" class="level2">
<h2 class="anchored" data-anchor-id="指令调度">指令调度</h2>
</section>
<section id="指令关系映射" class="level2">
<h2 class="anchored" data-anchor-id="指令关系映射">指令关系映射</h2>
</section>
<section id="实现targetstrinfo的子类" class="level2">
<h2 class="anchored" data-anchor-id="实现targetstrinfo的子类">实现TargetStrInfo的子类</h2>
</section>
<section id="分支折叠与if转换" class="level2">
<h2 class="anchored" data-anchor-id="分支折叠与if转换">分支折叠与If转换</h2>
</section>
</section>
<section id="六-指令选择器" class="level1">
<h1>六 指令选择器</h1>
<section id="选择合法化阶段" class="level2">
<h2 class="anchored" data-anchor-id="选择合法化阶段">选择合法化阶段</h2>
<section id="推广" class="level3">
<h3 class="anchored" data-anchor-id="推广">推广</h3>
</section>
<section id="展开" class="level3">
<h3 class="anchored" data-anchor-id="展开">展开</h3>
</section>
<section id="定制" class="level3">
<h3 class="anchored" data-anchor-id="定制">定制</h3>
</section>
<section id="合法的" class="level3">
<h3 class="anchored" data-anchor-id="合法的">合法的</h3>
</section>
</section>
<section id="调用约定" class="level2">
<h2 class="anchored" data-anchor-id="调用约定">调用约定</h2>
</section>
</section>
<section id="七-装配式打印机" class="level1">
<h1>七 装配式打印机</h1>
</section>
<section id="八-子目标支持" class="level1">
<h1>八 子目标支持</h1>
</section>
<section id="九-jit支持" class="level1">
<h1>九 JIT支持</h1>
<section id="机器码发射器" class="level2">
<h2 class="anchored" data-anchor-id="机器码发射器">机器码发射器</h2>
</section>
<section id="目标jit信息" class="level2">
<h2 class="anchored" data-anchor-id="目标jit信息">目标JIT信息</h2>


</section>
</section>

 ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-11-01-write-an-LLVM-backend.html</guid>
  <pubDate>Sun, 01 Nov 2020 10:38:38 GMT</pubDate>
</item>
<item>
  <title>Write an LLVM Backend(零)：简介</title>
  <link>https://sadsock.github.io/posts/2020-11-01-write-an-LLVM-backend-0.html</link>
  <description><![CDATA[ 




<section id="简介" class="level1">
<h1>简介</h1>
<p>本文档描述了编写编译器后端的技术， 这些后端将LLVM中间表示（IR）转换为特定机器或其他语言的代码， 这些代码可以是汇编代码也可以是二进制代码（可用于JIT编译器）。</p>
<p>LLVM的后端是一个平台独立的代码生成器， 它可以为不同类型的目标CPU生成代码， 包括X86、PowerPC、ARM和SPARC。 后端还可以为GPU或Cell处理器的SPU生成代码， 以支持计算内核的执行。</p>
<p>本文档主要关注<code>llvm/lib/Target</code>目录中的现有示例， 特别是为SPARC平台创建静态编译器(也就是发射汇编码)， 因为SPARC具有相当典型的特征， 比如RISC指令集和常见的调用约定。</p>
<section id="目标读者" class="level2">
<h2 class="anchored" data-anchor-id="目标读者">目标读者</h2>
<p>本文档的受众是需要编写LLVM后端来为特定的硬件或软件平台生成代码的开发者。</p>
</section>
<section id="前置知识" class="level2">
<h2 class="anchored" data-anchor-id="前置知识">前置知识</h2>
<p>在阅读本文档之前，必须先阅读以下重要文档：</p>
<ul>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/LangRef.html">LLVM Language Reference Manual</a>–LLVM汇编语言的参考手册。</p></li>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/CodeGenerator.html">The LLVM Target-Independent Code Generator</a>– 用于将LLVM内部表示转换为指定平台的机器码的组件（类和代码生成算法）的指南。 特别注意代码生成阶段：指令选择、调度和形成、基于SSA的优化、寄存器分配、Prolog/Epilog代码插入、后期机器代码优化和代码发射。</p></li>
<li><p><a href="https://releases.llvm.org/11.0.0/docs/TableGen/index.html">TableGen</a>–TableGen（tblgen）应用程序的描述文档， 该程序管理LLVM代码生成所需的特定信息。 TableGen能把目标描述文件（后缀.td）转换成用于代码生成的C++代码。</p></li>
</ul>
</section>
<section id="基本步骤" class="level2">
<h2 class="anchored" data-anchor-id="基本步骤">基本步骤</h2>
<p>编写将LLVM IR转换为指定平台的机器码或其他语言的代码的编译器后端，需要以下步骤：</p>
<ul>
<li>创建TargetMachine类的子类， 该类描述目标计算机的特征， 这一步可以参考已有后端的TargetMachine类及其头文件； 例如，直接复制SparcTargetMachine.cpp和SparcTargetMachine.h但更改其文件名， 并且更改引用“Sparc”的代码以引用您的代码。</li>
<li>描述目标平台的寄存器。 使用TableGen从RegisterInfo.td文件生成定义寄存器、寄存器别名和寄存器组的代码。 还可能需要为TargetRegisterInfo的子类编写代码，这些代码代用于支持寄存器的分配以及描述寄存器间的约束。</li>
<li>描述目标平台的指令集。 使用TableGen从TargetInstrFormats.td和TargetInstrInfo.td文件生成描述目标平台的指令集的代码。 您可能需要手动为TargetInstrInfo的子类编写代码，以描述目标平台支持的某些特殊指令。</li>
<li>描述指令选择规则，该过程将LLVM IR的有向无环图（DAG）表示转换到目标平台原生指令表示。 使用TableGen根据TargetInstrInfo.td文件从定义的模式来生成支持指令选择的代码， 有时需要手动为XXXISelDAGToDAG.cpp编写代码来完成DAG-to-DAG的转换， 有时还需要手动为XXXISelLowering.cpp文件编写代码来替换不被SelectionDAG原生支持的操作和数据类型。</li>
<li>编写汇编生成器，汇编生成器将LLVM IR转换为目标计算机的GAS格式。 你需要在TargetInstrInfo.td文件中增加assembly strings。 同时还需要实现AsmPrinter的子类以及TargetAsmInfo的子类， 来实现LLVM IR到汇编的转换。</li>
<li>（可选）添加对子平台(具有不同功能的变体)的支持。 实现TargetSubtarget的子类， 该类允许您使用<code>-mcpu=</code>和<code>-mattr=</code>命令行选项。</li>
<li>（可选）添加JIT支持并创建机器码发射器(TargetJITInfo的子类)， 它用于直接将二进制代码发送到内存中。</li>
</ul>
<p>在.cpp和.h文件中，首先为这些方法建立占位，然后在以后实现它们。 最初，您可能不知道这些类需要哪些私有成员，哪些类需要子类化。</p>
</section>
<section id="准备工作" class="level2">
<h2 class="anchored" data-anchor-id="准备工作">准备工作</h2>


</section>
</section>

 ]]></description>
  <category>LLVM</category>
  <guid>https://sadsock.github.io/posts/2020-11-01-write-an-LLVM-backend-0.html</guid>
  <pubDate>Sun, 01 Nov 2020 10:38:38 GMT</pubDate>
</item>
</channel>
</rss>
