A C library for compiler code generation with support for multiple architectures through a portable intermediate representation (IR) system.
- Portable source IR: Architecture-independent modules, functions, basic blocks, SSA values, typed constants, globals, and structured control flow.
- Source IR verifier:
anvil_module_codegen()rejects invalid IR before optimization or backend lowering. - Configurable optimizer: Copy propagation, constant folding, CSE, strength reduction, memory optimizations, CFG simplification, and DCE with deterministic pass ordering.
- MachineIR and register allocation: A generic backend layer with target-independent virtual registers, fixed ABI registers, stack/frame slots, spill materialization, copy coalescing, and linear-scan allocation.
- Backend architecture: The current x86, x86-64, ARM64, PowerPC and mainframe backends lower through MachineIR, with target descriptors and backend vtables.
- Multiple backend targets: x86, x86-64, S/370, S/370-XA, S/390, z/Architecture, PowerPC 32/64-bit, PPC64LE, and ARM64.
- CPU model system: Target-specific CPU model selection and feature flags.
- Assembly output: Generates assembly text (HLASM for mainframes and GAS for x86, x86-64, ARM64 and PowerPC, with target-specific object directives).
| Architecture | Bits | Endianness | Stack | FP Format | ABI | Syntax |
|---|---|---|---|---|---|---|
| x86 | 32 | Little | Down | IEEE 754 | System V | GAS |
| x86-64 | 64 | Little | Down | IEEE 754 | System V / Windows x64 | GAS |
| S/370 | 24 | Big | Up | HFP | MVS | HLASM |
| S/370-XA | 31 | Big | Up | HFP | MVS | HLASM |
| S/390 | 31 | Big | Up | HFP | MVS | HLASM |
| z/Architecture | 64 | Big | Up | HFP+IEEE | MVS | HLASM |
| PowerPC 32 | 32 | Big | Down | IEEE 754 | System V | GAS |
| PowerPC 64 | 64 | Big | Down | IEEE 754 | System V | GAS |
| PowerPC 64 LE | 64 | Little | Down | IEEE 754 | System V | GAS |
| ARM64 (Linux) | 64 | Little | Down | IEEE 754 | System V | GAS |
| ARM64 (macOS) | 64 | Little | Down | IEEE 754 | Darwin | GAS |
Floating-Point Formats:
- IEEE 754: Standard IEEE floating-point (binary)
- HFP: IBM Hexadecimal Floating Point (base-16 exponent, used in S/370, S/390)
- HFP+IEEE: Both formats supported (z/Architecture)
OS ABI Variants:
- System V: Standard Unix/Linux ABI
- Darwin: macOS/Apple ABI (underscore prefix, Mach-O format)
- MVS: ANVIL's MVS-oriented arena linkage (native interoperability incomplete)
# Build library and examples
make
# Library only
make lib
# Examples only
make examples
# Unit/regression tests for core, optimizer, MachineIR/regalloc, and reference ARM64 MIR lowering
make tests
# Runtime example: generate assembly, assemble/link it, and execute it
make test-examples
# Advanced examples (fp_math_lib, dynamic_array, base64_lib)
make examples-advanced
# Advanced runtime tests: generate, assemble/link, and execute generated libraries
make test-examples-advanced
# MCC sample compiler execution suite
make -C samples/mcc test-exec
# Clean
make clean
# Clean advanced examples
make clean-examples-advanced
# Install (requires root)
sudo make installUse Python 3, Clang/LLVM, and the MSVC libraries plus Windows SDK. The validated toolchain uses Clang's integrated assembler to produce COFF objects and native PE executables; MASM, NASM, FASM, MinGW and Wine are not needed for this path.
py -3 tests/run_windows.py --cc C:/llvm/bin/clang.exe --scope compiler
py -3 tests/run_windows_mcc.py --cc C:/llvm/bin/clang.exe
py -3 tests/run_windows_sanitize.py --cc C:/llvm/bin/clang.exe
py -3 tests/run_windows_abi.py --cc C:/llvm/bin/clang.exe --family aggregate
py -3 tests/run_windows_abi.py --cc C:/llvm/bin/clang.exe --family variadic
py -3 tests/fuzz_windows_optimizer.py --cc C:/llvm/bin/clang.exe --cases 128
# Full audit, including Smalltalk's currently unsupported POSIX services.
# This command returns nonzero while those failures remain.
py -3 tests/run_windows.py --cc C:/llvm/bin/clang.exe --scope allThe compiler and logs are retained in build/windows. MCC accepts
-arch=x86_64_windows (alias win64); it uses LLP64 and Windows calling conventions.
For a generated program using the sample stdio headers:
./build/windows/mcc.exe -arch=win64 -std=c99 -Isamples/mcc/includes -o build/windows/hello.s input.c
C:/llvm/bin/clang.exe build/windows/hello.s -Xlinker legacy_stdio_definitions.lib -o build/windows/hello.exe
./build/windows/hello.exeHost services use platform vtables selected by the build (HOST_PLATFORM=posix
or windows in Makefiles). Target ABI selection remains independent of the host.
See the Windows source review for tested coverage,
reproductions of outstanding defects, and IR/MIR optimization priorities.
#include <anvil/anvil.h>
int main(void)
{
// Create context
anvil_ctx_t *ctx = anvil_ctx_create_for_target(ANVIL_ARCH_X86_64);
// Create module
anvil_module_t *mod = anvil_module_create(ctx, "my_module");
// Create function type: int add(int a, int b)
anvil_type_t *i32 = anvil_type_i32(ctx);
anvil_type_t *params[] = { i32, i32 };
anvil_type_t *func_type = anvil_type_func(ctx, i32, params, 2, false);
// Create function
anvil_func_t *func = anvil_func_create(mod, "add", func_type, ANVIL_LINK_EXTERNAL);
// Set insertion point
anvil_block_t *entry = anvil_func_get_entry(func);
anvil_set_insert_point(ctx, entry);
// Get parameters
anvil_value_t *a = anvil_func_get_param(func, 0);
anvil_value_t *b = anvil_func_get_param(func, 1);
// Build IR: result = a + b
anvil_value_t *result = anvil_build_add(ctx, a, b, "result");
// Build IR: return result
anvil_build_ret(ctx, result);
// Generate code
char *output = NULL;
size_t len = 0;
anvil_error_t err = anvil_module_codegen(mod, &output, &len);
if (err != ANVIL_OK) {
fprintf(stderr, "codegen failed: %s\n", anvil_ctx_get_error(ctx));
anvil_module_destroy(mod);
anvil_ctx_destroy(ctx);
return 1;
}
printf("%s", output);
// Cleanup
free(output);
anvil_module_destroy(mod);
anvil_ctx_destroy(ctx);
return 0;
}anvil_build_add: Additionanvil_build_sub: Subtractionanvil_build_mul: Multiplicationanvil_build_sdiv/anvil_build_udiv: Division (signed/unsigned)anvil_build_smod/anvil_build_umod: Modulo (signed/unsigned)anvil_build_neg: Negation
anvil_build_and: ANDanvil_build_or: ORanvil_build_xor: XORanvil_build_not: NOTanvil_build_shl: Shift leftanvil_build_shr: Shift right (logical)anvil_build_sar: Shift right (arithmetic)
anvil_build_cmp_eq/anvil_build_cmp_ne: Equal / Not equalanvil_build_cmp_lt/anvil_build_cmp_le: Less than / Less or equalanvil_build_cmp_gt/anvil_build_cmp_ge: Greater than / Greater or equal- Unsigned versions:
_ult,_ule,_ugt,_uge
anvil_build_alloca: Stack allocationanvil_build_alloca_dyn: Runtime-sized stack allocationanvil_build_load: Load from memoryanvil_build_store: Store to memoryanvil_build_gep: Get Element Pointer (array indexing)anvil_build_struct_gep: Get Struct Field Pointeranvil_module_add_global: Add global variable
anvil_build_br: Unconditional branchanvil_build_br_cond: Conditional branchanvil_build_switch/anvil_switch_add_case: Multi-way branchanvil_build_call: Function callanvil_build_ret/anvil_build_ret_void: Return
anvil_build_trunc: Truncateanvil_build_zext: Zero extendanvil_build_sext: Sign extendanvil_build_bitcast: Bitcastanvil_build_ptrtoint/anvil_build_inttoptr: Pointer/integer conversion
anvil_build_fadd: FP Additionanvil_build_fsub: FP Subtractionanvil_build_fmul: FP Multiplicationanvil_build_fdiv: FP Divisionanvil_build_fneg: FP Negationanvil_build_fabs: FP Absolute valueanvil_build_fcmp: FP Comparison
anvil_build_fptrunc: Truncate (f64 → f32)anvil_build_fpext: Extend (f32 → f64)anvil_build_fptosi: FP to signed integeranvil_build_fptoui: FP to unsigned integeranvil_build_sitofp: Signed integer to FPanvil_build_uitofp: Unsigned integer to FP
anvil_build_phi: PHI nodeanvil_phi_add_incoming: Add PHI predecessor/value edgeanvil_build_select: Select (ternary)
- Boolean:
i1(one semantic bit, one-byte storage) - Integers:
i8,i16,i32,i64(signed) - Integers:
u8,u16,u32,u64(unsigned) - Floating point:
f32,f64 - Pointers:
anvil_type_ptr(ctx, pointee_type) - Arrays:
anvil_type_array(ctx, elem_type, count) - Structs:
anvil_type_struct(ctx, name, fields, num_fields) - Functions:
anvil_type_func(ctx, ret_type, params, num_params, variadic)
Function definitions/declarations are exposed as callable address values. In practice, anvil_func_get_value(func) has type ptr<func>, so it can be stored in memory, loaded back, and called indirectly. anvil_build_call_checked() derives both signature and immutable effective calling convention from that func/ptr<func> type. It returns true with a NULL result for a successful void call, eliminating the old NULL ambiguity.
| Architecture | Convention | Description |
|---|---|---|
| x86 | CDECL | Parameters on stack, caller cleanup |
| x86-64 | System V | RDI, RSI, RDX, RCX, R8, R9, then stack |
| S/370 | ANVIL MVS-oriented arena | R1 points to parameter list |
| S/390 | ANVIL MVS-oriented arena | R1 points to parameter list |
| z/Arch | ANVIL arena-64 | R1 points to parameter list (64-bit) |
| PPC32 | System V | r3-r10 for args, r3 for return |
| PPC64 BE | ELFv1 | r3-r10 for args, function descriptors |
| PPC64 LE | ELFv2 | r3-r10 for args, local entry points |
| ARM64 (Linux) | AAPCS64 | x0-x7 for args, x0 for return |
| ARM64 (macOS) | Apple ARM64 | x0-x7 for args, underscore prefix on symbols |
The S/370, S/370-XA, S/390 and z/Architecture implementations live in
src/backend/systemz/, with distinct ISA descriptors in targets/, linkage
policies in abi/, and HLASM printing in asm/. The PowerPC family follows the
same domain organization under src/backend/ppc/.
These backends have real MIR lowering, allocation and text emission. The current SystemZ linkage assumes caller-owned arena storage; it is not certified against native z/OS, Language Environment or GCCMVS. In particular, its 144-byte layout is not the IBM F4SA layout. Upward arena growth and R1 parameter lists describe ANVIL's present convention, not universal properties of IBM instruction sets.
Implementation notes and IBM manual references document the supported profiles, ABI discrepancies and required execution validation.
Backends are grouped by instruction-set family. src/backend/x86/ contains
both 32-bit x86 and x86-64, with targets/ for registration/capabilities,
abi/ for calling conventions and varargs, and asm/ for GAS emission.
Mode-specific lowering and legality remain separate. PPC and SystemZ follow
the same ownership boundaries; see the backend guide.
src/backend/common/gnu_data.{h,c} centralizes GAS data constants, relocations
and aggregate layout used by x86, PowerPC and AArch64. Backends retain control
of their sections and symbol spelling.
To add support for a new architecture:
-
Create backend files under
src/backend/<arch>/. -
Choose the implementation path:
- Use a direct source-IR emitter for a minimal bootstrap backend.
- Use MachineIR for production-quality backend work so register allocation, spills, fixed ABI registers, and frame/spill slots stay shared.
-
Implement the
anvil_backend_ops_tstructure:
const anvil_backend_ops_t anvil_backend_myarch = {
.name = "MyArch",
.arch = ANVIL_ARCH_MYARCH,
.init = myarch_init,
.cleanup = myarch_cleanup,
.reset = myarch_reset, // Clear cached IR pointers (optional but recommended)
.prepare_ir = myarch_prepare_ir, // Prepare/lower IR before codegen (optional)
.codegen_module = myarch_codegen_module,
.codegen_func = myarch_codegen_func,
.get_arch_info = myarch_get_arch_info
};- Add the architecture to
anvil.h:
typedef enum {
// ...
ANVIL_ARCH_MYARCH,
ANVIL_ARCH_COUNT
} anvil_arch_t;- Register the backend in
backend.c:
anvil_register_backend(&anvil_backend_myarch);ANVIL now has a two-level IR architecture:
Frontend/API
│
▼
Source IR
Modules -> Functions -> Blocks -> Instructions -> Values
│
├── source verifier
├── target-independent optimizer
▼
Backend lowering
│
├── direct emitter path
│ Source IR -> target assembly
│
└── MachineIR path
Source IR -> target lowerer -> virtual registers
-> ABI constraints -> regalloc -> spill materialization
-> target assembly
MachineIR is the production backend implementation path. ARM64 is the stable reference backend for validating the design and should be treated as the canonical implementation model. PowerPC and IBM mainframe targets also use shared MachineIR lowerers with target descriptors. x86 and x86-64 also use MachineIR in production. The five shared lowerers consume the same CFG analysis and parallel-copy implementation while retaining their target-specific ABI, legality and assembly-emission rules.
anvil_module_codegen() validates source IR before optimization and codegen. The verifier checks:
- value ownership: parameters and instruction results must belong to the function being verified
- typed memory operations:
load/storeaddress pointee types must match value/result types - call signatures: argument count, fixed parameter types, variadic minimum arity, and return type
- function values: direct and indirect callees are accepted as
funcorptr<func> - PHI incoming blocks: incoming values must correspond to real predecessors
- switch terminators: selector/case type consistency and valid destinations
- block termination: non-declaration functions must terminate every block
Optimization is target-independent and runs before backend prepare_ir/codegen. Passes are managed by anvil_pass_manager_t and executed in a fixed order designed to expose new opportunities while keeping DCE last in each iteration:
- scalar replacement of aggregates (SROA)
- promotion of local scalar storage to SSA (mem2reg)
- sparse conditional constant propagation (SCCP) and known integer bits/bounds
- copy propagation and constant folding
- local common subexpression elimination and dominance-based integer GVN
- strength reduction
- store-load propagation, dead store elimination and load elimination
- CFG simplification
- loop-invariant integer code motion (LICM, O3)
- complete unrolling of small constant-trip loops (O3)
- budgeted inlining of internal leaf functions (O3)
- target-costed floating-point SLP packing, explicitly enabled per function (O3)
- dead code elimination
The pass manager verifies the current function after every pass and iterates to
a bounded fixpoint. The bound defaults to 10 and is configurable through
anvil_pass_manager_set_iteration_limit(); pass failure, invalid IR, or failure
to converge is reported as an error. Only implemented passes are exposed by the
public pass enum.
Mem2reg starts at O1; SROA, SCCP and GVN start at O2. O3 additionally moves nontrapping integer invariants into loop preheaders, creating them and joining incoming PHI values when needed. Volatile memory accesses survive optimization and MIR lowering. Explicit function-effect contracts allow selected memory transformations around known calls; unknown and indirect calls remain conservative.
Inlining clones control flow and repairs PHIs, using callee-first module order.
Unrolling proves termination within eight iterations and limits code growth.
The x86-64 SIMD path supports 128-bit FP32/FP64 loads, stores and arithmetic.
Enable SLP packing with anvil_func_set_fp_vectorization(func, true) or MCC's
-O3 -ffp-vectorize; this permits changing FP exception observation order,
without reassociation or contraction. Unknown aliases and volatile accesses
prevent packing. The default preserves scalar FP evaluation order.
Integer/pointer atomics of 8, 16, 32 and 64 bits have verified IR/MIR contracts
and native x86-64/AArch64 emission, including strong compare/exchange, RMW and
fences. They require naturally aligned storage. MCC's C _Atomic frontend is
not yet connected to these APIs.
Optional statistics and a pass observer report instruction counts, changes, failures and host process CPU time per pass/function. CFG snapshots cache reachability and immediate dominators, detect exact topology changes and retain acquired snapshots safely across invalidation. Implementation limits and the remaining roadmap are tracked in OPTIMIZER_IMPLEMENTATION.md.
include/anvil/anvil_machine.h exposes a target-independent MachineIR layer:
- typed virtual registers with GPR/FPR/flags/special classes
- fixed physical register constraints for ABI values such as arguments and returns
- basic blocks, branches, direct calls, indirect calls, frame slots, string literals, and spill slots
- copy coalescing before allocation
- linear-scan allocation by register class
- bitset/worklist liveness, heap-sorted intervals and compatible spill-slot reuse
- local interval splitting around clobbers, with one saved value and lazy reloads
- shared parallel-copy scheduling with typed cycle temporaries
- spill materialization using backend-provided scratch register classes
- rematerialization of integer constants and reuse of repeated spilled operands
- verifier for MachineIR structural invariants
Backends can lower source IR to MachineIR, allocate registers, materialize spills, then emit target assembly from allocated machine instructions. This keeps optimization, value lifetime handling, spill insertion, and ABI fixed-register constraints in shared infrastructure while leaving instruction selection and final assembly emission target-specific.
ARM64 is a reference implementation for the generic MachineIR design. All ten targets exercise source IR lowering, register allocation, spill materialization and ABI-aware assembly emission through their five shared lowerers.
src/backend/arm64/arm64.c: backend lifecycle and module/function codegen entry pointssrc/backend/arm64/arm64_helpers.c: target sizes, alignment, ABI helpers, and constantssrc/backend/arm64/arm64_mir.c: source IR lowering, ARM64 MachineIR legality checks, regalloc bridge, and assembly emissionsrc/backend/arm64/opt/: target-specific preparation/optimization hooks
The ARM64 reference implementation currently covers:
- integer and floating-point arithmetic, comparisons, casts, and select
- stack frame slots, static
alloca, dynamicalloca, globals, and string literals - typed loads/stores including signed byte/halfword/word extension
- GEP lowering with constant-offset folding and runtime index scaling
- struct field addressing
- PHI lowering via edge copies, including conditional branches and parallel-copy cycles
- switch lowering to compare/branch chains
- direct calls (
bl symbol) and indirect function pointer calls (blr x16) - ABI fixed registers for x0-x7/d0-d7 arguments and x0/d0 returns
- outgoing stack arguments and incoming stack arguments
- Darwin variadic-call behavior with variadic arguments placed on the stack
- Darwin/Mach-O symbol prefixes and
@PAGE/@PAGEOFFrelocation forms
Function definitions and declarations keep their canonical ANVIL_TYPE_FUNC signature internally, but the callable value exposed through anvil_func_get_value() is ptr<func>. This matches C-style function pointer behavior:
anvil_type_t *params[] = { anvil_type_i32(ctx), anvil_type_i32(ctx) };
anvil_type_t *fn_type = anvil_type_func(ctx, anvil_type_i32(ctx), params, 2, false);
anvil_func_t *add_fn = anvil_func_create(mod, "add", fn_type, ANVIL_LINK_EXTERNAL);
anvil_type_t *fn_ptr_type = anvil_type_ptr(ctx, fn_type);
anvil_value_t *slot = anvil_build_alloca(ctx, fn_ptr_type, "slot");
anvil_build_store(ctx, anvil_func_get_value(add_fn), slot);
anvil_value_t *loaded = anvil_build_load(ctx, fn_ptr_type, slot, "loaded_fn");
anvil_value_t *args[] = { anvil_const_i32(ctx, 3), anvil_const_i32(ctx, 4) };
anvil_value_t *result = NULL;
anvil_build_call_checked(ctx, loaded, args, 2, "result", &result);Backend emitters choose the concrete call instruction. In the current ARM64 reference backend, direct calls emit bl, while loaded function pointers are copied to x16 and emitted as blr x16.
samples/mcc is the integration stress test for the generic IR, verifier,
optimizer and backend contract. Its execution corpus is checked against Clang
on native Windows x64, Linux x64 and AArch64 Linux under QEMU. The suite covers
arithmetic, arrays, pointers, structs, switches, recursion, preprocessing,
function pointers and scalar varargs. tests/run_arm64.py also checks atomics
and C ABI interoperability; see the review documents for commands and limits.
New debugging functionality for inspecting IR structures:
#include <anvil/anvil.h> // anvil_debug.h is now included automatically
// Print module IR to stdout
anvil_print_module(mod);
// Print function IR to stdout
anvil_print_func(func);
// Dump to FILE*
anvil_dump_module(stderr, mod);
anvil_dump_func(stderr, func);
anvil_dump_block(stderr, block);
anvil_dump_instr(stderr, instr);
// Convert to string (caller must free)
char *ir_str = anvil_module_to_string(mod);
printf("%s", ir_str);
free(ir_str);
// Check if block has terminator (ret, br, br_cond, switch)
if (!anvil_block_has_terminator(block)) {
anvil_build_ret_void(ctx); // Add implicit return
}
// Check if value is boolean (comparison result)
if (anvil_value_is_bool(cond)) {
// Already boolean, use directly in br_cond
} else {
// Need to compare with zero first
cond = anvil_build_cmp_ne(ctx, cond, zero, "tobool");
}
// Get type of a value
anvil_type_t *type = anvil_value_get_type(val);String escaping: String constants in IR dumps are properly escaped (\n, \t, \0, \xHH for non-printable characters).
Output format:
; ModuleID = 'my_module'
; Functions: 2, Globals: 1
@counter = external global i32 42
define external i32 @factorial(i32 %arg0) {
entry:
%cmp = cmp_le i8 %arg0, 1
br_cond %cmp, label %base_case, label %recurse
...
}
Improved cleanup flow to prevent dangling pointers and use-after-free issues:
- Backend reset function: New
resetcallback inanvil_backend_ops_tto clear cached IR pointers - Safe cleanup order:
anvil_ctx_destroy()now resets backend state before destroying modules - All backends updated: x86, x86-64, ARM64, S/370, S/370-XA, S/390, z/Architecture, PPC32, PPC64, PPC64LE
Three advanced examples demonstrate ANVIL's capabilities for generating linkable libraries:
-
examples/fp_math_lib/: Floating-point math library- Generates exportable FP functions:
fp_add,fp_sub,fp_mul,fp_div,fp_neg,fp_abs - Demonstrates ANVIL IR for floating-point operations
- Includes C test program that links with generated assembly (24 tests)
- Generates exportable FP functions:
-
examples/dynamic_array/: Dynamic array library with C library calls- Demonstrates calling external C functions:
malloc,free,memcpy - Functions:
array_create,array_destroy,array_copy,array_sum,array_max,array_min,array_count_if,array_scale - Shows pointer arithmetic, loops, conditionals, and memory management
- Includes comprehensive test suite (41 tests)
- Demonstrates calling external C functions:
-
examples/base64_lib/: Base64 encoding library- Demonstrates complex bitwise operations, byte manipulation, and lookup table logic
- Functions:
base64_encode,base64_encoded_len - Shows
selectoperations for conditional value computation - Includes test suite with RFC 4648 test vectors (28 tests)
ANVIL includes a configurable optimization pass infrastructure that can be enabled or disabled.
| Level | Name | Description |
|---|---|---|
| O0 | ANVIL_OPT_NONE |
No optimization (default) |
| Og | ANVIL_OPT_DEBUG |
Debug-friendly: copy propagation, store-load propagation |
| O1 | ANVIL_OPT_BASIC |
Og + mem2reg, constant folding and DCE |
| O2 | ANVIL_OPT_STANDARD |
O1 + SROA, SCCP, GVN, known bits, CFG, arithmetic and memory optimizations |
| O3 | ANVIL_OPT_AGGRESSIVE |
O2 + LICM, bounded unrolling, internal leaf inlining and explicitly enabled SLP |
| Pass | Level | Description |
|---|---|---|
| Constant Folding | O1+ | Evaluates constant expressions at compile time (3 + 5 → 8) |
| Dead Code Elimination (DCE) | O1+ | Removes unused instructions |
| Copy Propagation | Og+ | Replaces uses of copied values with originals |
| Store-Load Propagation | Og+ | Replaces load after store with stored value |
| Strength Reduction | O2+ | Replaces expensive ops with cheaper ones (x * 8 → x << 3) |
| CFG Simplification | O2+ | Merges blocks, removes unreachable code, and preserves switch CFG edges |
| Dead Store Elimination | O2+ | Removes stores overwritten before read |
| Redundant Load Elimination | O2+ | Reuses loaded values from same address |
| Common Subexpression Elimination (CSE) | O2+ | Reuses computed values |
#include <anvil/anvil_opt.h>
// Set optimization level
anvil_ctx_set_opt_level(ctx, ANVIL_OPT_STANDARD);
// Optimize module before codegen
anvil_module_optimize(mod);
// Or fine-grained control
anvil_pass_manager_t *pm = anvil_ctx_get_pass_manager(ctx);
anvil_pass_manager_enable(pm, ANVIL_PASS_CONST_FOLD);
anvil_pass_manager_disable(pm, ANVIL_PASS_DCE);Before optimization:
LA R2,3 Load constant 3
AHI R2,5 Add 5
LR R15,R2 Result in R15
After optimization:
LA R15,8 Load constant 8 directly
Before optimization:
LA R3,8 Load constant 8
MSR R2,R3 Multiply (expensive)
After optimization:
LA R3,3 Load shift amount
SLL R2,0(R3) Shift left by 3 (x * 8 = x << 3)
- Binary opcode generation
- Migrate existing direct text emitters to the MachineIR/regalloc path
- Extend bounded unrolling to partial/runtime unrolling and broaden vectorization
- Broaden MachineIR coverage for additional ABIs and future targets
- RISC-V support
- Debug info (DWARF)
- Deeper CPU-model-specific instruction selection
See DOCUMENTATION.md for API reference and detailed usage examples. See samples/mcc/README.md and samples/mcc/docs/ for the C compiler sample.
Unlicense