Rust には 128 ビットの整数があり、これらはデータ型i128
(およびu128
unsigned int の場合) で表されます。
let a: i128 = 170141183460469231731687303715884105727;
i128
Rust はどのようにしてこれらの値を 64 ビット システムで動作させるのでしょうか。たとえば、これらの値に対してどのように演算を行うのでしょうか。
私の知る限り、値は x86-64 CPU の 1 つのレジスタに収まらないので、コンパイラは 1 つのi128
値に対して何らかの方法で 2 つのレジスタを使用しているのでしょうか? それとも、代わりに何らかの大きな整数構造体を使用して値を表していたりするのでしょうか?
ベストアンサー1
Rustの整数型はすべて次のようにコンパイルされます。LLVM 整数LLVM抽象マシンは1から2^23-1までの任意のビット幅の整数を許可します。* LLVM説明書通常、任意のサイズの整数で動作します。
明らかに、8388607ビットアーキテクチャはそれほど多くないので、コードがネイティブマシンコードにコンパイルされるとき、LLVMはそれをどのように実装するかを決定する必要があります。次のような抽象命令のセマンティクスは、add
LLVM 自体によって定義されます。通常、ネイティブ コードで同等の単一命令を持つ抽象命令はそのネイティブ命令にコンパイルされますが、そうでないものは複数のネイティブ命令でエミュレートされる可能性があります。マッカートンの答えLLVM がネイティブ命令とエミュレートされた命令の両方をコンパイルする方法を示します。
(これは、ネイティブ マシンがサポートできるよりも大きい整数だけでなく、小さい整数にも適用されます。たとえば、最新のアーキテクチャではネイティブの 8 ビット演算がサポートされていない可能性があるため、add
2 つのi8
s に対する命令は、余分なビットを破棄して、より広い命令でエミュレートされる可能性があります。)
コンパイラは、1 つの
i128
値に対して何らかの方法で 2 つのレジスタを使用していますか? それとも、何らかの大きな整数構造体を使用してそれらを表現しているのでしょうか?
LLVM IRレベルでは、答えはどちらでもありません。i128
他のすべてのレジスタと同様に、単一のレジスタに収まります。単一値型一方、機械語に変換されると、構造体は整数と同じようにレジスタに分解できるため、両者の間には実際のところ違いはありません。ただし、算術演算を行う場合、LLVM は全体を 2 つのレジスタにロードする可能性が高いでしょう。
* ただし、すべての LLVM バックエンドが同じように作成されるわけではありません。この回答は x86-64 に関するものです。128 より大きいサイズと 2 の累乗以外のサイズに対するバックエンドのサポートは不十分であると理解しています (これが、Rust が 8、16、32、64、128 ビットの整数のみを公開する理由を部分的に説明している可能性があります)。Redditのest31によると、rustc は、ネイティブに 128 ビット整数をサポートしていないバックエンドをターゲットとする場合、ソフトウェアで 128 ビット整数を実装します。