clang/LLVM ARM ABI,非易失性寄存器被破坏

3
我正在尝试使用clang/llvm作为ARM Cortex-M的交叉编译器。 根据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的输出要好得多)

问题在于arms abi说不要破坏r4及以上,当然也不是像这里一样破坏r4和r7,而且它没有保存链接寄存器以便从此函数返回(尽管我猜它看到这是一个无限循环并没有返回(请不要告诉我我又掉进了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-get 获取的版本为 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上不好,所以:
“如何使用Clang/LLVM交叉编译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”应该允许并行可构建的makefiles,但我在其中有一个问题。有一两个地方可以工作,一个地方不行,必须一遍又一遍地运行,或者最终串行运行。因此,最后的构建方式就是这样。

使用发布版本构建后,二进制文件明显较小,而不是数十GB,整个安装只有1.x GB。

我认为构建速度并没有更快。持续时间与1990年代构建gcc相当。

1个回答

3
答案很简单:您的函数从未返回。因此,保存/恢复被调用者保存的寄存器没有任何意义。
如果您更改源代码以允许函数终止,像这样:
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);
}

所有内容都将按您期望的方式保存/恢复。

附注:我不会评论无限循环是否UB(未定义行为)。

附加说明:您可能希望以发布模式编译llvm / clang - 二进制文件将更小,链接时间将大大缩短。


哦,太有趣了!多年来我使用的编译器从未出现过这种情况。例如,如果你删除返回语句,有些编译器会抱怨函数没有返回值,甚至有些编译器会检测到这个问题并抱怨返回语句是无法到达的代码。谢谢,我可能会删除这段代码,因为它太琐碎了,而我却忽略了它... - old_timer
有趣的是交叉编译确实推动了寄存器,所以这可能是一个不应该出现的问题。 - old_timer
@Frant 好的,没问题。 - old_timer
哇,谢谢你的发布构建评论,它明显更小了! - old_timer
@Frant,是的,我打算这样做,还在构建和测试中... - old_timer
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接