ヘッダーオンリーのC++20ライブラリで、コンパイル時のx86/x86-64命令エンコーディングを提供します。
このプロジェクトはBoost Software License 1.0とMIT Licenseのデュアルライセンスです。どちらかのライセンスを選択できます。
#include "static_asm.hpp"
using namespace static_asm::x86::registers;
using namespace static_asm::x86::instructions;
// コンパイル時にマシンコードを構築
constexpr auto code = core::assemble(
mov(rax, 0x12345678), // mov rax, imm32
add(rax, rcx), // add rax, rcx
xor_(r8, r8), // xor r8, r8
call(rax), // call rax
ret() // ret
);
// code は std::array<uint8_t, N> - 完全にconstexpr!
// レジスタ間
add(rax, rbx); // 48 01 D8
sub(ecx, edx); // 29 D1
and_(r8, r9); // 4D 21 C8
or_(rsi, rdi); // 48 09 FE
xor_(eax, eax); // 31 C0 (レジスタをゼロにする一般的なイディオム)
cmp(rax, rcx); // 48 39 C8
// 即値とレジスタ
add(rax, 0x10); // 48 83 C0 10 (符号拡張されたimm8)
add(rax, 0x10000); // 48 05 00 00 01 00 (imm32)
sub(ecx, 100); // 83 E9 64
and_(rdx, 0xFF); // 48 83 E2 FF
// メモリオペランド付き
add(eax, dword_ptr(rbx)); // 03 03
add(rax, qword_ptr(rcx + 0x10)); // 48 03 41 10
sub(dword_ptr(rsp + 0x20), eax); // 29 44 24 20
// レジスタ間
mov(rax, rbx); // 48 89 D8
mov(eax, ecx); // 89 C8
mov(r8, r9); // 4D 89 C8
// 即値をレジスタへ
mov(rax, 0x12345678); // 48 C7 C0 78 56 34 12
mov(eax, 0xDEADBEEF); // B8 EF BE AD DE
// メモリ操作
mov(rax, qword_ptr(rbx)); // 48 8B 03
mov(eax, dword_ptr(rcx + 0x10)); // 8B 41 10
mov(qword_ptr(rsp + 0x8), rax); // 48 89 44 24 08
mov(dword_ptr(rbp - 0x20), 0x100); // C7 45 E0 00 01 00 00
// ゼロ/符号拡張
movzx(eax, bl); // 0F B6 C3 (バイトをdwordにゼロ拡張)
movzx(rax, bx); // 48 0F B7 C3 (ワードをqwordにゼロ拡張)
movsx(eax, cl); // 0F BE C1 (バイトをdwordに符号拡張)
movsx(rax, dx); // 48 0F BF C2 (ワードをqwordに符号拡張)
movsxd(rax, ecx); // 48 63 C1 (dwordをqwordに符号拡張)
// 実効アドレスのロード
lea(rax, qword_ptr(rbx + rcx * s4)); // 48 8D 04 8B
lea(rax, qword_ptr(rbx + rcx * s8 + 0x10)); // 48 8D 44 CB 10
// 交換
xchg(rax, rbx); // 48 87 D8
// [ベース + インデックス * スケール]
mov(eax, dword_ptr(rbx + rcx * s1)); // 8B 04 0B
mov(eax, dword_ptr(rbx + rcx * s2)); // 8B 04 4B
mov(eax, dword_ptr(rbx + rcx * s4)); // 8B 04 8B
mov(eax, dword_ptr(rbx + rcx * s8)); // 8B 04 CB
// [ベース + インデックス * スケール + 変位]
mov(rax, qword_ptr(rbx + rcx * s4 + 0x10)); // 48 8B 44 8B 10
mov(rax, qword_ptr(r12 + r13 * s8 + 0x1000)); // 4B 8B 84 EC 00 10 00 00
// SIBアドレスへのストア
mov(dword_ptr(rax + rdx * s4), ecx); // 89 0C 90
mov(qword_ptr(rbx + rsi * s8 + 0x20), rax); // 48 89 44 F3 20
// LEAとSIB(アドレス計算に便利)
lea(rax, qword_ptr(rbx + rcx * s4)); // 48 8D 04 8B
lea(rax, qword_ptr(rdi + rsi * s8 + 0x100)); // 48 8D 84 F7 00 01 00 00
// 1によるシフト
shl(eax, 1); // D1 E0
shr(rax, 1); // 48 D1 E8
sar(ecx, 1); // D1 F9
// 即値によるシフト
shl(eax, 4); // C1 E0 04
shr(rax, 8); // 48 C1 E8 08
sar(rdx, 16); // 48 C1 FA 10
// CLレジスタによるシフト
shl(eax, cl); // D3 E0
shr(rax, cl); // 48 D3 E8
// ローテート
rol(eax, 1); // D1 C0
ror(rax, 8); // 48 C1 C8 08
rcl(ecx, cl); // D3 D1
rcr(rdx, 1); // 48 D1 DA
// 単一オペランド(結果はrdx:rax)
mul(rbx); // 48 F7 E3 (符号なし: rdx:rax = rax * rbx)
imul(rcx); // 48 F7 E9 (符号付き: rdx:rax = rax * rcx)
div(rbx); // 48 F7 F3 (符号なし: rax = rdx:rax / rbx, rdx = 余り)
idiv(rcx); // 48 F7 F9 (符号付き除算)
// 2オペランドIMUL (dest = dest * src)
imul(rax, rbx); // 48 0F AF C3
imul(ecx, edx); // 0F AF CA
// 3オペランドIMUL (dest = src * imm)
imul(rax, rbx, 10); // 48 6B C3 0A
imul(ecx, edx, 1000); // 69 CA E8 03 00 00
// 無条件ジャンプ
jmp(0x10); // EB 10 (short, 8ビットオフセット)
jmp(0x1000); // E9 00 10 00 00 (near, 32ビットオフセット)
jmp(rax); // FF E0 (間接)
jmp(here); // EB FE (jmp $, 無限ループ)
// 条件付きジャンプ (8ビットオフセット)
jz(0x10); // 74 10
jnz(0x20); // 75 20
jb(0x08); // 72 08 (below/carry)
jae(0x08); // 73 08 (above or equal/no carry)
jl(0x10); // 7C 10 (less than, 符号付き)
jge(0x10); // 7D 10 (greater or equal, 符号付き)
// 条件付きジャンプ (長い分岐用の32ビットオフセット)
jz_near(0x10000); // 0F 84 00 00 01 00
jnz_near(0x20000); // 0F 85 00 00 02 00
// CallとReturn
call(rax); // FF D0 (間接call)
call(0x100); // E8 00 01 00 00 (相対call)
ret(); // C3
ret(0x10); // C2 10 00 (returnして16バイトポップ)
// 条件が真の場合に移動(分岐ペナルティなし!)
cmovz(rax, rbx); // 48 0F 44 C3 (ゼロなら移動)
cmovnz(eax, ecx); // 0F 45 C1 (ゼロでなければ移動)
cmovl(rax, rdx); // 48 0F 4C C2 (lessなら移動, 符号付き)
cmovge(ecx, esi); // 0F 4D CE (greater or equalなら移動, 符号付き)
cmovb(rax, rbx); // 48 0F 42 C3 (belowなら移動, 符号なし)
cmovae(edx, edi); // 0F 43 D7 (above or equalなら移動, 符号なし)
// メモリソース付き
cmovz(rax, qword_ptr(rbx)); // 48 0F 44 03
cmovnz(eax, dword_ptr(rcx + 0x10)); // 0F 45 41 10
// ビットテスト
bt(eax, 5); // 0F BA E0 05
bt(rax, rbx); // 48 0F A3 D8
// ビットテスト & セット/リセット/補数
bts(eax, 10); // 0F BA E8 0A (テストしてセット)
btr(rax, rcx); // 48 0F B3 C8 (テストしてリセット)
btc(edx, 3); // 0F BA FA 03 (テストして補数)
// ビットスキャン
bsf(eax, ecx); // 0F BC C1 (前方スキャン、最初の1を探す)
bsr(rax, rbx); // 48 0F BD C3 (後方スキャン、最初の1を探す)
// ポピュレーションカウントと先頭/末尾ゼロ
popcnt(eax, ecx); // F3 0F B8 C1
lzcnt(rax, rbx); // F3 48 0F BD C3
tzcnt(eax, edx); // F3 0F BC C2
// バイトスワップ
bswap(eax); // 0F C8 (バイト順を逆にする)
bswap(rax); // 48 0F C8
// 基本文字列操作([rsi] および/または [rdi] に対して動作)
movsb(); // A4 (バイト [rsi] -> [rdi] に移動)
movsw(); // 66 A5
movsd(); // A5
movsq(); // 48 A5
cmpsb(); // A6 ([rsi] と [rdi] を比較)
stosb(); // AA (al -> [rdi] にストア)
lodsb(); // AC ([rsi] -> al にロード)
scasb(); // AE (al と [rdi] を比較)
// REPプレフィックス付き(rcx回繰り返し)
rep_movsb(); // F3 A4 (memcpy)
rep_movsq(); // F3 48 A5 (高速memcpy、8バイトずつ)
rep_stosb(); // F3 AA (memset)
rep_stosq(); // F3 48 AB
// REPE/REPNE付き(等しい/等しくない間繰り返し)
repe_cmpsb(); // F3 A6 (不一致が発生するまで文字列を比較)
repne_scasb(); // F2 AE (文字列内のバイトをスキャン)
push(rax); // 50
push(rbx); // 53
push(r8); // 41 50
push(0x10); // 6A 10 (push imm8)
push(0x1000); // 68 00 10 00 00 (push imm32)
pop(rax); // 58
pop(rbx); // 5B
pop(r15); // 41 5F
// システムコール
syscall_(); // 0F 05 (64ビットsyscall)
sysenter(); // 0F 34
sysexit(); // 0F 35
// 割り込み
int3(); // CC (ブレークポイント)
int_(0x80); // CD 80 (Linux 32ビットsyscall)
int_(0x21); // CD 21 (DOS割り込み)
// CPU情報
cpuid(); // 0F A2
rdtsc(); // 0F 31
rdtscp(); // 0F 01 F9
// 特権
cli(); // FA (割り込みクリア)
sti(); // FB (割り込みセット)
hlt(); // F4 (停止)
// 割り込みからの復帰
iret(); // CF (16ビット)
iretd(); // CF (32ビット)
iretq(); // 48 CF (64ビット)
core::assemble() を使用して命令のバイト配列を連結します:
constexpr auto prologue = core::assemble(
push(rbp),
mov(rbp, rsp),
sub(rsp, 0x20)
);
constexpr auto epilogue = core::assemble(
add(rsp, 0x20),
pop(rbp),
ret()
);
// 組み合わせる
constexpr auto full_function = core::assemble(prologue, epilogue);
CMakeLists.txt に追加:
include(FetchContent)
FetchContent_Declare(
static_asm
GIT_REPOSITORY https://github.com/mahmoudimus/static_asm.git
GIT_TAG v1.0.0 # または特定のコミット
)
FetchContent_MakeAvailable(static_asm)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
クローンするか、gitサブモジュールとして追加:
git submodule add https://github.com/mahmoudimus/static_asm.git external/static_asm
次に CMakeLists.txt で:
add_subdirectory(external/static_asm)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
add_subdirectory または FetchContent でインクルードした場合、static_asm::static_asm インターフェースライブラリターゲットのみがプロジェクトに追加されます。テストとサンプルは、-DSTATIC_ASM_BUILD_TESTS=ON で明示的に有効にしない限りビルドされません。
static_asm.hpp をリリースページからダウンロードし、直接インクルード:
#include "static_asm.hpp"
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --install build --prefix /usr/local
その後、find_package を使用:
find_package(static_asm REQUIRED)
target_link_libraries(your_target PRIVATE static_asm::static_asm)
# 設定
cmake -B build -DCMAKE_BUILD_TYPE=Release
# ビルド
cmake --build build
# テスト実行
ctest --test-dir build --output-on-failure
# サンプル付きでビルド (Clangのみ、インラインアセンブリを使用)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSTATIC_ASM_BUILD_EXAMPLES=ON
プラットフォームサポート:
注: core::emit() インラインアセンブリ機能は、-O2最適化を有効にしたClangが必要です。
オペランドサポート:
[ベース + インデックス * スケール + 変位]、スケールファクター1, 2, 4, 8注意: SIMD/AVX拡張はまだありません。
このプロジェクトではPythonツール(コード生成、単一ヘッダーアマルガメーション)に uv を使用しています。
# uvのインストール(まだの場合)
curl -LsSf https://astral.sh/uv/install.sh | sh
# インストールの確認
uv --version
# Pythonスクリプトはすべてuvで直接実行可能(依存関係は自動管理)
uv run scripts/gen_from_x86ref.py --help
uv run scripts/amalgamate.sh
必要なツール:
開発用のオプショナルツール:
| ツール | 目的 | インストール |
|---|---|---|
| clang-format | コードフォーマット | LLVMまたはシステムパッケージ経由 |
| clang-tidy | 静的解析 | LLVMまたはシステムパッケージ経由 |
プロジェクトには、x86reference XMLデータベースを解析するジェネレーターが含まれています:
# 命令データベースの概要表示
uv run scripts/gen_from_x86ref.py
# 特定の命令の詳細表示
uv run scripts/gen_from_x86ref.py -i lea
uv run scripts/gen_from_x86ref.py -i imul
# 命令データベースファイルの生成
uv run scripts/gen_from_x86ref.py --generate-db
# 網羅的なテストファイルの生成
uv run scripts/gen_from_x86ref.py --generate-tests
instdb、prefix_db、prefix_0fdb 配列)encoder.hpp にエンコーダーをコーディングするか、既存のエンコーダーを拡張任意のマルチファイルC++ライブラリを、手動での準備なしに信頼性の高いヘッダーオンリーバージョンに変換できる自動ツールはありません。以下のテクニックは、ライブラリが正しく、メンテナンス可能で、標準に準拠したまま、単一ヘッダーファイルにアマルガメーションできることを保証するのに役立ちます。
注: このプロジェクトはアマルガメーションに quom を、これらのルールの自動適用に
google-build-using-namespaceチェック付きの clang-tidy を使用しています。quomはまた、その処理機能を通じてテクニック#4(インラインマーカー)を部分的に処理します。
using namespace を避ける.cpp ファイルのファイルスコープでの using namespace は、それらのファイルが後でヘッダーにインクルードされる場合に危険です。ヘッダーをインクルードするすべての翻訳単位のグローバル名前空間を汚染します。
代わりに推奨されるパターン:
// 推奨: 実装を名前空間でラップ
namespace MyLib {
Foo::Foo() {
// ...
}
}
または
// 明示的な修飾(より冗長だが非常に明確)
MyLib::Foo::Foo() {
// ...
}
パブリックAPIはメインの名前空間に配置する必要があります。エンドユーザーを対象としないものはすべて、detail や impl などのネストされた名前空間に隠す必要があります。
一般的な慣例:
namespace MyLib {
namespace detail { // 広く使用されている
// 内部クラス、関数など
}
}
または
namespace MyLib::impl { // より短く、これも一般的
// 内部実装の詳細
}
C++17以降では、インラインのネストされた名前空間定義をサポートしており、よりクリーンです:
namespace MyLib::detail {
class InternalHelper { /* ... */ };
}
static inline クラスメンバーに変換.cpp ファイルで定義されたファイルスコープの static 変数は、ヘッダーオンリーの世界では問題になります(多重定義、ODR違反)。
モダン (C++17以上) な解決策:
// 以前 ( .cpp 内)
namespace MyLib {
static int s_counter = 0;
int next_id() {
return ++s_counter;
}
}
// 以後 (ヘッダーでも安全)
namespace MyLib::detail {
struct Globals {
static inline int counter = 0;
};
}
inline int MyLib::next_id() {
return ++detail::Globals::counter;
}
static inline 変数は、複数回インクルードされても単一の定義を持つことが保証されています。
inline とマーク本体がヘッダーに現れる(しかしクラス定義の内部ではない)すべての関数、メンバー関数、コンストラクター、デストラクターは、One Definition Rule (ODR) 違反を避けるために inline とマークする必要があります。
多くのアマルガメーションスクリプトは純粋にテキストベースでC++セマンティクスを解析しないため、開発中はプレースホルダーマクロ(例: inline_t)を使用する一般的な慣例があります:
// MyLib.h (または共通ヘッダー)
#define inline_t /* 通常のビルドでは空 */
// MyLib.cpp (開発中)
namespace MyLib::detail {
inline_t void Helper::do_work() {
// 実装
}
}
アマルガメーション中に、ツールが inline_t を inline に置き換えます:
// アマルガメーション/変換後
inline void MyLib::detail::Helper::do_work() {
// ...
}
任意のマクロ名を選択できます(例: MYLIB_INLINE, INLINE_IMP など)。 それに応じてアマルガメーションスクリプトを設定してください。
注: quom のようなツールは、C++インクルードセマンティクスを理解し、アマルガメーション中に関数定義を適切に処理することで、多くの場合に手動の
inlineマーカーの必要性を減らし、このプロセスを部分的に自動化できます。
using namespace … を決して書かない。detail / impl) に入れる。static データを、構造体/クラスの static inline メンバーに置き換える。これらの4つのプラクティスに従うことで、純粋なテキストベースのアマルガメーションツールを使用する場合でも、単一ヘッダー配布への移行がはるかにスムーズになり、エラーが大幅に減少します。
このプロジェクトは Midi12 による cx_assembler をベースにしています。元のライブラリは、C++におけるコンパイル時x86アセンブリエンコーディングの基盤を提供しました。
| カテゴリ | 命令 |
|---|
| ALU | ADD, ADC, SUB, SBB, AND, OR, XOR, CMP, TEST |
| 単項 | INC, DEC, NEG, NOT |
| 乗算/除算 | MUL, IMUL (1/2/3オペランド形式), DIV, IDIV |
| データ移動 | MOV, MOVABS, MOVZX, MOVSX, MOVSXD, LEA, XCHG, PUSH, POP |
| シフト/ローテート | SHL, SHR, SAL, SAR, ROL, ROR, RCL, RCR |
| 制御フロー | JMP, CALL, RET, RETF |
| 条件付きジャンプ | JZ/JE, JNZ/JNE, JB/JC, JNB/JNC, JBE/JNA, JNBE/JA, JL, JNL, JLE, JNLE, JO, JNO, JS, JNS, JP, JNP (8ビットおよび32ビットオフセット) |
| 条件付き移動 | CMOVA, CMOVAE, CMOVB, CMOVBE, CMOVE, CMOVG, CMOVGE, CMOVL, CMOVLE, CMOVNE, CMOVNO, CMOVNP, CMOVNS, CMOVO, CMOVP, CMOVS |
| ビット操作 | BT, BTC, BTR, BTS |
| ビットスキャン/カウント | BSF, BSR, POPCNT, LZCNT, TZCNT, BSWAP |
| 文字列操作 | MOVSB/W/D/Q, CMPSB/W/D/Q, LODSB/W/D/Q, STOSB/W/D/Q, SCASB/W/D/Q (REP/REPE/REPNEプレフィックス付き) |
| システム | SYSCALL, SYSENTER, SYSEXIT, INT, INT3, IRET/D/Q, CLI, STI, HLT, CPUID, RDTSC, RDTSCP |
| その他 | NOP, UD2 |
| ツール | 目的 | インストール |
|---|
| uv | Pythonパッケージ/プロジェクトマネージャー | curl -LsSf https://astral.sh/uv/install.sh | sh |
| quom | 単一ヘッダーアマルガメーション | uv tool install quom |
| CMake 3.19以上 | ビルドシステム | cmake.org |
| C++20コンパイラ | GCC 11+, Clang 14+, MSVC 2022+ | - |