High-level Overview of the Native-Code Generation Process

Representation of Pointers

عند إصدار الشيفرة إلى ملف كائن، سيتم إصدار المؤشرات كإعادة موضع. ستضمن شيفرة إلغاء التسلسل أن أي كائن كان يشير إلى أحد هذه الثوابت سيتم إعادة إنشائه ويحتوي على المؤشر الصحيح في وقت التشغيل.

بخلاف ذلك، سيتم إصدارها كثوابت حرفية.

لإصدار أحد هذه الكائنات، قم باستدعاء literal_pointer_val. سيتولى تتبع قيمة جوليا و LLVM global، مما يضمن أنها صالحة لكل من وقت التشغيل الحالي وبعد إلغاء التسلسل.

عند إصدارها في ملف الكائن، يتم تخزين هذه المتغيرات العالمية كمرجع في جدول كبير gvals. وهذا يسمح لعملية فك التسلسل بالإشارة إليها بواسطة الفهرس، وتنفيذ آلية يدوية مخصصة مشابهة لجدول الإزاحة العالمية (GOT) لاستعادتها.

تتم معالجة مؤشرات الدوال بشكل مشابه. يتم تخزينها كقيم في جدول كبير يسمى fvals. مثل المتغيرات العالمية، يتيح ذلك لمزيل التسلسل الإشارة إليها بواسطة الفهرس.

لاحظ أن الدوال extern يتم التعامل معها بشكل منفصل، مع الأسماء، من خلال آلية حل الرموز المعتادة في الرابط.

لاحظ أيضًا أن دوال ccall يتم التعامل معها بشكل منفصل، من خلال جدول الارتباط العام اليدوي (GOT) وجدول ربط الإجراءات (PLT).

Representation of Intermediate Values

تُمرر القيم في هيكل jl_cgval_t. يمثل هذا قيمة R، ويتضمن معلومات كافية لتحديد كيفية تعيينها أو تمريرها إلى مكان ما.

يتم إنشاؤها من خلال أحد البناة المساعدة، عادةً: mark_julia_type (للقيم الفورية) و mark_julia_slot (للمؤشرات إلى القيم).

تقوم الدالة convert_julia_type بتحويل أي نوعين بينهما. تعيد قيمة R مع تعيين cgval.typ إلى typ. ستقوم بتحويل الكائن إلى التمثيل المطلوب، مما يجعل صناديق heap، ويخصص نسخًا على المكدس، ويحسب الاتحادات المعلّمة حسب الحاجة لتغيير التمثيل.

بالمقابل، ستقوم update_julia_type بتغيير cgval.typ إلى typ، فقط إذا كان ذلك ممكنًا بتكلفة صفرية (أي دون إصدار أي كود).

Union representation

يمكن أن يتم تخصيص أنواع الاتحاد المستنتجة على المكدس من خلال تمثيل نوع مُوسم.

الروتينات البدائية التي تحتاج إلى القدرة على التعامل مع الاتحادات المميزة هي:

  • نوع العلامة
  • تحميل محلي
  • store-local
  • isa
  • هو
  • emit_typeof
  • emit_sizeof
  • مربّع
  • فتح الصندوق
  • متخصص cc-ret

يجب أن يكون كل شيء آخر ممكنًا التعامل معه في الاستدلال باستخدام هذه البدائيات لتنفيذ تقسيم الاتحاد.

تمثيل الاتحاد المسمى هو كزوج من < void* union, byte selector >. يتم تحديد حجم المحدد كـ byte & 0x7f، وسيقوم بتسمية الاتحاد لأوائل 126 من عناصر isbits. يسجل العدد القائم على العمق الأول (one-based depth-first count) في نوع الاتحاد لعناصر isbits الموجودة بالداخل. يشير فهرس الصفر إلى أن union* هو في الواقع jl_value_t* مخصص في الذاكرة، ويحتاج إلى أن يُعامل ككائن مُعبأ عادي بدلاً من كاتحاد مُسمى.

يمكن اختبار البت العالي من المحدد (byte & 0x80) لتحديد ما إذا كان void* هو في الواقع صندوق مخصص في الكومة (jl_value_t*)، مما يتجنب تكلفة إعادة تخصيص صندوق، مع الحفاظ على القدرة على التعامل بكفاءة مع تقسيم الاتحاد بناءً على البتات المنخفضة.

من المضمون أن byte & 0x7f هو اختبار دقيق لنوع البيانات، إذا كان يمكن تمثيل القيمة بواسطة علامة – فلن يتم وضع علامة byte = 0x80. ليس من الضروري أيضًا اختبار علامة النوع عند اختبار isa.

قد يتم تخصيص منطقة الذاكرة union* بأي حجم. القيد الوحيد هو أنه يجب أن يكون كبيرًا بما يكفي لاحتواء البيانات المحددة حاليًا بواسطة selector. قد لا يكون كبيرًا بما يكفي لاحتواء الاتحاد لجميع الأنواع التي يمكن تخزينها هناك وفقًا لحقل نوع الاتحاد المرتبط. استخدم العناية المناسبة عند النسخ.

Specialized Calling Convention Signature Representation

يصف كائن jl_returninfo_t تفاصيل اتفاقية الاستدعاء لأي قابل للاستدعاء.

إذا كان أي من المعاملات أو نوع الإرجاع لطريقة ما يمكن تمثيله بدون تغليف، وكانت الطريقة ليست varargs، فسيتم منحها توقيع اتفاقية استدعاء محسّن بناءً على حقول specTypes و rettype.

المبادئ العامة هي أن:

  • تُمرر الأنواع البدائية في سجلات int/float.
  • تُمرر مجموعات من أنواع VecElement في سجلات المتجهات.
  • تُمرر الهياكل على المكدس.
  • تُعالج قيم الإرجاع بشكل مشابه للوسائط، مع حد حجم يتم عنده إرجاعها بدلاً من ذلك عبر وسيط مخفي sret.

المنطق الكلي لذلك يتم تنفيذه بواسطة get_specsig_function و deserves_sret.

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