質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

1730閲覧

smulh命令の理由

grape_ll

総合スコア83

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2021/06/21 11:50

編集2021/06/22 12:17

C

1#define M 2#define N 3long arith (long x, long y) 4{ 5long result = x*M + y/N; 6return result; 7}

上記のようなプログラムにいろいろなM とN の整数値を入れてgcc –O –c でコンパイルしてアセンブリ言語ファイル(.o ファイル)を生成し,objdump -dで逆アセンブルすることで.式xM + y/N がどのように計算されるかを見ています.

あるファイルについてこれを行った時.アセンブリ言語で下のように書かれていました.smulhは調べてみたところ,2 つの 64 ビットレジスタ Xn と Xm を乗算し、その 128 ビットの乗算結果の上位 64 ビットをレジスタ (Xd) に書き込む,とのことでしたが,ここではx0に変数x,x1に変数yが入っているので,どうなっているのかよくわからなくなってしまいました.

smulhをただの乗算として考えてみてもsubのところでx1,つまりyを63回算術右シフトして引いていて,よくわからないです.
このふたつではどのようなことが起きているのか教えていただきたいです.

私の考えをアセンブリの下に記しておきます.

0: b205cfe2 mov x2, #0x7878787878787878 // #8680820740569200760 4: f28f0f22 movk x2, #0x7879 8: 9b427c22 smulh x2, x1, x2 c: 9343fc42 asr x2, x2, #3 10: cb81fc41 sub x1, x2, x1, asr #63 14: 8b000020 add x0, x1, x0 18: d65f03c0 ret

 最初の命令で.mov命令によって
x2= 0111100001111000011110000111100001111000011110000111100001111000b
次のmovk命令によってx2 =0111100001111000011110000111100001111000011110000111100001111001b
次のsmulh命令によって,x2=8680820740569200761*y
次のasr命令によって
x2=0000111100001111000011110000111100001111000011110000111100001111b * y

###追記
いくつかのケースで試しに実行してみてもsmulhを使う命令は出てきませんでした.
また,このようなケースを通じて,基本的な除算の仕組みはある程度理解していると思っています.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sazi

2021/06/21 13:13

> x1に変数x どこで?
ppaul

2021/06/21 13:51

MとNがなんであるかによって、コンパイラの出す機械命令は変わります。 MとNを示さずに質問するのはやめましょう。
grape_ll

2021/06/21 15:41

変数の部分はミスでした.変数はyが入るはずです. 申し訳ございません.
grape_ll

2021/06/21 15:41

MとNはこのアセンブリから推測できるので,その推測をいましている状況です.
ikadzuchi

2021/06/22 00:46

> 上記のようなプログラムにいろいろなM とN の整数値を入れて…見ています. > MとNはこのアセンブリから推測できるので,その推測をいましている状況です. 質問からはMとNはあなたが決めた整数値だと読めますが、いま推測しているということは質問の内容と違いそうですね。なにかの課題でしょうか。 除算自体理解していないのに他と組み合わさった演算を理解しようとするのが間違っています。 まずは定数除算1個を逆アセンブルして理解してください。
grape_ll

2021/06/22 04:10

いくつかのケースで試してはみたのですが,smulhを使うものがでてきませんでした. どういうケースならばこの命令を使うのでしょうか.
rubato6809

2021/06/22 04:21

kazumaさんの回答にある #define M 1 #define N 17 を試してみましたか>質問者
grape_ll

2021/06/22 04:52

ただいま外出中でありますので,帰宅しだい確認してみるつもりではいましたが,この場合にもsmulhは出てくるということでしょうか.
ikadzuchi

2021/06/22 04:53

> いくつかのケースで試してはみたのですが,smulhを使うものがでてきませんでした. 下の回答へのコメントの > 簡単な例を用いて除算の動きを見ること自体は済ませてあります と合わせると、定数除算1個ではsmulhが出てこなかったということでしょうか。 なら最初からそれを書いてください。今からでも遅くないので質問を編集してください。
grape_ll

2021/06/22 04:56

分かりました.ご指摘ありがとうございます.
guest

回答2

0

ベストアンサー

ARM のコンパイラがないので、Intel でやってみました。

C

1#include <stdio.h> 2 3#define M 1 4#define N 17 5 6long arith (long x, long y) 7{ 8 long result = x*M + y/N; 9 return result; 10}

逆アセンブル結果

0: f3 0f 1e fa endbr64 4: 48 ba 79 78 78 78 78 movabs $0x7878787878787879,%rdx b: 78 78 78 e: 48 89 f0 mov %rsi,%rax 11: 48 f7 ea imul %rdx 14: 48 c1 fa 03 sar $0x3,%rdx 18: 48 c1 fe 3f sar $0x3f,%rsi 1c: 48 29 f2 sub %rsi,%rdx 1f: 48 8d 04 3a lea (%rdx,%rdi,1),%rax 23: c3 retq

movabs から sub までが 17による割り算です。
lea が足し算です。

Intel は除算命令があるのに、gcc は乗算命令を使うコードを吐きます。

追記
y / 17 を掛け算でやろうとすると、y * (1/17)。
しかし、整数演算では 1/17 は 0 だからダメです。

浮動小数点では、1.0/17 = 0.058823529411764705...
これを 2進で表すと、
0.000011110000111100001111000011110000111100001111000011110000111100001111...
これを 3ビット左にシフトすると
0.011110000111100001111000011110000111100001111000011110000111100001111...
これを 64ビット左にシフトすると、
0111100001111000011110000111100001111000011110000111100001111000.01111...
16進で表すと 0x7878787878787878。
小数点以下切り捨てだと誤差が出るので末尾を +1 して0x7878787878787879。

64ビットのレジスタに x2 に、1/17*(2の67乗) が符号付きで入りました。
64ビットの y の値とこの値を掛け算すると 128ビットの値になりますが、
この値は、y を 17で割った値の 2の67乗倍です。
まず、64ビット右シフトしてからさらに 3ビット右シフトすればよいでしょう。
64ビット右シフトするには、128ビットの値の上位64ビットを取り出せばよいということです。
smulh 命令でそれができます。
その後 3ビット右シフトして 17で割った値がレジスタ x2 に求まりました。

しかし、y が負の場合、結果に 1 を足す補正が必要です。
sub x1, x2, x1, asr #63 で、x1 が y です。
それを 63ビット算術右シフトすると符号ビットが拡張されて、
正の場合は 0、負の場合は、全ビット 1で値は -1。
これを x2 から引くことで補正されます。

投稿2021/06/21 18:14

編集2021/06/22 11:43
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubato6809

2021/06/22 01:01

> 除算命令があるのに、gcc は乗算命令を使う 乗算命令の方が除算命令より速いからでしょう。
grape_ll

2021/06/22 04:15

ご回答いただきありがとうございます. しかし,簡単な例を用いて除算の動きを見ること自体は済ませてありますので,smulhの挙動について伺えればと思うのですが,いかがでしょうか.
ikadzuchi

2021/06/22 04:59

> Intel は除算命令があるのに Armも除算命令はありますね。
grape_ll

2021/06/22 12:11

kazuma-sさん 解説,大変分かりやすく,理解することが出来ました. ありがとうございます.
guest

0

そもそものはなし、わざわざ逆アセンブルするんじゃなく、アセンブルリストを直接出したほうがよくないですか?

投稿2021/06/21 11:55

y_waiwai

総合スコア87800

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

grape_ll

2021/06/21 12:40

逆アセンブルすることは可能であるという確認のためですので,気にしないでください. 回りくどくて申し訳ございません.
y_waiwai

2021/06/21 13:25

ちとそのリストが信用できるとも言い難いので、気にしないことにしておきます
grape_ll

2021/06/21 15:43

リストが信用できないとはどういうことでしょうか
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問