Rigo的胡言乱语录

Rigo's Stupid Blogs Archives

View on GitHub

Clang?

2025/04/19

首先得定义一个clang的target triplet。<arch><sub>-<vendor>-<sys>-<abi>,我设想是loongarch64-unknown-windows-gnu。

参见:

其实编译到各种平台都有点大同小异的感觉。不过,Linux和Windows还是不太一样的,比如Linux有GOT PLT,而Windows不使用GOT PLT机制(但是用的类似的办法);Linux的TLS机制和Win不太一样(不过,线程环境指针应该都是在GS寄存器里,如果是x86的话);其他的,应该是链接器的活。还有SEH。栈结构、栈回溯信息啥的。

主要想看的就是为同一种CPU编译时,Linux target和Windows target的编译器它们走的代码路径有何不同。不然都不知道怎么下手改。

产生汇编码应该是在SSA形式之后,把SSA降低到机器操作的环节。这部分代码应该在哪里呢?

问问LLM,ChatGPT说这其实是LLVM干的。也对,Clang就是个语言前端,产生的都是LLVM IR。LLVM后端把这些SSA变成机器码。看看ChatGPT的原话:

  1. Clang Frontend:

    • Clang parses C/C++/ObjC source code and lowers it into LLVM IR (in SSA form).
    • This part ends with an LLVM Module in memory, filled with functions in SSA form.
  2. LLVM Middle-End / Optimizer:

    • LLVM passes perform various IR-level optimizations, all still in SSA form.
  3. LLVM Back End (the part you’re asking about):

    • The IR in SSA is lowered to machine code (non-SSA).
    • This involves instruction selection, register allocation, and code emission.

然后LLVM和GCC其实都有Machine description这样的东西,LLVM的在llvm/lib/Target/<目标机器>里面,后缀叫.td(TableGen Description)。这玩意在编译LLVM的时候会用Tblgen生成.inc文件,里面是C++代码,应该是会编译进LLVM里。

x86这里面可以看到x86的Calling convention,这个也很重要,因为x86和ARM是Win calling convention的唯二重大用户,是重要参考。

llvm\lib\Target\X86\X86CallingConv.td里面先看看对Win做了什么特判记个笔记:

后面的东西有点大同小异了,我觉得没必要列出来,看得到怎么做的特判就差不多了。然后再列出一些我觉得重要的地方。

再来看看ARM64的Windows调用约定吧。很相似地被放在了类似路径的文件里:llvm\lib\Target\AArch64\AArch64CallingConvention.td

最后,Callee saved register (CSR,不是Control and Status Register)有另一堆东西定义(x86也有但我忘了写),对于Win ARM64来说还有一个细节:

  // Win64 has unwinding codes for an (FP,LR) pair, save_fplr and save_fplr_x.
  // We put FP before LR, so that frame lowering logic generates (FP,LR) pairs,
  // and not (LR,FP) pairs.
  def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add X19, X20, X21, X22, X23, X24,
                                              X25, X26, X27, X28, FP, LR,
                                              D8, D9, D10, D11,
                                              D12, D13, D14, D15)>;

看起来栈回溯方案是如何实现的也需要考虑到。

然后就是Control Flow Guard在被调用时保存哪些寄存器,

// The Control Flow Guard check call uses a custom calling convention that also
// preserves X0-X8 and Q0-Q7.
def CSR_Win_AArch64_CFGuard_Check : CalleeSavedRegs<(add CSR_Win_AArch64_AAPCS,
                                               (sequence "X%u", 0, 8),
                                               (sequence "Q%u", 0, 7))>;

看起来就是X0-X8(整数)Q0-Q7(SIMD),显然它们都是参数寄存器。设计龙的CFG时应该也是这样用。

再看看龙的Calling convention在哪里定义的吧。理论上,需要添加的Calling convention应该和AArch64_Win64PCS一样直接复用就好,关于Vararg也不用太担心,毕竟根本没有标准,自己定义就行。文件一样的在llvm\lib\Target\LoongArch\LoongArchCallingConv.td

一看就乐了,非常简洁只有一点点东西。它只定义了软浮点、单精度浮点、双精度浮点三种情况下的Callee-saved register,其实就是所有在ELF psABI里定义的静态寄存器。至于具体的Calling convention,后面再看看。

需要了解一下LoongArch上Linux上的栈回溯方式进行参考,不然SEH什么的没法定义。ARM64的Windows unwinding数据输出参考:llvm\lib\Target\AArch64\MCTargetDesc\AArch64WinCOFFStreamer.cppllvm\lib\MC\MCWin64EH.cpp

Misc chat log

📍 Where SSA Is Lowered in LLVM

  1. Instruction Selection (IR → MachineInstrs)

This is where SSA form begins to be “lowered”:

File: llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp

File: llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp (if using GlobalISel instead of SelectionDAG)

These translate LLVM IR (SSA) into MachineInstr (not in SSA, but in a virtual register format).

  1. Register Allocation (Virtual → Physical registers)

This finalizes the break from SSA, as registers are now assigned:

File: llvm/lib/CodeGen/RegAlloc/

Look at files like:

    RegAllocGreedy.cpp

    RegAllocBase.cpp

Also involves:

    llvm/lib/CodeGen/MachineRegisterInfo.cpp
  1. PHI Node Elimination / SSA Destruction

LLVM destroys SSA form by inserting copies where needed:

File: llvm/lib/CodeGen/PhiElimination.cpp

💡 Tip: Tracing from a Function

To trace from your own IR:

Use llc (LLVM static compiler) with -debug and -print-after-all flags.

Or run opt and llc with -view-dag-combine1-dags to see SelectionDAGs.

Look at MachineFunction, MachineBasicBlock, and MachineInstr dumps.

Summary |Stage | LLVM Subsystem | Relevant File(s)| |-|-|-| |IR (SSA) → MachineInstr | SelectionDAG / GlobalISel | SelectionDAGISel.cpp, IRTranslator.cpp| |PHI Lowering / SSA Destruction | PhiElimination | PhiElimination.cpp| |Register Allocation | RegAlloc | RegAllocGreedy.cpp, RegAllocBase.cpp|