====== 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).