在x86_64中,我知道mul和div opp代码支持128个整数,把下位64位放在rax中,上位放在rdx寄存器中。 我在英特尔的内在指南中寻找某种内在代码来实现这个功能,但我找不到。 我正在编写一个大数库,其中字的大小是64位。 现在我正在做这样的单字除法。
int ubi_div_i64(ubigint_t* a, ubi_i64_t b, ubi_i64_t* rem)
{
if(b == 0)
return UBI_MATH_ERR;
ubi_i64_t r = 0;
for(size_t i = a->used; i-- > 0;)
{
ubi_i64_t out;
__asm__("\t"
"div %[d] \n\t"
: "=a"(out), "=d"(r)
: "a"(a->data[i]), "d"(r), [d]"r"(b)
: "cc");
a->data[i] = out;
//ubi_i128_t top = (r << 64) + a->data[i];
//r = top % b;
//a->data[i] = top / b;
}
if(rem)
*rem = r;
return ubi_strip_leading_zeros(a);
}
如果能在x86intrinsics.h头中使用一些东西,而不是内联asm,那就更好了。
gcc有 __int128
和 __uint128
类型。
它们的运算 应 当存在正确的汇编指令时,它应该使用正确的汇编指令;我过去曾用它们来获取产品的上64位,尽管我从未用它来进行划分。如果它没有使用正确的,酌情提交一个bug报告功能请求。
我最后一次调查时,内在的都在变化中. 在这种情况下,内在的主要原因似乎是由于64位模式下的MSVC不允许内联装配。
在MSVC(我想还有ICC)中,你可以使用 _umul128
对于 mul
和 _mulx_u64
对于 mulx
. 这些在GCC中不起作用 , 至少在GCC 4.9中不起作用(_umul128
比GCC 4.9老得多)。) 我不知道GCC是否计划支持这些,因为你可以得到 mul
和 mulx
间接通过 __int128
(取决于你的编译选项)或直接通过内联汇编。
__int128
在你需要一个更大的类型和128位的携带之前,这个方法都很好用。然后你需要 adc
, adcx
或 adox
而这些在本质上更是一个问题。英特尔的文档与MSVC不一致,而编译器似乎并没有生成 adox
然而有了这些内在的东西。请看这个问题。_addcarry_u64和_addcarryx_u64与MSVC和ICC的关系。.
内联装配可能是GCC(甚至可能是ICC)的最佳解决方案。