GCC访问双机字类型中的高/低机器字(包括asm)

sbl*_*sbl 7 c assembly portability gcc

我使用各种双机器字类型,例如x86_64上的例如(u)int128_t和GCC中的i386,ARM等上的(u)int64_t.我正在寻找一种正确/便携/干净的方式来访问和操作各个实际的机器字(主要是汇编程序).例如,在32位机器上,我想直接访问gcc在内部使用的int64_t的高/低32位部分,而不使用如下所示的愚蠢错误代码.类似地,对于"本机"128位类型,我想访问gcc使用的64b部分(不是用于下面的例子,因为"add"很简单,但一般而言).

考虑以下代码中的32位ASM路径,将两个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
}
Run Code Online (Sandbox Code Playgroud)

请注意,我没有太多使用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;
Run Code Online (Sandbox Code Playgroud)

Mat*_*son 1

我听说这个网站并不是真正的“代码审查”,但由于这对我来说是一个有趣的方面,我想我可以提供一些建议:

在 32 位版本中,您可以执行HIGHHIGH和 co。巧妙地覆盖 int/uint 数组,而不是移位和与。使用联合是实现此目的的一种方法,另一种方法是指针“魔术”。由于代码的汇编器部分首先并不是特别可移植,因此以类型双关转换或不可移植联合的形式使用不可移植代码并不是什么大问题。

编辑:依靠单词中的位置也可能没问题。例如,仅将输入和输出的地址传递到寄存器中,然后使用 (%0+4) 和 (%1+4) 来完成其余部分绝对是一种选择。

如果你必须这样做来进行乘法和除法,那么它当然会变得更有趣......我不确定我是否愿意去那里......