تم إنشاء shecc
من الصفر، مستهدفًا معماريات Arm وRISC-V ذات 32 بت، كمترجم ذاتي التجميع لمجموعة فرعية من لغة 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/RV32IM.stage2
: يتم استدعاء الملف الثنائي ARMv7-A/RV32IM الذي تم إنشاؤه (عبر QEMU أو يعمل على أجهزة Arm وRISC-V) باستخدام كود المصدر الخاص به كمدخل ويولد ثنائي ARMv7-A/RV32IM آخر.bootstrap
: أنشئ مترجمي stage1
و stage2
، وتحقق من تطابقهما من حيث البايت. إذا كان الأمر كذلك، فيمكن لـ shecc
تجميع كود المصدر الخاص بها وإنتاج إصدارات جديدة من نفس البرنامج. لا يعتمد منشئ الأكواد في shecc
على أدوات مساعدة خارجية. ما عليك سوى برامج التحويل البرمجي العادية للغة C مثل gcc
و clang
. ومع ذلك، فإن shecc
سوف يقوم بتمهيد نفسه، ويلزم محاكاة Arm/RISC-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
تحقق من أن الـ IRs المنبعثة متطابقة مع اللقطات عن طريق تحديد هدف 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
التنفيذ في المصدر lib/cc
لـ var_arg
. يمكن إعادة توزيع shecc
بحرية بموجب ترخيص البند BSD 2. يخضع استخدام كود المصدر هذا لترخيص نمط BSD الذي يمكن العثور عليه في ملف LICENSE
.