たとえば、x86_64 では (u)int128_t、i386 では (u)int64_t、GCC では ARM など、さまざまなダブル マシン ワード タイプを使用します。個々の実際の機械語にアクセスして操作するための正しい/ポータブル/クリーンな方法を探しています(主にアセンブラーで)。たとえば、32 ビット マシンでは、gcc が内部で使用する int64_t の上位/下位 32 ビット部分に、以下のような愚かなエラーが発生しやすいコードを使用せずに直接アクセスしたいと考えています。同様に、「ネイティブ」の 128 ビット タイプについては、gcc が使用している 64b パーツにアクセスしたいと考えています (「追加」は十分に単純ですが、一般的には以下の例ではありません)。
次のコードの 32 ビット ASM パスを考慮して、2 つの int128_t を一緒に追加します (これは、gcc に対して「ネイティブ」、マシンに対して「ネイティブ」、またはマシンに対して「半分ネイティブ」である可能性があります)。それは恐ろしく、維持するのが難しい(そして遅い)。
#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
掘り下げた後、次の理論的な解決策を見つけました。これは機能しますが、すべてをメモリに強制するため、不必要に遅くなります (reg/mem/おそらく imm) . また、たとえば 32 ビット マシンの 64 ビット int で "r" 制約を使用すると、gcc は実際には両方の値を 2 つのレジスタ (eax と ebx など) に格納することもわかりました。問題は、2 番目の部分に確実にアクセスできないことです。gccにその2番目の部分にアクセスしたいことを伝えるのが難しい隠し演算子修飾子がいくつかあると確信しています。
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;