我正在GAS内联汇编中编写程序集长,
template <std::size_t NumBits>
void inline KA_add(vli<NumBits> & x, vli<NumBits> const& y);
如果我专注我可以做:
template <>
void inline KA_add<128>(vli<128> & x, vli<128> const& y){
asm("addq %2, %0; adcq %3, %1;" :"+r"(x[0]),"+r"(x[1]):"g"(y[0]),"g"(y[1]):"cc");
}
很好用,现在如果我尝试概括以允许模板的内联,并让我的编译器工作任何长度......
template <std::size_t NumBits>
void inline KA_add(vli<NumBits> & x, vli<NumBits> const& y){
asm("addq %1, %0;" :"+r"(x[0]):"g"(y[0]):"cc");
for(int i(1); i < vli<NumBits>::numwords;++i)
asm("adcq %1, %0;" :"+r"(x[i]):"g"(y[i]):"cc");
};
嗯,它不起作用我不能保证进位(CB)传播。它不在第一个asm线和第二个asm线之间保存。它可能是逻辑因为循环增量i并因此“删除”CB I事物,它应该存在GAS约束以在两个ASM调用上保存CB。不幸的是我没有找到这样的信息。
有什么想法吗?
谢谢你,Merci!
PS我重写了我的函数以删除C ++意识形态
template <std::size_t NumBits>
inline void KA_add_test(boost::uint64_t* x, boost::uint64_t const* y){
asm ("addq %1, %0;" :"+r"(x[0]):"g"(y[0]):"cc");
for(int i(1); i < vli<NumBits>::numwords;++i)
asm ("adcq %1, %0;" :"+r"(x[i]):"g"(y[i]):"cc");
};
asm给出(GCC调试模式),
APP
addq %rdx, %rax;
NO_APP
movq -24(%rbp), %rdx
movq %rax, (%rdx)
.LBB94: .loc 9 55 0
movl $1, -4(%rbp)
jmp .L323
.L324:
.loc 9 56 0
movl -4(%rbp), %eax
cltq
salq $3, %rax
movq %rax, %rdx
addq -24(%rbp), %rdx <----------------- Break the carry bit
movl -4(%rbp), %eax
cltq
salq $3, %rax
addq -32(%rbp), %rax
movq (%rax), %rcx
movq (%rdx), %rax
APP
adcq %rcx, %rax;
NO_APP
正如我们所读到的,还有额外的addq,它会破坏CB的传播
答案 0 :(得分:1)
我认为无法明确告诉编译器必须在没有影响C
标志的指令的情况下创建循环代码。
肯定可以这样做 - 使用lea
向上计算数组地址,dec
向下计数循环并测试Z
结束条件。这样,除了实际数组和之外,循环中的任何内容都不会更改C
标志。
你必须做一个手动的事情,比如:
long long tmp; // hold a register
__asm__("0:
movq (%1), %0
lea 8(%1), %1
adcq %0, (%2)
lea 8(%2), %2
dec %3
jnz 0b"
: "=r"(tmp)
: "m"(&x[0]), "m"(&y[0]), "r"(vli<NumBits>::numwords)
: "cc", "memory");
对于热门代码,紧密循环不是最佳选择;例如,指令具有依赖性,并且每次迭代的指令明显多于内联/展开的adc
序列。更好的序列类似于(%rbp
resp。%rsi
具有源和目标数组的起始地址):
0:
lea 64(%rbp), %r13
lea 64(%rsi), %r14
movq (%rbp), %rax
movq 8(%rbp), %rdx
adcq (%rsi), %rax
movq 16(%rbp), %rcx
adcq 8(%rsi), %rdx
movq 24(%rbp), %r8
adcq 16(%rsi), %rcx
movq 32(%rbp), %r9
adcq 24(%rsi), %r8
movq 40(%rbp), %r10
adcq 32(%rsi), %r9
movq 48(%rbp), %r11
adcq 40(%rsi), %r10
movq 56(%rbp), %r12
adcq 48(%rsi), %r10
movq %rax, (%rsi)
adcq 56(%rsi), %r10
movq %rdx, 8(%rsi)
movq %rcx, 16(%rsi)
movq %r8, 24(%rsi)
movq %r13, %rbp // next src
movq %r9, 32(%rsi)
movq %r10, 40(%rsi)
movq %r11, 48(%rsi)
movq %r12, 56(%rsi)
movq %r14, %rsi // next tgt
dec %edi // use counter % 8 (doing 8 words / iteration)
jnz 0b // loop again if not yet zero
并仅围绕这些块循环。优点是负载被阻塞,并且您只需处理一次循环计数/终止条件。
老实说,我会尽量不使通用位宽特别“整洁”,而是专门用于明确展开的代码,例如,二次幂的位宽。而是将一个标志/构造函数消息添加到非优化模板实例,告诉用户“使用两个幂”?