5

私が理解していることから、SPARCでは、32ビット整数量は単一のレジスタに格納され、64ビット整数量は隣接するレジスタペアに格納され、偶数レジスタには上位32ビットが含まれ、奇数レジスタには下位32ビットが含まれます。

64 ビット整数のダブルワード ペアを処理する特殊な SPARC インライン アセンブリ マクロ (インライン アセンブリ関数でも問題ありません) をいくつか作成する必要があります。私のインラインアセンブリのペアの半分。私のアセンブリ マクロは、以下に示す MULTIPLY() マクロよりも少し複雑になりますが、乗算の例が機能する場合は、64 ビットのダブルワード ペアの 2 つの半分を処理する方法を示します。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;
}

でコンパイルすると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  

gccの出力を調べたところ-S -fverbose-asm、結果レジスタ (偶数) の奇妙なシフトと、隣接する奇数レジスタへの書き込みが行われています。私の問題は、拡張 asm 構文で隣接する奇数レジスタを一般的に参照する方法がわからないことです。おそらく 'h' asm 制約が"=h"(r)関係しているのではないかと思いましたが、使用方法の例が見つかりません。

4

3 に答える 3

0

の代わりにumul使用しているため、古い指示を受けていると思います。ドキュメントによると、後者は と同義になるように変更されました: 「最も一般的なアーキテクチャ」の命令を生成しますが、特定のアーキテクチャで使用するためにそれらを最適化します。つまり、「V8 sparc 用に生成しますが、Ultrasparc 用に最適化します」という意味です。を使用すると、生の 64 ビット乗算が得られます。-mcpu=-march=-mtune=-mcpu=ultrasparc-march=ultrasparc


編集:すべての議論と他の回答に基づいて、構成されたgcc 3.2-m64はで動作しないようです。これにより、Solaris 2(32ビットアドレス空間と、ほとんどの場合、32 -bit レジスタ (%gおよび%oレジスタに格納されている値を除く)。十分に新しい gcc-m64では、でコンパイルできるはずです。(そして-march=niagara2、特定のターゲット ハードウェアに応じて、追加などを行うことができます。) gcc 4.7.0 から次のように、binutils の完全なセットもインストールする必要がある場合がありますconfig/sparc/sparc.h

#if TARGET_CPU_DEFAULT == TARGET_CPU_v9
/* ??? What does Sun's CC pass?  */
#define CPP_CPU64_DEFAULT_SPEC "-D__sparc_v9__"
/* ??? It's not clear how other assemblers will handle this, so by default
   use GAS.  Sun's Solaris assembler recognizes -xarch=v8plus, but this case
   is handled in sol2.h.  */
#define ASM_CPU64_DEFAULT_SPEC "-Av9"
#endif
#if TARGET_CPU_DEFAULT == TARGET_CPU_ultrasparc
#define CPP_CPU64_DEFAULT_SPEC "-D__sparc_v9__"
#define ASM_CPU64_DEFAULT_SPEC "-Av9a"
#endif
...

これで、インライン アセンブリに頼ることなく、通常の C コードで 2 つの 64 ビット値を乗算して 64 ビットの結果を得ることができるはずです。

(それ以外の場合は、gcc 3.2 用に最終的に思いついたコードのようなものが必要になります。)

于 2012-03-27T07:41:55.193 に答える
0

このumul命令は、2 つのレジスタの下位半分にある 2 つの 32 ビット (unsigned int) 値を乗算し、64 ビットの結果の下位半分をデスティネーション レジスタに格納します。結果の上位半分が Y レジスタに書き込まれます。デスティネーションレジスタの上半分がクリアされます。したがって、おそらくそれを使用するために必要なのは次のようなものです。

#define MULTIPLY(u, r, a, b) /* (u,r = a * b) */     \
asm("umul %2, %3, %0;"   /* unsigned mul */          \
    "rd %%y, %1;"        /* get hi word of result */ \
    : /* regs out */  "=r"(r), "=r"(u)               \
    : /* regs in  */  "r" (a), "r" (b)               \
    : /* clobbers */  "%y" );

ただし、オペランドuint64_tまたはunsigned long longオペランドを使用して、C で乗算を記述するだけの方がほぼ確実に優れていることに注意してください。

于 2012-03-27T05:15:29.130 に答える
0

まず、Chris Dodd、torek、gbulmer の努力と支援に感謝します。ここで見つけたいくつかのコメントを使用して、これを行う方法を理解することができました。以下に、一部を複製します (形式はわずかに編集されていますが、内容は編集されていません)。

スレッド: RFE: "h" および "U" asm 制約と "H" および "L" 修飾子。
[...]一部の v8+ ABI インライン asm に対する次の 2 つの制約 (gcc.info から引用):
'h' SPARC-V8+ アーキテクチャの 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) );
    }
制約として "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));  
      }
免責事項: これらの例はその場で書かれたものであり、初期のクロバーや同様の問題に関して正しくない場合があります。
-ポール

それに基づいて、問題のステートメントから独自の「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. */

私の結果は次のとおりです。

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
于 2012-03-29T14:52:33.660 に答える