
2026/05/26 6:01
Riscrithm – Go で記述された直感的な RISC-V アッセンブラおよび最適化ツール
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Riscrithm は、ネイティブ金属(bare-metal)システムのために直接純粋な RISC-V アセンブリーにコンパイルされる高レベルのマクロアセンブリの方言です。これは、高レベルでの可読性と低レベルでのハードウェア効率を橋渡しします。
riscrithm CLI(例:riscrithm "source" "target" [-o/--optimize])を通じて 2 パスアーキテクチャを用いてコンパイルされます。パス 1 はコードのサニタイズとマクロ処理(define を使用したテキスト置換)を担い、パス 2 は解析、最適化(デッドコード除去および強さ削減を含む)、および出力生成を行います。この言語はセクションとグローバルシンボルを設定する特定のヘッダーディレクティブ(header default、entrypoint main)をサポートし、コメントには # を使用します(コンパイル中に削除される)。また、インデントされた指示と末尾に : で終わる標準ラベルを区別して構文を使用します。生のアセンブリーブロックは !! で迂回されます。システム制御ではキーワードが RISC-V オペコードにマッピングされます(例:interrupt.u→uret、halt→ecall)。開発には厳格な命名規則(変数で camelCase、ラベルで snake_case)および割り当て演算子(=)、スタック/ヘイプアクセス(.b/.w/.d、&)、ビット演算および数学演算などのコアオペレーターが含まれます。制御フローはインライン条件式とラベル/ジャンプによる手動ループ構造を使用します。出力アセンブリーは可読性の向上のために自動で整形されます。今後のバージョン 1.1.0 ではモジュールのインポート、正確なエラー追跡(行/列)、ガード句、早期リターン構文が導入され、組み込み RISC-V 開発における手動作業をさらに削減します。本文
Riscrithm 開発者マニュアル:純粋な RISC-V アセンブリのためのマクロアセンブリ
Riscrithm は、高レベル言語の可読性とネイティブハードウェアの制御性の中間地点にある架け橋です。本書では、コンパイラの仕組み、構文規則、および裏で起きている処理を詳細に解説します。
1. コマンドラインインターフェース (CLI)
ソースコードをコンパイルするには、
riscrithm という CLI ツールを使用します。
riscrithm "source_code_file" "assembly_target_file" [-o/--optimize]
パラメータ詳細
- ソースコード: Riscrithm の入力ファイルです。
- ターゲットファイル: 生成される
アセンブリファイルです(既に存在しなくても動的に作成されます)。.s - 最適化: スイープ有効化には、
または-o
を指定してください。--optimize
2. ファイル構造とグローバル設定
すべての Riscrithm ファイルで、ファイルの先頭にターゲットセクションとエントリーポイントの宣言を行う必要があります。定義(マクロ)が含まれる一行は、ラベルブロック内を除いて完全にインデントされていない状態で存在させることが許可される唯一の行です。
ヘッダーとエントリーポイント
: アセンブリのセクションを設定します(例:header
はheader default
に相当)。.section .text
: プログラムの開始位置を定義します(例:entrypoint
はentrypoint main
に相当)。.globl main
header default entrypoint main
定義(マクロ)
文字置換マクロは
define キーワードを用いて定義します。レジスタのエイリアス設定や、単一行の内联関数(inline function)作成に適しています。
define foo = x1 define bar = x2 define clearFoo = foo ^^
- 動作: パーサーが
を見つけた際、ロジック処理前にfoo
に置換します。x1
コメント
コメントは
シンボルを使用して記述します。コンパイラはこの以降の内容を削除するため、配置場所には制限がありません。#
# この行は無視されます
3. ラベル、インデントと生アセンブリブロック
Riscrithm は厳格なインデント規則によって作用範囲(スコープ)を制御します。
標準的なラベル
- ラベル: 実行ブロックの定義に使用され、必ずコロン (
) で終わらなければなりません。自身にはインデントがありません。: - 命令: ラベル内のすべての命令はスペースまたはタブでインデントする必要があります(インデントがないと
)。SyntaxError
main: load foo = apple move bar = foo
生アセンブリ用のラベル (!!)
プリプロセッサを回避してそのままの RISC-V アセンブリを記述する必要がある場合は、ラベルに
をプレフィックスとして付加します。!!
- コンパイラは感嘆符を除き、ブロック内の内容を完全に保持します。
- マacro や省略表記は展開されません。
!!raw_block: li x1, 10 foo ^^ # コードは元のままです!
4. コア機能と命令
Riscrithm は可読性の高いステートメントを直接ハードウェア命令にマッピングします。
システムおよび中断制御
| Riscrithm | RISC-V アセンブリ | 説明 |
|---|---|---|
| | ユーザーモードへのトラップリターン |
| | サブビジナ(supervisor)モードへのトラップリターン |
| | マシンモードへのトラップリターン |
| | 中断を待機(低消費電力状態) |
| | デバッグャートラップ |
| | システム環境コール / ハルト |
5. ネーミング規則
コンパイラはコードの可読性と一貫性のために、明確な命名方法を強く推奨します。
-
変数とレジスタ (
)camelCase- 小文字から始め、その後の単語ごとに大文字を開始させます。
- 例:
,firstNum
,addressRegisterstackOffset
-
ラベルとコードブロック (
)snake_case- アンダースコアで区切られた小文字単語を使用し、視覚的に際立たせます。
- 例:
,loop_start
,on_trueerror_handler
-
定数とリテラル (
)SCREAMING_SNAKE_CASE- 変更しない設定値やグローバル定義に大文字使用します。
- 例:
,DEFAULT_HEADER
,MAX_BUFFER_SIZEIMM_VALUE
6. 完全な演算子と式参照
| Riscrithm シンタックス | カテゴリ | 内部展開 / 動作 |
|---|---|---|
| 代入 | ダイレクトイミディエート代入 () |
| 代入 | レジスタ間コピー () |
| 値の交換 | トリプル XOR 非破壊的交換 |
| スタックメモリ | ポインタ減算、格納 |
| スタックメモリ | 読み込み、ポインタ増算 |
| スタックメモリ | スタックトップからの値の Peek |
| ヒープメモリ | ベースレジスタによるメモリ読み出し |
| ヒープメモリ | ベースレジスタによるメモリ書込み |
算術およびビット演算子
-
自己オペレーター:
:var ++var = var + 1
:var --var = var - 1
:var ^^
(レジスタクリア)var = var ^ var
-
複合タグ (
,op+=
など):op-=- 算術演算:
,+
,-
,*
,/% - ビット演算:
,<<>>
- 算術演算:
-
ベース算術(即座値再調整サポート):
: 加算 (var = + imm
)addi
: 減算 (var = - imm
)subi
: ビット論理積 AND (var = & imm
)andi
: ビット論理和 OR (var = \| imm
)ori
: ビット反転 XOR (var = ^ imm
)xori
: 論理的左シフト (var = << imm
)slli
: 論理的右シフト (var = >> imm
)srli
: ハードウェア乗算 (M-拡張)var = * imm
: ハードウェア除算 (M-拡張)var = / imm
: ハードウェア剰余 (M-拡張)var = % imm
分岐と条件分岐
- 無条件ジャンプ:
シンボルを使用します。@label - 条件付き分岐: インラインの三元記法形式 (
) を使用します。コンパイラは自動的にif ... else ...
,beq
,bne
などにマッピングし、動的にレジスタを入れ替えます。blt
if foo == bar @true_block else @false_block if foo > baz @greater_block else @lesser_block
ループ(無限および条件付き)
Riscrithm は
while や for キーワードを持っていません。従来の方法(ラベル、分岐、ジャンプ)を用いてループを構築します。
無限ループ:
infinite_loop: foo ++ @infinite_loop
条件付きループ:
loop_start: if foo == bar @loop_end else @loop_body loop_body: foo ++ @loop_start loop_end: halt
7. メモリ操作(スタックとヒープ)
データ幅拡張を指定する必要があります(
.b:バイト、.w:ワード、.d:ダブルワード)。
スタック操作
- プッシュ (
):->
(foo -> stack.w
を 4 減少し、ワードを格納)sp - ポップ (
):<-
(ダブルワードを読み込み、bar <- stack.d
を 8 増加)sp - Peek (
):=
(baz = stack.b
を動かさずにバイトを読み出し)sp
ヒープ操作
ベースアドレスレジスタは
& ポインタ構文を使用します。
- ストア (
):->foo -> heap.w from &bar - ロード (
):<-baz <- heap.b from &foo
8. 完全なスニペット例
以下の機能を一括で確認できます:
main: # Setup load foo = 10 load bar = 20 baz ^^ # クリア # 数学とメモリ操作 foo += 5 foo -> stack.w bar *= foo baz <- heap.w from &bar # 分岐 if foo != bar @continue else @fail continue: foo swap bar halt fail: trap
9. コンパイラのアーキテクチャと最適化 (-o / --optimize)
Riscrithm は超高速な二段階システムで動作します。
- 第 1 段階(サニタイズ): コメント削除、空白文字標準化、インデント規則検証。
- 第 2 段階(パースと最適化): マacro 置換、省略表記展開、および
フラグ有効時の即時最適化。-o
最適化によるクリーンアップ内容 (-o
の場合)
-o- 死んだ代入の排除: 連続した重複修正や冗長なロード/ムーブシーケンスは破棄されます。
- 例: 連続して
を呼んでも、結果として 1 つの命令のみが生成されます。load foo = 128
- 例: 連続して
- 恒等算術の排除: 値を変更しない演算(例:
,foo = foo + 0
)は完全に削除されます。bar = bar / 1 - 強度還元(ビット演算の畳み込み): 計算コストが高い乗算と除算を、効率的なビットシフトとして書き直します。
→foo = bar * 2
(左シフト)slli foo, bar, 1
→baz = foo / 8
(右シフト)srli baz, foo, 3
10. クリーンで即用可能な出力
生成されるアセンブリファイルは、自動的に**整形(pretty-printed)**されています。
- ブロック内の命令は適切にインデントされます。
- ラベルは揃った位置に配置されます。
- ハードウェアシミュレータやデバッグャーで即座に使用可能です。
コーディング愉快!
11. ロードマップ:v1.1.0 で何を用意していますか?
現在のコンパイラエンジンは符号解決とコード生成を分離していますが、開発者体験 (DX) を向上させるため以下の機能を強化します。
- 適切なモジュールインポート: コードの分割を容易にし、クリーンで再利用可能なモジュール構造を構築中。
- より良いエラー処理: 正確な行・列追跡と、人間が読みやすいエラーメッセージの導入。
- ガード節と単純な if ステートメント: 空の
ブロックを書かずに早期リターンパターン (else
) をネイティブサポート。if ... return
貢献とフィードバック
構文に対するアイデアやバグ(特にレジスタ割り当てのエッジケース)を見つけた場合は、Issue のオープンや PR の投稿をお待ちしています。