5

次のコードは、x と y の積を計算し、結果をメモリに格納します。データ型 ll_t は long long と同等に定義されています。

typedef long long ll_t;
void store_prod(ll_t *dest, int x, ll_t y) {
    *dest = x*y;
}

gcc は、計算を実装する次のアセンブリ コードを生成します: %ebp+8 の dest、%ebp+12 の x、%ebp+16 の y

1 movl 16(%ebp), %esi
2 movl 12(%ebp), %eax
3 movl %eax, %edx
4 sarl $31, %edx
5 movl 20(%ebp), %ecx
6 imull %eax, %ecx
7 movl %edx, %ebx
8 imull %esi, %ebx
9 addl %ebx, %ecx
10 mull %esi
11 leal (%ecx,%edx), %edx
12 movl 8(%ebp), %ecx
13 movl %eax, (%ecx)
14 movl %edx, 4(%ecx)

このコードは、32 ビット マシンで 64 ビット演算を実装するために必要な多倍精度演算を実装するために、3 つの乗算を使用します。積の計算に使用されるアルゴリズムを説明し、アセンブリ コードに注釈を付けて、アルゴリズムがどのように実現されるかを示します。

上記のアセンブリ コードの 8 行目と 9 行目がわかりません。誰でも助けることができますか?

4

3 に答える 3

5

これを intel 構文に変換しました。

mov esi, y_low
mov eax, x
mov edx, eax
sar edx, 31
mov ecx, y_high

imul ecx, eax ; ecx = y_high *{signed} x

mov ebx, edx

imul ebx, esi ; ebx = sign_extension(x) *{signed} y_low

add ecx, ebx ; ecx = y_high *{signed} x_low + x_high *{signed} y_low

mul esi ; edx:eax = x_low *{unsigned} y_low

lea edx, [ecx + edx] ; edx = high(x_low *{unsigned} y_low + y_high *{signed} x_low + x_high *{signed} y_low)

mov ecx, dest
mov [ecx], eax
mov [ecx + 4], edx

上記のコードが行うことは、積の最下位 64 ビットを保持する 2 つの 64 ビット符号付き整数の乗算です。

他の 64 ビット被乗数はどこから来るのですか? It's x sign-extended to 32 bits to 64. この命令は、符号ビットを のすべてのビットにsar複製するために使用されます。この符号のみからなる値を と呼びます。実際にルーチンに渡される値です。x'sedxx'sx_highx_lowx

y_lowとは、 とと同じように、の最も重要でないy_high部分と最も重要な部分です。yx's x_lowx_high

ここからはとても簡単です:

product = y*{signed} x=
( y_high* 2 32 + y_low) *{signed} ( x_high* 2 32 + x_low) =
y_high*{signed} x_high* 2 64 +
y_high*{signed} x_low* 2 32 +
y_low*{signed} x_high* 2 32 +
y_low*{署名済み}x_low

y_high*{signed} x_high* 2 64は、積の最下位 64 ビットに寄与しないため、計算されません。完全な 128 ビット製品 (うるさい人向けの完全な 96 ビット製品) に興味がある場合は、それを計算します。

y_low*{signed}x_lowは、符号なし乗算を使用して計算されます。2 の補数の符号付き乗算は符号なし乗算と同じ最下位ビットを与えるため、これは合法です。例:
-1 *{signed} -1 = 1
0xFFFFFFFFFFFFFFFF *{unsigned} 0xFFFFFFFFFFFFFFFF = 0xFFFFFFFFFFFFFFFE0000000000000001 (最下位64ビットが1に相当)

于 2012-07-27T03:54:30.997 に答える
2

8 行目と 9 行目のコンテキストを考えてみましょう。

この時点で、ESIの下半分が含まれており、yが含まれています。したがって、8 行目は計算して に格納するだけです。EBXsgn(x)sgn(x) * (y % 2^32)EBX

9 行目はその結果を利用しています。行 9 が発生するまでECXに、乗算の部分的な上半分、つまりx * (y >> 32)符号付きが含まれます。したがってEBX+ECX、最後のステップで計算したものに、前の行で見つけた部分的な上半分を加えたものになります。

完全なアルゴリズム自体はかなりきちんとしています ;)

編集:以下のコメントに応じて...

行 4: SAR EDX, 31(または必要に応じてsar $31, %edx) が実際に何を意味するかを考えてみましょう。は 32 ビット レジスタであるためEDX、最終的には 2 つの値のいずれかになります。どの2つ?符号付き算術のコンテキストでそれらが何を意味するかを考えてみましょう。

行 7:EDXこの時点で、次の操作に非常に役立つものが含まれています。必要な場所に移動するだけです。

于 2012-07-27T03:12:10.897 に答える
0

imul が行うことは、eax の内容を ecx で乗算し、下位 32 ビットを eax に保存し、上位 32 ビットを edx に保存することです。

私が覚えている限り、addl は 2 つのレジスタを追加し、最初のレジスタに保存するため、この場合は ebx. (それが何か他のことをするかどうかはわかりません。addl の後の l は long を表します)

于 2012-07-27T02:57:43.060 に答える