CrossTL: Hub-Based Translation of GPU Programs and the Preservation of Source Rules
تقدم هذه الورقة CrossTL، وهو مترجم قائم على مركز توزيع مفتوح المصدر يوجه عشر لغات برمجة إلى أحد عشر هدفاً عبر لغة وسيطة حاملة للقواعد (CrossGL)، مما يثبت أن الحفاظ الصريح على القواعد الدلالية الخاصة بالمصدر يقلل بشكل كبير من التباينات في الترجمة مقارنة بالنهج القائم على بناء الجملة فقط، رغم أنه لا يمكنه ضمان الصحة بشكل كامل بسبب عدم توافق التنفيذ الأساسي ولا يقدم سوى مكاسب طفيفة في سهولة الاستخدام على الأكواد البرمجية الضخمة التابعة لجهات خارجية.
تعتمد ألعاب الفيديو الحديثة، والمحاكاة العلمية، والذكاء الاصطناعي جميعها على نوع خاص من برامج الكمبيوتر التي تعمل على معالجات الرسوميات. هذه الرقائق سريعة للغاية في إجراء العديد من الحسابات في وقت واحد، لكنها متطلبة للغاية بشأن كيفية تعاملها مع الأرقام. تستخدم الشركات المختلفة التي تصنع هذه الرقائق — مثل أبل، وإنفيديا، وإيه إم دي — لغات مختلفة لكتابة هذه البرامج. وبينما تبدو هذه اللغات متشابهة في الظاهر، إلا أنها غالباً ما تختلف في القواعد الأساسية للحساب. فعلى سبيل المثال، قد تنص لغة ما على أن جمع رقم مع نفسه عندما تكون النتيجة كبيرة جداً يجب أن يلتف حول رقم سالب صغير، بينما قد تنص لغة أخرى على أن هذا الإجراء غير محدد وقد يؤدي إلى أي شيء. إذا كتب مطور برنامجاً لنوع معين من الرقائق وحاول نقله إلى نوع آخر، يجب على أدوات الترجمة أن تقرر أي قاعدة ستتبع. وإذا أخطأت هذه الأدوات، فقد يتم تجميع البرنامج وتشغيله، ولكنه سينتج إجابة خاطئة، مما قد يؤدي إلى تعطل لعبة أو إفساد بيانات علمية.
وضع فريق من الباحثين حداً لهذه المشكلة من خلال بناء مترجم عالمي لهذه البرامج الرسومية. لقد أنشأوا نظاماً يسمى "CrossTL"، يعمل كمركز محوري. وبدلاً من بناء مترجم منفصل لكل زوج ممكن من اللغات، يقوم نظامهم بأخذ الكود من عشر لغات مصدر مختلفة وتحويله إلى تنسيق محايد واحد قبل إرساله إلى إحدى عشرة لغة هدف مختلفة. وكان التحدي الجوهري الذي واجهوه هو أن هذا التنسيق المحايد، بحد ذاته، لا يتذكر أي قاعدة محددة كان من المفترض أن يتبعها جزء من الكود. فهو يرى علامة جمع بسيطة، لكنه لا يستطيع التمييز ما إذا كانت هذه العلامة تعني "الالتفاف حول الرقم" أو "التوقف وإظهار خطأ". وبدون هذه الذاكرة، قد يغير المترجم معنى البرنامج عن طريق الخطأ، مما يؤدي إلى حالات فشل صامتة حيث يعمل الكود ولكنه يحسب نتائج خاطئة.
ولحل هذه المشكلة، طور الباحثون طريقة تسمى "حمل القواعد" (rule carrying). فقد قاموا بتعديل المترجم الخاص بهم ليرفق ملاحظة محددة بكل عملية رياضية يواجهها. تسجل هذه الملاحظة بالضبط أي قاعدة استخدمتها اللغة الأصلية لتلك العملية. وعندما ينتقل الكود عبر المركز ويتجه نحو لغة هدف جديدة، يتحقق المترجم من هذه الملاحظة. فإذا كانت لغة الهدف تتبع نفس القاعدة، فإنه يمرر العملية كما هي. أما إذا كانت لغة الهدف تتبع قاعدة مختلفة، فإن المترجم يستبدل العملية بقطعة كود مخصصة تجبر لغة الهدف على التصرف تماماً مثل اللغة المصدر. وهذا يضمن أن البرنامج يحسب النتيجة نفسها على الأجهزة الجديدة كما فعل على الأجهزة القديمة، بغض النظر عن الاختلافات في اللغات الأساسية.
اختبر الفريق هذا النظام بصرامة باستخدام مجموعة من اثنين وستين برنامج اختبار مصممة بعناية، تُعرف باسم "نواة المخاطر" (hazard kernels). صُممت هذه الاختبارات لإثارة القواعد المحددة التي تختلف فيها اللغات، مثل ما يحدث عند القسمة على صفر أو جمع أرقام كبيرة جداً. وقاموا بتشغيل هذه الاختبارات على مجموعة متنوعة من الأجهزة والبيئات البرمجية الواقعية، بما في ذلك معالجات الرسوميات من شركة أبل وعدة برامج تشغيل مفتوحة المصدر. وأظهرت النتائج أنه بدون طريقتهم الجديدة، تفقد أدوات الترجمة الحالية القواعد الصحيحة في حالات عديدة، مما يؤدي إلى مخرجات غير صحيحة. وعندما قام الباحثون بتفعيل ميزة "حمل القواعد"، نجح النظام في الحفاظ على السلوك الصحيح في كل الحالات تقريباً. ومن بين مئات عمليات الاختبار، انخفض عدد الأخطاء بشكل كبير، حيث تم إرجاع حالات الفشل القليلة المتبقية إلى خصائص معينة في كيفية تعامل بعض برامج التشغيل مع الكود، وليس إلى عيوب في منطق الترجمة نفسه.
كما بحثت الدراسة في كيفية أداء هذه الطالة الجديدة على آلاف البرامج الواقعية التي كتبها أطراف ثالثة، والتي تتراوح من مكتبات تعلم الآلة إلى محركات الرسوميات. ووجد الباحثون أنه بينما كان نظامهم الجديد أقل عرضة لإنتاج كود يقبله المترجم (compiler) مقارنة ببعض الأدوات الراسخة، إلا أنه كان أكثر موثوقية في ضمان أن الكود الذي يتم تجميعه يعمل بشكل صحيح بالفعل. وفي الواقع، لم يغير الأسلوب الجديد معدل نجاح الكود بأكثر من واحد بالمائة، مما أثبت إمكانية اعتماده دون كسر سير العمل الحالي. وخلص الباحثون إلى أنه لكي يكون المترجم أميناً حقاً، لا يمكنه مجرد نقل الصيغة (syntax) من لغة إلى أخرى، بل يجب أن يحمل القواعد الدلالية (semantic rules) للغة المصدر معه في الرحلة، لضمان بقاء معنى البرنامج سليماً طوال الطريق.
ملخص تقني: CrossTL: الترجمة القائمة على المحور لبرامج وحدة معالجة الرسومات والحفاظ على قواعد لغة المصدر
1. بيان المشكلة
تُكتب برامج وحدة معالجة الرسومات (GPU) بلغات خاصة بالموردين (مثل GLSL، وHLSL، وMetal Shading Language (MSL)، وCUDA، وOpenCL C) والتي تشترك في مفردات نحوية ولكنها تختلف بشكل كبير في قواعدها الدلالية (semantics). وتشمل هذه الاختلافات:
تجاوز السعة الصحيح (Integer Overflow): تُعرف لغات GLSL وWGSL تجاوز السعة الصحيح كعملية التفاف (wrapping)؛ بينما تعامل اللغات القائمة على C++ (مثل MSL وCUDA وOpenCL C) والمترجمات الحديثة لـ HLSL (مثل DXC) هذا التجاوز كـ "سلوك غير محدد" (undefined behavior - UB) أو سلوك سام (poison).
القسمة على صفر: تختلف القواعد بين إرجاع قيمة محددة (مثل 0xFFFFFFFF في Direct3D القديم)، أو إرجاع قيمة غير محددة، أو اعتبارها سلوكاً غير محدد.
تقلص الفاصلة العائمة (Floating-Point Contraction): تختلف السلوكيات الافتراضية فيما يتعلق بالضرب والجمع المدمج (FMA) والافتراضات حول قيم NaN/اللانهاية عبر اللغات وإعدادات المترجم المختلفة.
تخطيط البيانات (Data Layout): الخطوط (strides) للمخازن المؤقتة (مثل float3 في Metal مقابل HLSL) وقواعد محاذاة الذاكرة غير متسقة.
التحدي الجوهري: في بنية الترجمة القائمة على المحور (لغة وسيطة واحدة تربط M من المصادر بـ N من الأهداف)، فإن المترجم الذي يسجل فقط بناء عملية ما (syntax) لا يمكنه التمييز بين قواعد "الالتفاف" في لغة المصدر وقواعد "السلوك غير المحدد" في لغة الهدف. وبناءً على ذلك، قد ينجح البرنامج المترجم في التجميع على لغة الهدف ولكنه ينتج نتائج حسابية خاطئة (تباعد دلالي - semantic divergence). غالباً ما ترث الأدوات الموجودة دلالات لغة وسيطة واحدة (مثل دلالات الالتفاف في WGSL) أو تعتمد على أعلام (flags) خاصة بالهدف لا يتم تسجيلها أو فرضها دائماً، مما يؤدي إلى "سقوط كامن" (latent drops) حيث تُفقد الالتزامات بصمت.
2. المنهجية
يدرس المؤلفون CrossTL، وهو مترجم مفتوح المصدر من المصدر إلى المصدر (source-to-source)، يقوم باستيراد عشر لغات لوحدة معالجة الرسومات إلى لغة محور نصية، CrossGL، ثم يصدر إحدى عشرة لغة هدف.
2.1 بنية المحور
CrossGL: لغة من عائلة C مع امتدادات من Rust وWGSL. وهي تعمل كلغة تمثيل وسيط (IR) نصية.
المستوردات/المصدرات (Importers/Emitters): تقوم المستوردات عشرة بتحليل أشجار البناء النحوية الأصلية وطباعة لهجات CrossGL؛ وتقوم المصدرات الإحدى عشرة بعمل مسح لشجرة بناء CrossGL لتوليد كود الهدف.
القصور: المحور نصي وغير محدد النوع (untyped) فيما يتعلق بالقواعد الدلالية. لا يمكنه بطبيعته التمييز ما إذا كانت عملية مثل x + 1 تتبع قاعدة الالتفاف في GLSL أم قاعدة السلوك غير المحدد في MSL.
يعرف البحث الترجمة الأمينة بأنها "التحسين حتى الرفض" (refinement up to rejection) تحت تكوين تجميع وحالة مشفرة مسجلة.
الالتزامات (Obligations): يحمل البناء (construct) التزاماً إذا كان رسمه إلى نظيره في لغة الهدف دون تعديل سيؤدي إلى سلوك غير أمين (أي أن قاعدة الهدف لا تُحسّن/تُحدد قاعدة المصدر).
المعالجات (Treatments): يجب على المترجم التعامل مع الالتزامات عن طريق:
التفريغ (Discharging): إصدار بناء أو تكوين حيث تُحسّن قاعدة الهدف قاعدة المصدر.
التحقق (Checking): جعل المُحمّل (loader) يرفض الحالات التي لا يمكن فيها الوفاء بالالتزام.
**الافتراض (Assuming): تقييد مدخلات الفضاء أو افتراضات التنفيذ.
الرفض (Refusing): التوقف مع تشخيص (diagnostic).
الإسقاط (Dropping): عدم القيام بأي مما سبق (غير أمين).
2.3 ترجمة حمل القواعد (CrossTL-R)
لمعالجة عدم قدرة المحور على التمييز بين القواعد، يقترح المؤلفون حمل القواعد (rule carrying):
التعليق (Annotation): تمرير تعليق يحدد حالات حدوث العمليات ويعيد كتابتها إلى استدعاءات داخلية (intrinsics) متميزة (مثل __cgl_wrap_add) تمثل قاعدة المصدر المحددة.
التنزيل (Lowering): جدول قواعد لكل هدف يربط هذه الاستدعاءات الداخلية بـ:
الأصلية (Native - N): نظيرها في لغة الهدف (إذا كانت تُحسّن قاعدة المصدر).
الحل البديل (Polyfill - P): تنفيذ CrossGL باستخدام "قاعدة موثوقة" من العمليات (مثل إعادة تفسير البتات، والحسابات غير الموقعة) لمحاكاة قاعدة المصدر.
الرفض (Refusal - Ref): إذا لم يستطع الهدف التعبير عن القاعدة.
السلامة (Soundness): يقدم البحث نظرية تنص على أنه إذا كانت التعليقات سليمة وكاملة وجدول القواعد سليماً، فإن الترجمة تكون أمينة.
2.4 التقييم
يستخدم التقييم:
مجموعة اختبار المخاطر (Hazard Suite): 66 نواة (kernel) تغطي 18 فئة من الاختلافات الدلالية عبر ست لغات مصدر.
المتن (Corpus): 2,585 وحدة ترجمة من أطراف ثالثة من 12 مشروعاً (بما في ذلك MLX، وعينات Vulkan، وggml).
بيئات التشغيل (Runtimes): مترجم Apple Metal، وMoltenVK، وKosmicKrisp (برنامج تشغيل Vulkan متوافق)، وMesa's lavapipe/llvmpipe، وDXC.
التحقق الميكانيكي: يتحقق مُحل SMT (Z3) من سلامة مدخلات جدول القواعد مقابل مواصفات اللغات.
3. المساهمات الرئيسية
كتالوج دلالي (Semantic Catalogue): مواصفات مستمدة من الكتالوج لـ 18 فئة من اختلافات العمليات عبر ثماني لغات GPU (الجدول 1).
نموذج رسمي للأمانة: تعريف للترجمة الأمينة كتحسين حتى الرفض، مع التمييز بين الالتزامات التي يمكن تفريغها، أو التحقق منها، أو افتراضها، أو رفضها.
آلية حمل القواعد (CrossTL-R): نموذج وتنفيذ (CrossTL-R) يسجل قواعد المصدر عبر المحور. يثبت البحث أن حمل القواعد يحافظ على المعنى عندما يكون التعليق سليماً وكاملاً وجدول القواعد سليماً. كما يوضح متى يكفي وجود دلالات واحدة هي الأقوى مقابل ضرورة تسجيل قاعدة المصدر المحددة: بالنظر إلى إطار عمل دقيق، يجب أن تُحسّن كل بنية في الهدف قاعدة المصدر، ويجب على المحور الذي لا يرفض أن يسجل عملية المصدر بدقة حيث تتعارض القواعد المعرفة.
التنفيذ والتحليل: وصف لـ CrossTL، بما في ذلك monomorphiser لقوالب C++ الخاصة بـ Metal، وتحقق ميكانيكي من جدول القواعد باستخدام مُحل SMT، والذي حدد 13 مدخلاً غير سليم من أصل 131.
4. النتائج
الحفاظ على القواعد:
المترجمات القياسية (SPIRV-Cross، وnaga، وTint، وSlang، وCrossTL غير المعدل) تفقد قواعد المصدر في بيئات تشغيل مختلفة. على سبيل المثال، يفشل SPIRV-Cross وCrossTL في الحفاظ على قواعد الالتفاف في Metal لأن مترجم Metal يختزل x + 1 > x إلى true بناءً على افتراضات تجاوز السعة غير المحدد.
CrossTL-R يزيل 32 من أصل 37 تباعداً في القواعد المحمولة. الـ 5 تباعدات المتبقية تُعزى إلى فشل مكدس وقت التشغيل (runtime stack failures) (مثل خطأ في التجميع في MolventVK وKosmicKrisp عبر معاملة الجمع غير الموقع كجمع موقع).
في 462 نواة عشوائية محجوزة، سجل CrossTL-R تحت الكتالوج المصححصفر تباعدات، بينما تباعد CrossTL غير المعدل في 120 عملية تنفيذ.
فحص السلامة: وجد فحص SMT أن 13 من أصل 131 مدخلاً في جدول القواعد غير سليم كما هو مسجل (مثل التعامل مع NaN في SPIR-V بدون خصائص جهاز محددة).
الصلاحية على كود الطرف الثالث:
تنتج المترجمات الراسخة كود رسوميات صالحاً بشكل أكبر من CrossTL في 31 من أصل 39 مقارنة في المتن.
معدل الترجمة القابلة للاستخدام لـ CrossTL هو حوالي 31%.
تغيير حمل القواعد (CrossTL-R) يغير حصة الأهداف المقبولة بمقدار 0.2 نقطة مئوية فقط، مما يشير إلى أن حمل القواعد لا يقلل بشكل كبير من الصلاحية النحوية.
التكاليف:
يضيف حمل القواعد تكلفة زمن تشغيل ضئيلة جداً على Metal لمعظم العمليات (الحلول البديلة تُترجم إلى عمليات عتادية).
ومع ذلك، فإن استخدام "دلالات واحدة هي الأقوى" لجميع المصادر سيتطلب ما يقرب من 9 أضعاف عمليات إعادة الكتابة (السيول البديلة/polyfills) مقارنة بحمل قواعد المصدر المحددة، خاصة للغات مثل CUDA وHIP حيث يكون التجاوز الصحي غير محدد.
5. الأهمية والادعاءات
يزعم البحث أن تسجيل قاعدة لغة المصدر لكل عملية كافٍ للحفاظ على المعنى في مترجم قائم على المحور، بشرط أن يكون التعليق سليماً وكاملاً وأن يكون جدول القواعد سليماً.
الضرورة: عندما تتعارض قواعد المصدر (مثل الالتفاف مقابل السلوك غير المحدد) ولا يوجد تحسين مشترك، فإن المحور الذي لا يرفض يجب أن يسجل قاعدة المصدر المحددة؛ إن "دلالة واحدة هي الأقوة" غير كافية وتؤدي إلى عمليات إعادة كتابة أو رفض غير ضرورية.
الكفاية: إذا كانت التعليقات سليمة وكاملة والجدول سليماً، فإن الأمانة مضمونة.
الأثر العملي: تسلط الدراسة الضوء على أن حتى برامج التشغيل "المتوافقة" (مثل KosmicKrisp) يمكن أن تخطئ في تجميع الكود الصحيح إذا كان المترجم يعتمد على افتراضات (مثل الالتفاف) لا يفرضها مكدس وقت التشغيل. لذلك، يجب ألا يكتفي المترجم الأمين بحمل القواعد فحسب، بل يجب أيضاً تسجيل التكوين المطلوب واختباره على مكدسات وقت التشغيل المحددة لمستخدميه.
يظل المؤلفون متواضعين، مشيرين إلى أن تنفيذهم ليس موثقاً رسمياً، وأن أعداد التباعد هي حدود دنيا للمدخلات المختبرة، وأن التقييم محدود لأجهزة معينة (Apple Silicon) وبرامج تشغيل CPU. ويؤكد العمل الادعاءات السابقة بأن CrossGL يلتقط دلالات جميع اللغات، مع الإقرار بأنه بدون حمل القواعد، يفقد المحور التمايزات الدلالية الحرجة.