【问题标题】:GCC access high/low machine words in double machine word types (including asm)GCC以双机器字类型(包括asm)访问高/低机器字
【发布时间】:2012-12-10 09:00:19
【问题描述】:

我使用各种双机器字类型,例如(u)int128_t on x86_64 和 (u)int64_t on i386, ARM etc. in GCC. 我正在寻找一种正确/便携/干净的方式来访问和操作各个实际机器字(主要是在汇编程序中)。例如。在 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/可能我)。我还发现,如果你使用“r”约束,例如在 32 位机器上是 64 位 int,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;

【问题讨论】:

    标签: c gcc assembly portability


    【解决方案1】:

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

    在 32 位版本中,您可以使用 HIGHHIGH 和 co。巧妙地叠加 int/uint 数组,而不是移位和与运算。使用联合是一种方法,另一种是指针“魔术”。由于代码的汇编部分一开始就不是特别可移植,因此以类型双关语强制转换或不可移植联合的形式使用不可移植代码并不是什么大不了的事。

    编辑:依靠单词中的位置也可以。所以例如,只在寄存器中传入输入和输出的地址,然后使用 (%0+4) 和 (%1+4) 完成其余部分绝对是一种选择。

    如果你必须为乘法和除法做这个当然会变得更有趣......我不确定我想去那里......

    【讨论】:

    • 感谢您的意见!关于不可移植性,您是对的,我更多的是指使用一些“GCC 魔术约束”来告诉 GCC 我想访问这些部分,所以在其他平台上我只需要更改实际的 ASM。我已经尝试将指针传递给第一个元素(我理解方法/方法),但我无法在语法方面做到这一点,所以如果你愿意给我看一个例子,我会很高兴.在旁注中,我已经实现了许多功能(包括 mul 和 div),这些功能主要使用轮班并使用其他更简单的功能。我只需要快速的 ADD/SUB 和 SHL/SHR。
    猜你喜欢
    • 1970-01-01
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    • 2019-10-07
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    相关资源
    最近更新 更多