اللغات: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
← واجهة NeverC الثنائية للإضافات
تكشف PluginIR.h تمثيلَ LLVM الوسيط عبر ستة جداول قدرات ومخطَّطٍ مولَّد. تستطيع
الإضافة أن تقرأ التمثيل الوسيط وتعيد كتابته، وأن تسجّل تمريرات في خمس نقاط مستقرة
من خط الأنابيب، وأن تعرّف تحليلاتها الخاصة، أو أن تستبدل توليدَ التمثيل الوسيط
وخط أنابيب التحسين بالكامل — دون أن تضمّ ملفًا ترويسيًا واحدًا من LLVM.
رموز العمليات وأنواع الأنماط وخصائص التعليمات كلها معرّفات مخطَّط مستقرة، لا قيم تعدادية من LLVM. وهذه الوساطة هي ما يجعل إضافةً تُترجم اليوم تظل عاملةً حين ينتقل المضيف إلى إصدار LLVM جديد.
#include "neverc/Plugin/PluginIR.h"| الواجهة | الجدول | الفتحات | الغرض |
|---|---|---|---|
NEVERC_INTERFACE_IR_CORE_{HIGH,LOW} |
NevercIRCoreAPI |
99 | قراءة وتحرير الوحدات والقيم والأنواع والثوابت والبيانات الوصفية والسمات |
NEVERC_INTERFACE_IR_BUILDER_{HIGH,LOW} |
NevercIRBuilderAPI |
29 | البناء المعامَلاتي |
NEVERC_INTERFACE_IR_ANALYSIS_{HIGH,LOW} |
NevercIRAnalysisAPI |
13 | التحليلات المدمجة وتحليلات الإضافات |
NEVERC_INTERFACE_IR_PASS_{HIGH,LOW} |
NevercIRPassAPI |
1 | RegisterPass |
NEVERC_INTERFACE_IR_GEN_{HIGH,LOW} |
NevercIRGenAPI |
5 | استبدال الخفض من SemanticUnit إلى التمثيل الوسيط |
NEVERC_INTERFACE_IR_OPTIMIZATION_{HIGH,LOW} |
NevercIROptimizationAPI |
7 | استبدال خط أنابيب التحسين بأسره |
كلٌّ منها NEVERC_INTERFACE_STABLE عند الرقم الرئيسي 1. تفاوض باستخدام
NEVERC_IR_*_API_MAJOR / _MINOR المقابلة، وتحقّق أن TableSize يبلغ آخر فتحة
تستدعيها، تمامًا كما يفعل pluginsdk/examples/FunctionPass.c:
Status = Bootstrap->QueryInterface(
Bootstrap->Context,
(NevercInterfaceID){NEVERC_INTERFACE_IR_PASS_HIGH,
NEVERC_INTERFACE_IR_PASS_LOW},
NEVERC_IR_PASS_API_MAJOR, NEVERC_IR_PASS_API_MINOR, &Table, &Minor,
&StructSize);
if (!Table ||
StructSize < offsetof(NevercIRPassAPI, RegisterPass) +
sizeof(((NevercIRPassAPI *)0)->RegisterPass))
return fail(NEVERC_STATUS_ABI_MISMATCH);ثماني مراحل للتمثيل الوسيط:
| المرحلة | السياسة |
|---|---|
neverc.ir.generate |
OBSERVABLE، INTERCEPTABLE، REPLACEABLE |
neverc.ir.optimize |
OBSERVABLE، INTERCEPTABLE، REPLACEABLE |
neverc.ir.pass.pre_opt |
OBSERVABLE، INTERCEPTABLE |
neverc.ir.pass.pipeline_start |
OBSERVABLE، INTERCEPTABLE |
neverc.ir.pass.optimizer_last |
OBSERVABLE، INTERCEPTABLE |
neverc.ir.pass.post_opt |
OBSERVABLE، INTERCEPTABLE |
neverc.ir.pass.pre_codegen |
OBSERVABLE، INTERCEPTABLE |
neverc.ir.final_verify |
OBSERVABLE، بوابة مضيف مختومة |
مراحل pass.* الخمس هي ما يشير إليه NevercIRPassDescriptor.Phase. وتشغّل
neverc.ir.final_verify مدقّقَ LLVM، ولا يستطيع أي شيء اعتراضها أو استبدالها أو
تخطّيها — بما في ذلك مزوّد التحسين.
Schema/PluginIRSchema.inc مولَّد ويضمّه PluginIR.h. وهو ينشر بصمةً ومجموعات
الثوابت التالية:
#define NEVERC_IR_SCHEMA_CAPABILITY_MAJOR UINT16_C(1)
#define NEVERC_IR_SCHEMA_DIGEST "4302919d…"
#define NEVERC_IR_TYPE_KIND_COUNT UINT32_C(22)
#define NEVERC_IR_VALUE_KIND_COUNT UINT32_C(29)
#define NEVERC_IR_OPCODE_COUNT UINT32_C(67)
#define NEVERC_IR_PREDICATE_COUNT UINT32_C(26)
#define NEVERC_IR_LINKAGE_COUNT UINT32_C(11)
#define NEVERC_IR_CALLING_CONVENTION_COUNT UINT32_C(21)
#define NEVERC_IR_PROPERTY_COUNT UINT32_C(23)تُوسَم المعرّفات بمجالها في البايت الأعلى — 0x41…… للأنواع، و0x42…… لأنواع
القيم، و0x43…… لرموز العمليات، و0x49…… للخصائص — فالقيمة المستعملة في غير
موضعها تُرفض بدل أن تُقرأ خطأً.
مقابض التمثيل الوسيط أزواج {Owner, Value} معتِمة نطاقها مهمة واحدة، وكل ما
وراءها مملوك للمضيف.
- لا تحتفظ بمقبض بعد انتهاء ردّ ندائه أو مهمته.
- لا تستعمل مقبضًا في جلسة أو مهمة أخرى.
- الاستبدال المودَع يُبطل مقابض الأشياء التي استُبدلت.
- التعديل المُجهَض يجعل المقابض التي أنشأها بائدة.
- الأخطاء هي
NEVERC_STATUS_STALE_HANDLEأوWRONG_SCOPEأوWRONG_TYPE— ولن يكون الناتج أبدًا مؤشر LLVM خامًا.
السلاسل ورؤى البايتات العائدة من استعلامٍ ما مستعارة طوال ردّ النداء. والاستثناء
الوحيد هو ExportModule، فهو يعيد NevercIRSerializedBufferHandle يجب أن
تعيده إلى ReleaseSerializedBuffer.
تُقرأ المجموعات عبر مؤشر يحمل جيلَه الخاص، فيُكتشف أي تعديل يقع في منتصف الاجتياز بدل أن تُتخطّى المدخلات صمتًا:
NevercIRValueCursor Cursor = {0};
Cursor.Header = (NevercABITableHeader){sizeof(Cursor),
NEVERC_IR_CORE_API_MAJOR,
NEVERC_IR_CORE_API_MINOR, 0};
Core->BeginValueCursor(Core->Context, Task, Module,
NEVERC_IR_COLLECTION_MODULE_FUNCTIONS, &Cursor);
NevercIRValueHandle Batch[32];
uint64_t Count = 0;
for (;;) {
Core->CollectValueCursor(Core->Context, Task, &Cursor, Batch, 32, &Count);
if (Count == 0)
break;
for (uint64_t I = 0; I != Count; ++I) {
NevercStringView Name;
Core->GetValueName(Core->Context, Task, Batch[I], &Name);
}
}كرّر حتى يعود Count صفرًا. والمجموعات السبع هي MODULE_FUNCTIONS
وMODULE_GLOBALS وMODULE_ALIASES وMODULE_I_FUNCS وFUNCTION_ARGUMENTS
وFUNCTION_BLOCKS وBLOCK_INSTRUCTIONS.
وما عدا ذلك استعلامٌ مباشر: GetValueKind وGetValueType
وGetOperandCount / GetOperand / SetOperand وGetValueUseCount /
GetValueUse وGetTerminator وGetPredecessor* وGetSuccessor*
وGetPHIIncoming*، وعلى مستوى الوحدة GetModuleIdentifier
وGetModuleTargetTriple وGetModuleDataLayout وGetModuleInlineAssembly مع
ضوابطها.
الأنواع مُدمَجة داخليًا، فالسؤال مرتين يعطي المقبض نفسه:
NevercIRTypeHandle I32, Ptr, Fn;
Core->GetIntegerType(Core->Context, Task, 32, &I32);
Core->GetPointerType(Core->Context, Task, /*AddressSpace=*/0, &Ptr);
NevercIRTypeHandle Params[] = {I32, Ptr};
Core->GetFunctionType(Core->Context, Task, I32, Params, 2,
/*Variadic=*/0, &Fn);تأخذ GetPrimitiveType نوعَ مخطَّط مثل NEVERC_IR_TYPE_VOID أو _FLOAT أو
_DOUBLE أو _TOKEN، بينما تغطي البقيةَ GetArrayType وGetVectorType (مع
راية Scalable) وGetStructType (مسمّاة أو حرفية، محزومة أو لا).
تُبنى الثوابت الصحيحة والعائمة من كلمات 64 بتًا صغيرة النهاية، فلا يحتاج i128
مسارًا خاصًا:
uint64_t Words[2] = {0xFFFFFFFFFFFFFFFFULL, 0x1ULL};
NevercIRValueHandle C;
Core->CreateIntegerConstant(Core->Context, Task, I128, Words, 2, &C);وتغطي GetNullConstant وGetPoisonConstant وGetUndefConstant
وCreateAggregateConstant وGetGlobalAddressConstant الحالاتِ البسيطة، بينما
تبني CreateConstantBinaryExpression وCreateConstantCastExpression
وCreateConstantCompareExpression وCreateConstantGEPExpression التعابيرَ
الثابتة.
بدل موصِّل لكل راية، تمرّ تفاصيل التعليمة عبر قيمة خاصية موسومة مفتاحُها معرّف المخطَّط:
typedef struct NevercIRPropertyValue {
NevercABITableHeader Header;
NevercIRPropertyValueKind Kind; /* BOOL, UINT, ENUM, FLAGS, STRING, TYPE */
uint32_t Reserved;
uint64_t UnsignedValue;
NevercIRTypeHandle TypeValue;
NevercStringView StringValue;
} NevercIRPropertyValue;
NevercIRPropertyValue Value = {0};
Value.Header = /* … */;
Core->GetInstructionProperty(Core->Context, Task, Instruction,
NEVERC_IR_PROPERTY_ALIGNMENT, &Value);والخصائص الـ23 هي NAME وFAST_MATH_FLAGS وNUW وNSW وEXACT
وDISJOINT وVOLATILE وALIGNMENT وATOMIC_ORDERING وSYNC_SCOPE
وPREDICATE وCALLING_CONVENTION وTAIL_CALL_KIND وINDICES وWEAK
وSUCCESS_ORDERING وFAILURE_ORDERING وINBOUNDS وSOURCE_ELEMENT_TYPE
وALLOCATED_TYPE وATTRIBUTES وCLEANUP وNUSW. وتمتد الترتيبات الذرّية من
NOT_ATOMIC إلى SEQUENTIALLY_CONSISTENT، وأنواع نداء الذيل هي NONE
وTAIL وMUST_TAIL وNO_TAIL، ورايات fast-math هي البتات السبع المعتادة من
ALLOW_REASSOC إلى APPROX_FUNC.
السمات قيمٌ تنشئها ثم تُرفقها، وهذا ما يجعل الأنواع الأربعة (ENUM وINTEGER
وSTRING وTYPE) متسقة:
NevercIRAttributeHandle NoInline;
Core->CreateEnumAttribute(Core->Context, Task, SV("noinline"), &NoInline);
Core->AddFunctionAttribute(Core->Context, Task, Function,
NEVERC_IR_ATTRIBUTE_LOCATION_FUNCTION,
/*ParameterIndex=*/0, NoInline);
NevercBool Present = NEVERC_FALSE;
Core->HasFunctionAttribute(Core->Context, Task, Function, SV("noinline"),
&Present);ويستعمل pluginsdk/examples/CustomCallConvPlugin.c هذا مع
GetFunctionStringAttribute لقيادة اصطلاح نداءٍ معرَّف بالبيانات.
يمرّ كل تغيير بنيوي عبر NevercIRBuilderAPI. التعديل هو المعامَلة، والباني مؤشر
داخلها.
NevercIRMutationHandle Mutation;
NevercIRBuilderHandle Builder;
Builders->BeginMutation(Builders->Context, Task,
NEVERC_IR_MUTATION_SCOPE_FUNCTION, Function,
&Mutation);
Builders->CreateBuilder(Builders->Context, Task, Mutation, &Builder);
Builders->SetInsertBefore(Builders->Context, Task, Builder, Terminator);
NevercIRValueHandle Sum;
Builders->BuildBinary(Builders->Context, Task, Builder,
NEVERC_IR_OPCODE_ADD, Left, Right, SV("sum"), &Sum);
Status = Builders->CommitMutation(Builders->Context, Task, Mutation);
if (Status.Code != NEVERC_STATUS_OK)
Builders->AbortMutation(Builders->Context, Task, Mutation);
Builders->DestroyBuilder(Builders->Context, Task, Builder);
Builders->DestroyMutation(Builders->Context, Task, Mutation);النطاقات هي NEVERC_IR_MUTATION_SCOPE_MODULE و_FUNCTION و_LOOP، ويسمّي
ScopeRoot الدالةَ أو ترويسةَ الحلقة. والإيداع يتحقق من المرشَّح وينشر ذريًّا —
فإذا أخفق المدقّق تراجع المضيفُ وبقيت الوحدة السابقة دون مساس.
ونداءات البناء هي BuildBinary وBuildUnary وBuildCompare وBuildCast
وBuildSelect وBuildAlloca وBuildLoad وBuildStore
وBuildGetElementPtr وBuildCall وBuildPhi وBuildBranch
وBuildConditionalBranch وBuildUnreachable وBuildReturn
وBuildReturnVoid. وتنطبق SetDebugLocation وSetFastMathFlags على كل ما
يصدره الباني بعدهما.
لاحظ عدم التناظر: تأخذ AddPhiIncoming وCreateFunction وCreateBasicBlock
التعديلَ لا الباني، لأنها غير مرتبطة بموضع إدراج.
وDestroyMutation منفصل عن الإيداع والإجهاض. فكل BeginMutation يحتاج
DestroyMutation واحدًا بالضبط، أيًّا كانت النهاية التي بلغتها المعامَلة.
NevercIRPassDescriptor Pass = {0};
Pass.Header = (NevercABITableHeader){sizeof(Pass), NEVERC_IR_PASS_API_MAJOR,
NEVERC_IR_PASS_API_MINOR, 0};
Pass.PassID = SV("example.function-pass");
Pass.Phase = (NevercInterfaceID){
NEVERC_PHASE_IR_PASS_PIPELINE_START_HIGH,
NEVERC_PHASE_IR_PASS_PIPELINE_START_LOW};
Pass.Level = NEVERC_IR_PASS_LEVEL_FUNCTION;
Pass.Deterministic = NEVERC_TRUE;
Pass.Cacheable = NEVERC_TRUE;
Pass.Run = run_function;
PassAPI->RegisterPass(PassAPI->Context, RegistrarContext, &Pass);المستويات هي MODULE وCGSCC وFUNCTION وLOOP. ولا يحمل النداء إلا المقابض
الصالحة لمستواه:
typedef struct NevercIRPassInvocation {
NevercABITableHeader Header;
NevercTaskHandle Task;
NevercInterfaceID Phase;
NevercStringView PassID;
NevercIRPassLevel Level;
NevercIROptimizationLevel OptimizationLevel; /* O0…O3, Os, Oz */
NevercIRModuleHandle Module;
NevercIRValueHandle Function; /* FUNCTION و LOOP */
NevercIRValueHandle LoopHeader; /* LOOP فقط */
const NevercIRValueHandle *SCCFunctions; /* CGSCC فقط */
uint64_t SCCFunctionCount;
const NevercIRCoreAPI *Core;
const NevercIRBuilderAPI *Builder;
const NevercIRAnalysisAPI *Analyses;
uint64_t Reserved[2];
} NevercIRPassInvocation;تأتي مؤشرات الواجهات الثلاثة مع النداء، فلا يحتاج جسمُ التمريرة جدولًا مخزّنًا.
وأبلغ عمّا نجا عبر OutPreserved:
OutPreserved->Flags = NEVERC_IR_PRESERVE_ALL; /* أو _NONE، أو _CFG */NEVERC_IR_PRESERVE_CFG يعني أن مخطط تدفق التحكم سليم وإن تغيّرت التعليمات.
وتُحفظ التحليلات المخصّصة بإدراجها في CustomAnalyses. ولا تدّعِ
PRESERVE_ALL بعد تغيير التمثيل الوسيط — فالمهايئ يقارن جيلَ الوحدة ويرفض
الادّعاء الكاذب.
وقد تعمل تمريرات الدوال والحلقات على التوازي، فيجب أن توافق حالةُ الإضافة
القابلة للتغيير نموذجَ NevercConcurrencyModel الذي أعلنته الإضافة.
سبعة تحليلات مدمجة قابلة للاستعلام بالمعرّف: DOMINATOR_TREE
وPOST_DOMINATOR_TREE وLOOP_INFO وSCALAR_EVOLUTION وMEMORY_SSA
وCALL_GRAPH وALIAS.
NevercIRAnalysisResultHandle Loops;
Analyses->QueryBuiltin(Analyses->Context, Task,
NEVERC_IR_ANALYSIS_LOOP_INFO, Function, &Loops);
uint64_t LoopCount = 0;
Analyses->GetLoopCount(Analyses->Context, Task, Loops, &LoopCount);
for (uint64_t I = 0; I != LoopCount; ++I) {
NevercIRValueHandle Header;
Analyses->GetLoopHeader(Analyses->Context, Task, Loops, I, &Header);
}ولكلٍّ منها موصِّلات مُنمّطة بدل كتلة معتِمة: DominatorTreeDominates
وGetLoopCount / GetLoopHeader / GetLoopForBlock
وGetScalarEvolutionConstantTripCount وGetMemoryAccessKind (NONE وUSE
وDEF وPHI وLIVE_ON_ENTRY) وGetDirectCalleeCount / GetDirectCallee
وAlias (NO وMAY وPARTIAL وMUST).
ويُسجَّل تحليل الإضافة بدورة حياته الخاصة:
NevercIRAnalysisDescriptor Analysis = {0};
Analysis.Header = /* … */;
Analysis.AnalysisID = MyAnalysisID;
Analysis.Name = SV("example.my-analysis");
Analysis.Level = NEVERC_IR_PASS_LEVEL_FUNCTION;
Analysis.Dependencies = Deps;
Analysis.DependencyCount = DepCount;
Analysis.Compute = compute;
Analysis.Query = query;
Analysis.Invalidate = invalidate;
Analysis.Destroy = destroy;
Analyses->RegisterAnalysis(Analyses->Context, RegistrarContext, &Analysis);ويُخبَر Invalidate بالسبب — INVALIDATED_BY_PASS أو
INVALIDATED_BY_PLAN_DESTROY. وتُخزَّن النتائج مؤقتًا لكل نداء وتُسقَط بحسب ما
حفظته التمريرةُ الجارية. وتُرفض حلقاتُ التبعية عند التسجيل، كما يُرفض تعديل
التمثيل الوسيط من داخل ردّ نداء تحليل.
تستبدل NevercIRGenAPI المرحلةَ neverc.ir.generate:
NevercIRGeneratePhaseInput In = {0};
In.Header = /* … */;
Gen->GetGeneratePhaseInput(Gen->Context, Frame, Frame->Input, &In);
/* In.SemanticUnit و.TargetTriple و.DataLayout و.SourceIdentity و
.SourceDigest */
const NevercIRCoreAPI *Core;
const NevercIRBuilderAPI *Builders;
Gen->CreateModule(Gen->Context, Frame, SV("my.module"), &Core, &Builders);
/* … ابنِ الوحدة … */
NevercIRModuleArtifactDescriptor Descriptor = {0};
Descriptor.Header = /* … */;
Descriptor.Product = MyProductID;
Descriptor.DependencyDigest = Digest;
Gen->PublishModule(Gen->Context, Frame, &Descriptor, &Output);وتبدأ ImportModule من bitcode أو تمثيل وسيط نصّي بدل وحدة فارغة. ولـ
NevercIROptimizationAPI الشكلُ نفسه للمرحلة neverc.ir.optimize، مضافًا
إليه GetInputModule للوصول إلى الوحدة الداخلة، وRunBuiltinPipeline لتفويض
خط الأنابيب المدمج ثم معالجة نتيجته لاحقًا.
كلا المسارين ينشران عبر المضيف بدل إعادة مؤشر، ويتحققان من توافق الهدف،
ويحفظان الوحدةَ القديمة ذريًّا إن أخفق النشر. وتظل neverc.ir.final_verify
تعمل بعد ذلك.
| الملف | ما يعرضه |
|---|---|
pluginsdk/examples/FunctionPass.c |
تمريرة دالة للقراءة فقط، مع مفاوضة ABI |
pluginsdk/examples/ExamplePlugin.c |
تمريرة على مستوى الوحدة تجتاز الدوال بمؤشر قيم |
pluginsdk/examples/CustomCallConvPlugin.c |
السمات وخصائص موضع النداء |
cmake --build build-neverc --target neverc-plugin-example-function-pass
build-neverc/bin/neverc \
-fplugin=build-neverc/neverc/pluginsdk/examples/host/FunctionPass.so \
-O2 -c input.c -o input.oاستعمل لاحقة الوحدة التي أنتجها CMake لمنصّتك.
- أعِد
NevercStatusمن كل ردّ نداء. فإخفاق الإضافة يصير تشخيصًا مُهيكلًا، ولا تدع استثناءً يعبر حدّ لغة C أبدًا. - صفّر كل بنية خرج واضبط
Headerفيها قبل النداء الذي يملؤها. - لا تكتب أعدادًا حرفية لرموز العمليات أو الأنواع أو الخصائص. استعمل أسماء
PluginIRSchema.incكي تصير مراجعةُ المخطَّط خطأ ترجمة. - كل
BeginMutationيبلغDestroyMutationواحدًا بالضبط، وكلCreateBuilderيبلغDestroyBuilderواحدًا بالضبط، على مسارات الخطأ أيضًا. - حرّر ما تسلّمه من
ExportModuleبـReleaseSerializedBuffer. - لا تدّعِ
NEVERC_IR_PRESERVE_ALLبعد تعديل التمثيل الوسيط. - افترض أن تمريرات الدوال والحلقات تعمل على التوازي، ما لم تعلن الإضافة
NEVERC_CONCURRENCY_SESSION_SERIAL. neverc.ir.final_verifyمختومة. ولا شيء تفعله إضافةٌ يمكنه تخطّيها.
انظر PluginIR.h وSchema/IRSchema.json وSchema/PluginIRSchema.inc
وSchema/PhaseSchema.json وcoverage.json للتصريحات المعيارية وللمخطَّط
نفسه ولثوابته المولَّدة وسياسات المراحل وأدلة الاختبارات.