SPARC的GCC内联汇编:如何处理整数双字对?

pho*_*ger 5 c c++ gcc sparc inline-assembly

据我所知,在SPARC中,32位整数存储在单个寄存器中,64位整数存储在相邻寄存器对中,偶数寄存器包含高32位,奇数寄存器包含低32位.

我需要编写一些专门的SPARC内联汇编宏(内联汇编函数也可以)处理64位整数双字对,我无法弄清楚如何一般地引用(使用GCC扩展内联汇编)到我的内联装配中两半的一对.虽然我的汇编宏比下面显示的MULTIPLY()宏稍微复杂一些,但乘法示例(如果有效)将演示如何处理64位双字对的两半.谁能告诉我如何修复我的MULTIPLY()宏?

万一重要,我正在......

bash-2.03 $ uname -a
SunOS [...] 5.8 Generic_117350-39 sun4u sparc SUNW,Ultra-80

这是我的简单示例程序(在C中):

#include <stdio.h>
//#include <stdint.h>
#define uint32 unsigned long int
#define uint64 unsigned long long int


#define MULTIPLY(r, a, b)  /* (r = a * b) */   \
   asm("umul %1, %2, %0;"  /* unsigned mul */  \
       : /* regs out */  "=h"(r)               \
       : /* regs in  */  "r"(a),   "r"(b));
#if 0
       : /* clobbers */  "%y" );
#endif


int main(int argc, char** argv)
{
   uint64 r;
   uint32 a=0xdeadbeef, b=0xc0deba5e;

   // loses the top 32 bits of the multiplication because the result is
   // truncated at 32 bits which then gets assigned to the 64-bit 'r'...
   r = a * b;
   printf("u64=u32*u32  ---->  r=a*b           "
          "---->  0x%016llx = 0x%x * 0x%x\n",
          r, a, b);

   // force promotion of 'a' to uint64 to get 64-bit multiplication
   // (could cast either a or b as uint64, which one doesn't matter,
   // as one explicit cast causes the other to be promoted as well)...
   r = ((uint64)a) * b;
   printf("u64=u64*u32  ---->  r=((u64)a)*b    "
          "---->  0x%016llx = 0x%x * 0x%x\n",
          r, a, b);

   MULTIPLY(r, a, b);
   printf("u64=u64*u32  ---->  MULTIPLY(r,a,b) "
          "---->  0x%016llx = 0x%x * 0x%x\n",
          r, a, b);

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

在编译时gcc-3.2-sun4u/bin/gcc -o mult -mcpu=ultrasparc mult.c,会产生以下输出:

u64=u32*u32  ---->  r=a*b           ---->  0x00000000d3c7c1c2 = 0xdeadbeef * 0xc0deba5e  
u64=u64*u32  ---->  r=((u64)a)*b    ---->  0xa7c40bfad3c7c1c2 = 0xdeadbeef * 0xc0deba5e  
u64=u64*u32  ---->  MULTIPLY(r,a,b) ---->  0xd3c7c1c2deadbeef = 0xdeadbeef * 0xc0deba5e  
Run Code Online (Sandbox Code Playgroud)

我查看-S -fverbose-asm了gcc 的输出,它正在做一些奇怪的结果寄存器移位(甚至是)并写入相邻的奇数寄存器.我的问题是我不知道如何在扩展的asm语法中一般引用相邻的奇数寄存器.我想也许'h'asm约束"=h"(r)可能与它有关,但我找不到任何如何使用它的例子.

pho*_*ger 0

首先,非常感谢 Chris Dodd、torek 和 gbulmer 的努力和帮助。我设法弄清楚如何通过我在这里找到的一些评论来做到这一点,部分复制(并对形式但不内容进行了稍微编辑)如下:

主题:RFE:“h”和“U”asm 约束以及“H”和“L”修饰符。
[...]某些 v8+ ABI 内联汇编的以下两个约束(引自 gcc.info):
SPARC-V8+ 架构的“h”64 位全局或输出寄存器。
'U' 偶数寄存器
“U”需要为 ldd/std 分配寄存器(它为 uint64_t 分配偶数 + 奇数对)。例如:
    void atomic64_set(volatile uint64_t *p, uint64_t v) {
        asm volatile ( "std %1, %0" : "=m"(*p) : "U"(v) );
    }
Run Code Online (Sandbox Code Playgroud) 无论有或没有“U”作为约束,都可以在模板中使用“H”和“L”作为修饰符来获取用于 64 位值的一对高寄存器和低寄存器。“h”约束分配一个寄存器,根据 v8+ ABI,人们可以安全地使用所有 64 位(仅限全局或输出寄存器)。以下(人工)示例演示了“h”约束以及“H”和“L”修饰符:
    void ex_store64(uint64_t *p, uint64_t v) {  
       register int tmp; // Don't say uint64_t or GCC thinks we want 2 regs  
       asm volatile (  
          "sllx %H2,32,%1 \n\t" // tmp = HI32(v) << 32  
          "or %1,%L2,%1 \n\t" // tmp |= LO32(v)  
          "stx %0, %1" // store 64-bit tmp  
          :  "=m"(*p),  "=&h"(tmp)  :  "r"(v));  
      }
Run Code Online (Sandbox Code Playgroud) 免责声明:这些示例是当场编写的,对于早期攻击和类似问题可能不正确。
——保罗

基于此,我能够从问题陈述中找出如何重写我自己的“MULTIPLY”宏:

#define MULTIPLY(r, a, b)     /* r = a * b          */\
   asm("umul %1, %2, %L0;"    /* umul a,b,r         */\
       "srlx %L0, 32, %H0;"                           \
       : /* regs out */   "=r"(r)                     \
       : /* regs in  */   "r"(a),   "r"(b));
       /* re: clobbbers "none": I tried specifying :"%y"
        *     in various ways but GCC kept telling me
        *     there was no y, %y, or %%y register. */
Run Code Online (Sandbox Code Playgroud)

我现在的结果是:

u64=u32*u32  ---->  r=a*b           ---->  0x00000000d3c7c1c2 = 0xdeadbeef * 0xc0deba5e  
u64=u64*u32  ---->  r=((u64)a)*b    ---->  0xa7c40bfad3c7c1c2 = 0xdeadbeef * 0xc0deba5e  
u64=u64*u32  ---->  MULTIPLY(r,a,b) ---->  0xa7c40bfad3c7c1c2 = 0xdeadbeef * 0xc0deba5e
Run Code Online (Sandbox Code Playgroud)