
تم إنشاء 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"
} }<stdarg.h> . بدلاً من ذلك، تحقق من printf التنفيذ في المصدر lib/cc لـ var_arg . يمكن إعادة توزيع shecc بحرية بموجب ترخيص البند BSD 2. يخضع استخدام كود المصدر هذا لترخيص نمط BSD الذي يمكن العثور عليه في ملف LICENSE .