shecc
は、C 言語のサブセット用の自己コンパイル コンパイラとして、32 ビット Arm アーキテクチャと RISC-V アーキテクチャの両方をターゲットとして、最初から構築されています。その単純な性質にもかかわらず、スタンドアロンの最適化コンパイラーとして基本的な最適化戦略を実行できます。
shecc
次の構文で記述された C ソース ファイルをコンパイルできます。
+=
、 -=
、 *=
int i = [expr]
#define
、 #ifdef
、 #elif
、 #endif
、 #undef
、および#error
__VA_ARGS__
識別子を持つ入れ子になっていない可変個引数マクロバックエンドは、Raspberry Pi 3 で検証された Linux ABI を備えた armv7hf をターゲットにしており、QEMU で検証された RISC-V 32 ビット アーキテクチャもサポートしています。
shecc
ブートストラップを検証する手順は次のとおりです。
stage0
: shecc
ソース コードは、最初にネイティブ実行可能ファイルを生成する通常のコンパイラーを使用してコンパイルされます。生成されたコンパイラはクロスコンパイラとして使用できます。stage1
: ビルドされたバイナリは、独自のソース コードを入力として読み取り、ARMv7-A/RV32IM バイナリを生成します。stage2
: 生成された ARMv7-A/RV32IM バイナリは、独自のソース コードを入力として (QEMU 経由、または Arm および RISC-V デバイス上で実行されて) 呼び出され、別の ARMv7-A/RV32IM バイナリを生成します。bootstrap
: stage1
およびstage2
コンパイラをビルドし、それらがバイト単位で同一であることを確認します。そうであれば、 shecc
独自のソース コードをコンパイルし、同じプログラムの新しいバージョンを生成できます。 shecc
のコード ジェネレーターは外部ユーティリティに依存しません。 gcc
やclang
などの通常の C コンパイラのみが必要です。ただし、 shecc
自身をブートストラップするため、Arm/RISC-V ISA エミュレーションが必要です。 GNU/Linux に Arm/RISC-V ユーザー エミュレーション用の QEMU をインストールします。
$ sudo apt-get install qemu-user
macOS または Microsoft Windows 上でもshecc
ビルドすることは可能です。ただし、 qemu-arm
存在しないため、第 2 段階のブートストラップは失敗します。
スナップショット テストを実行するには、以下のパッケージをインストールします。
$ sudo apt-get install graphviz jq
必要なバックエンドを構成します。shecc shecc
ARMv7-A および RV32IM バックエンドをサポートします。
$ make config ARCH=arm
# Target machine code switch to Arm
$ make config ARCH=riscv
# Target machine code switch to RISC-V
make
実行すると、次のように表示されるはずです。
CC+LD out/inliner
GEN out/libc.inc
CC out/src/main.o
LD out/shecc
SHECC out/shecc-stage1.elf
SHECC out/shecc-stage2.elf
File out/shecc
第 1 段階のコンパイラです。その使用法:
$ shecc [-o output] [+m] [--no-libc] [--dump-ir] < infile.c >
コンパイラ オプション:
-o
: 出力ファイル名を指定します (デフォルト: out.elf
)+m
: ハードウェア乗除算命令を使用します (デフォルト: 無効)--no-libc
: 組み込み C ライブラリを除外します (デフォルト: 組み込み)--dump-ir
: 中間表現 (IR) をダンプします。例:
$ out/shecc -o fib tests/fib.c
$ chmod +x fib
$ qemu-arm fib
make
呼び出すときにcheck-snapshots
ターゲットを指定して、発行された IR がスナップショットと同一であることを確認します。
$ make check-snapshots
shecc
は単体テストが付属しています。テストを実行するには、引数としてcheck
指定します。
$ make check
参考出力:
...
int main(int argc, int argv) { exit(sizeof(char)); } => 1
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; case 3: a = 10; break; case 1: return 0; } exit(a); } => 10
int main(int argc, int argv) { int a; a = 0; switch (3) { case 0: return 2; default: a = 10; break; } exit(a); } => 10
OK
生成されたコンパイラ ファイルをクリーンアップするには、コマンドmake clean
を実行します。アーキテクチャ構成をリセットするには、コマンドmake distclean
を使用します。
オプション--dump-ir
がshecc
に渡されると、中間表現 (IR) が生成されます。たとえば、 tests/fib.c
ファイルを考えてみましょう。これは再帰的なフィボナッチ数列関数で構成されます。
int fib ( int n )
{
if ( n == 0 )
return 0 ;
else if ( n == 1 )
return 1 ;
return fib ( n - 1 ) + fib ( n - 2 );
}
以下を実行して IR を生成します。
$ out/shecc --dump-ir -o fib tests/fib.c
C ソースと IR 間の行ごとの説明:
C Source IR Explanation
-- -- -- -- -- -- -- -- -- + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
int fib ( int n ) def int @ fib ( int % n ) Indicate a function definition
{ {
if ( n == 0 ) const %. t1001 , $0 Load constant 0 into a temporary variable ".t1001"
%. t1002 = eq % n , %. t1001 Test "n" equals ".t1001" or not , and write the result in temporary variable ".t1002"
br %. t1002 , . label . 1177 , . label . 1178 If ".t1002" equals zero , goto false label ".label.1178" , otherwise ,
goto true label ".label.1177"
. label . 1177
return 0 ; const %. t1003 , $0 Load constant 0 into a temporary variable ".t1003"
ret %. t1003 Return ".t1003"
j . label . 1184 Jump to endif label ".label.1184"
. label . 1178
else if ( n == 1 ) const %. t1004 , $1 Load constant 1 into a temporary variable ".t1004"
%. t1005 = eq % n , %. t1004 Test "n" equals ".t1004" or not , and write the result in temporary variable ".t1005"
br %. t1005 , . label . 1183 , . label . 1184 If ".t1005" equals zero , goto false label ".label.1184" . Otherwise ,
goto true label ".label.1183"
. label . 1183
return 1 ; const %. t1006 , $1 Load constant 1 into a temporary variable ".t1006"
ret %. t1006 Return ".t1006"
. label . 1184
return
fib ( n - 1 ) const %. t1007 , $1 Load constant 1 into a temporary variable ".t1007"
%. t1008 = sub % n , %. t1007 Subtract ".t1007" from "n" , and store the result in temporary variable ".t1008"
push %. t1008 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
+ retval %. t1009 Store return value in temporary variable ". t1009 "
fib ( n - 2 ); const %. t1010 , $2 Load constant 2 into a temporary variable ".t1010"
%. t1011 = sub % n , %. t1010 Subtract ".t1010" from "n" , and store the result in temporary variable ".t1011"
push %. t1011 Prepare parameter for function call
call @ fib , 1 Call function " fib " with one parameter
retval %. t1012 Store return value in temporary variable ". t1012 "
%. t1013 = add %. t1009 , %. t1012 Add ". t1009 " and ". t1012 ", and store the result in temporary variable " . t1013 "
ret %. t1013 Return ".t1013"
} }
使用できません。あるいは、ソースlib/cc
のprintf
実装でvar_arg
を確認してください。shecc
BSD 2 条項ライセンスに基づいて自由に再配布できます。このソース コードの使用は、 LICENSE
ファイルにある BSD スタイルのライセンスによって管理されます。