Skip to content

v2.gen.arm64 #

fn Gen.new #

fn Gen.new(mod &mir.Module) &Gen

fn Linker.new #

fn Linker.new(macho &MachOObject) &Linker

fn MachOObject.new #

fn MachOObject.new() &MachOObject

struct Gen #

struct Gen {
pub:
	mod &mir.Module
mut:
	macho &MachOObject
pub mut:
	stack_map      map[int]int
	alloca_offsets map[int]int
	stack_size     int
	curr_offset    int

	block_offsets      []int // indexed by block_id, -1 = not yet visited
	pending_label_blks []int
	pending_label_offs []int
	func_count         int
	total_pending      int
	total_resolved     int

	// Register allocation
	reg_map    map[int]int
	used_regs  []int
	next_blk   int
	cur_blk_id int // current block being generated (for phi copy emission)

	// Track which string literals have been materialized (value_id -> str_data offset)
	string_literal_offsets map[int]int

	// Cache for parsed constant integer values (value_id -> parsed i64)
	const_cache map[int]i64

	// Current function's return type (for handling struct returns)
	cur_func_ret_type int
	cur_func_name     string

	// Stack offset where x8 (indirect return pointer) is saved for large struct returns
	x8_save_offset int
	// Cache for deduplicating string data in cstring section (content -> offset)
	string_data_cache map[string]int
	// alloca values whose addresses are stored in sumtype `_data` fields and
	// therefore must outlive the current stack frame.
	sumtype_data_heap_allocas map[int]bool
	// Type layout caches/guards to avoid recursive size/alignment loops.
	type_size_cache  []int  // indexed by type_id, 0 = not cached (valid sizes are > 0 or == 0 only for void)
	type_align_cache []int  // indexed by type_id, 0 = not cached
	type_size_stack  []bool // indexed by type_id (recursion guard)
	type_align_stack []bool // indexed by type_id (recursion guard)
	// Cache for struct field offset calculations (key: typ_id << 16 | field_idx)
	struct_field_offset_cache map[int]int
	// Lookup caches for O(1) name resolution
	func_by_name   map[string]int // function name → index in g.mod.funcs
	global_by_name map[string]int // global name → index in g.mod.globals
	// Per-function cache for alloca pointer analysis (cleared per function)
	alloca_ptr_cache map[int]u8 // alloca_id → 1=has_ptrs, 2=no_ptrs
	// Cached environment variables for debug tracing (read once at init)
	env_dump_funcrefs     string
	env_trace_skip_dead   string
	env_dump_stackmap     string
	env_dump_blocks       string
	env_trace_paramspill  string
	env_trace_val         string
	env_trace_instr       string
	env_trace_cmp         string
	env_trace_store       string
	env_trace_load        string
	env_trace_call        string
	env_trace_ret         string
	env_trace_bitcast     string
	env_trace_assign      string
	env_trace_extract     string
	env_trace_struct_init string
	env_trace_agg_copy    string
	env_trace_insert      string
	env_trace_callcount   string
	env_trace_callarg     string
	env_trace_struct_addr string
	env_trace_strlit      string
	env_trace_storeval    string
	env_trace_regalloc    string
	env_no_regalloc       bool
	// SP-relative addressing: sp_base_offset = callee_saved_size + stack_size
	// so that fp - N = sp + (sp_base_offset - N) for positive sp-relative offsets.
	sp_base_offset int
	sp_adjusted    bool // true when sp is temporarily modified (call arg push)
	sp_adjust_amt  int  // how much SP was decremented (valid when sp_adjusted)
	// Reverse map: val_id → block_id for block-kind values.
	// Value.index is unreliable in ARM64-compiled binaries, so use this instead.
	val_to_block []int
	// Last-store cache for eliminating redundant store-then-load sequences.
	// After store_reg_to_val records (reg, val_id), the next load_val_to_reg
	// for the same val_id can reuse the register instead of loading from stack.
	// Cleared at block boundaries, function calls, and any register write.
	last_store_reg            int = -1
	last_store_val            int
	last_store_blk            int = -1
	last_store_next_instr_idx int = -1
	// Current block instruction list and index, for lookahead optimizations.
	cur_blk_instrs    []int
	cur_blk_instr_idx int
	// Function boundaries for dead-stripping.
	fn_starts  []int    // text offset where each function begins
	fn_ends    []int    // text offset where each function ends
	fn_names   []string // symbol name of each function
	fn_sym_ids []int    // symbol index in macho.symbols
	// Stats counters for optimization analysis.
	stats_total_stores   int
	stats_skipped_stores int
	stats_cache_hits     int
}

fn (Gen) gen #

fn (mut g Gen) gen()

fn (Gen) gen_pre_pass #

fn (mut g Gen) gen_pre_pass()

gen_pre_pass registers global symbols and builds lookup caches. Must be called before any gen_func calls.

fn (Gen) gen_post_pass #

fn (mut g Gen) gen_post_pass()

gen_post_pass emits the unresolved stub, global data, and patches symbol addresses. Must be called after all gen_func calls.

fn (Gen) pre_populate_type_caches #

fn (mut g Gen) pre_populate_type_caches()

new_worker_clone creates a new Gen instance for parallel code generation. The worker shares the read-only MIR module and lookup caches, but has its own MachOObject buffers for independent code emission. pre_populate_type_caches computes type_size and type_align for ALL types in the type store, so that workers can share the caches read-only.

fn (Gen) new_worker_clone #

fn (g &Gen) new_worker_clone() &Gen

fn (Gen) merge_worker #

fn (mut g Gen) merge_worker(w &Gen)

merge_worker merges a parallel worker's output buffers into the main Gen. text_data, str_data, symbols, and relocations are concatenated with offset adjustment.

fn (Gen) gen_func #

fn (mut g Gen) gen_func(func mir.Function)

fn (Gen) write_file #

fn (mut g Gen) write_file(path string)

fn (Gen) extract_function #

fn (mut g Gen) extract_function(fn_name string) []u8

extract_function extracts the raw machine code bytes for a named function. Used for hot code reloading: the extracted bytes can be loaded into a JIT page and called via a function pointer.

struct Linker #

struct Linker {
	macho &MachOObject
pub mut:
	// Frameworks to link (e.g. ['Metal', 'Cocoa', 'QuartzCore'])
	frameworks []string
mut:
	// Output buffer
	buf []u8

	// Segment/section info
	text_vmaddr   u64
	text_fileoff  int
	text_size     int
	data_vmaddr   u64
	data_fileoff  int
	data_size     int
	linkedit_off  int
	linkedit_size int

	// External symbols needing binding
	extern_syms []string

	// GOT entries for external symbols
	got_offset int // Offset within __DATA segment
	got_size   int

	// Stubs for external function calls
	stubs_offset int
	stubs_size   int

	// Symbol to GOT index mapping
	sym_to_got map[string]int

	// Multi-dylib support: dylib paths and per-symbol ordinal mapping
	dylibs       []string       // ['/usr/lib/libSystem.B.dylib', '/usr/lib/libobjc.A.dylib', ...]
	sym_to_dylib map[string]int // symbol name → index into dylibs[] (ordinal = idx + 1)

	// Code start offset (after header + load commands)
	code_start int
}

struct MachOObject #

struct MachOObject {
pub mut:
	text_data []u8
	str_data  []u8
	data_data []u8

	relocs      []RelocationInfo
	symbols     []Symbol
	str_table   []u8
	sym_by_name map[string]int // symbol name → index in symbols
}

fn (MachOObject) add_symbol #

fn (mut m MachOObject) add_symbol(name string, addr u64, is_ext bool, sect u8) int

fn (MachOObject) add_undefined #

fn (mut m MachOObject) add_undefined(name string) int

fn (MachOObject) add_reloc #

fn (mut m MachOObject) add_reloc(addr int, sym_idx int, typ int, pcrel bool)

fn (MachOObject) write #

fn (mut m MachOObject) write(path string)