私はかなり奇妙な問題に直面しています。私はビット演算をサポートしないアーキテクチャ用のコンパイラに取り組んでいます。ただし、符号付き16ビット整数演算を処理するため、以下のみを使用してビット単位の演算を実装できるかどうか疑問に思いました。
- 加算(c = a + b)
- 減算(c = a --b )
- 除算(c = a / b)
- 乗算(c = a * b)
- 係数(c = a%b)
- 最小(c = min(a、b))
- 最大(c = max(a、b))
- 比較(c =(a <b)、c =(a == b)、c =(a <= b)など)
- ジャンプ(goto、for、et.c.)
サポートできるようにしたいビット演算は次のとおりです。
- または(c = a | b)
- そして(c = a&b)
- Xor(c = a ^ b)
- 左シフト(c = a << b)
- 右シフト(c = a >> b)
- (すべての整数は符号付きなので、これは問題です)
- 符号付きシフト(c = a >>> b)
- 1の補数(a = 〜b )
- (すでに解決策が見つかりました。以下を参照してください)
通常、問題はその逆です。ビット単位のハックを使用して算術最適化を実現する方法。ただし、この場合はそうではありません。
このアーキテクチャでは書き込み可能なメモリが非常に少ないため、ビット単位の演算が必要です。ビット単位の関数自体は、多くの一時変数を使用するべきではありません。ただし、一定の読み取り専用データと命令メモリは豊富です。ここでの注意点は、ジャンプとブランチは高価ではなく、すべてのデータが簡単にキャッシュされることです。ジャンプのコストは、算術(ロード/ストアを含む)命令の半分のサイクルです。つまり、上記でサポートされているすべての関数は、1回のジャンプの2倍のサイクルのコストがかかります。
役立つかもしれないいくつかの考え:
次のコードで1の補数(ビットを否定)を実行できることがわかりました。
// Bitwise one's complement
b = ~a;
// Arithmetic one's complement
b = -1 - a;
また、2の累乗で除算するときの古いシフトハックを覚えているので、ビット単位のシフトは次のように表すことができます。
// Bitwise left shift
b = a << 4;
// Arithmetic left shift
b = a * 16; // 2^4 = 16
// Signed right shift
b = a >>> 4;
// Arithmetic right shift
b = a / 16;
残りのビット演算については、私は少し無知です。このアーキテクチャのアーキテクトがビット演算を提供してくれればよかったのにと思います。
また、メモリデータテーブルを使用せずに(シフト操作の場合)2の累乗を計算する高速で簡単な方法があるかどうかも知りたいです。素朴な解決策は、掛け算の分野に飛び込むことです:
b = 1;
switch (a)
{
case 15: b = b * 2;
case 14: b = b * 2;
// ... exploting fallthrough (instruction memory is magnitudes larger)
case 2: b = b * 2;
case 1: b = b * 2;
}
またはセット&ジャンプアプローチ:
switch (a)
{
case 15: b = 32768; break;
case 14: b = 16384; break;
// ... exploiting the fact that a jump is faster than one additional mul
// at the cost of doubling the instruction memory footprint.
case 2: b = 4; break;
case 1: b = 2; break;
}