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.
استخدام 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.
يحدث هذا التحسين فقط عندما يكون لـ 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)
يتم استخدام هذه العملية لرفع الدوال الداخلية الخاصة بجوليا خارج الحلقات. على وجه التحديد، تقوم بتنفيذ التحولات التالية:
قم برفع
gc_preserve_begin
وغمرgc_preserve_end
خارج الحلقات عندما تكون الكائنات المحفوظة غير متغيرة داخل الحلقة.- نظرًا لأن الكائنات المحفوظة داخل حلقة من المحتمل أن تبقى محفوظة طوال مدة الحلقة، يمكن أن تقلل هذه التحويلة من عدد أزواج
gc_preserve_begin
/gc_preserve_end
في IR. وهذا يسهل علىLateLowerGCPass
تحديد الأماكن التي يتم فيها حفظ كائنات معينة.
- نظرًا لأن الكائنات المحفوظة داخل حلقة من المحتمل أن تبقى محفوظة طوال مدة الحلقة، يمكن أن تقلل هذه التحويلة من عدد أزواج
رفع حواجز الكتابة مع الكائنات الثابتة
- هنا نفترض أن هناك جيلين فقط يمكن أن يكون الكائن جزءًا منهما. بناءً على ذلك، يحتاج حاجز الكتابة إلى التنفيذ مرة واحدة فقط لأي زوج من نفس الكائن. وبالتالي، يمكننا رفع حواجز الكتابة خارج الحلقات عندما يكون الكائن الذي يتم الكتابة إليه غير متغير في الحلقة.
قم برفع التخصيصات خارج الحلقات عندما لا تخرج من الحلقة
- نستخدم تعريفًا محافظًا جدًا للهروب هنا، نفس التعريف المستخدم في
AllocOptPass
. يمكن أن تقلل هذه التحويلة من عدد التخصيصات في IR، حتى عندما يهرب تخصيص من الدالة تمامًا.
- نستخدم تعريفًا محافظًا جدًا للهروب هنا، نفس التعريف المستخدم في
هذا التصريح مطلوب للحفاظ على MemorySSA (Short Video, Longer Video) و ScalarEvolution (Newer Slides Older Slides) التحليلات.
- nislideshttps://llvm.org/devmtg/2015-02/slides/chisnall-pointers-not-int.pdf