call
ほとんどのレジスタが破壊されていると仮定することを強制することのオーバーヘッドはかなり高くなります。高いパフォーマンスを得るには、すべてを完全に最適化できるように、関数を手動で asm にインライン化する必要があります。
コンパイラにスタンドアロンの定義を発行させ、それを呼び出すことは、パフォーマンスが重要ではないコードに対してのみ考慮する必要があります。あなたは asm で何を書いているのか、またはその理由を言いませんでしたが、パフォーマンスが重要であると想定しています。それ以外の場合は、C で記述します (特別な命令にはインライン asm を使用しますか?)。
手動でインライン化するのではなく、これらの小さなインライン C 関数をループ内で使用する場合は、すべてを C で記述した方がパフォーマンスが向上する可能性があります。これにより、コンパイラはより多くのコードを最適化できます。 .
x86-64 で使用される register-arg 呼び出し規則は優れていますが、call-clobber されたレジスタが多数あるため、計算中に呼び出しを行うと、できるだけ多くのデータをレジスタに保持できなくなります。
アセンブリ コードはインライン化を利用できますか? つまり、インライン アセンブリではなく、.S ファイルのようです。
いいえ、inline-asm の逆の構文はありません。もしあれば、それは次のようなものです: 入力が入っているレジスター、出力を入れたいレジスター、および上書きできるレジスターをコンパイラーに伝えます。
手書きの asm とコンパイラ出力の間の共通部分式の除去やその他の重要な最適化は、手書きの asm を本当に理解するコンパイラ、またはそれをソース コードとして扱い、最適化されたバージョンの全部。
コンパイラ出力を asm に最適にインライン化するには、通常、asm を調整する必要があります。これが、それを行うプログラムがない理由です。
私がやろうとしていることを達成するためのより良い方法はありますか?
コメントであなたの目標が何であるかを説明したので、その逆ではなく、使用したい特別な命令のために C で小さなラッパーを作成します。
#include <stdint.h>
struct __attribute__((packed)) lgdt_arg {
uint16_t limit;
void * base; // FIXME: always 64bit in long mode, including the x32 ABI where pointers and uintptr_t are 32bit.
// In 16bit mode, base is 24bit (not 32), so I guess be careful with that too
// you could just make this a uint64_t, since x86 is little-endian.
// The trailing bytes don't matter since the instruction just uses a pointer to the struct.
};
inline void lgdt (const struct lgdt_arg *p) {
asm volatile ("lgdt %0" : : "m"(*p) : "memory");
}
// Or this kind of construct sometimes gets used to make doubly sure compile-time reordering doesn't happen:
inline void lgdt_v2 (struct lgdt_arg *p) {
asm volatile ("lgdt %0" : "+m"(*(volatile struct lgdt_arg *)p) :: "memory");
}
// that puts the asm statement into the dependency chain of things affecting the contents of the pointed-to struct, so the compiler is forced to order it correctly.
void set_gdt(unsigned size, char *table) {
struct lgdt_arg tmp = { size, table };
lgdt (&tmp);
}
set_gdt
-O3
( godbolt のgcc 5.3) にコンパイルします。
movw %di, -24(%rsp)
movq %rsi, -22(%rsp)
lgdt -24(%rsp)
ret
を含むコードを書いたことはありませんlgdt
。コンパイル時にロード/ストアが並べ替えられないようにするために、私が行ったように「メモリ」クロバーを使用することをお勧めします。これにより、それが指している GDT が実行前に完全に初期化されていることが確認されますLGDT
。( も同様LIDT
)。コンパイラはインライン asm に GDT への参照を与えることに気づき、base
その内容が同期していることを確認するかもしれませんが、私にはよくわかりません。ここで「メモリ」クロバーを使用するだけのマイナス面はほとんどまたはまったくないはずです。
Linux (カーネル) は、この種のラッパーをあらゆる場所で 1 つか 2 つの命令に使用し、asm でできるだけ少ないコードを記述します。必要に応じて、インスピレーションを探してください。
re: あなたのコメント: はい、ブート セクタを asm で書きたいと思うでしょう。gcc の -m16 コードはばかげているので (基本的には 32 ビット コードです)、他の 16 ビット コードも書きたいと思うでしょう。
いいえ、C コンパイラの出力を手動で asm にインライン化する方法はありません。同じ理由で、アセンブリを最適化するプログラムはありません。(つまり、asm ソースの読み取り、最適化、別の asm ソースの書き込み)。
そのようなプログラムが何をしなければならないかを考えてみてください:手書きの asm を壊さずに何を変更できるかを知るには、手書きの asm を理解する必要があります。ソース言語としての Asm は、オプティマイザーに多くの作業を提供しません。