اللغات: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
← واجهة NeverC الثنائية للإضافات
يكشف PluginMIR.h عن Machine IR: دوال الآلة، والكتل، والتعليمات، والمعاملات،
والسجلات الافتراضية والفيزيائية، وإطار المكدس، ومجمّع الثوابت، وجداول القفز،
ومعاملات الذاكرة. وتُعلِّق الإضافة مروراتها على تسعة خطّافات مستقرة في توليد
الشيفرة، أو تستبدل خفض IR إلى MIR بالكامل.
يلتقي هنا مخططان. المخطط العام مستقل عن الهدف ومتاح دائمًا. وأي شيء خاص
بالهدف — كود تشغيلي حقيقي، أو رقم سجل، أو صنف سجلات — يتطلّب مخطط هدف
متفاوَضًا عليه، وكل قيمة تحتاج إليه تُعلن ذلك عبر الراية
RequiresTargetSchema.
#include "neverc/Plugin/PluginMIR.h"| الواجهة | الجدول | الخانات | الغرض |
|---|---|---|---|
NEVERC_INTERFACE_MIR_{HIGH,LOW} |
NevercMIRAPI |
89 | قراءة دوال الآلة وتعديلها |
NEVERC_INTERFACE_MIR_ANALYSIS_{HIGH,LOW} |
NevercMIRAnalysisAPI |
11 | الحيوية، والمُهيمنات، والحلقات، والضغط |
NEVERC_INTERFACE_MIR_PASS_{HIGH,LOW} |
NevercMIRPassAPI |
1 | RegisterPass |
NEVERC_INTERFACE_MIR_PROVIDER_{HIGH,LOW} |
NevercMIRProviderAPI |
3 | استبدال خفض IR → MIR |
الأربع جميعًا NEVERC_INTERFACE_STABLE عند الإصدار الرئيسي 1. قارن قيمة
TableSize المُعادة بإزاحة آخر خانة تستعملها، وتجاهل أي شيء ألحقه مُضيف أحدث
بعدها.
عشر مراحل MIR، تسع منها خطّافات للمرورات:
| المرحلة | متى |
|---|---|
neverc.mir.pass.post_isel |
بعد اختيار التعليمات |
neverc.mir.pass.post_legalize |
بعد التقنين |
neverc.mir.pass.pre_scheduler |
قبل الجدولة |
neverc.mir.pass.post_scheduler |
بعد الجدولة |
neverc.mir.pass.pre_regalloc |
قبل تخصيص السجلات |
neverc.mir.pass.post_regalloc |
بعد تخصيص السجلات |
neverc.mir.pass.post_prolog_epilog |
بعد إدراج المقدمة/الخاتمة |
neverc.mir.pass.preemit |
قُبيل الإصدار مباشرة |
neverc.mir.pass.final |
آخر خانة متاحة للإضافات |
neverc.mir.final_verify |
MachineVerifier المختوم لدى المُضيف |
الخطّافات التسعة كلها OBSERVABLE | INTERCEPTABLE. وأي التحليلات تكون موجودة
يعتمد على موضع تعليقك: فترات الحياة غير متاحة قبل تخصيص السجلات، والسجلات
الافتراضية تختفي بعده.
تشغّل neverc.mir.final_verify مُدقِّق MachineVerifier من LLVM بعد آخر خانة
للإضافات. ولا تستطيع أي إضافة تعطيله أو استبداله أو تخطّيه.
Schema/PluginMIRSchema.inc مُولَّد ويُضمّنه PluginMIR.h:
#define NEVERC_MIR_SCHEMA_DIGEST "6b523b20…"
#define NEVERC_MIR_ENTITY_COUNT UINT32_C(4)
#define NEVERC_MIR_OPERAND_COUNT UINT32_C(21)
#define NEVERC_MIR_GENERIC_OPCODE_COUNT UINT32_C(266)
#define NEVERC_MIR_PROPERTY_COUNT UINT32_C(11)وأربعة استدعاءات تصف المخطط أثناء التشغيل، ويعيد كلٌّ منها
NevercMIRSchemaEntry يحمل الاسم القياسي، وقيمة LLVM الأساسية، وما إذا كان
مخطط الهدف لازمًا:
NevercMIRSchemaEntry Entry = {0};
Entry.Header = /* … */;
MIR->GetGenericOpcodeInfo(MIR->Context, Opcode, &Entry);
/* Entry.StableID, .LLVMValue, .RequiresTargetSchema, .CanonicalName */والبقية هي GetEntityInfo وGetOperandKindInfo و
GetMachinePropertyInfo. ويعيد GetSchemaDigest بصمة الربط المستعمل فعليًا
— قارنها بـNEVERC_MIR_SCHEMA_DIGEST قبل أن تثق بأي قيمة خاصة بالهدف.
الاجتياز عبر قائمة مزدوجة الوصل، لا عبر مؤشر تصفّح:
NevercMachineBasicBlockHandle Block;
MIR->GetFirstBasicBlock(MIR->Context, Task, Function, &Block);
while (!neverc_handle_is_null(Block)) {
NevercMachineInstrHandle Instruction;
MIR->GetFirstInstruction(MIR->Context, Task, Block, &Instruction);
while (!neverc_handle_is_null(Instruction)) {
NevercMIRInstructionInfo Info = {0};
Info.Header = (NevercABITableHeader){sizeof(Info), NEVERC_MIR_API_MAJOR,
NEVERC_MIR_API_MINOR, 0};
MIR->GetInstructionInfo(MIR->Context, Task, Instruction, &Info);
/* Info.StableOpcode, .TargetOpcode, .RequiresTargetSchema,
.IsBranch, .IsCall, .IsReturn, .IsTerminator, .IsBarrier,
.IsInlineAssembly, .IsDebugInstruction, .IsPseudo, .IsBundle,
.Flags, .OperandCount, .MemoryOperandCount */
MIR->GetNextInstruction(MIR->Context, Task, Instruction, &Instruction);
}
MIR->GetNextBasicBlock(MIR->Context, Task, Block, &Block);
}أما CollectBasicBlocks وCollectInstructions فتملآن مصفوفة محدودة بدلًا من
ذلك، وGetLastBasicBlock / GetPreviousInstruction تسيران إلى الوراء.
واستعلامات رسم تدفّق التحكم هي GetSuccessorCount / GetSuccessor (التي
تُنتج NevercMIRCFGEdge تحمل احتمال التفرّع كزوج بسط ومقام)، و
GetPredecessorCount / GetPredecessor، وGetLiveInCount / GetLiveIn.
ورايات التعليمات هي البتات الثماني عشرة الممتدة من FRAME_SETUP و
FRAME_DESTROY مرورًا بمجموعة fast-math وصولًا إلى NO_MERGE و
UNPREDICTABLE وNO_CONVERGENT.
تعود أنواع المعاملات الواحد والعشرون كلها عبر اتحاد موسوم واحد:
NevercMIROperandValue Value = {0};
Value.Header = /* … */;
MIR->GetOperandValue(MIR->Context, Task, Operand, &Value);
switch (Value.Kind) {
case NEVERC_MIR_OPERAND_REGISTER:
/* Value.Payload.Register.Number, .SubRegister, .Flags, .IsPhysical */
break;
case NEVERC_MIR_OPERAND_IMMEDIATE:
/* Value.Payload.Immediate */
break;
case NEVERC_MIR_OPERAND_MACHINE_BASIC_BLOCK:
/* Value.Payload.BasicBlock */
break;
case NEVERC_MIR_OPERAND_GLOBAL_ADDRESS:
/* Value.Payload.SymbolOffset.Symbol, .Offset */
break;
}الأنواع هي REGISTER وIMMEDIATE وC_IMMEDIATE وFP_IMMEDIATE
وMACHINE_BASIC_BLOCK وFRAME_INDEX وCONSTANT_POOL_INDEX وTARGET_INDEX
وJUMP_TABLE_INDEX وEXTERNAL_SYMBOL وGLOBAL_ADDRESS وBLOCK_ADDRESS
وREGISTER_MASK وREGISTER_LIVE_OUT وMETADATA وMC_SYMBOL وCFI_INDEX
وINTRINSIC_ID وPREDICATE وSHUFFLE_MASK وDBG_INSTR_REF.
ورايات معامل السجل هي DEF وIMPLICIT وKILL وDEAD وUNDEF و
EARLY_CLOBBER وRENAMABLE وINTERNAL_READ وDEBUG. وتصل القيم الفورية
العائمة على هيئة NevercMIRWordView — كلمات بترتيب البايت الأصغر أولًا، مع
عرض بالبتات وواحدة من سبع دلالات عائمة من IEEE_HALF إلى
PPC_DOUBLE_DOUBLE — فلا يتدخّل أي نوع عائم من أنواع المُضيف.
يُوصف السجل الافتراضي بنوع منخفض المستوى مع تخصيص:
NevercMIRVirtualRegisterDesc Desc = {0};
Desc.Header = /* … */;
Desc.AssignmentKind = NEVERC_MIR_REG_ASSIGNMENT_CLASS;
Desc.TargetID = RegisterClassID; /* needs the target schema */
Desc.Type.Kind = NEVERC_MIR_LLT_SCALAR;
Desc.Type.ScalarSizeInBits = 32;
uint32_t Register = 0;
MIR->CreateVirtualRegister(MIR->Context, Task, Mutation, &Desc, &Register);أنواع التخصيص هي NONE وGENERIC وCLASS وBANK؛ وأنواع الأنواع منخفضة
المستوى هي INVALID وSCALAR وPOINTER وVECTOR وPOINTER_VECTOR، مع
IsScalable للمتجهات القابلة للتوسّع.
واستعلامات التعريف والاستعمال هي GetRegisterDefCount / GetRegisterDef و
GetRegisterUseCount / GetRegisterUse؛ ويعيد ReplaceRegister كتابة كل
ورود في عملية مُجهَّزة واحدة. أما المدخلات الحيّة على مستوى الدالة فتقرن سجلًا
فيزيائيًا بالسجل الافتراضي الذي نُسخ إليه (GetFunctionLiveIn و
AddFunctionLiveIn وRemoveFunctionLiveIn)، بينما تحمل المدخلات الحيّة على
مستوى الكتلة قناع مسارات (AddBasicBlockLiveIn وRemoveBasicBlockLiveIn).
int32_t FrameIndex = 0;
MIR->CreateStackObject(MIR->Context, Task, Mutation, /*Size=*/16,
/*Alignment=*/8, /*IsSpillSlot=*/NEVERC_FALSE,
/*StackID=*/0, &FrameIndex);يضع CreateFixedStackObject كائنًا عند إزاحة معلومة (مع IsImmutable و
IsAliased)، ويتكفّل CreateVariableSizedStackObject بالتخصيص الديناميكي.
وتُعدِّل SetFrameObjectSize وSetFrameObjectAlignment و
SetFrameObjectOffset كائنًا بعد إنشائه.
ويُبلِّغ NevercMIRFrameObjectInfo عن Index وFlags وSize وOffset و
Alignment وStackID؛ ورايات الإطار هي FIXED وSPILL_SLOT و
VARIABLE_SIZED وIMMUTABLE وALIASED وDEAD وPREALLOCATED. وتُقرأ حالة
السجلات المحفوظة لدى المُستدعَى بـGetCalleeSaved وتُستبدل جملةً بـ
SetCalleeSaved.
تحمل مُدخلات مجمّع الثوابت قيمتها بوصفها NevercMIRWordView، فيأخذ المُدخل
الصحيح والمُدخل العائم الشكل نفسه:
NevercMIRConstantPoolEntryDesc Desc = {0};
Desc.Header = /* … */;
Desc.Kind = NEVERC_MIR_CONSTANT_INTEGER;
Desc.Alignment = 8;
Desc.Value.Data = Words;
Desc.Value.Count = 1;
Desc.Value.BitWidth = 64;
uint32_t Index = 0;
MIR->CreateConstantPoolEntry(MIR->Context, Task, Mutation, &Desc, &Index);وتُنشأ جداول القفز من مصفوفة كتل وجهة، بأحد سبعة أنواع مُدخلات
(BLOCK_ADDRESS وGP_REL64_BLOCK_ADDRESS وGP_REL32_BLOCK_ADDRESS و
LABEL_DIFFERENCE32 وLABEL_DIFFERENCE64 وINLINE وCUSTOM32).
ومعاملات الذاكرة هي أغنى الواصفات: رايات (LOAD وSTORE وVOLATILE و
NON_TEMPORAL وDEREFERENCEABLE وINVARIANT، إضافة إلى ثلاث رايات خاصة
بالهدف)، وحجم ومحاذاة، ومؤشر من أحد تسعة أنواع (IR_VALUE وFIXED_STACK و
STACK وCONSTANT_POOL وJUMP_TABLE وGOT وUNKNOWN_STACK و
TARGET_CUSTOM وUNKNOWN)، وترتيبان ذرّيان للنجاح والفشل، ونطاق مزامنة،
ومراجع TBAA وalias-scope وno-alias وrange. وتُلحَق بـ
AddInstructionMemoryOperand.
كل تغيير يُجهَّز داخل تعديل مربوط بدالة آلة واحدة:
NevercMIRMutationHandle Mutation;
MIR->BeginMutation(MIR->Context, Task, Function, &Mutation);
NevercMIRInstructionOpcode Opcode = {0};
Opcode.StableOpcode = MyGenericOpcode;
NevercMachineInstrHandle New;
MIR->CreateInstruction(MIR->Context, Task, Mutation, Block,
/*InsertBefore=*/Terminator, Opcode, &New);
NevercMIROperandValue Op = {0};
Op.Header = /* … */;
Op.Kind = NEVERC_MIR_OPERAND_IMMEDIATE;
Op.Payload.Immediate = 42;
MIR->AppendOperand(MIR->Context, Task, Mutation, New, &Op, &Operand);
Status = MIR->CommitMutation(MIR->Context, Task, Mutation);
if (Status.Code != NEVERC_STATUS_OK)
MIR->AbortMutation(MIR->Context, Task, Mutation);
MIR->EndMutation(MIR->Context, Task, Mutation);يُجري الإيداع فحصًا بنيويًا تمهيديًا ثم مُدقِّق Machine IR. والمعاملات غير الصالحة، أو رسم تدفّق تحكم مكسور، أو أكواد تشغيلية عامة حيث يطلب مخطط الهدف كودًا حقيقيًا، أو ادّعاء خاصية غير مدعومة — كلها تُتراجَع ذرّيًا. والإجهاض يستعيد ترتيب الكتل والتعليمات والمعاملات وحوافّ رسم التدفّق وخصائص الآلة كما كانت تمامًا.
ويحرّر EndMutation المقبض، وهو منفصل عن الإيداع والإجهاض — فاستدعِه في
المسارين كليهما.
والعمليات المُجهَّزة هي CreateBasicBlock وMoveBasicBlock و
EraseBasicBlock وCreateInstruction وMoveInstruction و
EraseInstruction وAppendOperand وSetOperandValue و
SetInstructionFlags وAddCFGEdge وRemoveCFGEdge، واستدعاءات السجلات
والإطار المذكورة أعلاه، واستدعاءات مجمّع الثوابت وجداول القفز، واستدعاءات
معاملات الذاكرة، وSetMachinePropertyWithProof.
خصائص الآلة الإحدى عشرة — IS_SSA وNO_PH_IS وTRACKS_LIVENESS و
NO_V_REGS وFAILED_I_SEL وLEGALIZED وREG_BANK_SELECTED وSELECTED و
TIED_OPS_REWRITTEN وFAILS_VERIFICATION وTRACKS_DEBUG_USER_VALUES —
تُقرأ بحرية لكنها لا تُضبط بحرية أبدًا:
NevercMIRPropertyProof Proof = {0};
Proof.Header = /* … */;
Proof.Property = NEVERC_MIR_PROPERTY_IS_SSA;
Proof.Kind = NEVERC_MIR_PROPERTY_PROOF_INVALIDATION;
Proof.Value = NEVERC_FALSE;
MIR->SetMachinePropertyWithProof(MIR->Context, Task, Mutation, &Proof);والإثبات من نوعين. يمحو INVALIDATION خاصية نقض تغييرُك افتراضاتِها — وهذا
مقبول دائمًا، لأن التخلّي عن ضمانة أمرٌ آمن. أما STRUCTURAL_CHECK فيطلب من
المُضيف التحقق من الخاصية قبل تثبيتها، فادّعاء IS_SSA يكلّف فحصًا حقيقيًا لا
مجرد وعد.
NevercMIRPassDescriptor Pass = {0};
Pass.Header = (NevercABITableHeader){sizeof(Pass), NEVERC_MIR_PASS_API_MAJOR,
NEVERC_MIR_PASS_API_MINOR, 0};
Pass.PassID = SV("example.machine-pass");
Pass.Phase = (NevercInterfaceID){NEVERC_PHASE_MIR_PASS_PREEMIT_HIGH,
NEVERC_PHASE_MIR_PASS_PREEMIT_LOW};
Pass.Level = NEVERC_MIR_PASS_LEVEL_FUNCTION;
Pass.Deterministic = NEVERC_TRUE;
Pass.Run = run_machine_function;
PassAPI->RegisterPass(PassAPI->Context, RegistrarContext, &Pass);هذا هو pluginsdk/examples/MachinePass.c حرفيًا. والمستويات هي MODULE و
FUNCTION وBASIC_BLOCK. وRequiredAnalyses وPreservedAnalyses مصفوفتان
من NevercMIRBuiltinAnalysis، وRequiredTargetSchemaDigest يجعل المرور يرفض
العمل مقابل مخطط لم يُبنَ له.
ويحمل الاستدعاء Task وPhase وPassID وLevel، وFunction و
BasicBlock الصالحين لذلك المستوى، وجدولَي Core وAnalyses، وبصمة
TargetSchemaDigest النشطة.
وأبلِغ عن الحفاظ عبر OutPreserved — NEVERC_MIR_PRESERVE_NONE أو _CFG أو
_ALL، إضافة إلى قائمة صريحة في Analyses. وادّعاء PRESERVE_ALL بعد تعديل
مُودَع مرفوض.
ويمكن لمرورات الدوال أن تعمل في أقسام متوازية من توليد الشيفرة؛ أما مرورات مستوى الوحدة فتعمل عند حواجز مُسلسَلة في خط الأنابيب. ويظل نموذجا التزامن وإعادة الدخول المُعلَنان من الإضافة يحكمان حالتك الخاصة.
ستة مدمجة: LIVE_INTERVALS وLIVE_VARIABLES وSLOT_INDEXES و
DOMINATOR_TREE وLOOP_INFO وREGISTER_PRESSURE.
NevercMIRAnalysisResultHandle Intervals;
Analyses->QueryBuiltin(Analyses->Context, Task,
NEVERC_MIR_ANALYSIS_LIVE_INTERVALS, Function,
&Intervals);
uint64_t SegmentCount = 0;
Analyses->GetLiveIntervalSegmentCount(Analyses->Context, Task, Intervals,
Register, &SegmentCount);
for (uint64_t I = 0; I != SegmentCount; ++I) {
NevercMIRLiveRangeSegment Segment;
Analyses->GetLiveIntervalSegment(Analyses->Context, Task, Intervals,
Register, I, &Segment);
/* Segment.Start, Segment.End */
}ومتاح أيضًا: DominatorTreeDominates وGetLoopCount / GetLoopHeader /
GetLoopForBlock وGetSlotIndex وIsRegisterLiveInBlock و
GetRegisterPressureSetCount / GetRegisterPressure.
والإتاحة تعتمد على الخطّاف. فطلب فترات الحياة عند post_isel يفشل بـ
NEVERC_STATUS_CAPABILITY_UNAVAILABLE لأن تحليل LLVM الأساسي لم يوجد بعد.
والتعديل المُودَع يُبطل مقابض النتائج التي يمسّها.
NevercIRToMIRInputInfo In = {0};
In.Header = /* … */;
Provider->GetIRToMIRInput(Provider->Context, Frame, Frame->Input, &In);
/* In.Module, .IR, .TargetID, .CompatibilityKey, .TargetSchemaDigest,
.DefinedFunctionCount */
const NevercMIRAPI *MIR;
NevercMachineFunctionHandle MF;
Provider->GetOrCreateMachineFunction(Provider->Context, Frame, IRFunction,
&MIR, &MF);
/* … build the machine function … */
NevercMIRModuleCoverageDescriptor Coverage = {0};
Coverage.Header = /* … */;
Coverage.HandlesGlobals = NEVERC_TRUE;
Coverage.HandlesConstructors = NEVERC_TRUE;
Coverage.HandlesDebugInfo = NEVERC_FALSE;
Coverage.HandlesUnwind = NEVERC_FALSE;
Provider->PublishMIRModule(Provider->Context, Frame, &Coverage, &Output);واصف التغطية هو ما يُبقي المزوِّد الجزئي صادقًا: أعلِن فقط ما خفضته فعلًا، وسيتولّى المُضيف الباقي بنفسه بدل أن تسقط بصمت المتغيرات العامة أو المُنشئات أو معلومات التنقيح أو جداول فك الكدسة.
cmake --build build-neverc --target neverc-plugin-example-machine-pass
build-neverc/bin/neverc \
-fplugin=build-neverc/neverc/pluginsdk/examples/host/MachinePass.so \
-O2 -fno-lto -c input.c -o input.oاستخدم لاحقة الوحدة التي أنتجها CMake لمنصتك.
- لا تحتفظ بمقابض المهام أو مقابض MIR أو العروض المُستعارة بعد عودة رد النداء، ولا تصطنع أبدًا قيمة مقبض أو رقم كود تشغيلي من LLVM.
- قارن
GetSchemaDigestبالبصمة المُضمَّنة في بنائك قبل استهلاك أي قيمة مضبوطة لها رايةRequiresTargetSchema. - لا تُعدِّل إلا داخل تعديل. وكل
BeginMutationيبلغEndMutationواحدًا بالضبط، بعد إيداع أو إجهاض. - لا تدّعِ خاصية آلة بلا إثبات، وفضِّل
INVALIDATIONعلىSTRUCTURAL_CHECKحين يكون تغييرك قد تخلّى عن إحداها. - لا تدّعِ أبدًا
NEVERC_MIR_PRESERVE_ALLبعد تعديل مُودَع. - تأكَّد من أن التحليل الذي تحتاجه متاح فعلًا عند الخطّاف الذي اخترته.
- هيّئ كل ترويسة جدول وكل حقل محجوز؛ وأعِد الحالات عبر حدود C، ولا تدع استثناء C++ يعبرها أبدًا.
neverc.mir.final_verifyمختومة. تعمل مهما حدث.
انظر PluginMIR.h وSchema/MIRSchema.json وSchema/PluginMIRSchema.inc
وSchema/PhaseSchema.json وcoverage.json للإعلانات المِعيارية وللمخطط
نفسه ولثوابته المولَّدة وسياسات المراحل وأدلة التغطية.