clang/LLVM ARM ABI,non-volatile 寄存器被销毁

clang/LLVM ARM ABI, non-volatile registers being destroyed

我正在尝试使用 clang/llvm 作为 ARM cortex-m 的交叉编译器。

基于 an/some LLVM 页面,这就是我构建工具链的方式

rm -rf /opt/llvm/llvm10armv6m
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-10.0.0
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS='clang;lld' -DCMAKE_CROSSCOMPILING=True -DCMAKE_INSTALL_PREFIX=/opt/llvm/llvm10armv6m -DLLVM_DEFAULT_TARGET_TRIPLE=armv6m-none-eabi -DLLVM_TARGET_ARCH=ARM -DLLVM_TARGETS_TO_BUILD=ARM -G "Unix Makefiles" ../llvm
make -j 8
make -j 4
make
sudo make install

test.c

void fun ( unsigned int, unsigned int );
int test ( void )
{
    unsigned int ra;
    unsigned int rx;

    for(rx=0;;rx++)
    {
        ra=rx;
        ra|=((~rx)&0xFF)<<16;
        fun(0x12345678,ra);
    }
    return(0);
}

clang -Wall -O2 -nostdlib -ffreestanding -fomit-frame-pointer -c test.c -o test.o
arm-none-eabi-objdump -D test.o


Disassembly of section .text:

00000000 <test>:
   0:   20ff        movs    r0, #255    ; 0xff
   2:   0405        lsls    r5, r0, #16
   4:   2600        movs    r6, #0
   6:   4c06        ldr r4, [pc, #24]   ; (20 <test+0x20>)
   8:   4637        mov r7, r6
   a:   4629        mov r1, r5
   c:   43b1        bics    r1, r6
   e:   4339        orrs    r1, r7
  10:   4620        mov r0, r4
  12:   f7ff fffe   bl  0 <fun>
  16:   2001        movs    r0, #1
  18:   0400        lsls    r0, r0, #16
  1a:   1836        adds    r6, r6, r0
  1c:   1c7f        adds    r7, r7, #1
  1e:   e7f4        b.n a <test+0xa>
  20:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

(gnu 的输出要好得多)

这里的问题是 arm abi 说不要破坏 r4 和更高版本,当然不是像这里那样破坏 r4 和 r7,而且它没有为 return 保留 link 寄存器来自这个函数(虽然我猜它认为这是一个无限循环而不是 return(请不要告诉我我又陷入了 llvm 无限循环错误))。

有了帧指针,它并没有变得更好

00000000 <test>:
   0:   b580        push    {r7, lr}
   2:   af00        add r7, sp, #0
   4:   20ff        movs    r0, #255    ; 0xff
   6:   0405        lsls    r5, r0, #16
   8:   2400        movs    r4, #0
   a:   4626        mov r6, r4
   c:   4629        mov r1, r5
   e:   43a1        bics    r1, r4
  10:   4331        orrs    r1, r6
  12:   4804        ldr r0, [pc, #16]   ; (24 <test+0x24>)
  14:   f7ff fffe   bl  0 <fun>
  18:   2001        movs    r0, #1
  1a:   0400        lsls    r0, r0, #16
  1c:   1824        adds    r4, r4, r0
  1e:   1c76        adds    r6, r6, #1
  20:   e7f4        b.n c <test+0xc>
  22:   46c0        nop         ; (mov r8, r8)
  24:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

并为

构建工具链
armv6m-none-gnueabi

并没有改善

但是如果我使用通用的 apt-gotten clang/llvm

clang -Wall -O2 -nostdlib -ffreestanding -fomit-frame-pointer -target armv6m-none-gnueabi -mthumb -mcpu=cortex-m0 -c test.c -o test.o
arm-none-eabi-objdump -D test.o

Disassembly of section .text:

00000000 <test>:
   0:   b5f0        push    {r4, r5, r6, r7, lr}
   2:   b081        sub sp, #4
   4:   20ff        movs    r0, #255    ; 0xff
   6:   0405        lsls    r5, r0, #16
   8:   2600        movs    r6, #0
   a:   4c06        ldr r4, [pc, #24]   ; (24 <test+0x24>)
   c:   4637        mov r7, r6
   e:   4629        mov r1, r5
  10:   43b1        bics    r1, r6
  12:   4339        orrs    r1, r7
  14:   4620        mov r0, r4
  16:   f7ff fffe   bl  0 <fun>
  1a:   2001        movs    r0, #1
  1c:   0400        lsls    r0, r0, #16
  1e:   1836        adds    r6, r6, r0
  20:   1c7f        adds    r7, r7, #1
  22:   e7f4        b.n e <test+0xe>
  24:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

问题解决了。

现在是的,在撰写本文时,构建的是 v10,apt-gotten 是 v6(构建 v10,为什么构建需要永恒?为什么二进制文件如此庞大? )

对构建的命令行使用相同的命令行没有任何变化有 abi 问题。

现在如果我不优化可能只是运气不好:

00000000 <test>:
   0:   b580        push    {r7, lr}
   2:   b082        sub sp, #8
   4:   2000        movs    r0, #0
   6:   9000        str r0, [sp, #0]
   8:   e7ff        b.n a <test+0xa>
   a:   9800        ldr r0, [sp, #0]
   c:   9001        str r0, [sp, #4]
   e:   4668        mov r0, sp
  10:   7800        ldrb    r0, [r0, #0]
  12:   21ff        movs    r1, #255    ; 0xff
  14:   4048        eors    r0, r1
  16:   0400        lsls    r0, r0, #16
  18:   9901        ldr r1, [sp, #4]
  1a:   4301        orrs    r1, r0
  1c:   9101        str r1, [sp, #4]
  1e:   9901        ldr r1, [sp, #4]
  20:   4803        ldr r0, [pc, #12]   ; (30 <test+0x30>)
  22:   f7ff fffe   bl  0 <fun>
  26:   e7ff        b.n 28 <test+0x28>
  28:   9800        ldr r0, [sp, #0]
  2a:   1c40        adds    r0, r0, #1
  2c:   9000        str r0, [sp, #0]
  2e:   e7ec        b.n a <test+0xa>
  30:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

链接不擅长 SO,所以

How To Cross-Compile Clang/LLVM using Clang/LLVM

页面的标题是否有这样的信息

The CMake options you need to add are:

-DCMAKE_CROSSCOMPILING=True
-DCMAKE_INSTALL_PREFIX=<install-dir>
-DLLVM_TABLEGEN=<path-to-host-bin>/llvm-tblgen
-DCLANG_TABLEGEN=<path-to-host-bin>/clang-tblgen
-DLLVM_DEFAULT_TARGET_TRIPLE=arm-linux-gnueabihf
-DLLVM_TARGET_ARCH=ARM
-DLLVM_TARGETS_TO_BUILD=ARM

我从页面提到的 gnu 三元组开始,但后来看到 llvm 有子架构,所以添加了它,最初看起来一切都很好,直到我制作了一个包含多行代码的程序。

我是否错误地构建了 llvm?或者这仅仅是 llvm 无限循环的事情? (或其他...)

编辑

更新的构建脚本:

export THEPLACE=/opt/llvm/llvm10armv6m
export THETARGET=armv6m-none-eabi

rm -rf $THEPLACE
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout llvmorg-10.0.0
mkdir build
cd build
cmake \
-DLLVM_ENABLE_PROJECTS='clang;lld' \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CROSSCOMPILING=True \
-DCMAKE_INSTALL_PREFIX=$THEPLACE \
-DLLVM_DEFAULT_TARGET_TRIPLE=$THETARGET \
-DLLVM_TARGET_ARCH=ARM \
-DLLVM_TARGETS_TO_BUILD=ARM \
-G "Unix Makefiles" \
../llvm

make -j 8
make -j 4
make
sudo make install

tbl-gen 显然不需要。理论上,-G Unix Makefiles 应该允许并行构建 makefile,但我确实有一个问题。一两个地方它起作用了,一个地方不起作用,并且必须一次又一次地 运行 或最终串行。因此最终的结果就是这样。

对于 Release 版本,二进制文件明显更小而不是数十 GB,对于整个安装来说就像 1.somethingGB。

我不认为构建速度更快。持续时间仍然与 1990 年代构建 gcc 相当。

答案很简单:您的函数从不 returns。因此,保存/恢复被调用者保存的寄存器没有任何意义。

如果您更改源代码以允许函数终止,如下所示:

void fun ( unsigned int, unsigned int );
unsigned bar();
int test ( void )
{
    unsigned int ra;
    unsigned int rx;

    for(rx=0;rx<bar();rx++)
    {
        ra=rx;
        ra|=((~rx)&0xFF)<<16;
        fun(0x12345678,ra);
    }
    return(0);
}

一切都将按您的预期保存/恢复。

PS:无限循环是不是UB我就不评论了

PPS:您当然希望在 Release 模式下编译 llvm/clang – 二进制文件会更小并且链接时间会显着减少。