📅 创建时间:2026-06-03 🏷️ 标签:#MLIR #LLVM #Lowering #Codegen #GlobalISel #SelectionDAG #TargetTransformInfo #x86 #ARM 📚 前置知识:[[/04-ai/01-llm-engineering/07-llm-evolution]](LLM 发展脉络) 📚 相关知识:[[04-mlir-architecture]](MLIR 架构) [[14-backend-cpu]](CPU 后端)
从 MLIR 经 LLVM 降级到机器码 / Lowering from MLIR Through LLVM to Machine Code
┌─────────────────────────────────────────────────────────────────────────────┐
│ 场景:从 MLIR 到 x86 汇编的疑惑 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 你在研究 IREE 如何把一个 MLIR 程序编译成 x86 汇编: │
│ │
│ Linalg matmul → Linalg to Affine → Affine to SCF → SCF to LLVM → x86 │
│ │
│ 你想知道: │
│ - MLIR 的 dialect conversion 到底做了什么? │
│ - 为什么一个 Affine loop nest 能最终变成 x86 的 AVX-512 指令? │
│ - GlobalISel 和 SelectionDAG 是什么,哪个更好? │
└─────────────────────────────────────────────────────────────────────────────┘第1节 MLIR 到 LLVM IR 转换
1.1 Dialect Conversion 机制
MLIR 的核心特性之一是多层次 Dialect,Conversion Pass 负责把高层 Dialect 降到低层 Dialect:
mlir
// Dialect Conversion 示例
// 从 Linalg 到 LLVM 的完整转换路径
// ===== Stage 1: Linalg Dialect (高层) =====
#map = affine_map<(d0, d1, d2) -> (d0, d2)>
#map1 = affine_map<(d0, d1, d2) -> (d2, d1)>
#map2 = affine_map<(d0, d1, d2) -> (d0, d1)>
func.func @matmul(%A: tensor<128x256xf32>,
%B: tensor<256x512xf32>,
%C: tensor<128x512xf32>)
-> tensor<128x512xf32> {
// Linalg matmul:抽象的矩阵乘法描述
%D = linalg.matmul
ins(%A, %B: tensor<128x256xf32>, tensor<256x512xf32>)
outs(%C: tensor<128x512xf32>)
-> tensor<128x512xf32>
return %D : tensor<128x512xf32>
}
// ===== Stage 2: After Linalg to Affine (优化后) =====
// matmul 被展开为 Affine loop nest
func.func @matmul_affine(%A: memref<128x256xf32>,
%B: memref<256x512xf32>,
%C: memref<128x512xf32>) {
affine.for %i = 0 to 128 {
affine.for %j = 0 to 512 {
%c0 = arith.constant 0.0 : f32
affine.store %c0, %C[%i, %j] : memref<128x512xf32>
affine.for %k = 0 to 256 {
%a_val = affine.load %A[%i, %k] : memref<128x256xf32>
%b_val = affine.load %B[%k, %j] : memref<256x512xf32>
%c_val = affine.load %C[%i, %j] : memref<128x512xf32>
%new_c = arith.addf %c_val, %a_val : f32
affine.store %new_c, %C[%i, %j] : memref<128x512xf32>
}
}
}
return
}1.2 Builtin 到 LLVM Dialect 转换
mlir
// Type 转换规则
// tensor<...> → memref<...> → LLVM pointer
// Before: Tensor Type
%tensor = "test.tensor"() : () -> tensor<128x256xf32>
// After: Memref Type
%memref = "test.to_memref"(%tensor) : (tensor<128x256xf32>) -> memref<128x256xf32>
// After: LLVM Pointer Type
%ptr = "test.to_llvm_ptr"(%memref) : (memref<128x256xf32>) -> !llvm.ptr<f32>
// ===== Complete Type Conversion =====
"""
tensor<128x256xf32>
│
▼ (bufferization)
memref<128x256xf32, strided<[256, 1], offset: 0>>
│
▼ (memref to LLVM)
!llvm.struct<(ptr<f32>, ptr<f32>, i64, array<2xi64>, array<2xi64>)>
│
▼ (LLVM lowering)
!llvm.ptr<f32> (with metadata)
"""1.3 Function Calling Convention
mlir
// MLIR 函数到 LLVM 函数
// 函数签名转换
// MLIR 函数(高层)
func.func @compute(%arg0: tensor<128xf32>, %arg1: tensor<128xf32>)
-> tensor<128xf32> {
%result = "arith.addf"(%arg0, %arg1) : (tensor<128xf32>, tensor<128xf32>)
-> tensor<128xf32>
return %result : tensor<128xf32>
}
// Lower 到 LLVM Dialect
llvm.func @compute(
%arg0: !llvm.ptr<f32>, // 替换 tensor
%arg1: !llvm.ptr<f32>,
%arg2: !llvm.ptr<f32> // output ptr (return via out-param)
) {
// 使用 LLVM intrinsics
%0 = llvm.load %arg0 : !llvm.ptr<f32>
%1 = llvm.load %arg1 : !llvm.ptr<f32>
%2 = llvm.fadd %0, %1 : f32
llvm.store %2, %arg2 : !llvm.ptr<f32>
llvm.return
}第2节 LLVM Pass 流水线
2.1 LLVM 优化 Pass 概览
┌─────────────────────────────────────────────────────────────────────────────┐
│ LLVM Optimization Pipeline │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ LLVM IR Input (from MLIR) │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Analysis Passes (不修改 IR,只提供信息) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Dominator │ │ PostDominator│ │ LoopInfo │ │
│ │ Tree │ │ Tree │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ ScalarEvoul │ │ Alias │ │ Target │ │
│ │ │ │ Analysis │ │ TransformInfo│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Transform Passes │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ IPO (Interprocedural Optimization) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ArgPromo │ │DeadFunc │ │Inline │ │MergeFunc│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Scalar Passes │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │SROA │ │InstComb │ │ GVN │ │LICM │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Vectorization Passes │ │
│ │ ┌───────────────────┐ ┌───────────────────┐ │ │
│ │ │ SLP Vectorizer │ │ Loop Vectorizer │ │ │
│ │ │ (horizontal) │ │ (vertical) │ │ │
│ │ └───────────────────┘ └───────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ CodeGen Passes │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ RegAlloc │ │ Instruction │ │ Peephole │ │
│ │ (寄存器分配) │ │ Select │ │ Optimizer │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Assembly/MC Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ x86_64 │ │ AArch64 │ │ RISC-V │ │
│ │ Assembly │ │ Assembly │ │ Assembly │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────┘2.2 关键 Pass 详解
llvm
; 1. SROA (Scalar Replacement of Aggregates)
; 把 aggregate (struct/array) 拆分到独立变量
; Before SROA
%agg = alloca { i32, float }
%val = load %agg
%sum = add i32 %val.0, 42
; After SROA
%val.0 = alloca i32
%val.1 = alloca float
%loaded.0 = load i32* %val.0
%sum = add i32 %loaded.0, 42
; 2. InstCombine (Instruction Combining)
; 代数化简
; Before
%a = add i32 %x, 0 ; x + 0 → x
%b = mul i32 %a, 1 ; * 1 → 保持
%c = and i32 %b, -1 ; & -1 → 保持
; After InstCombine
%c = and i32 %x, -1 ; 合并为单一操作
; 3. GVN (Global Value Numbering)
; 消除冗余计算
; Before
%a = load i32* %ptr
%b = load i32* %ptr ; 相同的 load
%c = add i32 %a, %b
; After GVN
%a = load i32* %ptr
%c = add i32 %a, %a ; 使用已有值
; 4. LICM (Loop Invariant Code Motion)
; 循环不变式外提
; Before
for (i = 0; i < n; i++) {
x = compute(); ; 循环不变
y = arr[i] + x;
}
; After LICM
x = compute(); ; 提到循环外
for (i = 0; i < n; i++) {
y = arr[i] + x;
}2.3 Vectorization Passes
llvm
; SLP Vectorizer (Scalar to Vector)
; 合并独立的标量操作到向量操作
; Before (处理4个独立的加法)
%a0 = fadd float %x0, %y0
%a1 = fadd float %x1, %y1
%a2 = fadd float %x2, %y2
%a3 = fadd float %x3, %y3
; After SLP Vectorization (一条向量指令)
%vec.x = vector.shuffle <4 x float> %x0, %x1, %x2, %x3
%vec.y = vector.shuffle <4 x float> %y0, %y1, %y2, %y3
%vec.a = fadd <4 x float> %vec.x, %vec.y
; Loop Vectorizer (向量化循环)
; 把标量循环转为向量循环
; Before
for (i = 0; i < 1024; i++)
y[i] = x[i] + z[i];
; After Loop Vectorization (AVX-512, 16元素并行)
; <16 x float> 表示16个float的向量
for (i = 0; i < 1024; i += 16)
%vx = vector.load <16 x float> &x[i]
%vy = vector.load <16 x float> &y[i]
%vz = vector.load <16 x float> &z[i]
%vr = fadd <16 x float> %vx, %vy
vector.store %vr, &y[i]第3节 LLVM Codegen 架构
3.1 指令选择概述
LLVM 的核心任务之一是把 LLVM IR 指令转换为目标机器指令,这称为指令选择:
llvm
; LLVM IR (抽象)
%result = fadd float %a, %b
%result2 = fmul float %result, %c
; 可能的 x86_64 目标代码
vmovss (%rdi), %xmm0 ; load %a
vaddss (%rsi), %xmm0, %xmm0 ; add %b
vmulss (%rdx), %xmm0, %xmm0 ; multiply %c3.2 SelectionDAG vs GlobalISel
| 特性 | SelectionDAG | GlobalISel |
|---|---|---|
| 设计时间 | 2000年代初期 | 2015年后 |
| 架构 | DAG(有向无环图) | 单一函数 IR |
| 选择方式 | 两步:ISel + RegAlloc | 一步:Combined |
| 处理范围 | BB 内 | 函数级 |
| 寄存器分配 | 单独 pass | 集成 |
| 支持目标 | 所有目标 | x86, AArch64, RISC-V |
| 性能 | 成熟稳定 | 新,但潜力更大 |
| 复杂度 | 复杂(多 pass) | 简洁(一体化) |
3.3 SelectionDAG 详解
llvm
// SelectionDAG 的工作流程
; 1. DAG 构建阶段
; LLVM IR → SelectionDAG
%add = fadd float %a, %b ; DAG 节点: FDIV
%mul = fmul float %add, %c ; DAG 节点: FMUL
DAG 拓扑:
┌─────┐
│ 0 │ (Entry)
└──┬──┘
│
┌──┴──┐
│FDIV │
└──┬──┘
│
┌──┴──┐
│FMUL │
└──┬──┘
│
┌──┴──┐
│ 0 │ (Finish)
└─────┘
; 2. 指令选择阶段
; DAG 节点 → 目标机器指令
; x86_64 目标:
FDIV → vaddss (使用 FADD 或根据精度选择)
FMUL → vmulss
; ARM 目标:
FDIV → vadd.f32 (NEON)
FMUL → vmul.f32 (NEON)
; 3. 调度阶段
; 确定指令执行顺序
; 发射顺序:
vmovss (%rdi), %xmm0 ; 1. load a
vmovss (%rsi), %xmm1 ; 2. load b
vaddss %xmm1, %xmm0 ; 3. add
vmovss (%rdx), %xmm1 ; 4. load c
vmulss %xmm1, %xmm0 ; 5. multiply3.4 GlobalISel 详解
llvm
// GlobalISel 的优势:一体化处理
; 1. 函数级 IR
; 直接在 MachineFunction 上操作,不经过 DAG
MachineFunction:
%bb.0:
%a:f32 = LGLOBAL_LOAD_F32 %p0
%b:f32 = LGLOBAL_LOAD_F32 %p1
%c:f32 = LGLOBAL_LOAD_F32 %p2
%add:f32 = G_FADD %a, %b ; 统一的指令
%mul:f32 = G_FMUL %add, %c ; 无需转换为 DAG
SGLOBAL_STORE_F32 %mul, %p3
RET
; 2. Combined 指令选择和寄存器分配
; 使用寄存器分配器同时做选择
; Register Allocator 工作:
; - 计算 SSA 值的生命周期
; - 分配物理寄存器
; - 插入 copy 和 spill 指令
; 3. 发射 x86_64 代码
; 最终输出:
vmovss 16(%rsp), %xmm0 ; load a
vaddss 24(%rsp), %xmm0 ; add b
vmulss 32(%rsp), %xmm0 ; multiply c
vmovss %xmm0, 40(%rsp) ; store result第4节 TargetTransformInfo
4.1 TTI 的作用
TargetTransformInfo (TTI) 是 LLVM 用来描述目标硬件特性的接口,让优化 pass 能根据具体硬件特性做决策:
cpp
// TTI 抽象的硬件特性
class TargetTransformInfo {
// 1. 向量化和 SIMD 能力
int getMaxVF(unsigned EF, Type *Ty); // 最大向量宽度
bool hasNeon(Type *Ty); // NEON 可用 (ARM)
bool hasAVX512(Type *Ty); // AVX-512 可用 (x86)
// 2. 内存层次
unsigned getCacheLineSize(); // 缓存行大小
L1CacheCost getCacheSize(Level); // 各层缓存大小
// 3. 指令延迟和吞吐量
unsigned getLatency(unsigned Opcode); // 指令延迟
unsigned getIssueWidth(); // 发射宽度
// 4. 并行化能力
bool supportsOutOfOrderExecution(); // 乱序执行
unsigned getNumberOfRegisters(); // 寄存器数量
};4.2 TTI 在优化中的应用
llvm
; TTI 帮助 Vectorizer 做出正确决策
; 输入: 处理 10000 个 float 的加法
; 询问 TTI: hasAVX512() = true (目标机器支持)
; 询问 TTI: getMaxVF(f32) = 16 (AVX-512 = 512bit / 32bit = 16)
; 决策: 使用 <16 x float> 向量
; 生成的 x86_64 (AVX-512) 代码:
vmovups (%rdi), %zmm0 ; load 16 floats
vaddps (%rsi), %zmm0, %zmm0 ; add 16 floats
vmovups %zmm0, (%rdx) ; store 16 floats
; 循环 10000 / 16 = 625 次迭代完成
; 如果 TTI 报告 hasAVX512 = false,则选择 SSE/AVX2:
; vmovups + vaddps (128/256bit,每次处理 4/8 个 float)第5节 具体 Lowering 链路演示
5.1 完整 Lowering 路径
┌─────────────────────────────────────────────────────────────────────────────┐
│ MLIR → x86_64 ASM 完整 Lowering 路径 │
└─────────────────────────────────────────────────────────────────────────────┘
Stage 1: Linalg Dialect (抽象的算子描述)
─────────────────────────────────────────
linalg.matmul ins(A, B) outs(C)
│
▼ (linalg-to-affine pass)
Stage 2: Affine Dialect (索引表达式)
─────────────────────────────────────────
affine.for i = 0 to M
affine.for j = 0 to N
affine.for k = 0 to K
%c[i,j] += %a[i,k] * %b[k,j]
│
▼ (affine-to-scf pass)
Stage 3: SCF Dialect (结构化控制流)
─────────────────────────────────────────
scf.for i = 0 to M
scf.for j = 0 to N
scf.for k = 0 to K
│
▼ (scf-to-cf pass + affine-loop-collapsed pass)
Stage 4: CF/Arith Dialect (基础控制流和算术)
─────────────────────────────────────────
br label %i_loop
i_loop:
%i_phi = phi [0, %entry], [%i_next, %i_loop]
...
│
▼ (memref-to-llvm pass)
Stage 5: LLVM Dialect (类型转换为 LLVM)
─────────────────────────────────────────
llvm.func @matmul(
%A: !llvm.ptr<f32>,
%B: !llvm.ptr<f32>,
%C: !llvm.ptr<f32>,
%M: i64, %N: i64, %K: i64
)
│
▼ (builtin-to-llvm pass)
Stage 6: LLVM IR (标准 LLVM 中间表示)
─────────────────────────────────────────
define void @matmul(float* %A, float* %B, float* %C,
i64 %M, i64 %N, i64 %K) {
entry:
br label %i_loop
i_loop:
%i = phi i64 [0, %entry], [%i_next, %i_loop]
...
│
▼ (LLVM optimization pipeline)
Stage 7: Optimized LLVM IR
─────────────────────────────────────────
; 已做 loop tiling, vectorization, 注册压力减少等优化
│
▼ (llc/x86_64 codegen)
Stage 8: x86_64 Assembly
─────────────────────────────────────────
matmul:
vmovups (%rdi), %xmm0
vaddps %xmm1, %xmm0, %xmm0
...
ret5.2 Affine Loop 变换到 Vectorized ASM
mlir
// 完整转换示例
// ===== Input: Linalg matmul =====
func.func @matmul(%A: tensor<128x256xf32>,
%B: tensor<256x512xf32>,
%C: tensor<128x512xf32>) -> tensor<128x512xf32> {
%D = linalg.matmul ins(%A, %B: tensor<128x256xf32>, tensor<256x512xf32>)
outs(%C: tensor<128x512xf32>) -> tensor<128x512xf32>
return %D : tensor<128x512xf32>
}
// ===== After LinalgToAffine + Tile =====
// 循环分块 (tile 32x32)
func.func @matmul_tiled(...) {
scf.for %i = 0 to 128 step 32:
scf.for %j = 0 to 512 step 32:
scf.for %k = 0 to 256 step 32:
// 处理 32x32x32 tile
linalg.matmul ins(%A[%i:%i+32, %k:%k+32], ...)
outs(%C[%i:%i+32, %j:%j+32])
}
// ===== After AffineToSCF + Vectorization =====
// <8 x float> = AVX2 (256bit) 向量
scf.for %i = 0 to 128 step 8: // 向量化 factor = 8
scf.for %j = 0 to 512 step 4: // 4 accumulator
%acc = vector.broadcast ... // broadcast
scf.for %k = 0 to 256:
%va = vector.load %A[%i, %k]
%vb = vector.load %B[%k, %j:%j+4]
%acc = vector.fma %va, %vb, %acc // fused multiply-add
vector.store %acc, %C[%i, %j]
}
// ===== Generated x86_64 ASM (伪代码) =====
// 实际生成代码(AVX2):
matmul_kernel:
; Setup
mov %rdi, %rax ; A pointer
mov %rsi, %rbx ; B pointer
mov %rdx, %rcx ; C pointer
.loop_i:
vbroadcastss (%rax), %ymm0 ; broadcast A[i,k] to 8 elements
xor %rbp, %rbp ; j = 0
.loop_j:
vmovups (%rbx,%rbp,4), %ymm1 ; load B[k,j:j+8]
vfmadd231ps %ymm1, %ymm0, %ymm2 ; %ymm2 += %ymm0 * %ymm1
add $8, %rbp ; j += 8
cmp $512, %rbp ; j < N
jl .loop_j
vmovups %ymm2, (%rcx,%rbp,4) ; store result
add $8, %rax ; i += 8
...第6节 Target Machine 与指令编码
6.1 Target Machine 抽象
cpp
// LLVM TargetMachine 抽象
class TargetMachine {
// 1. 目标描述
const Triple &getTargetTriple(); // "x86_64-unknown-linux-gnu"
// 2. 后端选择
const TargetPassConfig *createPassConfig();
// 3. 指令编码
const MCAsmInfo *getMCAsmInfo(); // 汇编格式信息
const MCInstrInfo *getInstrInfo(); // 指令编码
const MCRegisterInfo *getRegInfo(); // 寄存器编码
};
// 常用 TargetMachine
TargetMachine *TM =
createX86TargetMachine(Triple("x86_64-unknown-linux-gnu"),
CPU::Generic,
Features::AVX2);6.2 x86_64 指令编码基础
x86_64 指令编码结构:
┌─────────────────────────────────────────────────────────────────────────────┐
│ 指令编码格式 │
├─────────────────────────────────────────────────────────────────────────────┤
│ Prefix | Opcode | ModR/M | SIB | Displacement | Immediate │
│ 1-4B | 1-3B | 0-1B | 0-1B | 0,1,2,4B | 0,1,2,4,8B │
└─────────────────────────────────────────────────────────────────────────────┘
AVX-512 vmulps 指令示例:
vmulps %ymm1, %ymm2, %ymm3
编码: [EVEX] [Opcode] [ModRM]
62 F1 72 08 D1
62: EVEX 前缀 (指定向量长度、掩码等)
F1: Opcode (vmulps = 59)
72: ModR/M (指定操作数: %ymm2, %ymm3)
08: 额外扩展
D1: ModR/M (指定 %ymm1)6.3 MC Layer:从 IR 到目标文件
llvm
; MC Layer 的任务
; 1. LLVM IR → MCInst
; 每个 LLVM IR 指令映射到一个或多个 MCInst
; 2. MCInst → 字节序列
; 编码指令为机器码
; 3. 输出目标文件
; ELF (Linux), MachO (macOS), COFF (Windows)
; 示例:生成 ELF .o 文件
; LLVM IR
define void @foo() {
ret void
}
; 汇编
.text
.globl foo
.type foo, @function
foo:
ret
.size foo, .-foo
; 编译命令
; llc -filetype=obj -o foo.o foo.ll
; 或
; clang -c foo.s -o foo.o
; ELF 输出 (objdump -d foo.o)
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <foo>:
0: c3 retq升华
┌─────────────────────────────────────────────────────────────────────────────┐
│ MLIR-LLVM 集成核心原则 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 层次化 Lowering 是关键 │
│ Linalg → Affine → SCF → LLVM → 机器码,每层解决不同抽象层次的问题 │
│ │
│ 2. Conversion Pattern 是桥梁 │
│ DialectConversion framework 提供声明式转换规则,简化多层次 lowering │
│ │
│ 3. TTI 抽象硬件差异 │
│ TargetTransformInfo 让优化 pass 能够根据具体硬件特性做出最优决策 │
│ │
│ 4. GlobalISel 是未来 │
│ 一体化指令选择更简洁高效,但 SelectionDAG 仍然可靠 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
必须理解(不理解就等于不会):
- 🔴 MLIR Dialect Conversion 机制:通过 PatternRewrite 系统进行层次化 lowering
- 🔴 LLVM Pass 分类:Analysis(分析)、Transform(变换)、CodeGen(代码生成)
- 🔴 Vectorization 的两种类型:SLP(水平合并)、Loop(垂直向量化)
- 🔴 SelectionDAG vs GlobalISel:传统 DAG 两步选择 vs 新的一体化选择
- 🔴 Lowering 路径:Linalg → Affine → SCF → LLVM → ASM 的转换顺序
AI 可查(知道去哪查就行):
- ✅ 特定 x86_64 指令编码:Intel SDM 指令集参考
- ✅ 具体 LLVM Pass 的实现细节:LLVM 源码
lib/Target/X86/ - ✅ TTI 各接口的精确语义:LLVM 文档
TargetTransformInfo.rst - ✅ GlobalISel 的具体设计:LLVM 论文和源码
- ✅ MC Layer 的汇编解析:LLVM 源码
lib/MC/
学习状态:🟡 开始学习