我有一些基本上需要在汇编语句中使用小表达式的代码,其中表达式非常简单,如i * 4,但GCC似乎没有意识到在编译时(尝试没有-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 */
}
}
给出此错误:
temp.c:15: warning: asm operand 2 probably doesn’t match constraints
temp.c:15: error: impossible constraint in ‘asm’
我也试过
"shld $" #c ", %1...
但这有其自身的问题,因为在进行字符串化时,parens仍然存在。我的意图是整个循环展开,但是-funroll-all-loops似乎没有在过程中尽早发生,导致i * 4成为常量。有任何想法吗?替代方案非常难看,但是如果有一种方法可以在宏观中实现自动化,那就更好了:
SHIFT(a, b, 1);
SHIFT(a, b, 2);
...
SHIFT(a, b, 24);
答案 0 :(得分:0)
将 asm 块标记为 volatile 是否有任何特定原因?当 volatile 出现时,几乎不可能进行任何优化。
答案 1 :(得分:0)
不确定为什么你左移23 * 4 = 92,但是......
可能有。您可以使用__builtin_constant_p()和__builtin_choose_expr()来选择要编译的表达式;
之类的东西__builtin_choose_expr(__builtin_constant_p(c), SHIFT(h, l, c), slower_code_here);
如果它选择slow_code_here,那么它“无法”确定c
是不变的。如果它抱怨“不可能的约束”,那么它知道它是恒定的,但由于某种原因无法将其变成立即数。
它有时令人惊讶,它的想法是什么并且不是恒定的;前几天我一直在玩,它抱怨__builtin_choose_expr(sizeof(char[__builtin_choose_expr(..., 1, -1)]),...)
。
(我假设%2,%1,%0顺序是有意的;我会预期%0,%1,%2但文档含糊不清,我永远不会记得使用哪种asm语法。)
答案 2 :(得分:0)
您假设编译器将展开您的循环并每次替换i * 4
的值...这有点多假设。 * 4
看起来像是要进行某种寻址修改,为什么不传入i
并写下指令来执行* 4
?仔细查看 查看GCC处理的约束,并确保您的指令真正采用了约束可能引发的所有组合。
答案 3 :(得分:0)
使用 Boost预处理器库(实际上是一组cpp
宏,以及可以与普通C一起使用的Boost的唯一部分)可以实现“丑陋”的方式:
#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)
}
剩下的“丑陋”比特是,BOOST_PP_REPEAT*
可以“迭代”的宏仅限于一个用户提供的参数,因此你必须“嵌入”a
或者在此示例中为b
到实际的宏名称中。也许这可以通过另一个间接级别解决(将SHIFT(a)
转换为SHIFT_a
?)。没试过。
答案 4 :(得分:0)
我怀疑你仍然对这个问题的反馈感兴趣,但是因为你从未接受任何其他答案......
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 */
}
}
最重要的变化是:
使用-O2 -m64 -funroll-all-loops -S
编译,我们看到:
/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
有趣的是,如果你使用i * 6而不是i * 4,你会看到gcc使用immediates直到60,然后开始使用cl。
多田!