====== Lyx – Attribute & Pragmas (@-Referenz) ====== Attribute (auch Pragmas genannt) sind Compiler-Anweisungen, die mit ''@'' beginnen und vor einer Deklaration stehen. Sie steuern Optimierungen, Sicherheits-Verifikation, Speicherlayout und FFI-Verhalten. Attribute verändern nie die Semantik des Programms für korrekte Eingaben — sie sind Zusatzinformationen für den Compiler, nicht für den Laufzeit-Interpreter. → [[lyx_-_programmiersprache:tools:compiler-parameter|Compiler-Flags (CLI)]] · [[lyx_-_programmiersprache:guides:do-178c|DO-178C Compliance]] · [[lyx_-_programmiersprache:guides:das-energy-aware-programmiermodell|Energy-Aware Modell]] · [[lyx_-_programmiersprache:sprache:ffi|FFI – C-Interop]] ---- ===== Übersicht ===== Alle Attribute auf einen Blick: ^ Attribut ^ Ziel ^ Kategorie ^ Beschreibung ^ | ''@dal(A–D)'' | Unit, Fn | Safety | Design Assurance Level nach DO-178C | | ''@flight_crit'' | Unit, Fn | Safety | Strikte Luftfahrt-Regeln | | ''@critical'' | Fn | Safety | Als kritisch markieren | | ''@integrity(mode: …)'' | Fn (''scrubbed'' auch vor ''unit'') | Safety | ''mode: scrubbed'' (Sweep über die geladenen Codeseiten) oder ''mode: software_lockstep'' (Rückgabeausdruck doppelt gerechnet) | | ''@redundant'' | Variable | Safety | Triple Modular Redundancy (TMR) | | ''@stack_limit(N)'' | Fn | Safety | Maximaler Stack-Verbrauch in Bytes | | ''@wcet(N)'' | Fn | Safety | Worst-Case Execution Time in Zyklen | | ''@energy(1–5)'' | Unit, Fn | Performance | Energy-Aware-Backend-Level | | ''@packed'' | Struct | Layout | Struct-Padding unterdrücken | | ''@align(N)'' | Variable | Layout | Allokations-Alignment in Bytes | | ''@big_endian'' | Struct-Feld | Layout | Feld liegt in Network Byte Order; Zugriffe erzeugen einen Byte-Swap. **''@little_endian'' gibt es am Feld nicht** — siehe unten | | ''@volatile'' | Variable | FFI | Speicherzugriff nicht wegoptimieren | | ''@description'' / ''@author'' / ''@copyright'' / ''@version'' | Modulkopf | Doku | Beschreibende Angaben vor ''unit''/''import'' | ===== Prüfstand: was jedes Attribut mit 1.1.14A tatsächlich tut ===== Jedes Attribut einzeln übersetzt, jeweils gegen dasselbe Programm ohne Attribut verglichen (Rückgabewert, Meldungen, Größe des Erzeugnisses). ^ Attribut ^ Angenommen ^ Wirkung, gemessen ^ | ''@dal(A)''…''@dal(D)'' | ja | **Warnung**: „''@dal'': die Zusicherung wird vom Compiler NICHT nachgewiesen". Erzeugnis unverändert. ''@dal(Z)'' bricht ab: „Stufe muss A, B, C oder D sein" | | ''@flight_crit'' | ja, ohne Warnung | Erzeugnis **wächst** (4243 → 4327 Byte) — das Attribut schaltet tatsächlich etwas frei (FPU-Traps, #1140) | | ''@critical'' | ja | **Warnung**: wird nicht nachgewiesen. Erzeugnis unverändert | | ''@integrity(mode: scrubbed)'', ''@integrity(mode: software_lockstep)'' | ja | **wird nachgewiesen** (seit 1.1.15A): Hashtabelle ''METASAF2'' plus SIGALRM-Sweep bzw. doppelt gerechneter Rückgabeausdruck. Nur x86-64. Die Kurzformen ohne ''mode:'' und der Modusname ''memory_scrubbing'' werden **abgewiesen** | | ''@redundant'' | ja | Wirkt: ''%%--%%verify-tmr'' meldet „1 ''@redundant''-Variable(n), 1 gevotete(r) Lesezugriff(e), 0 am Voter vorbei" | | ''@stack_limit(N)'' | ja | **Wird nachgewiesen und bricht ab**: ''@stack_limit(8)'' an einer Funktion mit 16-Byte-Rahmen → „''@stack_limit(8)'' verletzt — der Rahmen belegt 16 Byte". Gezählt wird der **Rahmen**, nicht statisch abgelegte Felder | | ''@wcet(N)'' | ja | **Wird nachgewiesen**: ''@wcet(1)'' an einer Schleife mit 1000 Durchläufen → „''@wcet(1)'' verletzt — der Rumpf laeuft bis zu 1000 Iterationen". Ein Aufruf ohne Schranke bricht ebenfalls ab: „''PrintLn'' liegt nicht in dieser Einheit und traegt keine Schranke" | | ''@energy(1–5)'' | ja | Übersetzt; Erzeugnis in diesem Prüffall unverändert | | ''@packed'' | ja | Wirkt: ''struct{int16,int8,int16}'' misst 5 statt 6 Byte | | ''@align(N)'' | ja | Übersetzt an einer lokalen Variablen | | ''@big_endian'' | ja, an jedem Feld | Byte-Swap bei jedem Zugriff | | ''@little_endian'' | **nein** | Parse error, auch am ersten Feld ([[https://github.com/SEOLizer/LyX-Compiler/issues/1864|#1864]]) | | ''@volatile'' | ja | Übersetzt an einer lokalen Variablen | | ''@description'', ''@author'', ''@copyright'', ''@version'' | ja | Übersetzt; reine Metadaten | > **Die Warnung bei ''@dal'', ''@critical'' und ''@integrity'' ist die ehrliche Auskunft, kein Mangel.** Der Compiler sagt damit: das Attribut **dokumentiert** eine Zusicherung, er prüft sie nicht. Nachgewiesen werden nur ''@stack_limit'', ''@wcet'' und — mit ''%%--%%verify-tmr'' — ''@redundant''. Wer die drei anderen in einem Zertifizierungsnachweis führt, braucht dafür ein Verfahren außerhalb des Compilers. ---- Externe Funktionen werden **ohne Attribut** deklariert — mit dem Schlüsselwort ''extern'' (siehe [[lyx_-_programmiersprache:sprache:ffi|FFI]]). > **Diese Attribute gibt es nicht.** Frühere Fassungen dieser Seite führten sie mit eigenen Abschnitten; der Compiler weist jeden unbekannten Attributnamen seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1099|#1099]] mit ''unbekanntes Attribut'' ab: > > ''@inline'' · ''@noinline'' · ''@no_opt'' · ''@parallel'' · ''@section("name")'' · ''@at(N)'' · ''@stdcall'' · ''@variadic'' · ''@allow_unsafe'' > > ''@export'' gehört **nicht** in diese Liste — es existiert und wird an ''extern fn'' angenommen. ([[https://github.com/SEOLizer/LyX-Compiler/issues/1247|Issue #1247]]) > > Damit entfällt auch die Vorstellung, Inlining oder Vektorisierung ließen sich per Annotation steuern — das Backend entscheidet das selbst, beeinflussbar allein über ''@energy'' und ''--target-energy''. > ''@allow_unsafe'' hätte ''unsafe''-Blöcke freigeben sollen; auch die gibt es nicht (''undefined symbol '\''unsafe'\''''). ---- ===== Safety-Attribute ===== Attribute für sicherheitskritischen und zertifizierungspflichtigen Code: ==== @dal(A–D) ==== Legt das **Design Assurance Level** nach DO-178C fest. Beeinflusst, welche Lint-Prüfungen aktiv sind und ob bestimmte Sprachfeatures verboten werden. @dal(A) unit flight_control; @dal(B) fn ProcessSensorData(raw: int64): int64 { ... } ^ Level ^ Bedeutung ^ Einschränkungen ^ | ''@dal(A)'' | Katastrophaler Ausfall möglich | Kein unbounded loop, exhaustive match | | ''@dal(B)'' | Gefährlicher Ausfall möglich | Unbounded loops mit Warnung | | ''@dal(C)'' | Größerer Ausfall möglich | Empfehlungen, keine harten Verbote | | ''@dal(D)'' | Geringfügiger Ausfall möglich | Nur Dokumentation, keine Compiler-Einschränkungen | ---- ==== @flight_crit ==== Aktiviert den strengsten Kompilierungsmodus für sicherheitskritische Funktionen: @flight_crit @dal(A) @stack_limit(512) fn ComputeFlightPath(state: int64): FlightCommands { ... } Wirkung zur Laufzeit: Der Prolog der Funktion schaltet die SSE-Ausnahmen für //invalid// (NaN) und //divide-by-zero// frei und sichert MXCSR, der Epilog stellt den alten Wert wieder her. Eine entstehende NaN oder Inf löst damit SIGFPE aus statt still weiterzurechnen; der mitgegebene Handler meldet ''panic: FPU-Ausnahme (NaN/Inf oder Division durch 0) unter @flight_crit in `NAME`'' und endet mit 134. **Die Reichweite ist dynamisch:** MXCSR ist Thread-Zustand, der Trap gilt also auch für alles, was die Funktion ruft, bis sie zurückkehrt. Überlauf, Unterlauf und Ungenauigkeit bleiben maskiert. Impliziert automatisch: * ''@energy(1)'' — kein aggressives Unrolling, keine SIMD-Spekulation * keine FPU-Optimierungen für diese Funktion (ein globales Flag dafür gibt es nicht) * Verbot von ''new''/''alloc'' im Rumpf * Verbot von Rekursion ohne ''@stack_limit'' * Exhaustive ''match'' über alle Enum-Werte (kein ''default'' in DAL-A) Das Speicherverbot ist seit lyxc 1.0.21B durchgesetzt ([[https://github.com/SEOLizer/LyX-Compiler/issues/1529|#1529]]) und meldet, gemessen mit 1.1.3I: sema error: Speicheranforderung unter @flight_crit nicht erlaubt 'alloc' sema error: new ist unter @flight_crit nicht erlaubt — der Speicher muss ausserhalb angelegt und hereingereicht werden sema error: lokales Feld unter @flight_crit nicht erlaubt (es entsteht per mmap, nicht auf dem Stapel) > **Die Prüfung reicht seit ''lyxc 1.1.4A'' über Aufrufgrenzen hinweg** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1701|#1701]], nachgemessen mit 1.1.11B). Ruft eine ''@flight_crit''-Funktion einen Helfer, der ''alloc'' benutzt, meldet der Compiler: > > ''sema error: Speicheranforderung in einer Funktion, die aus einer @flight_crit-Funktion gerufen wird 'alloc' '' > > Die Prüfung folgt der Aufrufkette mit Zyklusschutz — auch über zwei und mehr Ebenen — und kennt neben ''new''/''alloc'' auch die allozierenden Zeichenketten-Funktionen (''IntToStr'', ''StrConcat'', ''StrNew'', ''StrCopy'', ''StrSub'', ''StrTrim'', ''FloatToStr''). > > **Ein Aufruf über einen Funktionszeiger wird jetzt rundheraus abgewiesen** — ''indirekter Aufruf unter @flight_crit nicht erlaubt — das Sprungziel steht erst zur Laufzeit fest und laesst sich nicht nachweisen''. Was sich nicht nachweisen lässt, gilt damit nicht mehr als unauffällig. Kompatibel mit ''@energy(2)'' wenn explizit annotiert (zulässig für DAL-B): @flight_crit @energy(2) // energy-Barriere: überschreibt das implizite @energy(1) @dal(B) fn FilterAltitude(raw: f64): f64 { ... } ---- ==== @integrity(...) ==== Aktiviert Laufzeit-Integritätsprüfung. Zwei Modi: // Modus 1: Memory Scrubbing (periodischer CRC32-Sweep des Code-Segments) @integrity(mode: scrubbed, interval: 100) unit nav.core; // Modus 2: Software Lockstep (duplizierte ALU-Operationen, Vergleich vor Return) @integrity(mode: software_lockstep) @dal(A) fn ComputeHeading(imu: int64): f64 { ... } ^ Parameter ^ Typ ^ Beschreibung ^ | ''mode: scrubbed'' | – | //Vorgesehen//: CRC32-Sweep des Code-Segments alle ''interval'' ms | | ''mode: software_lockstep'' | – | //Vorgesehen//: Berechnungen doppelt ausführen, Ergebnis vergleichen | | ''interval: N'' | int (ms) | Sweep-Intervall für ''scrubbed''-Modus | > **''@integrity'' hat heute keine Wirkung** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1878|#1878]]). Der Compiler nimmt das Attribut an, meldet ''warning: @integrity: die Zusicherung wird vom Compiler NICHT nachgewiesen'' — und erzeugt ein **byte-gleiches** Programm (4243 Byte mit ''scrubbed'', mit ''software_lockstep'' und ohne Attribut, gemessen mit 1.1.14A). Kein Sweep, keine doppelte Berechnung, kein Recovery-Handler. > > Auch die ''.meta_safe''-Sektion entsteht dadurch **nicht** — die gibt es nur mit ''%%--%%meta-safe'' auf der Kommandozeile. > > Ursache im Quelltext: ''src/ir/ir_safety.lyx'' trägt die Pragma-Logik, wird aber nirgends importiert oder gebaut. Das erklärt, warum ''@integrity'', ''@critical'' und ''@dal'' allesamt nur warnen, während ''@stack_limit'' und ''@wcet'' — die im Codegen sitzen — tatsächlich nachgewiesen werden. → Details: [[lyx_-_programmiersprache:guides:do-178c:memory-scrubbing|Memory Scrubbing]] · [[lyx_-_programmiersprache:guides:do-178c:software-lockstep|Software Lockstep]] ---- ==== @redundant ==== Speichert eine Variable dreifach im RAM (Sektionen ''.tmr_a'', ''.tmr_b'', ''.tmr_c''). Jeder Lesezugriff führt einen Mehrheitsentscheid durch; ein korrumpiertes Exemplar wird automatisch repariert. @redundant var flight_mode: int64 := 0; @redundant var altitude_m: f64 := 0.0; * Nur für globale und Unit-Level-Variablen; Stack-Variablen werden vom Compiler abgelehnt * Speicherbedarf: 3× Variablengröße plus Alignment * Die Adresse-von-Form ''@x'' liefert die Adresse **einer** Kopie und umgeht damit den Voter — ein Schreibzugriff darüber geht beim nächsten Mehrheitsentscheid verloren. Der Compiler meldet das: unter ''%%--%%verify-tmr'' als Fehler, sonst als Warnung * ''%%--%%verify-tmr'' druckt die Bilanz (Variablen, gevotete Lesezugriffe, dreifache Schreibzugriffe, Umgehungen) und endet mit Exit 1, sobald ein Zugriff am Voter vorbeigeht → Details: [[lyx_-_programmiersprache:guides:do-178c:triple_modular_redundancy|TMR]] ---- ==== @stack_limit(N) ==== Setzt ein hartes Limit für den Stack-Verbrauch einer Funktion in Bytes. Wird bei **jeder** Übersetzung geprüft — ein Flag dafür gibt es nicht und ist nicht nötig. Geprüft werden zwei Dinge: die **Rahmengröße** der Funktion selbst gegen die Schranke, und dass die Funktion **nicht rekursiv** ist (auch nicht indirekt über den Aufrufgraphen) — ohne nachweisbare Aufruftiefe wäre der Verbrauch unbeschränkt. Beides sind harte Fehler. **Nicht** erfasst: der Verbrauch der aufgerufenen Funktionen entlang der Aufrufkette und dynamisch angeforderter Speicher. Zu beachten: ein ''[N]int64'' liegt nicht im Rahmen — Arrays bekommen einen Heap-Block, der Rahmen wächst nur um 8 Byte je Variable. @stack_limit(2048) fn ParsePacket(buf: pchar, len: int64): bool { ... } @stack_limit(128) @flight_crit fn SysTick_Handler(): void { ... } // ISR: sehr wenig Stack erlaubt * Bei Überschreitung: Compiler-Fehler (kein Laufzeitfehler) * Rekursion ohne nachweisbare Schranke → automatischer Fehler ---- ==== @wcet(N) ==== Gibt die maximal erlaubte **Worst-Case Execution Time** in **Iterationen** an — nicht in CPU-Zyklen. Eine Zyklenzahl bräuchte ein Mikroarchitekturmodell; Iterationen sind das, was sich am Syntaxbaum abzählen lässt. @wcet(200) @dal(A) fn ReadIMU(buf: int64): void { ... } @wcet(500) fn FilterAltitude(raw: f64): f64 { ... } * Der Compiler zählt die Iterationen kumulativ über Loop-Bounds und Aufrufgraph: eine Schleife mit Schranke B, in deren Rumpf I Iterationen stecken, trägt ''B * (1 + I)'' bei — zwei geschachtelte Zehnerschleifen ergeben 110 * Abzählbar sind ''for … to''/''downto'' und ''for i in a..b'' mit literalen Grenzen, ''for i in range(A, B)'', ''while (c) limit(N)'' und ''while (i < C)'' mit literalem Startwert, konstanter Grenze und genau einer Fortschaltung ''i := i + K'', K > 0 * Alles andere ist in einer ''@wcet''-Funktion ein **Fehler**, kein stiller Durchlass: berechnete Schleifengrenzen, ''repeat/until'', das C-artige ''for'', Rekursion (auch indirekt) und der Aufruf einer Funktion ohne eigene Schranke — importierte Funktionen eingeschlossen, ''PrintLn'' also auch * Software-Lockstep verdoppelt die effektive WCET (~2×) * Ein ''%%--%%wcet''-Flag gibt es nicht — der Nachweis läuft bei jeder Übersetzung. Für den Aufrufgraphen ''%%--%%call-graph'' verwenden ---- ==== @energy(1–5) ==== Steuert das Energy-Aware-Backend für diese Funktion. Es überschreibt das globale ''--target-energy''-Level und erzeugt eine **Energy-Barriere** — inlinierte Aufrufer übernehmen das Level dieser Funktion nicht. Die Vorrangregel lautet seit lyxc 1.0.15C: - ''@energy(N)'' an der Funktion - ''@energy(N);'' als Direktive im Quelltext - ''--target-energy=N'' auf der Kommandozeile - Vorgabe **3** Der Quelltext schlägt also das Flag. Zu beachten: Funktionen **ohne** Attribut — auch die aus importierten Units — folgen weiterhin dem Flag; ein Programm ist deshalb mit und ohne ''--target-energy'' nur dann byteidentisch, wenn jede beteiligte Funktion annotiert ist. @energy(1) fn IdleSensor(): void { ... } // Batterie-optimiert @energy(5) fn MatMulKernel(a: int64, b: int64, out: int64): void { ... } // Max-Performance → Details und Level-Tabelle: [[lyx_-_programmiersprache:guides:das-energy-aware-programmiermodell|Energy-Aware Modell]] ---- ==== @packed ==== Unterdrückt Alignment-Padding in einer Struct-Definition. Felder folgen ohne Lücken aufeinander. Notwendig für Hardware-Register-Maps, Netzwerk-Paket-Header, binäre Protokolle. Es gibt zwei gleichwertige Schreibweisen — das Attribut vor der Deklaration oder das Schlüsselwort ''packed'' vor ''struct'': @packed type CANFrame = struct { id: int32; // 4 Bytes (11-Bit-ID + Flags) dlc: int8; // 1 Byte (Data Length Code) data: int64; // 8 Bytes (Nutzdaten) } type IpFragment = packed struct { ident: int16; frag_off: int16; ttl: int8; } Felder stehen **ohne** ''var'' und die Struktur braucht die Form ''type Name = struct''. Ein alleinstehendes ''class Name { … }'' wie in C++ oder Java weist der Compiler mit ''unexpected top-level token'' ab. Die Wirkung lässt sich mit ''sizeof'' nachmessen: type Locker = struct { a: int16; b: int8; c: int16; } type Dicht = packed struct { a: int16; b: int8; c: int16; } fn main(): int64 { PrintLn("struct = ", IntToStr(sizeof(Locker))); PrintLn("packed struct = ", IntToStr(sizeof(Dicht))); return 0; } struct = 6 packed struct = 5 Fünf Nutzbytes gegen sechs: die reguläre Ausrichtung legt jedes Feld auf ein Vielfaches seiner **eigenen** Breite (''int16'' auf 2, ''int8'' auf 1) — ''a'' auf 0, ''b'' auf 2, ein Füllbyte, ''c'' auf 4. Das entspricht dem Speicherbild eines C-Structs. **Bis 1.0.21A lag jedes Feld auf einer 8-Byte-Grenze** (#1516), derselbe Struct maß dort 18 Byte; ältere Fassungen dieser Seite zeigen diese Zahl. ''packed'' bleibt für Protokollköpfe richtig, wo auch das eine Füllbyte stört — nachgemessen mit 1.1.14A. * Unaligned-Zugriffe können auf manchen Architekturen (ARM Cortex-M0) Faults auslösen * Auf x86_64 immer sicher, auf ARM64 mit LDUR/STUR ---- ==== @big_endian ==== Markiert ein Struct-Feld als **Big-Endian** (Network Byte Order). Lese- und Schreibzugriffe auf das Feld erzeugen automatisch Byte-Swap-Instruktionen (''bswap'' auf x86_64, ''rev'' auf ARM64). type IpHeader = packed struct { @big_endian ethertype: int16; // z. B. 0x0800 für IPv4 @big_endian total_len: int16; @big_endian ident: int16; @big_endian frag_off: int16; ttl: int8; protocol: int8; @big_endian checksum: int16; }; Das Attribut steht direkt vor dem Feldnamen — ohne ''var'' und ohne Offset-Angabe. Für ein festes Speicherbild sorgt ''packed struct'' (→ [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]]). Seit lyxc 1.0.15G schreibt eine Feldzuweisung genau so viele Bytes, wie das Feld breit ist (''mov [rax+off], sil'' / ''si'' / ''esi'' bei 1, 2 und 4 Byte) — Nachbarfelder bleiben unberührt, in gepackten wie in ungepackten Strukturen. Die Reihenfolge der Zuweisungen spielt damit keine Rolle mehr. * Nur sinnvoll für Felder ≥ 2 Bytes (''int8'' ist byteorder-neutral) * Der Swap greift bei **jedem** Zugriff, in beide Richtungen. Schreiben und anschließendes Lesen desselben Feldes liefert deshalb wieder den Ausgangswert — sichtbar wird die Wirkung erst am Rohspeicher: Steht dort die ''1'', liefert das ''@big_endian''-Feld ''16777216'' (0x01000000). > **''@little_endian'' ist am Struct-Feld nicht schreibbar** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1864|#1864]], gemeldet aus dieser Prüfung). Der Name steht zwar in der Attributliste des Parsers und im Hilfetext, wird an einer Feldposition aber nie angenommen: > > type K = struct { @little_endian a: int32; // Parse error at line 2: expected IDENT, got @ '@' b: int32; } > > Mit ''@big_endian'' an derselben Stelle übersetzt dasselbe Programm. Little-Endian ist auf x86-64 und ARM64 ohnehin die Speicherordnung — wer sie ausdrücklich hinschreiben will, kann es derzeit nicht. Frühere Fassungen dieser Seite führten beide Namen gleichrangig. Gemessen mit 1.1.14A. > **Die frühere Schreibweise ''%%@at(0) @big_endian var feld: u16;%%'' ist in keinem Teil gültig.** ''@at'' existiert nicht (''unbekanntes Attribut''), und die Kombination scheitert schon am Parser (''expected IDENT, got @''). Auch ''var'' gehört nicht in eine Struct-Deklaration. > > ''@big_endian'' selbst arbeitet korrekt und braucht **keine** Offset-Angabe. ([[https://github.com/SEOLizer/LyX-Compiler/issues/1247|Issue #1247]]) ---- ===== Ressourcen einbetten ===== Seit [[https://github.com/SEOLizer/LyX-Compiler/issues/1961|#1961]] lassen sich Dateien **ins Erzeugnis einbetten** — das Gegenstück zu Delphis ''.res''. Es gibt zwei Wege, und sie beantworten verschiedene Fragen. ==== @resource("name", "pfad") ==== Auf **Unit-Ebene**. Trägt die Datei in eine **benannte Tabelle** ein, die zur Laufzeit durchsucht und aufgelistet werden kann: unit main; @resource("splash", "assets/splash.lyxi"); import std.res; fn main(): int64 { var bild: pchar := ResFind("splash"); var n: int64 := ResLen("splash"); return 0; } Der Pfad wird **relativ zur Quelldatei** aufgelöst. Fehlt die Datei, bricht die Übersetzung ab (''error: @resource: Datei nicht lesbar: …'') — eine fehlende Ressource fällt beim Bauen auf, nicht beim Kunden. Die Zugriffsfunktionen stehen in [[lyx_-_programmiersprache:units:res|std.res]]. **''@resource'' gibt es bisher nur mit ''%%--%%target=x86_64''.** Auf dem IR-Weg (''lyxos'', ''arm64'', ''riscv'', ''xtensa'') bricht die Übersetzung mit einer Meldung ab, die auf [[https://github.com/SEOLizer/LyX-Compiler/issues/1968|#1968]] verweist: die Tabelle besteht aus Zeigern in den Datenbereich, und dort gibt es bisher nur die nullterminierte Zeichenkettentabelle. ==== @embed("pfad") und @embed_len("pfad") ==== Im **Ausdruck**, aufgelöst zur Übersetzungszeit: var s: pchar := @embed("assets/hilfe.txt"); var n: int64 := @embed_len("assets/hilfe.txt"); Der kürzere Weg, wenn genau eine Datei an genau einer Stelle gebraucht wird — dafür ohne Namen, ohne Auflisten und ohne Auswahl zur Laufzeit. Auf den IR-Zielen ist das der einzige Weg, und dort für **Textdateien**. **Warum ein Zeiger und kein ''[N]uint8''-Feld:** Lyx legt Felder mit **acht Byte je Element** ab, auch bei ''uint8''. Ein eingebettetes 1-MB-Bild bräuchte damit 8 MB im Erzeugnis. Die Bytes liegen deshalb **gepackt** im Datensegment, und ''@embed'' liefert die Adresse. Gelesen wird mit ''peek8''. ---- ===== FFI-Attribute ===== Attribute für die Interoperabilität mit externen Bibliotheken und C-Code: ==== extern (Schlüsselwort, kein Attribut) ==== Externe Funktionen werden mit dem Schlüsselwort ''extern'' deklariert — ein Attribut ''@extern'' gibt es nicht. @capabilities([system.exit, system.memory.heap, system.time]) import std.io; extern fn time(t: int64): int64 link "libc.so.6"; fn main(): int64 { PrintLn(IntToStr(time(0))); return 0; } * Kein Funktionskörper — nur Signatur * Die **''link''-Klausel ist Pflicht** und nennt die Bibliothek mit ihrem vollständigen Dateinamen (''"libc.so.6"'', nicht ''"c"'') * ABI: Standard-C-Aufrufkonvention (System V AMD64 auf Linux, AAPCS64 auf ARM64) * **Fail-closed**: Der Compiler kennt eine Zuordnung von FFI-Symbol zu Capability. Ohne passendes ''@capabilities'' wird die Deklaration abgewiesen (''extern fn: unbekanntes FFI-Symbol erfordert @capabilities([...])''), ebenso ein Symbol, das nicht in der Zuordnung steht. > **Die ''link''-Klausel ist seit lyxc 1.0.17C Pflicht** (#1179). Fehlt sie, bricht schon der Parser ab: ''extern fn: die link-Klausel fehlt''. Bis 1.0.16x übersetzte die Deklaration auch ohne, das Symbol wurde nie gebunden und der Aufruf lieferte still ''0'' — bei C-Funktionen ein plausibler Wert, der Fehler fiel damit kaum auf. Die eigene Standardbibliothek trug 33 solcher Deklarationen (''std.time.Now()'' gab 0, ''env_get'' immer ''""'', ''abort()'' beendete nichts); alle sind ersetzt. > > Ein **falscher** Bibliotheksname wird weiterhin nicht zur Übersetzungszeit gemeldet — den vollständigen Dateinamen angeben (''"libc.so.6"'', nicht ''"c"'') und einen neuen FFI-Aufruf einmal gegen einen bekannten Wert prüfen. → Details: [[lyx_-_programmiersprache:sprache:ffi|FFI – C-Interop]] · [[lyx_-_programmiersprache:sprache:capabilities|Capabilities]] ---- ==== @align(N) ==== Setzt das Allokations-Alignment einer Variablen in Bytes. Wirkt auf array- und heap-gestützte lokale Variablen; bei skalaren Stack-Variablen ohne Bedeutung. fn main(): int64 { @align(64) var puffer: array := [1, 2]; return puffer[0]; } ---- ==== @volatile ==== Verhindert, dass der Compiler Lese- und Schreibzugriffe auf eine Variable wegoptimiert, zwischenspeichert oder umordnet. Essentiell für Memory-Mapped I/O und ISR-Kommunikation. @volatile var uart_data_reg: int64 := 0x40011004; // UART-Datenregister-Adresse @volatile var isr_flag: int64 := 0; // Gesetzt von ISR, gelesen von Main fn WaitForData(): void { while (isr_flag == 0) limit(1000000) { } // Ohne @volatile würde der Compiler dieses Loop wegoptimieren isr_flag := 0; } ---- Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen). Letzte Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1961|#1961]], nachgemessen mit lyxc 1.2.2D) — Abschnitt „Ressourcen einbetten" ergänzt: ''@resource'' (benannte Tabelle, nur x86-64) und ''@embed''/''@embed_len'' (zur Übersetzungszeit aufgelöst). Vorherige letzte Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1908|#1908]], gemessen mit lyxc 1.1.18A) — ''@integrity''-Zeilen auf den gemessenen Stand gezogen: seit 1.1.15A nachgewiesen (kein „Erzeugnis unverändert" mehr), Prüfstandszeile von den ungültigen Kurzformen ''memory_scrubbing''/''software_lockstep'' auf ''mode: scrubbed'' bzw. ''mode: software_lockstep'' berichtigt. Vorherige letzte Aktualisierung: 2026-08-30 — alle 14 Attribute einzeln gegen ''lyxc 1.1.14A'' durchgemessen (siehe Abschnitt „Prüfstand"); ''struct''-Größe im ''@packed''-Beispiel berichtigt (6 statt 18, #1516 behoben); ''@little_endian'' am Feld als nicht schreibbar belegt und gemeldet (#1864).