Djinn Lang

Library Compilation

Djinn supports two library formats:

  • .ll — Raw LLVM IR, requires source or --std-decl for type information
  • .djlib — Self-contained library with metadata + bitcode, works without source code

A .djlib file packages LLVM bitcode alongside a JSON metadata blob containing all type definitions, function signatures, macros, and attributes. Consumers can type-check, call functions, and use types without needing the library source code.

Creating a .djlib

Set the output filename with .djlib extension:

djinn std/ -o std.djlib -out build

Or via djinn.proj:

project: "my-lib"
version: "1.0.0"

compiler:
  output:
    file: "mylib.djlib"
    directory: "build"
  libs: []

The .djlib extension automatically enables library mode (no main() required).

Using a .djlib

Link with -l:

djinn playground/ -l build/std.djlib

Or via djinn.proj:

compiler:
  libs:
    - ./build/std.djlib

When using .djlib libraries, the standard library source is not loaded — all types and functions come from the library metadata and bitcode.

What's Inside a .djlib

A .djlib is a binary file with three sections:

[Header 16 bytes]
[Metadata — JSON with all type information]
[LLVM Bitcode — compiled code]

The metadata includes:

  • Structs — fields, methods, properties, generic parameters, attributes, interfaces implemented
  • Functions — signatures, parameters, async/constexpr flags
  • Enums — variants with payload types, generic parameters
  • Interfaces — method signatures, generic parameters
  • Macros — rules, fragment types, token streams (fully functional in consumer)
  • Extern functions — C FFI declarations with ABI info
  • Impl blocks — operator overloads and interface implementations
  • Attributes — declarations with target flags and schema fields
  • Constants — constexpr/consteval values
  • Static variables — global variables with types and mutability

How It Works

1. Compiler reads .djlib header + metadata
2. Type names registered in parser (so user code can reference library types)
3. Symbols injected into binder (structs, functions, enums, interfaces)
4. User code is parsed and bound with full access to library types
5. Generator emits LLVM declarations for library symbols
6. Bitcode from .djlib is linked into the final module
7. LLVM optimizer + clang produce the final binary

The compiler generates declare statements for library functions (not define) — the actual implementations come from the bitcode during linking. This means zero code duplication.

.ll Libraries (Legacy)

Raw LLVM IR linking is still supported for advanced use cases.

Compiling a .ll Library

Use --lib mode to compile without requiring a main() function:

djinn --lib --no-std -c -r std/

Or configure via djinn.proj:

compiler:
  library-mode: true
  output:
    file: "mylib"
    directory: "build"
  libs: []

What --lib does

  • Skips main() wrapper generation
  • Skips async runtime initialization (coro wrappers, event loop)
  • All functions and struct types are exported with ExternalLinkage
  • Output is a .ll file ready for linking

Linking .ll Libraries

Use -l to link pre-compiled .ll files:

djinn -l build/mylib.ll main.djinn

Multiple libraries can be linked:

djinn --std-decl -l build/std.ll -l build/mylib.ll main.djinn

Declaration-Only Mode (--std-decl)

When linking a pre-compiled .ll, you need --std-decl to get type information without re-compiling the library source:

# Step 1: Compile the library
djinn --lib --no-std -c -r std/

# Step 2: Use it with declarations only
djinn --std-decl -l build/std.ll main.djinn

This is unnecessary with .djlib — the metadata already contains all declarations.

Generic Deduplication

Monomorphized generic functions (e.g., arr<i32>::push) use LinkOnceODR linkage with COMDAT groups. If both a library and user code instantiate the same generic type, the linker keeps one copy — no duplicate symbol errors.

TypeInfo Across Modules

TypeInfo globals also use LinkOnceODR + COMDAT. Type IDs are computed via FNV-1a hash of the type name, which is deterministic. The same type compiled in different modules produces the same ID, ensuring is expressions work correctly across linked modules.

Project File Reference

Library-related settings in djinn.proj:

compiler:
  library-mode: true          # Compile as library (no main required)
  reflection-mode: all        # none | annotated | all

  output:
    file: "mylib.djlib"       # .djlib = auto library mode + metadata
    directory: "build"

  libs:
    - std                     # "std" = djinn stdlib from source
    - ./vendor/math.djlib     # .djlib = library with metadata
    - ./vendor/legacy.ll      # .ll = raw LLVM IR linking

CLI Flags Reference

FlagDescription
-o file.djlibOutput as .djlib (auto-enables library mode)
--libLibrary mode (skip main, runtime)
--no-stdDon't include standard library source
--std-declInclude std declarations only (use with -l .ll)
-l file.djlibLink .djlib library (metadata + bitcode)
-l file.llLink raw LLVM IR module
--bundleBundle all namespaces into single output
-cCompile only (don't generate binary)
--reflect-allGenerate TypeInfoExt for all types
--reflect-annotatedGenerate TypeInfoExt for [Reflect] types only

On this page