【问题标题】:How to tell if a function argument is an immediate?如何判断函数参数是否为立即数?
【发布时间】:2012-11-29 08:57:24
【问题描述】:

我有一个包含内联程序集的内联函数定义。我想选择 根据参数是否存在不同的内联汇编实现 在构建时是否已知。

我的问题是如何在 C 代码或内联汇编中询问 address 值是否在构建时是已知的,因此是否适合作为立即值。如果您正在考虑 __builtin_constant_p - 请提前阅读。

这里有一段代码说明了我的意图:。我正在尝试找到一种实现“is_immediate”的方法。

static char arr[5];

void __attribute__((always_inline)) do_something(char * buf)
{
   if(is_immediate(buf) {

        // Argument is constant, can use immediate form

        asm volatile ("insn1 %0" : : "i"(buf));

   } else {

        // Argument is computed at runtime, use a register

        unsigned long tmp = (unsigned long)buf + 1;

        asm volatile("insn2 %0" : : "r"(tmp));

}


int main(void) 
{
   do_something(&arr);
}

乍一看,__builtin_constant_p() 似乎正是所需的魔法,但它不起作用。

原因是不起作用的,虽然在链接器将数组放入内存并为其选择地址之后,数组的地址将是已知的(因此它确实符合内联汇编的直接约束),在链接之前的编译时间不知道。

所以,我正在寻找一种询问方式 - “这个变量是否适合作为直接值?”而不是“这是一个常量表达式吗?”。

【问题讨论】:

  • 类型不匹配。为什么参数是char *,而不是char (*)[5]?
  • @KerrekSB 请注意,我将 arr 的地址作为参数发送,而不是 arr 本身。
  • @gby 这就是我认为的重点。传递 arr 就像传递 &arr[0] = char* 因为数组对象在左值到右值的转换过程中被转换。 OTOH &arr 已经是 char(*)[5] 类型的右值,因为 arr 是 char[5] 类型。
  • tl;dr 当您将数组用作右值时,数组会衰减为指向第一个成员的指针,因此在传递给函数或执行arr[i] 或将其分配给指针时,但是不是在做&arr 时,因为& 适用于左值。
  • @Kos 我会争论,但我不明白它与我的问题有什么关系 - 无论我使用 arr 还是 &arr,结果都是一样的,所以我现在就闭嘴。

标签: c gcc ld gnu-assembler


【解决方案1】:

如果我看看我的 x86 编译器生成了什么(毕竟这个 insn 没有意义),它就在这里:

static char arr[5];

void __attribute__((always_inline)) do_something(char * buf)
{

        // Argument is constant, can use immediate form

        asm volatile ("insn1 %0" : : "r"(buf));

        unsigned long tmp = (unsigned long)buf;

        asm volatile("insn2 %0" : : "r"(tmp));

}


int main(void)
{
   char brr[5];
   do_something(arr);
   do_something(brr);
}

来到

        .file   "inl.c"
        .text
.globl do_something
        .type   do_something, @function
do_something:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
#APP
# 8 "inl.c" 1
        insn1 %eax
# 0 "" 2
# 12 "inl.c" 1
        insn2 %eax
# 0 "" 2
#NO_APP
        popl    %ebp
        ret
        .size   do_something, .-do_something
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    $arr, %eax
        movl    %esp, %ebp
        subl    $16, %esp
#APP
# 8 "inl.c" 1
        insn1 %eax
# 0 "" 2
# 12 "inl.c" 1
        insn2 %eax
# 0 "" 2
#NO_APP
        leal    -5(%ebp), %eax
#APP
# 8 "inl.c" 1
        insn1 %eax
# 0 "" 2
# 12 "inl.c" 1
        insn2 %eax
# 0 "" 2
#NO_APP
        leave
        ret
        .size   main, .-main
        .local  arr
        .comm   arr,5,4
        .ident  "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]"
        .section        .comment.SUSE.OPTs,"MS",@progbits,1
        .string "OSpwg"
        .section        .note.GNU-stack,"",@progbits

所以我认为即时是有意义的,因为$arr 可能是即时的(而leal -5(%ebp), %eax 不是)。可能只是不支持上述优化。

但另一方面,

            movl    $arr, %eax
            insn1 %eax

并不比

差多少
            insn1 $arr

所以它仍然是可以接受的,恕我直言。

【讨论】:

  • 您是对的,与大多数用途相比,2 条指令似乎并不多。我碰巧编写了非常紧凑的嵌入式实时代码,每个周期都很重要。不过,我同意通常情况并非如此。
  • @gby 您可以添加一个宏,该宏最终具有asm 语句和"i" - 这应该比内联更可行。您可以保留上述内容作为计算地址或基于堆栈的地址的替代方法。
  • 内联函数实际上并没有改变任何东西 - 手动编码或使用宏不会改变行为
  • @gby mmm... 和#define do_something_with_imm(buf) do { asm volatile("insn2 %0" : : "i"(buf)); } while (0) 我得到了想要的行为。如果我用静态arr 调用它,它工作正常,如果我用位于堆栈上的brr 调用它,它会失败。符合预期。
  • 如果你把 asm volatile 放在一个没有 if 语句的内联函数中,它的工作原理是一样的。堆栈上的数组没有固定地址,难怪它不起作用。
猜你喜欢
  • 2013-03-30
  • 1970-01-01
  • 2013-09-09
  • 1970-01-01
  • 2017-03-05
  • 1970-01-01
  • 2013-08-14
  • 1970-01-01
  • 2010-09-10
相关资源
最近更新 更多