Custom LLVM Passes

تحتوي جوليا على عدد من التمريرات المخصصة لـ LLVM. بشكل عام، يمكن تصنيفها إلى تمريرات يجب تشغيلها للحفاظ على دلالات جوليا، وتمريرات تستفيد من دلالات جوليا لتحسين LLVM IR.

Semantic Passes

تُستخدم هذه التمريرات لتحويل LLVM IR إلى كود يمكن تشغيله على وحدة المعالجة المركزية. الغرض الرئيسي منها هو تمكين IR أبسط ليتم إصداره بواسطة codegen، مما يمكّن بعد ذلك تمريرات LLVM الأخرى من تحسين الأنماط الشائعة.

CPUFeatures

  • اسم الملف: llvm-cpufeatures.cpp
  • اسم الفئة: CPUFeaturesPass
  • اسم الخيار: module(CPUFeatures)

هذا التمرير يخفض الدالة julia.cpu.have_fma.(f32|f64) إلى إما true أو false، اعتمادًا على بنية الهدف والميزات المستهدفة الموجودة في الدالة. تُستخدم هذه الدالة غالبًا لتحديد ما إذا كان استخدام الخوارزميات المعتمدة على fused multiply-add العمليات أفضل من استخدام الخوارزميات القياسية غير المعتمدة على مثل هذه التعليمات.

DemoteFloat16

  • اسم الملف: llvm-demote-float16.cpp
  • ClassName: DemoteFloat16Pass
  • اسم الخيار function(DemoteFloat16)

يستبدل هذا التمرير عمليات float16 بعمليات float32 على المعماريات التي لا تدعم بشكل أصلي عمليات float16. يتم ذلك عن طريق إدراج تعليمات fpext و fptrunc حول أي عملية float16. على المعماريات التي تدعم عمليات float16 الأصلية، فإن هذا التمرير لا يقوم بأي عملية.

LateGCLowering

  • اسم الملف: llvm-late-gc-lowering.cpp
  • اسم الفصل: LateLowerGCPass
  • اسم الخيار: function(LateLowerGCFrame)

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

FinalGCLowering

  • اسم الملف: llvm-final-gc-lowering.cpp
  • اسم الفصل: FinalLowerGCPass
  • اسم الخيار: module(FinalLowerGC)

هذا التمرير يخفض بعض الدوال الداخلية الأخيرة إلى شكلها النهائي المستهدف في مكتبة libjulia. فصل هذا عن LateGCLowering يمكّن واجهات برمجة التطبيقات الأخرى (تجميع GPU) من تقديم تخفيضات مخصصة خاصة بها لهذه الدوال الداخلية، مما يمكّن خط أنابيب جوليا من الاستخدام على تلك الواجهات أيضًا.

LowerHandlers

  • اسم الملف: llvm-lower-handlers.cpp
  • اسم الفئة: LowerExcHandlersPass
  • اسم الخيار: function(LowerExcHandlers)

هذا التمرير يخفض الدوال الداخلية لمعالجة الاستثناءات إلى استدعاءات لدوال وقت التشغيل التي يتم استدعاؤها فعليًا عند معالجة الاستثناءات.

RemoveNI

  • اسم الملف: llvm-remove-ni.cpp
  • اسم الفصل: RemoveNIPass
  • اسم الخيار: module(RemoveNI)

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

SIMDLoop

  • اسم الملف: llvm-simdloop.cpp
  • اسم الفئة: LowerSIMDLoopPass
  • اسم الخيار: loop(LowerSIMDLoop)

هذا التمرير يعمل كالسائق الرئيسي لتعليمات @simd. يقوم توليد الشيفرة بإدراج علامة !llvm.loopid في الفرع الخلفي لحلقة، والتي يستخدمها هذا التمرير لتحديد الحلقات التي تم وضع علامة عليها في الأصل بـ @simd. بعد ذلك، يبحث هذا التمرير عن سلسلة من العمليات العائمة التي تشكل تقليصًا ويضيف علامات الرياضيات السريعة contract و reassoc للسماح بإعادة الترتيب (وبالتالي التوجيه). لا يحتفظ هذا التمرير بمعلومات الحلقة أو صحة الاستنتاج، لذا قد ينتهك دلالات جوليا بطرق مفاجئة. إذا كانت الحلقة قد تم وضع علامة عليها بـ ivdep أيضًا، فإن التمرير يحدد الحلقة على أنها لا تحتوي على تبعيات محمولة بالحلقات (السلوك الناتج غير محدد إذا كانت تعليمة المستخدم غير صحيحة أو تم تطبيقها على الحلقة الخاطئة).

LowerPTLS

  • اسم الملف: llvm-ptls.cpp
  • اسم الفئة: LowerPTLSPass
  • اسم الخيار: module(LowerPTLSPass)

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

إذا كان codegen ينتج دالة تحتوي على وسيط swiftself واتفاقية استدعاء، فإن هذه المرحلة تفترض أن وسيط swiftself هو pgcstack وستستبدل الدوال الداخلية بذلك الوسيط. يوفر القيام بذلك تسريعًا على المعماريات التي تحتوي على وصولات بطيئة للتخزين المحلي للموضوع.

RemoveAddrspaces

  • اسم الملف: llvm-remove-addrspaces.cpp
  • اسم الفئة: RemoveAddrspacesPass
  • اسم الخيار: module(RemoveAddrspaces)

هذا التمرير يعيد تسمية المؤشرات من مساحة عنوان إلى مساحة عنوان أخرى. يُستخدم ذلك لإزالة مساحات العنوان الخاصة بـ Julia من LLVM IR.

RemoveJuliaAddrspaces

  • اسم الملف: llvm-remove-addrspaces.cpp
  • اسم الفئة: RemoveJuliaAddrspacesPass
  • اسم الخيار: module(RemoveJuliaAddrspaces)

تقوم هذه العملية بإزالة مساحات العناوين الخاصة بـ Julia من LLVM IR. يتم استخدامها بشكل أساسي لعرض LLVM IR بتنسيق أقل ازدحامًا. داخليًا، يتم تنفيذها بناءً على عملية RemoveAddrspaces.

Multiversioning

  • اسم الملف: llvm-multiversioning.cpp
  • اسم الفئة: MultiVersioningPass
  • اسم الوحدة: module(JuliaMultiVersioning)

هذا المرور يقوم بإجراء تعديلات على وحدة لإنشاء وظائف محسّنة للتشغيل على معمارية مختلفة (انظر sysimg.md و pkgimg.md لمزيد من التفاصيل). من الناحية التنفيذية، يقوم بنسخ الوظائف وتطبيق سمات محددة الهدف مختلفة عليها للسماح للمحسّن باستخدام ميزات متقدمة مثل التوجيه والتخطيط التعليمي لتلك المنصة. كما أنه ينشئ بعض البنية التحتية لتمكين محمل صورة جوليا من اختيار النسخة المناسبة من الوظيفة للاستدعاء بناءً على المعمارية التي يعمل عليها المحمل. يتم التحكم في السمات المحددة الهدف بواسطة علامة وحدة julia.mv.specs، والتي يتم اشتقاقها أثناء التجميع من متغير البيئة JULIA_CPU_TARGET. يجب أيضًا تمكين المرور من خلال توفير علامة وحدة julia.mv.enable بقيمة 1.

Warning

استخدام llvmcall مع تعدد الإصدارات يعتبر خطيرًا. llvmcall يتيح الوصول إلى ميزات لا يتم الكشف عنها عادةً بواسطة واجهات برمجة التطبيقات في جوليا، وبالتالي فهي عادةً غير متاحة على جميع المعماريات. إذا تم تمكين تعدد الإصدارات وتم طلب توليد الشيفرة لمعمارية مستهدفة لا تدعم الميزة المطلوبة من تعبير llvmcall، فمن المحتمل أن يحدث خطأ في LLVM، على الأرجح مع إجهاض ورسالة LLVM ERROR: Do not know how to split the result of this operator!.

GCInvariantVerifier

  • اسم الملف: llvm-gc-invariant-verifier.cpp
  • اسم الفئة: GCInvariantVerifierPass
  • اسم الخيار: module(GCInvariantVerifier)

يتم استخدام هذه العملية للتحقق من ثوابت جوليا حول LLVM IR. يشمل ذلك أشياء مثل عدم وجود ptrtoint في non-integral address spaces [nislides] ووجود فقط تعليمات addrspacecast المباركة (Tracked -> Derived، 0 -> Tracked، إلخ). لا تقوم بإجراء أي تحويلات على IR.

Optimization Passes

تُستخدم هذه التمريرات لإجراء تحويلات على LLVM IR التي لن تقوم LLVM بتنفيذها بنفسها، مثل تمرير علامة الرياضيات السريعة، وتحليل الهروب، والتحسينات على الدوال الداخلية الخاصة بـ Julia. تستخدم هذه التمريرات المعرفة حول دلالات Julia لإجراء هذه التحسينات.

CombineMulAdd

  • اسم الملف: llvm-muladd.cpp
  • اسم الفئة: CombineMulAddPass
  • اسم الخيار: function(CombineMulAdd)

تعمل هذه العملية على تحسين التركيبة الخاصة من fmul العادية مع fadd السريعة إلى fmul تعاقدية مع fadd سريعة. يتم تحسين ذلك لاحقًا بواسطة الخلفية إلى تعليمة fused multiply-add، والتي يمكن أن توفر عمليات أسرع بشكل ملحوظ على حساب المزيد من unpredictable semantics.

Note

يحدث هذا التحسين فقط عندما يكون لـ fmul استخدام واحد فقط، وهو fadd السريع.

AllocOpt

  • اسم الملف: llvm-alloc-opt.cpp
  • اسم الفئة: AllocOptPass
  • اسم الخيار: function(AllocOpt)

جوليا لا تحتوي على مفهوم مكدس البرنامج كمكان لتخصيص الكائنات القابلة للتغيير. ومع ذلك، فإن تخصيص الكائنات على المكدس يقلل من ضغط جمع القمامة وهو أمر حاسم لتجميع GPU. وبالتالي، يقوم AllocOpt بتحويل الكائنات من الكومة إلى المكدس التي يمكنه إثبات أنها لا escape الوظيفة الحالية. كما أنه يقوم بعدد من التحسينات الأخرى على التخصيصات، مثل إزالة التخصيصات التي لا تستخدم أبدًا، وتحسين استدعاءات typeof للكائنات المخصصة حديثًا، وإزالة التخزينات للتخصيصات التي يتم الكتابة عليها على الفور. يتم تنفيذ تحليل الهروب في llvm-alloc-helpers.cpp. حاليًا، لا تستخدم هذه المرحلة المعلومات من EscapeAnalysis.jl، على الرغم من أن ذلك قد يتغير في المستقبل.

PropagateJuliaAddrspaces

  • اسم الملف: llvm-propagate-addrspaces.cpp
  • اسم الفئة: PropagateJuliaAddrspacesPass
  • اسم الخيار: function(PropagateJuliaAddrspaces)

يتم استخدام هذه العملية لنشر مساحات العنوان الخاصة بـ Julia من خلال العمليات على المؤشرات. لا يُسمح لـ LLVM بإدخال أو إزالة تعليمات addrspacecast من خلال التحسينات، لذا تعمل هذه العملية على القضاء على تحويلات addrspace الزائدة عن الحاجة من خلال استبدال العمليات بما يعادلها في مساحة عنوان Julia. لمزيد من المعلومات حول مساحات العنوان في Julia، انظر (TODO link to llvm.md).

JuliaLICM

  • اسم الملف: llvm-julia-licm.cpp
  • اسم الفئة: JuliaLICMPass
  • اسم الخيار: loop(JuliaLICM)

يتم استخدام هذه العملية لرفع الدوال الداخلية الخاصة بجوليا خارج الحلقات. على وجه التحديد، تقوم بتنفيذ التحولات التالية:

  1. قم برفع gc_preserve_begin وغمر gc_preserve_end خارج الحلقات عندما تكون الكائنات المحفوظة غير متغيرة داخل الحلقة.

    1. نظرًا لأن الكائنات المحفوظة داخل حلقة من المحتمل أن تبقى محفوظة طوال مدة الحلقة، يمكن أن تقلل هذه التحويلة من عدد أزواج gc_preserve_begin/gc_preserve_end في IR. وهذا يسهل على LateLowerGCPass تحديد الأماكن التي يتم فيها حفظ كائنات معينة.
  2. رفع حواجز الكتابة مع الكائنات الثابتة

    1. هنا نفترض أن هناك جيلين فقط يمكن أن يكون الكائن جزءًا منهما. بناءً على ذلك، يحتاج حاجز الكتابة إلى التنفيذ مرة واحدة فقط لأي زوج من نفس الكائن. وبالتالي، يمكننا رفع حواجز الكتابة خارج الحلقات عندما يكون الكائن الذي يتم الكتابة إليه غير متغير في الحلقة.
  3. قم برفع التخصيصات خارج الحلقات عندما لا تخرج من الحلقة

    1. نستخدم تعريفًا محافظًا جدًا للهروب هنا، نفس التعريف المستخدم في AllocOptPass. يمكن أن تقلل هذه التحويلة من عدد التخصيصات في IR، حتى عندما يهرب تخصيص من الدالة تمامًا.
Note

هذا التصريح مطلوب للحفاظ على MemorySSA (Short Video, Longer Video) و ScalarEvolution (Newer Slides Older Slides) التحليلات.

  • nislideshttps://llvm.org/devmtg/2015-02/slides/chisnall-pointers-not-int.pdf