shecc
ถูกสร้างขึ้นตั้งแต่เริ่มต้นโดยกำหนดเป้าหมายทั้งสถาปัตยกรรม Arm 32 บิตและ RISC-V ในฐานะคอมไพเลอร์ที่คอมไพล์ตัวเองสำหรับชุดย่อยของภาษา C แม้จะมีลักษณะที่เรียบง่าย แต่ก็สามารถใช้กลยุทธ์การปรับให้เหมาะสมขั้นพื้นฐานได้ในฐานะคอมไพเลอร์ปรับให้เหมาะสมแบบสแตนด์อโลน
shecc
สามารถรวบรวมไฟล์ต้นฉบับ C ที่เขียนในรูปแบบต่อไปนี้:
+=
, -=
, *=
int i = [expr]
#define
, #ifdef
, #elif
, #endif
, #undef
และ #error
__VA_ARGS__
แบ็กเอนด์กำหนดเป้าหมาย armv7hf ด้วย Linux ABI ซึ่งตรวจสอบแล้วบน Raspberry Pi 3 และยังรองรับสถาปัตยกรรม RISC-V 32 บิต ตรวจสอบด้วย QEMU
ขั้นตอนในการตรวจสอบการบูต shecc
:
stage0
: ซอร์สโค้ด shecc
จะถูกคอมไพล์ในขั้นต้นโดยใช้คอมไพเลอร์ธรรมดาซึ่งสร้างไฟล์ปฏิบัติการดั้งเดิม คอมไพเลอร์ที่สร้างขึ้นสามารถใช้เป็นคอมไพเลอร์ข้ามได้stage1
: ไบนารี่ที่สร้างขึ้นอ่านซอร์สโค้ดของตัวเองเป็นอินพุตและสร้างไบนารี ARMv7-A/RV32IMstage2
: ไบนารี ARMv7-A/RV32IM ที่สร้างขึ้นถูกเรียกใช้ (ผ่าน QEMU หรือทำงานบนอุปกรณ์ Arm และ RISC-V) โดยมีซอร์สโค้ดของตัวเองเป็นอินพุตและสร้างไบนารี ARMv7-A/RV32IM อื่นbootstrap
: สร้างคอมไพเลอร์ stage1
และ stage2
และตรวจสอบว่าเหมือนกันทุกไบต์ หากเป็นเช่นนั้น shecc
สามารถคอมไพล์ซอร์สโค้ดของตัวเองและสร้างเวอร์ชันใหม่ของโปรแกรมเดียวกันนั้นได้ ตัวสร้างโค้ดใน shecc
ไม่ต้องพึ่งพายูทิลิตี้ภายนอก คุณต้องการเพียงคอมไพเลอร์ภาษา C ธรรมดา เช่น gcc
และ clang
และจำเป็นต้องมีการจำลอง Arm/ shecc
-V ISA ติดตั้ง QEMU สำหรับการจำลองผู้ใช้ Arm/RISC-V บน GNU/Linux:
$ sudo apt-get install qemu-user
ยังคงสามารถสร้าง shecc
บน macOS หรือ Microsoft Windows ได้ อย่างไรก็ตาม การบูตสแตรปขั้นที่สองจะล้มเหลวเนื่องจากไม่มี 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
ตรวจสอบว่า IR ที่ปล่อยออกมาเหมือนกับสแน็ปช็อตโดยระบุเป้าหมาย check-snapshots
เมื่อเรียกใช้ make
:
$ 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"
} }
ได้ หรือตรวจสอบการใช้งาน printf
ใน source lib/cc
สำหรับ var_arg
shecc
สามารถแจกจ่ายต่อได้อย่างอิสระภายใต้สิทธิ์การใช้งานมาตรา BSD 2 การใช้ซอร์สโค้ดนี้อยู่ภายใต้ใบอนุญาตสไตล์ BSD ซึ่งสามารถพบได้ในไฟล์ LICENSE