Sprachen: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
Das Back-End besteht aus vier Headern und neunundzwanzig Phasen.
PluginTarget.h beschreibt ein Target und die Routen durch die
Codeerzeugung. PluginMC.h baut und beobachtet Maschinencode. Das Parsen und
Ausgeben von Assembly wohnt im selben Header. PluginObject.h verwandelt eine
relozierbare Datei in einen normalisierten Graphen und wieder zurück.
Zusammen erlauben sie einem Plugin, ein Target hinzuzufügen, einen einzelnen
Lowering-Schritt oder alle zu ersetzen, jede Instruktion beim Emittieren zu
beobachten, einen Assembly-Dialekt zu definieren oder eine Objektdatei
umzuschreiben — über ein reines C-ABI, das niemals ein MCInst, MCSection
oder object::ObjectFile von LLVM offenlegt.
#include "neverc/Plugin/PluginTarget.h"
#include "neverc/Plugin/PluginMC.h"
#include "neverc/Plugin/PluginObject.h" /* includes both of the above */| Schnittstelle | Tabelle | Slots | Zweck |
|---|---|---|---|
NEVERC_INTERFACE_TARGET_* |
NevercTargetAPI |
2 | RegisterTarget, RegisterCodeGenEdge |
NEVERC_INTERFACE_TARGET_ABI_* |
NevercTargetABIAPI |
1 | RegisterABI |
NEVERC_INTERFACE_CALLING_CONVENTION_* |
NevercCallingConventionAPI |
1 | RegisterCallingConvention |
NEVERC_INTERFACE_MC_* |
NevercMCAPI |
53 | Ein MCUnit lesen und ändern; Encoder, Decoder, Back-Ends registrieren |
NEVERC_INTERFACE_MC_EMISSION_* |
NevercMCEmissionAPI |
7 | Emissionsereignisse und Layout-Momentaufnahmen |
NEVERC_INTERFACE_MC_PROVIDER_* |
NevercMCProviderAPI |
4 | MIR → MC ersetzen |
NEVERC_INTERFACE_ASSEMBLY_PROVIDER_* |
NevercAssemblyProviderAPI |
8 | Assembly-Parser oder -Printer ersetzen |
NEVERC_INTERFACE_OBJECT_* |
NevercObjectAPI |
34 | Einen ObjectGraph lesen und ändern |
NEVERC_INTERFACE_OBJECT_FORMAT_* |
NevercObjectFormatAPI |
1 | RegisterFormat |
NEVERC_INTERFACE_OBJECT_PHASE_* |
NevercObjectPhaseAPI |
2 | GetGraph, GetImage |
Das ist die Regel, die alles Weitere hier bestimmt.
STABLE, und sicher fest zu verdrahten: target-unabhängige Deskriptoren, Phasen-IDs, Artefakt-IDs, die MC- und ObjectGraph-Container, Ausgabetransaktionen und jeder Callback-Vertrag.
LOCKSTEP, und ohne Prüfung unsicher: target-spezifische Schemata für Opcodes, Register, Operanden, Fixups, Relokationen und Aufrufkonventionen. Ihre Zahlenwerte sind nur gegenüber genau einer Schema-Revision bedeutsam.
Überall, wo ein LOCKSTEP-Wert auftaucht, steht ein Schema-Digest daneben. Vergleichen Sie ihn, bevor Sie den Wert lesen:
if (!string_equal(Target.SchemaDigest, MY_COMPILED_SCHEMA_DIGEST))
return fail(NEVERC_STATUS_ABI_MISMATCH);NeverC weist ein nicht passendes Schema ebenfalls zurück, bevor es einen Provider aufruft — die Prüfung ist also doppelt abgesichert. Ein Plugin, das sie überspringt und trotzdem einen rohen Opcode liest, wird Instruktionen jedoch stillschweigend falsch deuten.
Neunundzwanzig, in vier Domänen.
| Phase | Policy |
|---|---|
neverc.codegen.ir_to_mir |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.mir_to_mc |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.coarse_lower |
OBSERVABLE, INTERCEPTABLE, REPLACEABLE |
neverc.codegen.product_verify |
OBSERVABLE, SEALED |
neverc.mc.encode, neverc.mc.decode und neverc.mc.layout sind
OBSERVABLE, INTERCEPTABLE, REPLACEABLE.
neverc.mc.emission.pre_instruction ist das eine Emissionsereignis, das
zusätzlich REPLACEABLE ist — dort ersetzt man eine Instruktion. Die anderen
neun (unit_begin, unit_end, section_change, post_instruction,
post_encode, fixup, relaxation_round, pre_layout, post_layout)
dienen nur der Beobachtung.
neverc.assembly.parse und neverc.assembly.print sind REPLACEABLE.
neverc.assembly.final_verify und neverc.assembly.commit sind SEALED.
neverc.object.probe, read, write, pre_write und post_layout sind
REPLACEABLE; neverc.object.post_write ist nur INTERCEPTABLE;
neverc.object.final_verify und neverc.object.commit sind SEALED.
NevercTargetDescriptor ist der größte Deskriptor im ABI, weil er alles
transportiert, was Front-End und Back-End wissen müssen:
typedef struct NevercTargetDescriptor {
NevercABITableHeader Header;
NevercTargetID TargetID;
NevercStringView CanonicalName;
NevercStringArrayView Aliases;
NevercStructArrayView TripleMatchers; /* NevercTargetTripleMatcher[] */
NevercTargetABIID DefaultABI;
NevercCallingConventionID DefaultCallingConvention;
NevercInterfaceID MCSchemaID;
NevercInterfaceID DefaultObjectFormatID;
NevercTargetMachineDescriptor Machine;
NevercStructArrayView Macros; /* predefined macros */
NevercStructArrayView Builtins; /* target builtins + lowering */
NevercStructArrayView Registers; /* inline-asm register names */
NevercStructArrayView Constraints; /* inline-asm constraints */
NevercStringView Clobbers;
uint64_t Flags;
NevercTargetValidateCPUFn ValidateCPU;
NevercTargetCanonicalizeCPUFn CanonicalizeCPU;
NevercTargetListCPUsFn ListCPUs;
NevercTargetResolveFeaturesFn ResolveFeatures;
NevercCreateTargetMachineFn CreateTargetMachine;
NevercDestroyTargetMachineFn DestroyTargetMachine;
void *UserData;
NevercDestroyUserDataFn DestroyUserData;
} NevercTargetDescriptor;TripleMatchers entscheidet, wann das Target ausgewählt wird: Jeder Matcher
nennt Architektur, Hersteller, Betriebssystem und Umgebung sowie eine
Priority, die Gleichstände gegenüber den eingebauten Targets auflöst.
Machine ist ein NevercTargetMachineDescriptor — Datenlayout, Standard- und
Tuning-CPUs, die Feature-Tabelle, unterstützte ABIs, Aufrufkonventionen und
Objektformate, Adressräume, Relokations- und Codemodelle (sowohl als Standard
als auch als Unterstützungsmaske), Ausnahmemodell (NONE, DWARF, SJLJ,
SEH, WASM), Unwind-Modell, Endianness, die Breite von
pointer/int/long/long long, Stack-Ausrichtung, maximale Atom- und
Vektorbreiten, va_list-Art, Ausführungsebenen (USER, KERNEL,
HYPERVISOR, FIRMWARE) sowie TLS-Unterstützung.
Target-Builtins tragen ihren eigenen Lowering-Callback, der einen lebendigen IR-Builder erhält:
static NevercStatus NEVERC_CALL
lower_builtin(void *UserData,
const NevercTargetBuiltinLoweringInvocation *In,
NevercIRValueHandle *OutResult) {
/* In->Core, In->Builder, In->Mutation, In->IRBuilder,
In->ResultType, In->Arguments, In->ArgumentCount */
return In->Builder->BuildCall(/* … */);
}Ein ABI klassifiziert Funktionssignaturen:
static NevercStatus NEVERC_CALL
classify(void *UserData, const NevercABIFunctionQuery *Query,
NevercABIArgumentClassification *ReturnValue,
NevercABIArgumentClassificationArray *Arguments) {
ReturnValue->Kind = NEVERC_ABI_ARGUMENT_DIRECT;
for (uint64_t I = 0; I != Arguments->Count; ++I) {
NevercABIArgumentClassification *A = &Arguments->Data[I];
A->Kind = NEVERC_ABI_ARGUMENT_INDIRECT;
A->Flags = NEVERC_ABI_ARGUMENT_BYVAL;
}
return neverc_status_ok();
}Argumentarten sind DIRECT, EXTEND, INDIRECT, IGNORE, EXPAND,
INDIRECT_ALIASED und COERCE_AND_EXPAND; Flags sind BYVAL, REALIGN,
INREG, SRET_AFTER_THIS, CAN_BE_FLATTENED, SIGN_EXTEND und
PADDING_INREG. Die Umwandlung ist NONE, INTEGER, FLOAT oder
POINTER, und COERCE_AND_EXPAND liefert ein Array von
NevercABICoercionElement.
Eine Aufrufkonvention geht eine Ebene tiefer und weist die tatsächlichen Orte zu:
static NevercStatus NEVERC_CALL
plan(void *UserData, const NevercCallingConventionQuery *Query,
NevercCallingConventionPlan *Plan) {
/* Query->TargetID, ->CallingConventionID, ->SchemaDigest, ->Function */
/* Fill Plan->ReturnLocations and Plan->ArgumentLocations with
NevercCallingConventionLocation records: REGISTER or STACK,
ValueIndex, PieceOffset, Size, Alignment, RegisterNumber,
StackOffset, and INDIRECT / BYVAL flags. */
Plan->CalleeSavedRegisters = MySavedRegisters;
Plan->StackAlignment = 16;
return neverc_status_ok();
}Query->SchemaDigest ist ein LOCKSTEP-Wert — RegisterNumber bedeutet nur
etwas gegenüber dem Schema, das er benennt. Das vollständig ausgearbeitete
Beispiel finden Sie unter
Eigene Aufrufkonventionen und in
pluginsdk/examples/CustomCallConvPlugin.c.
Eine Route wird aus dem kanonischen NevercTargetKey ausgewählt: Target-ID,
Triple-Teile, CPU, Tuning-CPU, Features, ABI, Aufrufkonvention, Objektformat,
Relokationsmodell, Codemodell, Ausführungsebene, Zeigerbreite, Endianness und
Schema-Digest. Registrieren Sie die Kanten, die Sie bedienen können:
NevercCodeGenEdgeDescriptor Edge = {0};
Edge.Header = /* … */;
Edge.EdgeID = MyEdgeID;
Edge.CanonicalName = SV("com.example.mir-to-mc");
Edge.TargetID = MyTargetID;
Edge.InputKind = NEVERC_CODEGEN_PRODUCT_MIR;
Edge.OutputKind = NEVERC_CODEGEN_PRODUCT_MC;
Edge.CompatibilityKey = SV("…");
Edge.ProviderID = SV("com.example.backend");
Target->RegisterCodeGenEdge(Target->Context, RegistrarContext, &Edge);Produktarten sind IR, MIR, MC, ASSEMBLY, OBJECT_GRAPH,
OBJECT_IMAGE und CUSTOM. Die feingranulare Route lautet
IR → MIR → MC → ObjectGraph → ObjectImage.
NEVERC_CODEGEN_EDGE_COARSE zu setzen und CoarseLower bereitzustellen,
ersetzt die gesamte Spanne IR → ObjectImage in einem Schritt:
static NevercStatus NEVERC_CALL
coarse_lower(void *UserData, NevercTaskHandle Task,
const NevercCodeGenRequest *Request,
NevercCodeGenProductCandidate *OutCandidate) {
/* Request->Target, ->Input, ->InputKind, ->OutputKind,
->OptimizationLevel, ->HasFinalIRProof */
OutCandidate->Kind = NEVERC_CODEGEN_PRODUCT_OBJECT_IMAGE;
OutCandidate->Artifact = MyImage;
OutCandidate->ProductID = MyProductID;
return neverc_status_ok();
}Auch eine grobe Route durchläuft neverc.codegen.product_verify und das
transaktionale Festschreiben der Ausgabe. VerifyProduct wird mit genau den
Pflichten aufgerufen, deren Erfüllung der Host von Ihnen erwartet —
VERIFY_FINAL_IR, VERIFY_TARGET_KEY, VERIFY_PRODUCT_KIND,
VERIFY_PRODUCT_ID, VERIFY_STRUCTURE —, sodass ein Provider kein Gate
heimlich durch eine Abkürzung umgehen kann.
Ein MCUnit enthält Sections, Symbole, Ausdrücke, Fragmente, Instruktionen,
Operanden und Fixups. Gelesen wird per first/next-Iteration:
NevercMCUnitInfo Unit = {0};
Unit.Header = /* … */;
MC->GetUnitInfo(MC->Context, Task, UnitHandle, &Unit);
NevercMCSectionHandle Section;
MC->GetFirstSection(MC->Context, Task, UnitHandle, &Section);
while (!neverc_handle_is_null(Section)) {
NevercMCFragmentHandle Fragment;
MC->GetFirstFragment(MC->Context, Task, Section, &Fragment);
/* … */
MC->GetNextSection(MC->Context, Task, Section, &Section);
}Mutation ist transaktional, wie überall sonst:
NevercMCMutationHandle Mutation;
MC->BeginMutation(MC->Context, Task, Unit, &Mutation);
MC->CreateSection(MC->Context, Task, Mutation, &SectionDescriptor, &Section);
MC->CreateSymbol(MC->Context, Task, Mutation, &SymbolDescriptor, &Symbol);
MC->AppendInstruction(MC->Context, Task, Mutation, Section, &Instruction);
Status = MC->CommitMutation(MC->Context, Task, Mutation);
if (Status.Code != NEVERC_STATUS_OK)
MC->AbandonMutation(MC->Context, Task, Mutation);Handles sind task-gebunden und generationsgeprüft, sodass ein Handle aus einer abgebrochenen Mutation zurückgewiesen statt wiederverwendet wird.
Section-Flags sind ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE und
DEBUG. Symbolbindungen sind LOCAL, GLOBAL und WEAK; Typen sind NONE,
FUNCTION, OBJECT, SECTION und TLS; Definitionen sind UNDEFINED,
SECTION, ABSOLUTE und COMMON. Ausdrücke unterstützen die unären
Operatoren PLUS, MINUS, NOT sowie die binären ADD, SUBTRACT,
MULTIPLY, DIVIDE, AND, OR, XOR, SHIFT_LEFT, SHIFT_RIGHT.
Übergeben Sie NEVERC_MC_AUTOMATIC_OFFSET, wo der Host etwas für Sie
platzieren soll.
RegisterSchema veröffentlicht ein Target-MC-Schema, und GetSchemaToken /
GetSchemaTokenInfo übersetzen zwischen Name und LOCKSTEP-Token.
Der Emissionsstrom meldet zehn Ereignisarten der Reihe nach — eine je
neverc.mc.emission.*-Phase. Die ABI reserviert außerdem
NEVERC_MC_EMISSION_PRE_OBJECT_WRITE; das Objekt schreiben selbst ist die
eigene Phase neverc.object.pre_write. Abonnieren Sie als
Beobachter und lesen Sie das Ereignis:
NevercMCEmissionEventInfo Event = {0};
Event.Header = /* … */;
Emission->GetEvent(Emission->Context, Frame, Frame->Input, &Event);
/* Event.Kind, Event.Flags */Flags sagt, welche Teile des Ereignisses gefüllt sind: HAS_SECTION,
HAS_INSTRUCTION, HAS_ENCODING, HAS_FIXUP, HAS_LAYOUT und
CAN_REPLACE_INSTRUCTION. Prüfen Sie das Flag, bevor Sie das zugehörige Feld
lesen — ein Ereignis, das noch keine Kodierung hat, bekommt keine, nur weil Sie
danach fragen.
GetLayoutSection, GetLayoutFragment, GetLayoutSymbol und
GetLayoutFixup liefern Adressen und Größen, sobald HAS_LAYOUT gesetzt ist.
Bei pre_instruction und nur dann, wenn CAN_REPLACE_INSTRUCTION gesetzt
ist, können Sie ersetzen:
const NevercMCAPI *MC;
NevercMCUnitHandle Unit;
NevercMCInstHandle Instruction;
Emission->BeginInstructionReplacement(Emission->Context, Frame, Continuation,
&MC, &Unit, &Instruction);
/* mutate Instruction through MC->BeginMutation / … / CommitMutation */
Emission->PublishInstructionReplacement(Emission->Context, Frame, Continuation,
&OutResult->Output);pluginsdk/examples/MCObserverPlugin.c ist die rein lesende Variante davon.
Drei Registrierungen erweitern das Maschinencode-Back-End, alle über Target und Schema-Digest verschlüsselt:
MC->RegisterEncoder(MC->Context, RegistrarContext, &EncoderDescriptor);
MC->RegisterDecoder(MC->Context, RegistrarContext, &DecoderDescriptor);
MC->RegisterAsmBackend(MC->Context, RegistrarContext, &BackendDescriptor);Ein Encoder schreibt durch eine Senke, statt einen Puffer zurückzugeben — so bleibt die Eigentümerschaft auf der Host-Seite:
Sink->WriteBytes(Sink->Context, Bytes);
Sink->AddFixup(Sink->Context, &Fixup);Ein Decoder meldet eines von NEVERC_MC_DECODE_SUCCESS, _SOFT_FAIL,
_UNKNOWN oder _FAIL. Fixup-Arten beschreiben sich selbst über
NevercMCFixupKindInfo mit den Flags PC_RELATIVE, SIGNED, RELAXABLE und
TARGET.
Das Asm-Back-End besitzt die Relaxation. Das Layout gibt einen Beweis-Digest aus, und jede Mutation nach dem Layout entwertet diesen Beweis und erzwingt ein erneutes Layout, bevor das Objekt geschrieben werden kann — dasselbe Muster der Generationsprüfung, das auch der Link-Graph verwendet.
Ein Parser-Provider verbraucht Quellbytes und veröffentlicht ein MCUnit:
NevercAssemblyParseInputInfo In = {0};
In.Header = /* … */;
Asm->GetParseInput(Asm->Context, Frame, Frame->Input, &In);
NevercAssemblyTokenInfo Token = {0};
Asm->PeekSourceToken(Asm->Context, Frame, In.Source.Cursor, &Token);
Asm->AdvanceSourceToken(Asm->Context, Frame, In.Source.Cursor);
const NevercMCAPI *MC;
NevercMCUnitHandle Unit;
Asm->GetParseMCBuilder(Asm->Context, Frame, &MC, &Unit);
/* … build into Unit … */
Asm->PublishParsedMCUnit(Asm->Context, Frame, &Output);Quellen sind entweder NEVERC_ASSEMBLY_SOURCE_BUFFER oder
NEVERC_ASSEMBLY_SOURCE_RENDERED_TOKENS. Vorverarbeitete Assembly (.S)
durchläuft zuerst den normalen Frontend-Präprozessor und kommt als gerenderte
Token an; einfache Assembly (.s) gelangt direkt als Puffer in den Parser.
Ein Printer geht den umgekehrten Weg — GetPrintInput, dann
WritePrintOutput in die bereitgestellte Ausgabetransaktion, dann
PublishAssemblyOutput. Anderswohin zu schreiben wird nicht unterstützt: Die
Parse-/Print-Verifikation und das Commit-Gate des Hosts laufen, bevor Bytes
sichtbar werden, sodass ein fehlgeschlagenes Ausgeben keine halbe Datei
zurücklässt.
NevercObjectAPI normalisiert eine relozierbare Datei zu Sections, Symbolen,
Relokationen und COMDATs. Eingebaute Adapter decken ELF, COFF und Mach-O ab;
RegisterFormat fügt ein weiteres hinzu.
NevercObjectGraphInfo Info = {0};
Info.Header = /* … */;
Object->GetGraphInfo(Object->Context, Task, Graph, &Info);
/* Info.Target, .ObjectSchemaDigest, .Generation, .SectionCount,
.SymbolCount, .RelocationCount, .ComdatCount, .HasLayoutProof */
NevercObjectSymbolHandle Symbol;
Object->GetFirstSymbol(Object->Context, Task, Graph, &Symbol);
while (!neverc_handle_is_null(Symbol)) {
NevercObjectSymbolInfo SymInfo = {0};
SymInfo.Header = /* … */;
Object->GetSymbolInfo(Object->Context, Task, Symbol, &SymInfo);
Object->GetNextSymbol(Object->Context, Task, Symbol, &Symbol);
}Die Mutation folgt für alle vier Entitätsarten dem Muster
create/replace/move/erase und wird innerhalb von BeginMutation …
CommitMutation / AbandonMutation vorgemerkt.
Section-Flags sind ALLOCATED, EXECUTABLE, WRITABLE, MERGEABLE,
STRINGS, TLS, DEBUG, UNWIND, DISCARDABLE und RETAIN. Ziele von
Relokationen sind SYMBOL, SECTION, ABSOLUTE oder FORMAT_EXTENSION.
Jeder Deskriptor hat ein Tripel aus ExtensionOwner / ExtensionVersion /
Extension. So bewahrt ein Format Daten auf, für die der normalisierte Graph
kein Feld hat — die Bytes reisen mit der Entität mit und kommen beim Schreiben
zurück, statt beim Hin und Her verloren zu gehen.
NevercObjectFormatDescriptor Format = {0};
Format.Header = /* … */;
Format.FormatID = MyFormatID;
Format.CanonicalName = SV("com.example.myfmt");
Format.SupportedTargets = MyTargets;
Format.DefaultExtension = SV(".mof");
Format.Flags = NEVERC_OBJECT_FORMAT_CAN_PROBE |
NEVERC_OBJECT_FORMAT_CAN_READ |
NEVERC_OBJECT_FORMAT_CAN_WRITE;
Format.Probe = probe;
Format.Reader = read;
Format.Writer = write;
ObjectFormat->RegisterFormat(ObjectFormat->Context, RegistrarContext,
&Format);Probe meldet eine Confidence von 0 bis
NEVERC_OBJECT_PROBE_MAX_CONFIDENCE (1000), die erkannte
NevercObjectArtifactKind (RELOCATABLE, ARCHIVE, EXECUTABLE_IMAGE,
SHARED_IMAGE, UNIVERSAL_BINARY) und ein ConsumedMinimum — wie viele
Bytes es zur Gewissheit brauchte, begrenzt auf
NEVERC_OBJECT_PROBE_MAX_CONSUMED_MINIMUM (65536). Die höchste Zuversicht
gewinnt.
Reader bekommt einen Graphen und eine offene Mutation und füllt beide.
Writer bekommt den Graphen, seinen Layout-Beweis und den begrenzten
Binär-Builder.
- sondieren und Bytes in einen ObjectGraph einlesen;
- die Graph-Interceptors
object.pre_writeausführen; - Layout erstellen, dann
object.post_layoutausführen (nach jeder Mutation neu layouten); - ein begrenztes Kandidaten-Image schreiben;
- die Binär-Interceptors
object.post_writeausführen; - das versiegelte
object.final_verifyund das atomareobject.commitausführen.
Der Image-Zustand wandert CANDIDATE → VERIFIED → COMMITTED oder
ABORTED / FAILED_PARTIAL.
Beobachter erhalten rein lesende Brücken; eine aus einem Beobachter versuchte
Mutation wird mit NEVERC_STATUS_POLICY_VIOLATION abgelehnt. Writer und
Post-Write-Interceptors bekommen nur den begrenzten Builder
NevercMutableBinaryAPI — Reserve, Write, WriteAt, Tell, ReadAt,
Insert, Append, Resize. Ein Überlauf, ein fehlgeschlagener Callback oder
eine gescheiterte Verifikation bricht das Vormerken ab, sodass ein Fehlschlag
nie eine halbe Datei auf der Platte hinterlässt.
pluginsdk/examples/ObjectRewritePlugin.c ist eine vollständige
transaktionale Umschreibung.
- Vergleichen Sie den Schema-Digest, bevor Sie irgendeinen LOCKSTEP-Wert für Opcode, Register, Operand, Fixup, Relokation oder Aufrufkonvention verwenden.
- Halten Sie veränderlichen Zustand im vom Host bereitgestellten Process-, Session- und Task-Zustand.
- Cachen Sie keine Task-Handles oder geliehenen Views, nachdem ein Callback zurückgekehrt ist.
- Rufen Sie die Fortsetzung eines Interceptors höchstens einmal auf, und zwar auf dem Callback-Thread.
- Jedes
BeginMutationerreicht genau ein Commit oder ein Abandon. - Layouten Sie neu, nachdem Sie ein bereits layoutetes MCUnit oder einen ObjectGraph verändert haben; der alte Layout-Beweis ist veraltet und der Host wird ihn zurückweisen.
- Prüfen Sie
NevercMCEmissionEventInfo.Flags, bevor Sie ein Ereignisfeld lesen, und ersetzen Sie eine Instruktion nur, wennCAN_REPLACE_INSTRUCTIONgesetzt ist. - Schreiben Sie Ausgaben ausschließlich über die bereitgestellte Transaktion oder Byte-Senke.
- Geben Sie im Fehlerfall den ursprünglichen
NevercStatuszurück und veröffentlichen Sie nichts Teilweises. - Deklarieren Sie die engsten zutreffenden Modelle für Nebenläufigkeit und Reentranz.
codegen.product_verify,assembly.final_verify,assembly.commit,object.final_verifyundobject.commitsind versiegelt. Nur beobachten.
Die normativen Deklarationen finden Sie in PluginTarget.h, PluginMC.h,
PluginObject.h und Schema/PhaseSchema.json; die Entitäts-, Operanden-,
Fixup- und Abschnittsarten, die sie verwenden, stammen aus
Schema/MCSchema.json und Schema/ObjectSchema.json, aus denen
Schema/PluginMCSchema.inc und Schema/PluginObjectSchema.inc erzeugt
werden. coverage.json ordnet jeder dieser stabilen Phasen ihre positiven,
negativen, Ersetzungs-, Nur-Lese-Beobachter- und Sealed-Gate-Tests zu.