海湾合作委员会:禁止使用某些登记册

问题描述 投票:22回答:4

这是一个奇怪的要求,但我觉得它有可能。我想要的是将一些编译指示或指令插入到我的代码区域(用C编写),这样GCC的寄存器分配器就不会使用它们。

我知道我可以做这样的事情,这可能会为这个变量留下这个寄存器

register int var1 asm ("EBX") = 1984;
register int var2 asm ("r9") = 101;

问题是我直接插入新指令(用于硬件模拟器),而GCC和GAS还没有识别出这些指令。我的新指令可以使用现有的通用寄存器,我想确保我保留了一些(即r12-> r15)。

现在,我在一个模拟环境中工作,我想快速做我的实验。将来我会添加GAS并将内在函数添加到GCC中,但是现在我正在寻找快速修复。

谢谢!

c gcc assembly gas register-allocation
4个回答
15
投票

在编写GCC内联汇编程序时,您可以指定“clobber list” - 可能被内联汇编程序代码覆盖的寄存器列表。然后,GCC将在内联asm段的过程中执行保存和恢复这些寄存器中的数据(或首先避免使用它们)所需的任何操作。您还可以将输入或输出寄存器绑定到C变量。

例如:

inline unsigned long addone(unsigned long v)
{
    unsigned long rv;
    asm("mov $1, %%eax;"
        "mov %0, %%ebx;"
        "add %%eax, %%ebx"
        : /* outputs */  "b" (rv)
        : /* inputs */   "g" (v) /* select unused general purpose reg into %0 */
        : /* clobbers */ "eax"
       );
}

有关更多信息,请参阅GCC-Inline-Asm-HOWTO


5
投票

如果使用global explicit register variables,这些将在整个编译单元中保留,并且编译器不会将其用于任何其他内容(系统的库可能仍会使用它,因此请选择那些将由其恢复的内容)。本地寄存器变量不保证您的值始终在寄存器中,但仅在代码引用或作为asm操作数引用时。


5
投票

如果为新指令编写内联asm块,则会有一些命令通知GCC该块使用哪些寄存器以及它们的使用方式。然后GCC将避免使用这些寄存器或至少保存和重新加载其内容。


2
投票

内联汇编中的非硬编码临时寄存器

这不是对原始问题的直接回答,但自从我在这种背景下继续谷歌搜索,并且自从https://stackoverflow.com/a/6683183/895245被接受后,我将尝试为这个答案提供可能的改进。

改进如下:在可能的情况下,应避免对临时寄存器进行硬编码,以使寄存器分配器更加自由。

因此,作为在实践中无用的教育示例(可以在单个lea (%[in1], %[in2]), %[out];中完成),以下硬编码的临时寄存器代码:

bad.c

#include <assert.h>
#include <inttypes.h>

int main(void) {
    uint64_t in1 = 0xFFFFFFFF;
    uint64_t in2 = 1;
    uint64_t out;
    __asm__ (
        "mov %[in2], %%rax;" /* scratch = in2 */
        "add %[in1], %%rax;" /* scratch += in1 */
        "mov %%rax, %[out];" /* out = scratch */
        : [out] "=r" (out)
        : [in1] "r" (in1),
          [in2] "r" (in2)
        : "rax"
    );
    assert(out == 0x100000000);
}

如果您使用这个非硬编码版本,可以编译为更高效的东西:

good.c

#include <assert.h>
#include <inttypes.h>

int main(void) {
    uint64_t in1 = 0xFFFFFFFF;
    uint64_t in2 = 1;
    uint64_t out;
    uint64_t scratch;
    __asm__ (
        "mov %[in2], %[scratch];" /* scratch = in2 */
        "add %[in1], %[scratch];" /* scratch += in1 */
        "mov %[scratch], %[out];" /* out = scratch */
        : [scratch] "=&r" (scratch),
          [out] "=r" (out)
        : [in1] "r" (in1),
          [in2] "r" (in2)
        :
    );
    assert(out == 0x100000000);
}

因为编译器可以自由选择它想要的任何寄存器而不仅仅是rax

请注意,在这个例子中,我们必须将scratch标记为&的早期clobber寄存器,以防止它与输入放在同一个寄存器中,我已经在更详细地解释了:When to use earlyclobber constraint in extended GCC inline assembly?这个例子也恰好在实施我在没有&的情况下进行了测试。

在Ubuntu 18.10 amd64,GCC 8.2.0中测试,编译并运行:

gcc -O3 -std=c99 -ggdb3 -Wall -Werror -pedantic -o good.out good.c
./good.out

GCC manual 6.45.2.6“Clobbers and Scratch Registers”中也提到了非硬编码的临时寄存器,尽管他们的例子对于凡人来说太过分了:

不是通过clobbers为固定寄存器分配固定寄存器来为asm语句提供临时寄存器,另一种方法是定义一个变量并使其成为早期clobber输出,如下例中的a2和a3。这为编译器寄存器分配器提供了更多自由。您还可以定义一个变量并使其成为与输入绑定的输出,如同a0和a1,分别绑定到ap和lda。当然,对于绑定输出,在修改输出寄存器后,asm不能使用输入值,因为它们是同一个寄存器。更重要的是,如果省略输出的早期删除,如果GCC可以证明它们在进入asm时具有相同的值,则GCC可能会将相同的寄存器分配给另一个输入。这就是为什么a1有一个早期的破坏者。可以想象,它的绑定输入,lda可能具有值16而没有早期clobber与%11相同的寄存器。另一方面,ap不能与任何其他输入相同,因此不需要a0的早期删除。在这种情况下也是不可取的。 a0上的早期删除会导致GCC为“m”((const double()[])ap)输入分配单独的寄存器。请注意,将输入绑定到输出是设置由asm语句修改的初始化临时寄存器的方法。 GCC假定输出未绑定的输入不变,例如下面的“b”(16)设置%11到16,如果恰好需要值16,GCC可能在下面的代码中使用该寄存器。如果在使用临时指令之前消耗了可能共享同一寄存器的所有输入,则甚至可以使用普通的asm输出进行划痕。被asm语句破坏的VSX寄存器可能已经使用了这种技术,除了GCC对asm参数数量的限制。

static void
dgemv_kernel_4x4 (long n, const double *ap, long lda,
                  const double *x, double *y, double alpha)
{
  double *a0;
  double *a1;
  double *a2;
  double *a3;

  __asm__
    (
     /* lots of asm here */
     "#n=%1 ap=%8=%12 lda=%13 x=%7=%10 y=%0=%2 alpha=%9 o16=%11\n"
     "#a0=%3 a1=%4 a2=%5 a3=%6"
     :
       "+m" (*(double (*)[n]) y),
       "+&r" (n), // 1
       "+b" (y),  // 2
       "=b" (a0), // 3
       "=&b" (a1),    // 4
       "=&b" (a2),    // 5
       "=&b" (a3) // 6
     :
       "m" (*(const double (*)[n]) x),
       "m" (*(const double (*)[]) ap),
       "d" (alpha),   // 9
       "r" (x),       // 10
       "b" (16),  // 11
       "3" (ap),  // 12
       "4" (lda)  // 13
     :
       "cr0",
       "vs32","vs33","vs34","vs35","vs36","vs37",
       "vs40","vs41","vs42","vs43","vs44","vs45","vs46","vs47"
     );
}
© www.soinside.com 2019 - 2024. All rights reserved.