0

LinuxでAT&t構文のアセンブラーを使用しています。3 つの数値 (a、b、c) を除算して乗算する必要があります。演算は a/b*c である可能性が高く、idiv と imul istruction を使用してみましたが、もちろんこれらは整数で機能するため、それらから完全に不正確な結果が得られます。また、fidiv と fimul istruction を使用して float を計算に使用しようとしましたが、まったく間違った結果が得られました。おそらく、間違ったレジスタで操作を行っているのでしょう。AT&T で fidiv/fimul を使用する方法の例を教えてください。これらの命令はどのレジスタを使用しますか?

前もって感謝します。

4

1 に答える 1

1

AT&T は Gas (GNU Assembler) の出力構文であるため、あまり長く考えるべきではありません。C で記述し、-S スイッチを使用してアセンブラー出力を生成するだけです。

例:

ソースファイルabc.cに次のプログラムを入力すると

main(){
    int a = 7;
    int b = 3;
    int c = 2;
    return (double)a/(double)b*(double)c;
}

次に、それを使用してコンパイルしますgcc -S abc.c

次のアセンブリ ソース コードを取得します。

.file   "abc.c"
.text
.globl  main
.type   main, @function
main:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    $7, -4(%rbp)
movl    $3, -8(%rbp)
movl    $2, -12(%rbp)
cvtsi2sd    -4(%rbp), %xmm0
cvtsi2sd    -8(%rbp), %xmm1
movapd  %xmm0, %xmm2
divsd   %xmm1, %xmm2
movapd  %xmm2, %xmm1
cvtsi2sd    -12(%rbp), %xmm0
mulsd   %xmm1, %xmm0
cvttsd2si   %xmm0, %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size   main, .-main
.ident  "GCC: (Debian 4.6.3-1) 4.6.3"
.section    .note.GNU-stack,"",@progbits

それが何をするかは十分に明確であるはずです:スタックに3つのint用のスペースを確保し、それらに定数値を格納し、それらをdoubleに変換し(cvtsi2sd)、除算を実行します(注意:a/b順番に書きますdiv b, a、結果は2番目のレジスタに入ります)。などなど。明らかなのは、コンパイラが古い FPU 8087 命令セットをわざわざ使用しないことです。現在では、FPU スタックをいじらずに浮動小数点計算を実行する簡単な方法があるからです。質問はターゲットシステムについて何も言っていないので、コンパイラがそのような計算を実行するのと同じようにします。

一部の人々にとってはまだ不明確である可能性があるため(私の答えは反対票を投じられました)、gcc出力で少し並べ替えを行い(無駄なものを移動しないようにするため)、コメントを追加しました。唯一の落とし穴は、div の引数の順序です。どのレジスターにデータを入れて結果を取得するかは、リーダー次第です。

    .file   "abc.c"
    .text
    .globl  main
    .type   main, @function
    main:
    .LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6

    # Load integer 7 (variable a), convert it to double
    movl    $7, -4(%rbp)
    cvtsi2sd    -4(%rbp), %xmm0

    # Load integer 3, (variable b) convert it to double
    movl    $3, -8(%rbp)
    cvtsi2sd    -8(%rbp), %xmm1

    # Load integer 2, (variable c) convert it to double
    movl    $2, -12(%rbp)
    cvtsi2sd    -12(%rbp), %xmm2

    #   a / b -> written "div b, a" result goes in a (%xmm0)
    divsd   %xmm1, %xmm0 

    #   b * c -> result goes in c (%xmm2)
    mulsd   %xmm0, %xmm2 

    # convert result back to integer
    cvttsd2si   %xmm2, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
    .LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.6.3-1) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

Linux では、execute をコンパイルして結果を表示できます (int に切り捨てられ、プロセス結果であるため 256 に切り捨てられます)。

gcc abc.s ; ./a.out ; echo $?

答えをより完全にするために、古いFPUを使用して同等のプログラムを簡単に作成できます(FPUの切り捨てモードを設定する必要がなかったため、最も近い整数に切り捨てられた4ではなく5が得られる場合があります):

    .file   "abc.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6

    # Load integer 7 (variable a), convert it to double
    movl    $7, -4(%rbp)

    # Load integer 3, (variable b) convert it to double
    movl    $3, -8(%rbp)

    # Load integer 2, (variable c) convert it to double
    movl    $2, -12(%rbp)

    fild    -12(%rbp)
    fild    -8(%rbp)
    fild    -4(%rbp)

    #   a / b -> written "div b, a" result goes in a (%mm0)
    fdivp   %st(0), %st(1) 

    #   b * c -> result goes in c (%mm2)
    fmulp   %st(0), %st(1) 

    # convert result back to integer
    fist    -4(%rbp)
    movl    -4(%rbp), %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.6.3-1) 4.6.3"
    .section    .note.GNU-stack,"",@progbits
于 2012-07-15T00:25:20.010 に答える