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
없기 때문에 두 번째 단계 부트스트래핑은 실패합니다.
스냅샷 테스트를 실행하려면 아래 패키지를 설치하세요.
$ sudo apt-get install graphviz jq
원하는 백엔드를 구성하세요. 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
첫 번째 단계 컴파일러입니다. 사용법:
$ 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"
} }
사용할 수 없습니다. 또는 var_arg
에 대한 소스 lib/cc
에서 printf
구현을 확인하세요. shecc
는 BSD 2 조항 라이센스에 따라 자유롭게 재배포 가능합니다. 이 소스 코드의 사용은 LICENSE
파일에 있는 BSD 스타일 라이센스에 따라 관리됩니다.