有没有一种方法可以在gcc中使用编译时与内联asm一起计算的表达式?

有没有一种方法可以在gcc中使用编译时与内联asm一起计算的表达式?,gcc,assembly,compiler-errors,inline-assembly,Gcc,Assembly,Compiler Errors,Inline Assembly,我有一些代码基本上需要在汇编语句中使用一个小表达式,其中的表达式与I*4相当简单,但GCC在编译时似乎没有意识到这一点(尝试了no-O标志和-O3)。对于第三种用法,“i”和“n”约束在下面的代码段中都不起作用 #include <stdint.h> #include <stdlib.h> #define SHIFT(h, l, c) __asm__ volatile ( \ "shld %2, %1, %0\n\t" \ "sal %2, %1\n\t

我有一些代码基本上需要在汇编语句中使用一个小表达式,其中的表达式与I*4相当简单,但GCC在编译时似乎没有意识到这一点(尝试了no-O标志和-O3)。对于第三种用法,“i”和“n”约束在下面的代码段中都不起作用

#include <stdint.h>
#include <stdlib.h>

#define SHIFT(h, l, c) __asm__ volatile ( \
    "shld %2, %1, %0\n\t" \
    "sal %2, %1\n\t" \
    : "+r"(h), "+r"(l) : "i"(c))

void main(void) {
  uint64_t a, b;
  SHIFT(a, b, 1); /* 1 */
  SHIFT(a, b, 2*4); /* 2 */
  size_t i;
  for(i=0; i<24; i++) {
    SHIFT(a, b, (i*4)); /* 3 */
  }
}
我也试过了

"shld $" #c ", %1...
但这也有其自身的问题,因为帕伦家族在严格化后依然存在。我的意图是整个循环都展开,但是-funroll所有循环在这个过程中似乎没有足够早地发生,从而导致i*4变成常数。有什么想法吗?另一种方法非常难看,但如果有一种方法可以在宏中自动执行此操作,那总比没有好:

SHIFT(a, b, 1);
SHIFT(a, b, 2);
...
SHIFT(a, b, 24);

是否有任何特定原因将asm块标记为易失性?当volatile存在时,几乎不可能执行任何优化。

不确定为什么要向左移动23*4=92,但是

可能有。可以使用uu builtin_constant_p()和u builtin_choose_expr()选择要编译的表达式;差不多

__builtin_choose_expr(__builtin_constant_p(c), SHIFT(h, l, c), slower_code_here);
如果它在这里选择较慢的代码,那么它“无法”确定
c
是常数。如果它抱怨一个“不可能的约束”,那么它知道它是常量,但由于某种原因无法将其转化为立即变量

有时,它认为是和不是恒定的东西令人惊讶;前几天我在玩游戏,它抱怨类似于
\uuuuuBuiltin\uChoose\uExpr(sizeof(char[\uuuBuiltin\uChoose\uExpr(…,1,-1)]),…)


(我假设%2、%1、%0的顺序是故意的;我本来希望%0、%1、%2,但是文档很模糊,我永远记不起使用了哪种asm语法。)

您假设编译器每次都会展开循环并替换
I*4
的值。。。这是一个有点多的假设。
*4
看起来您需要某种寻址修改,为什么不传入
i
并编写指令来执行
*4
?仔细查看约束GCC句柄,并确保您的指令真的包含了约束可能带来的所有组合。

您的“丑陋”方式可以通过使用Boost预处理器库实现(实际上是一组
cpp
宏,也是Boost中唯一可以与普通C一起使用的部分):

#包括
#定义移位a(z,CNT,b)uuu asm_uuvolatile(\
shld%2,%1,%0\n\t\
sal%2,%1\n\t\
:“+r”(a),“+r”(b)
:“我”(CNT*4)
:“抄送”);
真空总管(真空){
uint64_t a,b;
//不管怎样。。。
从(1,25,a,b)到(1,25,b)的升压(PP)(重复)
}

这里剩下的“丑陋”一点是宏
BOOST\u PP\u REPEAT*
可以“迭代”到一个用户提供的参数中,因此在本例中,您必须将
a
b
嵌入到实际的宏名中。也许这可以通过另一个间接层来解决(将
SHIFT(a)
转换为
SHIFT\u a
?)。未尝试。

我怀疑您是否仍对这个问题的反馈感兴趣,但由于您从未接受过任何其他答案

OP代码有一些问题,但经过一点清理,您会得到:

#include <stdint.h>

#define SHIFT(h, l, c) __asm__ volatile ( \
    "shld %b2, %1, %0\n\t" \
    "sal %b2, %1\n\t" \
    : "+r"(h), "+r"(l) : "Jc"(c))

int main(void) {
  uint64_t a, b;
  a = b = 0;
  SHIFT(a, b, 1); /* 1 */
  SHIFT(a, b, 2*4); /* 2 */
  size_t i;
  for(i=0; i<16; i++) {
    SHIFT(a, b, (i*4)); /* 3 */
  }
}
有趣的是,如果您使用i*6而不是i*4,您会看到gcc使用immediates直到60,然后开始使用cl


塔达

我这样做只是为了避免它被优化,因为在这个例子中没有使用返回值。我尝试删除volatile,添加初始值,并在最后添加printf,但结果相同(不可能约束上的编译错误)。我的印象是,一个易失性asm仍然可以移动,您是否有一个链接描述它阻碍的优化(除了删除未使用的代码)呢?好的,那么问题可能与以下主题有关:
#include <boost/preprocessor/repetition/repeat.hpp>

#define SHIFT_a(z, CNT, b) __asm__ volatile ( \
    "shld %2, %1, %0\n\t" \
    "sal %2, %1\n\t" \
    : "+r"(a), "+r"(b)
    : "i"(CNT * 4)
    : "cc");

void main(void) {
    uint64_t a, b;

// whatever ...

    BOOST_PP_REPEAT_FROM_TO(1, 25, SHIFT_a, b)
}
#include <stdint.h>

#define SHIFT(h, l, c) __asm__ volatile ( \
    "shld %b2, %1, %0\n\t" \
    "sal %b2, %1\n\t" \
    : "+r"(h), "+r"(l) : "Jc"(c))

int main(void) {
  uint64_t a, b;
  a = b = 0;
  SHIFT(a, b, 1); /* 1 */
  SHIFT(a, b, 2*4); /* 2 */
  size_t i;
  for(i=0; i<16; i++) {
    SHIFT(a, b, (i*4)); /* 3 */
  }
}
/APP
 # 12 "shl.cpp" 1
        shld $1, %rdx, %rax
        sal $1, %rdx

 # 0 "" 2
 # 13 "shl.cpp" 1
        shld $8, %rdx, %rax
        sal $8, %rdx

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $0, %rdx, %rax
        sal $0, %rdx

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $4, %rdx, %rax
        sal $4, %rdx
...

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $60, %rdx, %rax
        sal $60, %rdx

 # 0 "" 2
/NO_APP