Skip to content

arxlang/irx

Repository files navigation

IRx

IRx is a Python library that lowers ARXLang ASTx nodes to LLVM IR using llvmlite. It provides a visitor-based codegen pipeline and a small builder API that can translate ASTs to LLVM IR text or produce runnable executables via clang.

Status: early but functional. Arithmetic, variables, functions, returns, basic control flow, and a few system-level expressions (e.g. PrintExpr) are supported.

Features

  • ASTx → LLVM IR via multiple-dispatch visitors (plum).

  • Back end: IR construction and object emission with llvmlite.

  • Native build: links with clang to produce an executable.

  • Optional runtime features: native capabilities are feature-gated per compilation unit instead of being linked into every binary.

  • PIE-friendly objects: emits PIC-compatible objects by default to work with modern PIE-default linkers.

  • Supported nodes (subset; exact ASTx class names):

    • Literals: LiteralInt16, LiteralInt32, LiteralString
    • Variables: Variable, VariableDeclaration, InlineVariableDeclaration
    • Ops: UnaryOp (++, --), BinaryOp (+ - * / < >) with simple type promotion
    • Flow: IfStmt, ForCountLoopStmt, ForRangeLoopStmt
    • Functions: FunctionPrototype, Function, FunctionReturn, FunctionCall
    • System: system.PrintExpr (string printing)
  • Built-ins: putchar, putchard (emitted as IR); puts declaration when needed.

  • Optional native runtimes: libc externs are routed through the runtime feature layer, and Arrow is now available as an optional native runtime feature.

Quick Start

Requirements

  • Python 3.10 – 3.13.
  • A recent LLVM/Clang toolchain available on PATH.
  • A working C standard library (e.g., system libc) for linking calls like puts.
  • Python deps: llvmlite, pytest, etc. (see pyproject.toml / requirements.txt).
    • Note: llvmlite has specific Python/LLVM compatibility windows; see its docs.

Install (dev)

git clone https://github.com/arxlang/irx.git cd irx conda env create --file conda/dev.yaml conda activate irx poetry install

You can also install it from PyPI: pip install pyirx.

More details: https://irx.arxlang.org/installation/

Minimal Examples

1) Translate to LLVM IR (no linking)

import astx from irx.builders.llvmliteir import LLVMLiteIR builder = LLVMLiteIR() module = builder.module() # int main() { return 0; } proto = astx.FunctionPrototype("main", astx.Arguments(), astx.Int32()) body = astx.Block() body.append(astx.FunctionReturn(astx.LiteralInt32(0))) module.block.append(astx.Function(prototype=proto, body=body)) ir_text = builder.translate(module) print(ir_text) # LLVM IR text (str)

translate returns a str with LLVM IR. It does not produce an object file or binary; use it for inspection, tests, or feeding another tool.

2) Build and run a tiny program that prints and returns 0

import astx from irx.builders.llvmliteir import LLVMLiteIR from irx.system import PrintExpr builder = LLVMLiteIR() module = builder.module() # int main() { print("Hello, IRx!"); return 0; } main_proto = astx.FunctionPrototype("main", astx.Arguments(), astx.Int32()) body = astx.Block() body.append(PrintExpr(astx.LiteralString("Hello, IRx!"))) body.append(astx.FunctionReturn(astx.LiteralInt32(0))) module.block.append(astx.Function(prototype=main_proto, body=body)) builder.build(module, "hello") # emits object + links with clang builder.run() # executes ./hello (or hello.exe on Windows)

How It Works

Builders & Visitors

  • LLVMLiteIR (public API)

    • translate(ast) -> str — generate LLVM IR text.
    • build(ast, output_path) — emit object via llvmlite and link with clang.
    • run() — execute the produced binary.
  • LLVMLiteIRVisitor (codegen)

    • Uses @dispatch to visit each ASTx node type.
    • Maintains a value stack (result_stack) and symbol table (named_values).
    • Emits LLVM IR with llvmlite.ir.IRBuilder.

System Printing

PrintExpr is an astx.Expr holding a LiteralString. Its lowering:

  1. Create a global constant for the string (with \0).
  2. GEP to an i8* pointer.
  3. Declare (or reuse) i32 @puts(i8*).
  4. Call puts.

Optional Runtime Features

IRx now has a generic runtime-feature system for native integrations that do not belong as handwritten LLVM container logic.

  • Features are registered by name, such as libc and arrow.
  • Features can declare external symbols, native C sources, objects, or static libraries.
  • The linker only compiles and links artifacts for features that are active in the current compilation unit.
  • This is intentionally separate from any future Arx import/module layer.

Arrow uses this path as its first substantial consumer:

  • native runtime implemented in C under src/irx/runtime/arrow/
  • opaque irx_arrow_* handles only
  • Arrow C Data import/export boundary
  • Python nanoarrow installed by default for interop and tests
  • arx-nanoarrow-sources installed by default for native runtime builds

The current MVP is intentionally narrow: primitive int32 arrays, lifecycle operations, inspection, and C Data roundtrip support. No full Arrow container semantics are encoded directly in LLVM IR.

Testing

pytest -vv

Example style (simplified):

def test_binary_op_basic(): builder = LLVMLiteIR() module = builder.module() decl_a = astx.VariableDeclaration("a", astx.Int32(), astx.LiteralInt32(1)) decl_b = astx.VariableDeclaration("b", astx.Int32(), astx.LiteralInt32(2)) a, b = astx.Variable("a"), astx.Variable("b") expr = astx.LiteralInt32(1) + b - a * b / a proto = astx.FunctionPrototype("main", astx.Arguments(), astx.Int32()) block = astx.Block() block.append(decl_a); block.append(decl_b) block.append(astx.FunctionReturn(expr)) module.block.append(astx.Function(proto, block)) ir_text = builder.translate(module) assert "add" in ir_text

Troubleshooting

macOS: ld: library 'System' not found

  • Ensure Xcode Command Line Tools are installed: xcode-select --install.

  • Verify clang --version works.

  • If needed:

    export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
  • CI note: macOS jobs currently run on Python 3.12 only.

Non-zero exit when function returns void

  • Define main to return Int32 and emit return 0. Falling off the end or returning void can yield an arbitrary exit code.

plum.resolver.NotFoundLookupError

  • A visitor is missing @dispatch or is typed against a different class than the one instantiated. Ensure signatures match the exact runtime class (e.g., visit(self, node: PrintExpr)).

Linker or clang not found

  • Install a recent LLVM/Clang. On Linux, use distro packages.
  • On macOS, install Xcode CLT.
  • On Windows, ensure LLVM’s bin directory is on PATH.

PIE mismatch (R_X86_64_32 ... can not be used when making a PIE object)

  • This usually means your linker is enforcing PIE while the object was compiled with non-PIE relocations.

  • Current IRx defaults to PIC-compatible object emission, which should work with PIE-default linkers.

  • If you are using an older ARX/IRX stack, update first.

  • If you must link externally as a workaround, use:

    clang -no-pie file.o -o program

Platform Notes

  • Linux & macOS: supported and used in CI.
  • Windows: expected to work with a proper LLVM/Clang setup; consider it experimental. builder.run() will execute hello.exe.

Roadmap

  • More ASTx coverage (booleans, arrays, structs, varargs/options).
  • Richer stdlib bindings (I/O, math).
  • Optimization toggles/passes.
  • Alternative backends and/or JIT runner.
  • Better diagnostics and source locations in IR.
  • Expand optional Apache Arrow runtime support: nullable arrays, more primitive types, streams, and higher-level handles.

Contributing

Please see the contributing guide. Add tests for new features and keep visitors isolated (avoid special-casing derived nodes inside generic visitors).

Acknowledgments

  • LLVM and llvmlite for the IR infrastructure.
  • ASTx / ARXLang for the front-end AST.
  • Contributors and users experimenting with IRx.

License

License: BSD-3-Clause. See LICENSE.