我在 GCC 中使用各种双机器字类型,例如 x86_64 上的 (u)int128_t 和 i386、ARM 上的 (u)int64_t 等。我正在寻找一种正确/便携/干净的方式来访问和操作各个实际机器字(主要是在汇编程序中)。例如,在 32 位机器上,我想直接访问 gcc 内部使用的 int64_t 的高/低 32 位部分,而不使用像下面这样容易出错的愚蠢代码。同样,对于“本机”128 位类型,我想访问 gcc 正在使用的 64b 部分(对于下面的示例,“添加”很简单,但通常如此)。
考虑以下代码中的 32bit ASM 路径,将两个 int128_t 加在一起(可能是 gcc 的“native”,机器的“native”或机器的“half native”);这是可怕且难以维护(而且速度较慢)。
#define BITS 64
#if defined(USENATIVE)
// USE "NATIVE" 128bit GCC TYPE
typedef __int128_t int128_t;
typedef __uint128_t uint128_t;
typedef int128_t I128;
#define HIGH(x) x
#define HIGHVALUE(x) ((uint64_t)(x >> BITS))
#define LOW(x) x
#define LOWVALUE(x) (x & UMYINTMAX)
#else
typedef struct I128 {
int64_t high;
uint64_t low;
} I128;
#define HIGH(x) x.high
#define HIGHVALUE(x) x.high
#define LOW(x) x.low
#define LOWVALUE(x) x.low
#endif
#define HIGHHIGH(x) (HIGHVALUE(x) >> (BITS / 2))
#define HIGHLOW(x) (HIGHVALUE(x) & 0xFFFFFFFF)
#define LOWHIGH(x) (LOWVALUE(x) >> (BITS / 2))
#define LOWLOW(x) (LOWVALUE(x) & 0xFFFFFFFF)
inline I128 I128add(I128 a, const I128 b) {
#if defined(USENATIVE)
return a + b;
#elif defined(USEASM) && defined(X86_64)
__asm(
"ADD %[blo], %[alo]\n"
"ADC %[bhi], %[ahi]"
: [alo] "+g" (a.low), [ahi] "+g" (a.high)
: [blo] "g" (b.low), [bhi] "g" (b.high)
: "cc"
);
return a;
#elif defined(USEASM) && defined(X86_32)
// SLOWER DUE TO ALL THE CRAP
int32_t ahihi = HIGHHIGH(a), bhihi = HIGHHIGH(b);
uint32_t ahilo = HIGHLOW(a), bhilo = HIGHLOW(b);
uint32_t alohi = LOWHIGH(a), blohi = LOWHIGH(b);
uint32_t alolo = LOWLOW(a), blolo = LOWLOW(b);
__asm(
"ADD %[blolo], %[alolo]\n"
"ADC %[blohi], %[alohi]\n"
"ADC %[bhilo], %[ahilo]\n"
"ADC %[bhihi], %[ahihi]\n"
: [alolo] "+r" (alolo), [alohi] "+r" (alohi), [ahilo] "+r" (ahilo), [ahihi] "+r" (ahihi)
: [blolo] "g" (blolo), [blohi] "g" (blohi), [bhilo] "g" (bhilo), [bhihi] "g" (bhihi)
: "cc"
);
a.high = ((int64_t)ahihi << (BITS / 2)) + ahilo;
a.low = ((uint64_t)alohi << (BITS / 2)) + alolo;
return a;
#else
// this seems faster than adding to a directly
I128 r = {a.high + b.high, a.low + b.low};
// check for overflow of low 64 bits, add carry to high
// avoid conditionals
r.high += r.low < a.low || r.low < b.low;
return r;
#endif
}
请注意,我很少使用 C/ASM,事实上这是我第一次尝试内联 ASM。习惯于 Java/C#/JS/PHP 等意味着对于例行 C 开发人员来说非常明显的东西对我来说可能并不明显(除了代码风格中明显的不安全怪癖;))。此外,所有这些都可以完全称为其他东西,因为我很难在网上找到有关该主题的任何内容(非母语人士也是如此)。
非常感谢!
编辑 1
经过大量挖掘后,我发现了以下理论解决方案,该解决方案有效,但不必要的慢(比更长的 gcc 输出慢!)因为它强制所有内容到内存,我正在寻找一个通用解决方案(reg/mem/可能是 imm) . 我还发现,如果您在 32 位机器上对例如 64 位 int 使用“r”约束,gcc 实际上会将这两个值放在 2 个寄存器中(例如 eax 和 ebx)。问题在于无法可靠地访问第二部分。我确信有一些隐藏的运算符修饰符很难找到告诉 gcc 我想访问第二部分。
uint32_t t1, t2;
__asm(
"MOV %[blo], %[t1]\n"
"MOV 4+%[blo], %[t2]\n"
"ADD %[t1], %[alo]\n"
"ADC %[t2], 4+%[alo]\n"
"MOV %[bhi], %[t1]\n"
"MOV 4+%[bhi], %[t2]\n"
"ADC %[t1], %[ahi]\n"
"ADC %[t2], 4+%[ahi]\n"
: [alo] "+o" (a.low), [ahi] "+o" (a.high), [t1] "=&r" (t1), [t2] "=&r" (t2)
: [blo] "o" (b.low), [bhi] "o" (b.high)
: "cc"
);
return a;