Skip to content

feat: implement export(abi), standalone Windows linking, and ASM stack contracts#316

Merged
LunaStev merged 2 commits into
wavefnd:masterfrom
LunaStev:feat/add-`export(abi)`-syntax,-standalone-Windows-linkage,-and-inline-ASM-stack-contracts
May 19, 2026
Merged

feat: implement export(abi), standalone Windows linking, and ASM stack contracts#316
LunaStev merged 2 commits into
wavefnd:masterfrom
LunaStev:feat/add-`export(abi)`-syntax,-standalone-Windows-linkage,-and-inline-ASM-stack-contracts

Conversation

@LunaStev
Copy link
Copy Markdown
Member

This PR introduces critical low-level features required for OS development, shared library creation, and bare-metal programming. Key highlights include the export(abi) syntax for exposing Wave functions to external callers, a fully standalone Windows linking pipeline, and a sophisticated "Stack Contract" system for inline assembly to prevent memory corruption in systems-level code.

Key Changes

1. export(abi) Support

Wave can now formally export functions to C or Assembly callers, eliminating the need for hacky internal symbol manipulation.

  • Syntax: Supports export(c) fun name(), export(c, "symbol_name") fun, and block-based export(c) { ... }.
  • Linkage: Functions marked with export receive External linkage in LLVM and the appropriate calling convention.
  • Entry Points: Custom symbol redirection ("symbol_name") allows developers to define exact OS entry points like _start (Linux) or efi_main (UEFI).

2. Standalone Windows GNU Linking

The Windows cross-compilation pipeline is now fully self-contained.

  • Direct Linker: The compiler now prefers and invokes the bundled ld.lld instead of relying on a system-installed gcc or mingw.
  • Bundled CRT: Automatically links against the bundled MinGW C runtime (crt2.o) and essential Windows libraries (-lkernel32, -luser32, -lmsvcrt) when the standard library is enabled.

3. Inline Assembly Stack Contracts

To prevent subtle and dangerous bugs in kernel-space, Wave now enforces "Stack Contracts" for inline assembly blocks.

  • Static Analysis: Implemented architecture-specific scanners (x86_64, AArch64, RISC-V) that inspect ASM instructions for SP mutations (push, pop, sub rsp, etc.) or branching (ret, jmp).
  • Pseudo-Clobbers:
    • clobber("stack"): Declares that the ASM block modifies the stack pointer.
    • clobber("noreturn"): Declares a terminal assembly block (e.g., a CPU reset or infinite loop). Generates an LLVM unreachable instruction for optimization.
    • clobber("nostack"): The default contract; asserts the stack pointer remains unchanged.
  • Strict Enforcement: The compiler will trigger a compile-time error if an ASM block modifies the stack pointer without an explicit clobber("stack") or clobber("noreturn").

4. Freestanding Backend Attributes

  • Kernel Safety: Functions targeted for freestanding (--freestanding) environments now automatically receive the noredzone and nounwind LLVM attributes.
  • Rationale: This ensures safe interrupt handling (no Red Zone corruption) and prevents the generation of implicit exception unwinding tables in environments where no unwinder exists.

5. Parser Refinement

  • deref Lowering: Fixed an issue where deref ptr = x assignments were handled incorrectly. These are now properly lowered into explicit Expression::Assignment nodes during parsing.
  • FFI Consolidation: Refactored parse_extern_header into a generalized parse_ffi_header to share logic between extern and export blocks.

Rationale

These features provide the "final mile" for developers using Wave for systems-level tasks. Explicit exports allow Wave code to be called from C/C++, while stack contracts and freestanding attributes provide the safety guarantees necessary for writing reliable kernels and firmware.

Example Usage

Exporting a function with a custom symbol:

export(c, "wave_add_i32") fun add_i32(a: i32, b: i32) -> i32 {
    return a + b;
}

export(c) {
    fun wave_inc_i32(a: i32) -> i32 {
        return a + 1;
    }

    fun wave_dec_i32(a: i32) -> i32 {
        return a - 1;
    }
}

LunaStev added 2 commits May 19, 2026 20:34
…e ASM stack contracts

This commit introduces several core features requested for systems programming and bare-metal environments. It adds explicit `export(abi)` and `export(abi, "symbol")` syntax to the frontend, allowing Wave to export functions to C/assembly callers without relying on hacky extern definitions. It also finalizes the Windows cross-compilation pipeline by providing bundled linking via `ld.lld`, eliminating the dependency on host GCC/MinGW installations.

[Details]
1. `export(abi)` Support:
  - Added new `TokenType::Export` to the lexer and `parse_export` to the parser.
  - Functions can now be explicitly exported with specific ABIs (e.g., `export(c) fun my_func() {}`), allowing LLVM to assign them `External` linkage and proper calling conventions.
  - Supports both single-function (`export(c) fun`) and block-based (`export(c) { fun ... }`) declarations, similar to the `extern` block syntax.
  - Added `export(c, "symbol")` to assign custom global symbol names, which is crucial for defining OS entry points (e.g., `_start` or `efi_main`).

2. Windows GNU Linkage Improvements:
  - `build_windows_gnu_linker_args` now natively prefers `ld.lld` if bundled, rather than deferring to a system GCC.
  - The build system automatically links core Windows dynamic libraries (`-lkernel32`, `-luser32`, `-lmsvcrt`, etc.) and the MinGW self-contained CRT (`crt2.o`) when standard libraries are enabled.

3. Inline Assembly Stack Contracts:
  - Implemented advanced static analysis in `x86_64_stack_analysis`, `aarch64_stack_analysis`, and `riscv64_stack_analysis` to inspect inline assembly for instructions that mutate the stack pointer (`push`, `pop`, `sub rsp`, etc.) or perform branching (`jmp`, `ret`, `call`).
  - Added pseudo-clobber strings: `clobber("stack")`, `clobber("nostack")`, and `clobber("noreturn")`.
  - The compiler now rigidly enforces stack contracts, immediately triggering a compile error if an inline ASM block modifies the stack pointer unpredictably without declaring `clobber("stack")` or `clobber("noreturn")`.
  - Added `builder.build_unreachable()` generation for `clobber("noreturn")` inline assembly, helping LLVM optimize kernel-level dead ends.

4. LLVM Code Generation Attributes:
  - Functions compiled for Freestanding (`--freestanding` or `*-none-elf`) targets now automatically receive `noredzone` and `nounwind` attributes, ensuring safe interrupt handling and avoiding implicit exception propagation in kernel space.

5. Frontend/Parser Improvements:
  - Fixed an issue where the `deref` pseudo-variable assignment fallback would leak into LLVM codegen. The parser now properly lowers `deref ptr = x` into an explicit `Expression::Assignment` target.
  - Refactored `parse_extern_header` into a generalized `parse_ffi_header` to share parsing logic between `extern` and `export`.

Signed-off-by: LunaStev <luna@lunastev.org>
… and enhance IR validation

This update introduces `run_link_tests_enabled` for conditional tests, specifies explicit targets (`x86_64-unknown-linux-gnu`), and refines IR checks to ensure expected GEP/store operations are present without requiring a host linker.

Signed-off-by: LunaStev <luna@lunastev.org>
@LunaStev LunaStev merged commit 17d9a5a into wavefnd:master May 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant