مُجمِّع خطي

في برمجة الحاسوب ، يعتبر المجمع المضمن ميزة لبعض المترجمات التي تسمح بتضمين التعليمات البرمجية منخفضة المستوى المكتوبة بلغة التجميع داخل البرنامج، من بين التعليمات البرمجية التي تم تجميعها من لغة ذات مستوى أعلى مثل C أو Ada .

الدافع والبدائل

يتم تضمين كود لغة التجميع عادةً لأحد هذه الأسباب: [ 1 ]

من جهة أخرى، يُشكّل المُجمِّع المُضمَّن مشكلةً مباشرةً للمُصرِّف نفسه، إذ يُعقِّد تحليل ما يُجرى على كل مُتغير، وهو جزء أساسي من تخصيص المُسجِّلات . وهذا يعني أن الأداء قد ينخفض ​​فعليًا. كما يُعقِّد المُجمِّع المُضمَّن عمليات نقل البرنامج وصيانته في المستقبل. [ 1 ]

تُوفّر عادةً تسهيلات بديلة لتبسيط العمل لكلٍّ من المُصرّف والمبرمج. وتُوفّر معظم المُصرّفات دوالًا مُضمّنة للتعليمات الخاصة، كما تتوفر أغلفة دوال C لاستدعاءات النظام المُختلفة على جميع منصات يونكس .

بناء الجملة

في معايير اللغة

يحدد معيار ISO C++ ومعايير ISO C (الملحق J) صيغة مدعومة شرطيًا للمجمع المضمن:

يأخذ تعريف لغة التجميع الشكل التالي : asm-declaration : asm ( string-literal ) ; تعريف لغة التجميع مدعوم بشكل مشروط؛ ومعناه محدد من قبل التنفيذ. [ 2 ]        

ومع ذلك، نادرًا ما يتم استخدام هذا التعريف في لغة C الفعلية، لأنه في نفس الوقت متساهل للغاية (في التفسير) ومقيد للغاية (في استخدام سلسلة حرفية واحدة فقط).

في المترجمات الفعلية

في الاستخدام العملي، نادرًا ما يكون كود التجميع المضمن الذي يتعامل مع القيم مستقلًا ككود حر. ولأن المبرمج لا يستطيع التنبؤ بالسجل الذي تُسند إليه قيمة المتغير، فإن المترجمات عادةً ما توفر طريقة لاستبدالها كامتداد.

بشكل عام، هناك نوعان من التجميع المضمن تدعمهما مترجمات لغة C/C++:

  • يُستخدم asm (أو __asm__ ) في GCC . يستخدم GCC امتدادًا مباشرًا لقواعد ISO: يُكتب قالب كود التجميع كسلاسل نصية، مع تحديد المدخلات والمخرجات والسجلات المُعدّلة بعد السلاسل النصية بين نقطتين رأسيتين. تُستخدم متغيرات C مباشرةً، بينما تُقتبس أسماء السجلات كسلاسل نصية حرفية. [ 3 ]
  • تُستخدم صيغة __asm ​​في Microsoft Visual C++ (MSVC) ومترجم Borland/Embarcadero C، وما يندرج تحتها. لا تعتمد هذه الصيغة على قواعد ISO إطلاقًا؛ إذ يكتب المبرمجون ببساطة كود ASM داخل كتلة دون الحاجة إلى الالتزام بصيغة C. وتُستخدم المتغيرات كما لو كانت سجلات، ويُسمح باستخدام بعض تعابير C. [ 4 ] كان مُترجم ARM يحتوي على ميزة مماثلة. [ 5 ]

تمثل عائلتا الامتدادات فهمين مختلفين لتقسيم العمل في معالجة لغة التجميع المضمنة. يحافظ نموذج GCC على البنية العامة للغة ويفصل ما يحتاج المترجم إلى معرفته: ما هو مطلوب وما هو مُعدّل. لا يتطلب هذا النموذج صراحةً من المترجم فهم أسماء التعليمات، إذ يكفي أن يستبدل المترجم تعيينات السجلات، بالإضافة إلى بعض عمليات النقل (mov) ، للتعامل مع متطلبات الإدخال. مع ذلك، يكون المستخدم عرضةً لتحديد السجلات المُعدّلة بشكل خاطئ. يوفر نموذج MSVC للغة مجال مُضمّنة سهولة في الكتابة، ولكنه يتطلب من المترجم نفسه معرفة أسماء رموز العمليات وخصائص تعديلها، مما يستلزم عنايةً إضافيةً في الصيانة والنقل. [ 6 ] لا يزال من الممكن التحقق من أخطاء التعديل في لغة التجميع بنمط GCC بمعرفة مجموعة التعليمات. [ 7 ]

يستخدم كل من GNAT (واجهة لغة Ada الأمامية لمجموعة GCC) و LLVM صيغة GCC. [ 8 ] [ 9 ] تستخدم لغة البرمجة D لغة برمجة خاصة بالمجال (DSL) مشابهة لامتداد MSVC رسميًا لمعالجات x86_64، [ 10 ] ولكن LDC المبني على LLVM يوفر أيضًا صيغة GCC على جميع البنى. [ 11 ] يدعم MSVC التجميع المضمن فقط على معالجات x86 ذات 32 بت. [ 4 ]

انتقلت لغة Rust منذ ذلك الحين إلى صيغة تُجرّد خيارات التجميع المضمنة بشكل أكبر من إصدار LLVM (نمط GCC). وهي توفر معلومات كافية للسماح بتحويل الكتلة إلى دالة مُجمّعة خارجيًا إذا لم يكن بإمكان الواجهة الخلفية التعامل مع التجميع المضمن. [ 6 ]

أمثلة

استدعاء نظام في GCC

لا يُمكن عمومًا استدعاء نظام التشغيل مباشرةً في نظام يستخدم ذاكرة محمية. يعمل نظام التشغيل بمستوى صلاحيات أعلى (وضع النواة) من المستخدم (وضع المستخدم)؛ ويُستخدم مقاطعة (برمجية) لإرسال الطلبات إلى نظام التشغيل. نادرًا ما تكون هذه ميزة في لغات البرمجة عالية المستوى، ولذلك تُكتب دوال التغليف لاستدعاءات النظام باستخدام لغة التجميع المضمنة.

يوضح مثال كود C التالي غلاف استدعاء نظام x86 بصيغة مُجمِّع AT&T ، باستخدام مُجمِّع GNU . تُكتب هذه الاستدعاءات عادةً باستخدام وحدات الماكرو؛ وقد أُدرج الكود الكامل للتوضيح. في هذه الحالة تحديدًا، يُنفِّذ الغلاف استدعاء نظام لرقم يُحدده المُستدعي بثلاثة مُعاملات، ثم يُعيد النتيجة. [ 12 ]

باختصار، يدعم GCC كلاً من التجميع الأساسي والتجميع الموسع . يقوم التجميع الأساسي ببساطة بتمرير النص حرفياً إلى المُجمِّع، بينما يقوم التجميع الموسع بإجراء بعض عمليات الاستبدال لمواقع السجلات. [ 3 ]

#include <errno.h>int syscall3 ( int num , int arg1 , int arg2 , int arg3 ) { int res = 0 ; asm ( "int $0x80" // إرسال الطلب إلى نظام التشغيل : "=a" ( res ), // إرجاع النتيجة في eax ("a") "+b" ( arg1 ), // تمرير arg1 في ebx ("b") [كإخراج "+" لأن استدعاء النظام قد يغيره] "+c" ( arg2 ), // تمرير arg2 في ecx ("c") [كذلك] "+d" ( arg3 ) // تمرير arg3 في edx ("d") [كذلك] : "a" ( num ) // تمرير رقم استدعاء النظام في eax ("a") : "memory" , "cc" , // إبلاغ المُصرّف بأنه قد تم تعديل رموز الذاكرة والحالة "esi" , "edi" , "ebp" // يتم تغيير هذه السجلات أيضًا [بواسطة استدعاء النظام] );// سيعيد نظام التشغيل قيمة سالبة عند حدوث خطأ؛ // ​​تُعيد الدوال المُغلِّفة القيمة -1 عند حدوث خطأ وتُعيِّن المتغير العام errno إذا ( -125 <= res && res < 0 ) { errno = -res ; res = -1 ; } return res ; }

تعليمات خاصة بالمعالج في لغة D

يوضح هذا المثال للتجميع المضمن من لغة البرمجة D التعليمات البرمجية التي تحسب ظل الزاوية x باستخدام تعليمات FPU الخاصة بـ x86 ( x87 ).

// حساب ظل الزاوية x حقيقي tan ( حقيقي x ) { asm { fld x [ EBP ] ; // تحميل x fxam ; // اختبار القيم الشاذة fstsw AX ; sahf ; jc trigerr ; // C0 = 1: x هي NAN أو ما لا نهاية أو فارغة // يمكن لـ 387 التعامل مع القيم غير الطبيعية SC18 : fptan ; fstp ST ( 0 ) ; // تفريغ X، وهي دائمًا 1 fstsw AX ; sahf ; // إذا لم يكن (fp_status & 0x20)) انتقل إلى Lret jnp Lret ; // C2 = 1: x خارج النطاق، قم باختزال الوسيط fldpi ; // تحميل π fxch ; SC17 : fprem1 ; // التذكير (جزئي) fstsw AX ; sahf ; jp SC17 ; // C2 = 1: تذكير جزئي، يلزم تكرار fstp ST ( 1 ) ; // إزالة باي من المكدس jmp SC18 ; } trigerr : return real . nan ; Lret : // لا حاجة لإرجاع أي شيء يدويًا لأن القيمة موجودة بالفعل في مكدس الفاصلة العائمة ; }

يُستخدم أسلوب fstsw-sahf متبوعًا بقفزة شرطية للوصول إلى بتات كلمة حالة وحدة المعالجة النقطية x87، وهما C0 وC2. يخزن الأمر fstsw الحالة في سجل للأغراض العامة؛ ويقوم الأمر sahf بتعيين سجل FLAGS إلى البتات الثمانية العليا من السجل؛ وتُستخدم القفزة للتحقق من أي بت علم يصادف أنه يطابق بت حالة وحدة المعالجة النقطية. [ 13 ]

مراجع

  1. 1 2 "لا تستخدم InlineAsm" . ويكي GCC . تم الاطلاع عليه بتاريخ 21 يناير 2020 .
  2. C++، [dcl.asm]
  3. 1 2 "تعليمات التجميع الموسعة - تعليمات التجميع مع معاملات تعبير C" . باستخدام مُصرّف GNU C. تم الاسترجاع في 15 يناير 2020 .
  4. 1 2 "المجمع المضمن" . docs.microsoft.com . 3 أغسطس 2021.
  5. "دليل الترحيل والتوافق: التجميع المضمن مع Arm Compiler 6" .
  6. 1 2 دانتراس، أمانيو (13 ديسمبر 2019). "Rust RFC-2873: stable inline asm" . تم الاطلاع عليه في 15 يناير 2020. ومع ذلك ، من الممكن تنفيذ دعم التجميع المضمن دون دعم من الواجهة الخلفية للمترجم باستخدام مُجمِّع خارجي بدلاً من ذلك.طلب سحب لتتبع الحالة
  7. "⚙ D54891 [ RFC ] التحقق من صحة التجميع المضمن" . reviews.llvm.org .
  8. "مرجع لغة LLVM: تعابير التجميع المضمنة" . وثائق LLVM . تم الاطلاع عليه بتاريخ 15 يناير 2020 .
  9. "التجميع المضمن" . وثائق Rust (1.0.0) . تم الاطلاع عليه بتاريخ 15 يناير 2020 .
  10. "المجمع المضمن" . لغة البرمجة D. تم الاطلاع عليه بتاريخ 15 يناير 2020 .
  11. "تعبيرات التجميع المضمنة في لغة LDC" . ويكيبيديا . تم الاطلاع عليه بتاريخ 15 يناير 2020 .
  12. دليل مبرمج لينكس – استدعاءات النظام من Manned.orgsyscall(2)  
  13. "FSTSW/FNSTSW — تخزين كلمة حالة وحدة الفاصلة العائمة x87" . يُستخدم شكل FNSTSW AX من التعليمات بشكل أساسي في التفرع الشرطي...