///|
// Memory address (amd64-style addressing)
pub(all) struct Addr {
mut offset : Con
mut base : Ref
mut index : Ref
mut scale : Int
} derive(Debug)
///|
pub fn Addr::new() -> Addr {
Addr::{ offset: Con::new(), base: Ref::none(), index: Ref::none(), scale: 1, }
}
///|
// Function - corresponds to struct Fn in QBE
pub(all) struct Fn {
name : String
mut start_id : Int // Start block id
blks : Array[Blk] // All basic blocks (index = block id)
blk_names : Map[String, Int] // Name -> block id
tmps : Array[Tmp] // Temporaries (index = tmp id)
cons : Array[Con] // Constants (index = con id)
mems : Array[Addr] // Memory addresses (index = mem id)
rpo : Array[Int] // Reverse Post-Order block ids
def_order : Array[Int] // Blocks in definition order
mut ret_ty : Int // Return type index, -1 if no aggregate
mut retr : Ref // Return reference
mut reg : UInt64 // Used register mask
mut slot : Int // Stack slot count
mut is_export : Bool
mut is_vararg : Bool
mut has_dynalloc : Bool
} derive(Debug)
///|
pub fn Fn::new(name : String) -> Fn {
Fn::{
name,
start_id: 0,
blks: Array::new(),
blk_names: Map([], capacity=16),
tmps: Array::new(),
cons: Array::new(),
mems: Array::new(),
rpo: Array::new(),
def_order: Array::new(),
ret_ty: -1,
retr: Ref::none(),
reg: 0,
slot: 0,
is_export: false,
is_vararg: false,
has_dynalloc: false,
}
}
///|
pub fn Fn::nblk(self : Fn) -> Int {
self.blks.length()
}
///|
pub fn Fn::ntmp(self : Fn) -> Int {
self.tmps.length()
}
///|
pub fn Fn::ncon(self : Fn) -> Int {
self.cons.length()
}
///|
// Add a new block, returns its id
pub fn Fn::add_blk(self : Self, name : String) -> Int {
let id = self.blks.length()
let blk = Blk::new(id, name)
self.blks.push(blk)
self.blk_names[name] = id
id
}
///|
// Find block id by name, returns -1 if not found
pub fn Fn::find_blk(self : Fn, name : String) -> Int {
match self.blk_names.get(name) {
Some(id) => id
None => -1
}
}
///|
// Add a new temporary, returns its id
pub fn Fn::add_tmp(self : Self, name : String, cls : Class) -> Int {
let id = self.tmps.length()
self.tmps.push(Tmp::new(name, cls))
id
}
///|
// Pre-populate Tmp0 (64) register temporaries (matches C parse.c:779-783)
// Called at the start of function parsing so register temps occupy indices 0..Tmp0-1
pub fn Fn::init_regs(self : Self) -> Unit {
for i in 0.. Int {
let id = self.tmps.length()
let name = if prfx == "" {
""
} else {
newtmp_counter[0] = newtmp_counter[0] + 1
"\{prfx}.\{newtmp_counter[0]}"
}
let tmp = Tmp::new(name, cls)
tmp.slot = -1
tmp.nuse = 1
tmp.ndef = 1
self.tmps.push(tmp)
id
}
///|
// Add a new constant, returns its id
pub fn Fn::add_con(self : Self, con : Con) -> Int {
let id = self.cons.length()
self.cons.push(con)
id
}
///|
// Intern a constant: return existing id if a matching one exists,
// otherwise add it. Matching is by raw bits (like C's union compare).
pub fn Fn::get_con_by(self : Self, con : Con) -> Int {
for i in 0.. Int {
for i in 0.. Blk {
self.blks[id]
}
///|
// Get tmp by id
pub fn Fn::tmp(self : Fn, id : Int) -> Tmp {
self.tmps[id]
}
///|
// Get con by id
pub fn Fn::con(self : Fn, id : Int) -> Con {
self.cons[id]
}