///|
pub fn Module::translate_llvm_module(
self : Self,
llvm_mod : @IR.Module,
) -> Unit raise MIRError {
self.llvm_mod = Some(llvm_mod)
// Step 1: Translate global values
self.translate_global_values(llvm_mod)
// Step 2: Translate functions
for func_pair in llvm_mod.functions {
let (_, func) = func_pair
if func.isDeclaration() {
self.external_functions.set(func.getName().unwrap(), func)
} else {
let fname = func.getName().unwrap()
let is_external = func.linkage.val is External
let is_variadic = func.fty.isVarArg
let f = self.add_function(fname, [], is_external, is_variadic~)
f.translate_llvm_function(func)
}
}
}
///|
pub fn Module::translate_global_values(
self : Self,
llvm_mod : @IR.Module,
) -> Unit raise MIRError {
let data_layout = llvm_mod.getDataLayout()
for global_name, gv in llvm_mod.globals {
match gv.asGlobalValueEnum() {
Function(_) => ()
GlobalVariable({ initializer: None, initializerTy, .. }) => {
let size = data_layout
.getTypeAllocSize(initializerTy)
.reinterpret_as_uint()
let _ = self.add_global_zero_data(global_name, size)
}
GlobalVariable({ initializer: Some(initializer), .. }) =>
self.translate_llvm_global_constant(global_name, initializer)
GlobalConstant({ value, .. }) =>
self.translate_llvm_global_constant(global_name, value)
}
}
}
///|
pub fn Module::translate_llvm_global_constant(
self : Self,
global_name : String,
global_constant : &@IR.Constant,
) -> Unit raise MIRError {
let gv = match global_constant.asConstantEnum() {
ConstantString({ data, .. }) =>
self.add_global_string(label=global_name, content=data)
_ =>
// Check if this is a zeroinitializer constant
if is_zero_initializer(global_constant) {
let size = get_constant_size(global_constant)
self.add_global_zero_data(global_name, size)
} else {
let data = llvm_global_constant_to_data(global_constant)
self.add_global_data(global_name, data)
}
}
let _ = gv
}
///|
/// Check if a constant is a zeroinitializer
pub fn is_zero_initializer(constant : &@IR.Constant) -> Bool {
match constant.asConstantEnum() {
ConstantArray({ data: Left(const_arr), .. }) => {
// Check if all elements are zero
for item in const_arr {
if !is_zero_initializer(item) {
return false
}
}
true
}
ConstantStruct({ elements, .. }) => {
// Check if all elements are zero
for element in elements {
if !is_zero_initializer(element) {
return false
}
}
true
}
ConstantInt(c) => c.getValueAsInt64() == 0
ConstantFP(c) => c.getValue() == 0.0
ConstantPointerNull(_) => true
_ => false
}
}
///|
/// Get the size of a constant in bytes
pub fn get_constant_size(constant : &@IR.Constant) -> UInt {
match constant.asConstantEnum() {
ConstantArray({ data: Left(const_arr), vty, .. }) => {
let element_size = get_type_size(vty.getElementType())
let count = const_arr.length()
element_size * count.reinterpret_as_uint()
}
ConstantStruct({ elements, .. }) => {
let mut total_size : UInt = 0
for element in elements {
total_size = total_size + get_constant_size(element)
}
total_size
}
ConstantInt(c) => get_type_size(c.getIntegerType())
ConstantFP(c) => get_type_size(c.getFPType())
ConstantPointerNull(_) => 8 // 64-bit pointer
_ => 0
}
}
///|
/// Get the size of a type in bytes
pub fn get_type_size(ty : &@IR.Type) -> UInt {
// This is a simplified implementation
// In a real implementation, we would need to handle all type kinds
match ty.asTypeEnum() {
Int1Type(_) => 1
Int8Type(_) => 1
Int16Type(_) => 2
Int32Type(_) => 4
Int64Type(_) => 8
FloatType(_) => 4
DoubleType(_) => 8
PointerType(_) => 8 // 64-bit pointer
ArrayType(arr_ty) => {
let element_size = get_type_size(arr_ty.getElementType())
let count = arr_ty.getElementCount()
element_size * count.reinterpret_as_uint()
}
StructType(_) =>
// For struct types, we need to get the size from the data layout
// This is a simplified implementation
0
_ => 0
}
}
///|
pub fn llvm_global_constant_to_data(
global_constant : &@IR.Constant,
) -> Array[GlobalValueData] raise MIRError {
match global_constant.asConstantEnum() {
ConstantInt({ vty, value, .. }) =>
match vty.asIntegerTypeEnum() {
Int1Type(_) => [Byte(value.to_byte())]
Int8Type(_) => [Byte(value.to_byte())]
Int16Type(_) => [Half(value.to_uint16())]
Int32Type(_) => [Word(value.to_int().reinterpret_as_uint())]
Int64Type(_) => [Quad(value.reinterpret_as_uint64())]
}
ConstantFP({ vty, value, .. }) =>
match vty.asFPTypeEnum() {
FloatType(_) => {
let bits = value.to_float().reinterpret_as_uint()
[Word(bits)]
}
DoubleType(_) => {
let bits = value.reinterpret_as_uint64()
[Quad(bits)]
}
ty =>
raise IRTranslateError(
"Unsupported floating point type \{ty} in global constant.",
)
}
ConstantPointerNull(_) => [Quad(0)]
ConstantArray({ data: Left(const_arr), .. }) => {
let data : Array[GlobalValueData] = Array::new()
for item in const_arr {
let item_data = llvm_global_constant_to_data(item)
data.append(item_data)
}
data
}
ConstantArray({ data: Right(Int8Array(arr)), .. }) => {
let length = arr.length()
let Int8Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let UInt8Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let Int16Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let UInt16Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let Int32Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let UInt32Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let Int64Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let UInt64Array(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let FloatArray(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let length = arr.length()
let DoubleArray(arr) = arr
let data : Array[GlobalValueData] = Array::new()
for i in 0.. {
let data : Array[GlobalValueData] = Array::new()
for element in elements {
let element_data = llvm_global_constant_to_data(element)
data.append(element_data)
}
data
}
_ => raise IRTranslateError("Unsupported constant type in global constant.")
}
}
///|
pub fn Function::translate_llvm_function(
self : Self,
llvm_func : @IR.Function,
) -> Unit raise MIRError {
self.llvm_func = Some(llvm_func)
// Step 1: Traverse llvm basic blocks in Reverse Post-Order
let llvm_machine_bbs = self.traverse_llvm_basic_blocks()
// Step 2: Translate parameters
self.translate_params()
// Step 3: Traverse all llvm instructions (for binding values to registers)
self.traverse_llvm_instructions(llvm_func)
// Step 4: Translate each basic block
for pair in llvm_machine_bbs {
let (llvm_bb, machine_bb) = pair
machine_bb.translate_llvm_basic_block(llvm_bb)
}
if self.terminal_blocks.is_empty() {
self.terminal_blocks.push(self.body.last().unwrap())
}
// Step 5: Phi Elimination
self.phi_elimination()
}
///|
#callsite(autofill(loc))
pub fn Function::translate_params(self : Self, loc~ : SourceLoc) -> Unit {
guard self.llvm_func is Some(llvm_func) else {
let msg =
$|Fatal Error happened in \{loc}
#|LLVM function is not set for Function \{self.name}
println(msg)
panic()
}
fn pattern_of_llvm_arg(arg : @IR.Argument) {
match arg.getType().isIEEELikeFPTy() {
true => F
false => I
}
}
let arg_patterns = llvm_func.arguments.map(pattern_of_llvm_arg)
self.set_params_by_patterns(arg_patterns)
let entry_bb = self.get_entry_block().unwrap()
let buiilder = IRBuilder::new(self, entry_bb)
for arg, param in llvm_func.arguments.zip_to_iter2(self.params) {
// Get the actual type size from LLVM argument type
let arg_ty = arg.getType()
let data_size = match arg_ty.asTypeEnum() {
Int1Type(_) | Int8Type(_) | Int16Type(_) | Int32Type(_) | FloatType(_) =>
32
Int64Type(_) | PointerType(_) | DoubleType(_) => 64
_ => 64 // Default to 64 for other types
}
if param is IRegister(r) {
let tmp = self.new_virtual_reg()
let move_inst = buiilder.build_imove(data_size, dst=tmp, src=r) catch {
_ => abort("")
}
entry_bb.push(move_inst)
self.value_map.set(arg, IRegister(tmp))
continue
}
if param is FRegister(r) {
let tmp = self.new_virtual_freg()
let move_inst = buiilder.build_fmove(data_size, dst=tmp, src=r) catch {
_ => abort("")
}
entry_bb.push(move_inst)
self.value_map.set(arg, FRegister(tmp))
continue
}
let is_fp = arg.getType().isIEEELikeFPTy()
guard param is Mem(base, offset)
if is_fp {
let tmp = self.new_virtual_freg()
let load_inst = buiilder.build_loadf(data_size, dst=tmp, base~, offset~) catch {
_ => abort("")
}
entry_bb.push(load_inst)
self.value_map.set(arg, FRegister(tmp))
} else {
let tmp = self.new_virtual_reg()
let load_inst = buiilder.build_loadi(data_size, dst=tmp, base~, offset~) catch {
_ => abort("")
}
entry_bb.push(load_inst)
self.value_map.set(arg, IRegister(tmp))
}
}
}
///|
#callsite(autofill(loc))
pub fn Function::traverse_llvm_basic_blocks(
self : Self,
loc~ : SourceLoc,
) -> Array[(@IR.BasicBlock, BasicBlock)] {
guard self.llvm_func is Some(llvm_func) else {
let msg =
$|Fatal Error happened in \{loc}
#|LLVM function is not set for Function \{self.name}
println(msg)
panic()
}
let sorted_bbs = sort_llvm_basick_block(llvm_func)
let llvm_machine_bbs : Array[(@IR.BasicBlock, BasicBlock)] = Array::new()
for llvm_bb in sorted_bbs {
let label = match llvm_bb.getName() {
Some(n) => n
None => "\{self.name}_L\{self.bbmap.length()}"
}
let machine_bb = BasicBlock::new(self, label)
self.body.push(machine_bb)
self.value_map.set(llvm_bb, Label(label))
self.bbmap.set(label, machine_bb)
llvm_machine_bbs.push((llvm_bb, machine_bb))
}
llvm_machine_bbs
}
///|
fn sort_llvm_basick_block(llvm_func : @IR.Function) -> Array[@IR.BasicBlock] {
let basic_blocks : Array[@IR.BasicBlock] = Array::new()
let entry_bb = llvm_func.getEntryBlock().unwrap()
fn dfs(bb : @IR.BasicBlock, visited : Set[@IR.BasicBlock]) -> Unit {
if visited.contains(bb) {
return
}
visited.add(bb)
for succ in bb.getSuccessors().rev_iter() {
dfs(succ, visited)
}
basic_blocks.push(bb)
}
dfs(entry_bb, Set::new())
basic_blocks.rev()
}
///|
fn Function::traverse_llvm_instructions(
self : Self,
llvm_func : @IR.Function,
) -> Unit raise MIRError {
let llvm_mod = llvm_func.getModule()
let data_layout = llvm_mod.getDataLayout()
for llvm_inst in llvm_func.instIter() {
self.visit_llvm_inst(llvm_inst, data_layout)
}
}
///|
fn Function::visit_llvm_inst(
self : Function,
llvm_inst : &@IR.Instruction,
data_layout : @IR.DataLayout,
) -> Unit raise MIRError {
match llvm_inst.asInstEnum() {
AllocaInst({ data_ty, align, .. }) => {
let data_size = data_layout.getTypeAllocSize(data_ty).to_int64()
let align = align.to_int64()
let var_stack_loc = self.extend_var_stack(data_size, align)
self.bind_llvm_value_to_mem(llvm_inst, FramePtrPrim, -var_stack_loc)
}
_ => {
// Skip instructions whose results are never used (dead code)
if llvm_inst.user_empty() && !(llvm_inst.asValueEnum() is CallInst(_)) {
return
}
let llvm_ty = llvm_inst.getType()
match llvm_ty.asTypeEnum() {
Int1Type(_)
| Int8Type(_)
| Int16Type(_)
| Int32Type(_)
| Int64Type(_)
| PointerType(_) => {
let dst = self.new_virtual_reg()
self.bind_llvm_value_to_register(llvm_inst, dst)
}
FloatType(_) | DoubleType(_) => {
let dst = self.new_virtual_freg()
self.bind_llvm_value_to_fregister(llvm_inst, dst)
}
VoidType(_) => ()
StructType(_) | ArrayType(_) =>
self.bind_struct_or_array_value_to_mem_group(
llvm_inst, llvm_ty, data_layout,
)
_ =>
raise IRTranslateError(
"Cannot bind LLVM instruction type \{llvm_ty} to a register, inst is \{llvm_inst}",
)
}
}
}
}
///|
fn Function::bind_struct_or_array_value_to_mem_group(
self : Function,
llvm_inst : &@IR.Instruction,
ty : &@IR.Type,
data_layout : @IR.DataLayout,
) -> Unit raise MIRError {
guard llvm_inst.asInstEnum() is LoadInst(load_inst) else {
raise IRTranslateError(
"Struct value binding only supports LoadInst, got \{llvm_inst}",
)
}
let { ptr, .. } = load_inst
let ptr_op = if self.get_operand_from_llvm_value(ptr) is Some(op) {
op
} else {
guard ptr.tryAsInst() is Some(ptr_inst) else {
raise IRTranslateError(
"Struct LoadInst pointer is not found in value_map",
)
}
self.visit_llvm_inst(ptr_inst, data_layout)
guard self.get_operand_from_llvm_value(ptr) is Some(op) else {
raise IRTranslateError(
"Struct LoadInst pointer is not found in value_map after visiting",
)
}
op
}
// Handle both IRegister and MemLoc cases
// For MemLoc, we just use the base register and add the offset to all offsets
let (base, base_offset) = match ptr_op {
IRegister(r) => (r, 0L)
MemLoc(base_reg, offset) => (base_reg, offset)
_ =>
raise IRTranslateError(
"Struct LoadInst pointer is not a register or memory location, it is \{ptr_op}",
)
}
let relative_offsets = match ty.asTypeEnum() {
StructType(sty) =>
self.bind_struct_value_to_mem_group(base, 0L, sty, data_layout)
ArrayType(aty) =>
self.bind_array_value_to_mem_group(base, 0L, aty, data_layout)
_ =>
raise IRTranslateError(
"Type \{ty} is not struct or array for struct/array value binding",
)
}
// Add base_offset to all relative offsets to get absolute offsets
let offsets = relative_offsets.map(fn(offset) { base_offset + offset })
let op = MemGroup(base, offsets)
self.value_map.set(llvm_inst, op)
}
///|
fn Function::bind_struct_value_to_mem_group(
self : Function,
base : IRegister,
offset : Int64,
sty : @IR.StructType,
data_layout : @IR.DataLayout,
) -> Array[Int64] raise MIRError {
let { elements: element_types, .. } = sty
let offsets : Array[Int64] = Array::new()
for i, ety in element_types {
let this_offset = data_layout.getStructTypeOffset(sty, i)
let this_offset = this_offset.to_int64() + offset
offsets.push(this_offset)
let nested_offsets = match ety.asTypeEnum() {
StructType(nested_sty) =>
self.bind_struct_value_to_mem_group(
base, this_offset, nested_sty, data_layout,
)
ArrayType(nested_aty) =>
self.bind_array_value_to_mem_group(
base, this_offset, nested_aty, data_layout,
)
_ => []
}
offsets.append(nested_offsets)
}
offsets
}
///|
fn Function::bind_array_value_to_mem_group(
self : Function,
base : IRegister,
offset : Int64,
aty : @IR.ArrayType,
data_layout : @IR.DataLayout,
) -> Array[Int64] raise MIRError {
let element_type = aty.getElementType()
let element_size = data_layout.getTypeAllocSize(element_type).to_int64()
let count = aty.getElementCount()
let offsets : Array[Int64] = Array::new()
for i in 0..
self.bind_struct_value_to_mem_group(
base, this_offset, nested_sty, data_layout,
)
ArrayType(nested_aty) =>
self.bind_array_value_to_mem_group(
base, this_offset, nested_aty, data_layout,
)
_ => []
}
offsets.append(nested_offsets)
}
offsets
}
///|
pub fn BasicBlock::translate_llvm_basic_block(
self : Self,
llvm_bb : @IR.BasicBlock,
) -> Unit raise MIRError {
let func = self.parent
let builder = IRBuilder::new(func, self)
for inst in llvm_bb.instIter() {
let insts = translate_llvm_inst(inst, builder)
insts.each(i => self.push(i))
}
}
///|
pub fn translate_llvm_inst(
llvm_inst : &@IR.Instruction,
builder : IRBuilder,
) -> Array[Instruction] raise MIRError {
let func = builder.func
let llvm_mod = llvm_inst.getModule()
let data_layout = llvm_mod.getDataLayout()
match llvm_inst.asInstEnum() {
// AllocaInst has been handled in traverse_llvm_instructions
AllocaInst(_) => []
LoadInst(load_inst) =>
translate_llvm_load_inst(load_inst, builder, func, data_layout)
StoreInst(store_inst) =>
translate_llvm_store_inst(store_inst, builder, func, data_layout)
FNegInst(fneg_inst) =>
translate_llvm_fneg_inst(fneg_inst, builder, func, data_layout)
BinaryInst(binary_inst) =>
translate_llvm_binary_inst(binary_inst, builder, func, data_layout)
ICmpInst(icmp_inst) =>
translate_llvm_icmp_inst(icmp_inst, builder, func, data_layout)
FCmpInst(fcmp_inst) =>
translate_llvm_fcmp_inst(fcmp_inst, builder, func, data_layout)
BranchInst(branch_inst) =>
translate_llvm_branch_inst(branch_inst, builder, func, data_layout)
SwitchInst(switch_inst) =>
translate_llvm_switch_inst(switch_inst, builder, func, data_layout)
CastInst(cast_inst) =>
translate_llvm_cast_inst(cast_inst, builder, func, data_layout)
GetElementPtrInst(gep_inst) =>
translate_llvm_gep_inst(gep_inst, builder, func, data_layout)
SelectInst(select_inst) =>
translate_llvm_select_inst(select_inst, builder, func, data_layout)
ReturnInst(ret_inst) => translate_llvm_return_inst(ret_inst, builder, func)
CallInst(call_inst) => translate_llvm_call_inst(call_inst, builder, func)
PHINode(phi_node) => translate_llvm_phi_node(phi_node, func)
ExtractValueInst(_) =>
raise IRTranslateError(
"Translation for LLVM instruction \{llvm_inst} is not implemented",
)
InsertValueInst(_) =>
raise IRTranslateError(
"Translation for LLVM instruction \{llvm_inst} is not implemented",
)
}
}
///|
pub fn translate_llvm_load_inst(
llvm_inst : @IR.LoadInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { vty, ptr, .. } = llvm_inst
guard func.get_operand_from_llvm_value(ptr) is Some(ptr_op) else {
raise IRTranslateError("LoadInst: pointer operand not found in value_map")
}
let (base, offset, minsts) : (IRegister, Int64, Array[Instruction]) = match
ptr_op {
MemLoc(base, offset) => (base, offset, [])
IRegister(r) => (r, 0L, [])
Label(label) => {
let tmp = func.new_virtual_reg()
let inst = builder.build_load_addr(tmp, label)
(tmp, 0L, [inst])
}
_ =>
raise IRTranslateError(
"LoadInst: pointer operand is not a memory address or integer register, it is \{ptr_op}, ptr is \{ptr}",
)
}
let data_size = data_layout.getTypeAllocSizeInBits(vty)
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
// If the destination operand is not found, it means this load instruction
// was skipped because its result is never used (dead code)
return []
}
let minst = match dst_op {
MemGroup(_) => return minsts // Will be handled in store.
IRegister(dst) => builder.build_loadi(data_size, dst~, base~, offset~)
FRegister(dst) => builder.build_loadf(data_size, dst~, base~, offset~)
dst_op =>
raise IRTranslateError(
"LoadInst: destination operand is not an integer or floating-point register: \{dst_op}",
)
}
minsts.push(minst)
minsts
}
///|
/// IRTranslate from LLVM IR to MoonMIR for StoreInst.
pub fn translate_llvm_store_inst(
llvm_inst : @IR.StoreInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { value, ptr, .. } = llvm_inst
guard func.get_operand_from_llvm_value(value) is Some(src_op) else {
raise IRTranslateError("StoreInst: value operand not found in value_map")
}
guard func.get_operand_from_llvm_value(ptr) is Some(ptr_op) else {
raise IRTranslateError("StoreInst: pointer operand not found in value_map")
}
let (base, offset, minsts) : (IRegister, Int64, Array[Instruction]) = match
ptr_op {
MemLoc(base, offset) => (base, offset, [])
IRegister(r) => (r, 0L, [])
Label(label) => {
let tmp = func.new_virtual_reg()
let inst = builder.build_load_addr(tmp, label)
(tmp, 0L, [inst])
}
_ =>
raise IRTranslateError(
"StoreInst: pointer operand is not a memory address or integer register",
)
}
let data_size = data_layout.getTypeAllocSizeInBits(value.getType())
let store_insts = match src_op {
IRegister(src) => [builder.build_storei(data_size, src~, base~, offset~)]
FRegister(src) => [builder.build_storef(data_size, src~, base~, offset~)]
Imm(src) => {
let tmp = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=tmp, src~),
builder.build_storei(data_size, src=tmp, base~, offset~),
]
}
FImm(src) => {
let tmp = func.new_virtual_freg()
[
builder.build_fmove_imm(data_size, dst=tmp, src~),
builder.build_storef(data_size, src=tmp, base~, offset~),
]
}
Label(label) => {
let tmp = func.new_virtual_reg()
[
builder.build_load_addr(tmp, label),
builder.build_storei(data_size, src=tmp, base~, offset~),
]
}
MemLoc(src_base, src_offset) => {
// MemLoc as a value means it's a pointer/address value
// Compute the address (src_base + src_offset) and store that address
let tmp = func.new_virtual_reg()
if src_offset == 0L {
// Just move the base register
[
builder.build_imove(64, dst=tmp, src=src_base),
builder.build_storei(data_size, src=tmp, base~, offset~),
]
} else {
// Compute address: tmp = src_base + src_offset
[
builder.build_ibinary_imm(
Add,
64,
dst=tmp,
src1=src_base,
src2=src_offset,
),
builder.build_storei(data_size, src=tmp, base~, offset~),
]
}
}
MemGroup(src_base, offsets) => {
minsts.append(
translate_multiple_load_store(
llvm_inst, builder, func, data_layout, src_base, offsets, base, offset,
),
)
return minsts
}
src_op =>
raise IRTranslateError(
"StoreInst: value operand is not a register, immediate, or label: \{src_op}",
)
}
minsts.append(store_insts)
minsts
}
///|
fn translate_multiple_load_store(
_llvm_inst : @IR.StoreInst,
builder : IRBuilder,
func : Function,
_data_layout : @IR.DataLayout,
src_base : IRegister,
src_offsets : Array[Int64],
dst_base : IRegister,
dst_base_offset : Int64,
) -> Array[Instruction] raise MIRError {
let minsts : Array[Instruction] = Array::new()
// The src_offsets array contains absolute offsets relative to src_base
// We need to extract the relative field offsets and apply them to the destination
guard src_offsets.length() > 0 else { return minsts }
let src_base_offset = src_offsets[0]
// For each field in the struct/array
for src_offset in src_offsets {
// Calculate the field's relative offset
let field_offset = src_offset - src_base_offset
// Load the value from source location (src_base + src_offset)
let temp_reg = func.new_virtual_reg()
let load_inst = builder.build_loadi(
32,
dst=temp_reg,
base=src_base,
offset=src_offset,
)
minsts.push(load_inst)
// Store the value to destination location (dst_base + dst_base_offset + field_offset)
let dst_offset = dst_base_offset + field_offset
let store_inst = builder.build_storei(
32,
src=temp_reg,
base=dst_base,
offset=dst_offset,
)
minsts.push(store_inst)
}
minsts
}
///|
/// IRTranslate from LLVM IR to MoonMIR for Int or Float binary instructions.
pub fn translate_llvm_binary_inst(
llvm_inst : @IR.BinaryInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
if llvm_inst.getType().isIEEELikeFPTy() {
translate_llvm_float_binary_inst(llvm_inst, builder, func, data_layout)
} else {
translate_llvm_int_binary_inst(llvm_inst, builder, func, data_layout)
}
}
///|
/// IRTranslate from LLVM IR to MIR for floating-point BinaryInst
pub fn translate_llvm_float_binary_inst(
llvm_inst : @IR.BinaryInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { opcode, lhs, rhs, .. } = llvm_inst
let data_size = data_layout.getTypeAllocSizeInBits(llvm_inst.getType())
let binop = match opcode {
FAdd => FAdd
FSub => FSub
FMul => FMul
FDiv => FDiv
FRem => FRem
_ => raise IRTranslateError("Unsupported floating-point binary opcode")
}
guard func.get_operand_from_llvm_value(lhs) is Some(lhs_op) else {
raise IRTranslateError("BinaryInst: lhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(rhs) is Some(rhs_op) else {
raise IRTranslateError("BinaryInst: rhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
guard dst_op is FRegister(dst) else {
raise IRTranslateError(
"BinaryInst: destination operand is not a floating-point register",
)
}
match (lhs_op, rhs_op) {
(FRegister(src1), FRegister(src2)) =>
[builder.build_fbinary(binop, data_size, dst~, src1~, src2~)]
(FRegister(src1), FImm(f)) => {
let ireg = func.new_virtual_reg()
let src2 = func.new_virtual_freg()
[
match data_size {
32 =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.to_float().reinterpret_as_int().to_int64(),
)
_ =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.reinterpret_as_int64(),
)
},
builder.build_fmove_from_ireg(data_size, dst=src2, src=ireg),
builder.build_fbinary(binop, data_size, dst~, src1~, src2~),
]
}
(FImm(f), FRegister(src2)) => {
let src1 = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
let move_imm = match data_size {
32 =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.to_float().reinterpret_as_int().to_int64(),
)
_ =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.reinterpret_as_int64(),
)
}
[
move_imm,
builder.build_fmove_from_ireg(data_size, dst=src1, src=ireg),
builder.build_fbinary(binop, data_size, dst~, src1~, src2~),
]
}
(FImm(f1), FImm(f2)) => {
let f = match binop {
FAdd => f1 + f2
FSub => f1 - f2
FMul => f1 * f2
FDiv => f1 / f2
FRem => f1 % f2
}
let ireg = func.new_virtual_reg()
let move_imm = match data_size {
32 =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.to_float().reinterpret_as_int().to_int64(),
)
_ =>
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.reinterpret_as_int64(),
)
}
[move_imm, builder.build_fmove_from_ireg(data_size, dst~, src=ireg)]
}
(lhs_op, rhs_op) =>
raise IRTranslateError(
"Unsupported operand types for float binary operation, \{lhs_op}, \{rhs_op}",
)
}
}
///|
///
/// IRTranslate from LLVM IR to MIR for Int BinaryInst
pub fn translate_llvm_int_binary_inst(
llvm_inst : @IR.BinaryInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { opcode, lhs, rhs, .. } = llvm_inst
let data_size = data_layout.getTypeAllocSizeInBits(llvm_inst.getType())
let binop = match opcode {
Add => Add
Sub => Sub
Mul => Mul
UDiv | SDiv => Div
URem | SRem => Rem
And => And
Or => Or
Xor => Xor
Shl => Shl
LShr => LShr
AShr => AShr
_ => raise IRTranslateError("Unsupported integer binary opcode")
}
guard func.get_operand_from_llvm_value(lhs) is Some(lhs_op) else {
raise IRTranslateError("BinaryInst: lhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(rhs) is Some(rhs_op) else {
raise IRTranslateError("BinaryInst: rhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
guard dst_op is IRegister(dst) else {
raise IRTranslateError(
"BinaryInst: destination operand is not an integer register",
)
}
match (lhs_op, rhs_op) {
(IRegister(src1), IRegister(src2)) =>
[builder.build_ibinary(binop, data_size, dst~, src1~, src2~)]
(IRegister(src1), Imm(src2)) =>
[builder.build_ibinary_imm(binop, data_size, dst~, src1~, src2~)]
(Imm(src1), IRegister(src2)) if binop.is_commutative() =>
[builder.build_ibinary_imm(binop, data_size, dst~, src1=src2, src2=src1)] // swap
(Imm(src1_imm), IRegister(src2)) => {
let src1 = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=src1, src=src1_imm),
builder.build_ibinary(binop, data_size, dst~, src1~, src2~),
]
}
(Imm(src1), Imm(src2)) => {
let src = match binop {
Add => src1 + src2
Sub => src1 - src2
Mul => src1 * src2
Div => src1 / src2
Rem => src1 % src2
And => src1 & src2
Or => src1 | src2
Xor => src1 ^ src2
Shl => src1 << src2.to_int()
LShr =>
(src1.reinterpret_as_uint64() >> src2.to_int()).reinterpret_as_int64()
AShr => src1 >> src2.to_int()
}
[builder.build_imove_imm(data_size, dst~, src~)]
}
(lhs_op, rhs_op) =>
raise IRTranslateError(
"Unsupported operand types for integer binary operation: \{lhs_op}, \{rhs_op}",
)
}
}
///|
///
/// IRTranslate from LLVM IR to MoonMIR for FNegInst
pub fn translate_llvm_fneg_inst(
llvm_inst : @IR.FNegInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { operand, .. } = llvm_inst
guard func.get_operand_from_llvm_value(operand) is Some(src_op) else {
raise IRTranslateError("FNegInst: operand not found in value_map")
}
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
guard dst_op is FRegister(dst) else {
raise IRTranslateError(
"FNegInst: destination operand is not a floating-point register",
)
}
let data_size = data_layout.getTypeAllocSizeInBits(llvm_inst.getType())
match src_op {
FRegister(src) => [builder.build_fneg(data_size, dst~, src~)]
FImm(f) => {
let ireg = func.new_virtual_reg()
let src = func.new_virtual_freg()
// Convert double bit representation to appropriate type
let bit_value = match data_size {
32 => {
// Convert double to float bit representation
let float_val = f.to_float()
float_val.reinterpret_as_int().to_int64()
}
64 => f.reinterpret_as_int64()
_ => f.reinterpret_as_int64()
}
[
builder.build_imove_imm(data_size, dst=ireg, src=bit_value),
builder.build_fmove_from_ireg(data_size, dst=src, src=ireg),
builder.build_fneg(data_size, dst~, src~),
]
}
src_op =>
raise IRTranslateError(
"FNegInst: operand is not a memory address, floating-point register, or immediate value: \{src_op}",
)
}
}
///|
/// IRTranslate from LLVM IR to MoonMIR for ICmpInst.
pub fn translate_llvm_icmp_inst(
llvm_inst : @IR.ICmpInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
// If the ICmpInst is only used by a BranchInst,
// we do not generate a separate instruction for it.
let inst_users = llvm_inst.getUsers().unwrap()
if inst_users.length() == 1 && inst_users[0].asValueEnum() is BranchInst(_) {
return []
}
let { predicate, lhs, rhs, .. } = llvm_inst
let opcode : ICmpOpCode = match predicate {
EQ => Eq
NE => Ne
SGT => Gt
SGE => Ge
SLT => Lt
SLE => Le
UGT => Gtu
UGE => Geu
ULT => Ltu
ULE => Leu
}
guard func.get_operand_from_llvm_value(lhs) is Some(lhs_op) else {
raise IRTranslateError("ICmpInst: lhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(rhs) is Some(rhs_op) else {
raise IRTranslateError("ICmpInst: rhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
guard dst_op is IRegister(dst) else {
raise IRTranslateError(
"ICmpInst: destination operand is not an integer register",
)
}
let data_size = data_layout.getTypeAllocSizeInBits(lhs.getType())
match (lhs_op, rhs_op) {
(IRegister(src1), IRegister(src2)) =>
[builder.build_icmp(opcode, data_size, dst~, src1~, src2~)]
(IRegister(src1), Imm(src2_imm)) => {
let src2 = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=src2, src=src2_imm),
builder.build_icmp(opcode, data_size, dst~, src1~, src2~),
]
}
(Imm(src1_imm), IRegister(src2)) => {
let src1 = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=src1, src=src1_imm),
builder.build_icmp(opcode, data_size, dst~, src1~, src2~),
]
}
(Imm(src1), Imm(src2)) => {
let src = match opcode {
Eq => if src1 == src2 { 1L } else { 0L }
Ne => if src1 != src2 { 1L } else { 0L }
Gt => if src1 > src2 { 1L } else { 0L }
Ge => if src1 >= src2 { 1L } else { 0L }
Lt => if src1 < src2 { 1L } else { 0L }
Le => if src1 <= src2 { 1L } else { 0L }
Gtu =>
if src1.reinterpret_as_uint64() > src2.reinterpret_as_uint64() {
1L
} else {
0L
}
Geu =>
if src1.reinterpret_as_uint64() >= src2.reinterpret_as_uint64() {
1L
} else {
0L
}
Ltu =>
if src1.reinterpret_as_uint64() < src2.reinterpret_as_uint64() {
1L
} else {
0L
}
Leu =>
if src1.reinterpret_as_uint64() <= src2.reinterpret_as_uint64() {
1L
} else {
0L
}
}
[builder.build_imove_imm(data_size, dst~, src~)]
}
opcode =>
raise IRTranslateError(
"Unsupported operand types for integer comparison: \{opcode}",
)
}
}
///|
pub fn translate_llvm_fcmp_inst(
llvm_inst : @IR.FCmpInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
guard dst_op is IRegister(dst) else {
raise IRTranslateError(
"FCmpInst: destination operand is not an integer register",
)
}
let { predicate, lhs, rhs, .. } = llvm_inst
match predicate {
FALSE => return [builder.build_imove_imm(64, dst~, src=0L)]
TRUE => return [builder.build_imove_imm(64, dst~, src=1L)]
_ => ()
}
let opcode : FCmpOpCode = match predicate {
OEQ => Feq
OGT => Fgt
OGE => Fge
OLT => Flt
OLE => Fle
ONE => Fne
UEQ => Feq // unordered or equal
UGT => Fgt // unordered or greater than
UGE => Fge // unordered or greater than or equal
ULT => Flt // unordered or less than
ULE => Fle // unordered or less than or equal
UNE => Fne // unordered or not equal
_ =>
raise IRTranslateError("Unsupported floating-point comparison predicate")
}
guard func.get_operand_from_llvm_value(lhs) is Some(lhs_op) else {
raise IRTranslateError("FCmpInst: lhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(rhs) is Some(rhs_op) else {
raise IRTranslateError("FCmpInst: rhs operand not found in value_map")
}
let data_size = data_layout.getTypeAllocSizeInBits(lhs.getType())
match (lhs_op, rhs_op) {
(FRegister(src1), FRegister(src2)) =>
[builder.build_fcmp(opcode, data_size, dst~, src1~, src2~)]
(FRegister(src1), FImm(f)) => {
let ireg = func.new_virtual_reg()
let src2 = func.new_virtual_freg()
[
builder.build_imove_imm(
data_size,
dst=ireg,
src=f.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(data_size, dst=src2, src=ireg),
builder.build_fcmp(opcode, data_size, dst~, src1~, src2~),
]
}
(FImm(f1), FRegister(src2)) => {
let src1 = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
data_size,
dst=ireg,
src=f1.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(data_size, dst=src1, src=ireg),
builder.build_fcmp(opcode, data_size, dst~, src1~, src2~),
]
}
(FImm(f1), FImm(f2)) => {
let src = match opcode {
Feq => if f1 == f2 { 1L } else { 0L }
Fne => if f1 != f2 { 1L } else { 0L }
Fgt => if f1 > f2 { 1L } else { 0L }
Fge => if f1 >= f2 { 1L } else { 0L }
Flt => if f1 < f2 { 1L } else { 0L }
Fle => if f1 <= f2 { 1L } else { 0L }
}
[builder.build_imove_imm(data_size, dst~, src~)]
}
(lhs_op, rhs_op) =>
raise IRTranslateError(
"Unsupported operand types for floating-point comparison: \{lhs_op}, \{rhs_op}",
)
}
}
///|
pub fn translate_llvm_branch_inst(
llvm_inst : @IR.BranchInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
match llvm_inst.condition {
Some(_) =>
translate_llvm_conditional_branch(llvm_inst, builder, func, data_layout)
None => translate_llvm_unconditional_branch(llvm_inst, builder, func)
}
}
///|
/// IRTranslate from LLVM IR to MoonMIR for conditional BranchInst.
pub fn translate_llvm_conditional_branch(
llvm_inst : @IR.BranchInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
guard llvm_inst
is {
condition: Some(cond),
trueBlock: Some(truebb),
falseBlock: Some(falsebb),
..,
} else {
raise IRTranslateError("BranchInst: Invalid conditional branch instruction")
}
guard func.get_operand_from_llvm_value(truebb) is Some(true_op) else {
raise IRTranslateError(
"BranchInst: trueBlock operand not found in value_map",
)
}
guard func.get_operand_from_llvm_value(falsebb) is Some(false_op) else {
raise IRTranslateError(
"BranchInst: falseBlock operand not found in value_map",
)
}
guard true_op is Label(true_label) else {
raise IRTranslateError("BranchInst: trueBlock operand is not a label")
}
guard false_op is Label(false_label) else {
raise IRTranslateError("BranchInst: falseBlock operand is not a label")
}
// Push the successors and predecessors
let machine_true_bb = func.bbmap.get(true_label).unwrap()
let machine_false_bb = func.bbmap.get(false_label).unwrap()
let current_bb = builder.bb
machine_true_bb.preds.push(current_bb)
machine_false_bb.preds.push(current_bb)
current_bb.succs.push(machine_true_bb)
current_bb.succs.push(machine_false_bb)
if !(cond.asValueEnum() is ICmpInst(_)) {
guard func.get_operand_from_llvm_value(cond) is Some(cond_op) else {
raise IRTranslateError(
"BranchInst: condition operand not found in value_map",
)
}
match cond_op {
IRegister(lhs) => {
let minst = builder.build_branch_imm(
Bne,
64,
lhs~,
rhs=0L,
true_label~,
false_label~,
)
return [minst]
}
Imm(val) => {
// Handle constant condition: branch to true_label if non-zero, false_label if zero
let target_label = if val != 0L { true_label } else { false_label }
return [builder.build_jmp(target_label~)]
}
_ =>
raise IRTranslateError(
"BranchInst: condition operand is not an integer register or immediate, it is \{cond_op}",
)
}
}
//
let cmp_users = cond.getUsers().unwrap()
let cmp_only_used_in_branch = cmp_users.length() == 1 &&
cmp_users[0].asValueEnum() is BranchInst(_)
// If the comparison instruction is not only used in this branch,
// generate Bne directly.
if !cmp_only_used_in_branch {
guard func.get_operand_from_llvm_value(cond) is Some(cond_op) else {
raise IRTranslateError(
"BranchInst: condition operand not found in value_map",
)
}
match cond_op {
IRegister(lhs) => {
let minst = builder.build_branch_imm(
Bne,
64,
lhs~,
rhs=0L,
true_label~,
false_label~,
)
return [minst]
}
Imm(val) => {
// Handle constant condition: branch to true_label if non-zero, false_label if zero
let target_label = if val != 0L { true_label } else { false_label }
return [builder.build_jmp(target_label~)]
}
_ =>
raise IRTranslateError(
"BranchInst: condition operand is not an integer register or immediate, it is \{cond_op}",
)
}
}
// If the comparison instruction only used in this branch,
// generate breach with the comparison.
guard cond.asValueEnum() is ICmpInst({ predicate, lhs, rhs, .. })
let opcode = match predicate {
EQ => Beq
NE => Bne
SGT => Bgt
SGE => Bge
SLT => Blt
SLE => Ble
UGT => Bgtu
UGE => Bgeu
ULT => Bltu
ULE => Bleu
}
guard func.get_operand_from_llvm_value(lhs) is Some(lhs_op) else {
raise IRTranslateError("BranchInst: lhs operand not found in value_map")
}
guard func.get_operand_from_llvm_value(rhs) is Some(rhs_op) else {
raise IRTranslateError("BranchInst: rhs operand not found in value_map")
}
let data_size = data_layout.getTypeAllocSizeInBits(lhs.getType())
match (lhs_op, rhs_op) {
(IRegister(lhs), IRegister(rhs)) =>
[
builder.build_branch(
opcode,
data_size,
lhs~,
rhs~,
true_label~,
false_label~,
),
]
(IRegister(lhs), Imm(rhs)) =>
[
builder.build_branch_imm(
opcode,
data_size,
lhs~,
rhs~,
true_label~,
false_label~,
),
]
(Imm(lhs_imm), IRegister(rhs)) => {
let lhs = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=lhs, src=lhs_imm),
builder.build_branch(
opcode,
data_size,
lhs~,
rhs~,
true_label~,
false_label~,
),
]
}
(Imm(lhs), Imm(rhs)) => {
let take_true_branch = match opcode {
Beq => lhs == rhs
Bne => lhs != rhs
Bgt => lhs > rhs
Bge => lhs >= rhs
Blt => lhs < rhs
Ble => lhs <= rhs
Bgtu => lhs.reinterpret_as_uint64() > rhs.reinterpret_as_uint64()
Bgeu => lhs.reinterpret_as_uint64() >= rhs.reinterpret_as_uint64()
Bltu => lhs.reinterpret_as_uint64() < rhs.reinterpret_as_uint64()
// Bleu
_ => lhs.reinterpret_as_uint64() <= rhs.reinterpret_as_uint64()
}
let target_label = if take_true_branch { true_label } else { false_label }
[builder.build_jmp(target_label~)]
}
(lhs_op, rhs_op) =>
raise IRTranslateError(
"Unsupported operand types for integer comparison in BranchInst: \{lhs_op}, \{rhs_op}",
)
}
}
///|
/// IRTranslate from LLVM IR to MoonMIR for unconditional BranchInst.
pub fn translate_llvm_unconditional_branch(
llvm_inst : @IR.BranchInst,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
guard llvm_inst
is { condition: None, trueBlock: Some(targetBB), falseBlock: None, .. } else {
raise IRTranslateError(
"BranchInst: Invalid unconditional branch instruction",
)
}
guard func.get_operand_from_llvm_value(targetBB) is Some(target_op) else {
raise IRTranslateError(
"BranchInst: trueBlock operand not found in value_map",
)
}
guard target_op is Label(target_label) else {
raise IRTranslateError("BranchInst: trueBlock operand is not a label")
}
let machine_target_bb = func.bbmap.get(target_label).unwrap()
let current_bb = builder.bb
machine_target_bb.preds.push(current_bb)
current_bb.succs.push(machine_target_bb)
let minst = builder.build_jmp(target_label~)
[minst]
}
///|
pub fn translate_llvm_switch_inst(
llvm_inst : @IR.SwitchInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { condition, defaultDest, cases, .. } = llvm_inst
// Get the condition operand
guard func.get_operand_from_llvm_value(condition) is Some(cond_op) else {
raise IRTranslateError(
"SwitchInst: condition operand not found in value_map",
)
}
guard cond_op is IRegister(cond_reg) else {
raise IRTranslateError(
"SwitchInst: condition operand is not an integer register",
)
}
// Get the default destination label
guard func.get_operand_from_llvm_value(defaultDest) is Some(default_op) else {
raise IRTranslateError(
"SwitchInst: defaultDest operand not found in value_map",
)
}
guard default_op is Label(default_label) else {
raise IRTranslateError("SwitchInst: defaultDest operand is not a label")
}
// Get the data size for comparison
let data_size = data_layout.getTypeAllocSizeInBits(condition.getType())
// Setup successors and predecessors for the current basic block
let current_bb = builder.bb
let machine_default_bb = func.bbmap.get(default_label).unwrap()
// If there are no cases, just jump to default
if cases.length() == 0 {
machine_default_bb.preds.push(current_bb)
current_bb.succs.push(machine_default_bb)
let jmp_inst = builder.build_jmp(target_label=default_label)
return [jmp_inst]
}
// For each case destination, setup successors and predecessors
for case in cases {
let (_, case_dest) = case
guard func.get_operand_from_llvm_value(case_dest) is Some(case_op) else {
raise IRTranslateError(
"SwitchInst: case destination operand not found in value_map",
)
}
guard case_op is Label(case_label) else {
raise IRTranslateError(
"SwitchInst: case destination operand is not a label",
)
}
let machine_case_bb = func.bbmap.get(case_label).unwrap()
machine_case_bb.preds.push(current_bb)
current_bb.succs.push(machine_case_bb)
}
// Generate instructions in the current basic block
let minsts : Array[Instruction] = Array::new()
// Strategy: Generate a chain of basic blocks, each checking one case
// The first case is checked in the current basic block
let mut prev_bb = current_bb
for i, case in cases.iter2() {
let (case_value, case_dest) = case
let case_imm = case_value.getValueAsInt64()
guard func.get_operand_from_llvm_value(case_dest) is Some(case_op) else {
raise IRTranslateError(
"SwitchInst: case destination operand not found in value_map",
)
}
guard case_op is Label(case_label) else {
raise IRTranslateError(
"SwitchInst: case destination operand is not a label",
)
}
// Determine the false label (where to go if this case doesn't match)
let false_label = if i + 1 < cases.length() {
// Create a new basic block for the next case check
"\{func.name}_switch\{llvm_inst.uid}_check\{i + 1}"
} else {
// Last case, jump to default if it doesn't match
default_label
}
// Generate beq instruction in the current/previous basic block
let curr_builder = IRBuilder::new(func, prev_bb)
let inst = curr_builder.build_branch_imm(
Beq,
data_size,
lhs=cond_reg,
rhs=case_imm,
true_label=case_label,
false_label~,
)
// Add instruction to the appropriate basic block
if i == 0 {
// First case: add to the return array
minsts.push(inst)
} else {
// Subsequent cases: add directly to the new basic block
prev_bb.push(inst)
}
// Create a new basic block for the next case check (if not the last case)
if i + 1 < cases.length() {
let next_check_label = "\{func.name}_switch\{llvm_inst.uid}_check\{i + 1}"
let next_check_bb = func.append_basic_block(next_check_label)
// Setup the predecessor for the next check block
next_check_bb.preds.push(prev_bb)
prev_bb.succs.push(next_check_bb)
// Update prev_bb for the next iteration
prev_bb = next_check_bb
} else {
// Last case: setup default as successor
machine_default_bb.preds.push(prev_bb)
prev_bb.succs.push(machine_default_bb)
}
}
minsts
}
///|
pub fn translate_llvm_cast_inst(
llvm_inst : @IR.CastInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { opcode, from_val, to_ty, .. } = llvm_inst
guard func.get_operand_from_llvm_value(from_val) is Some(from_op) else {
raise IRTranslateError("CastInst: source operand not found in value_map")
}
let from_ty_size = data_layout.getTypeAllocSizeInBits(from_val.getType())
let to_ty_size = data_layout.getTypeAllocSizeInBits(to_ty)
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
return []
}
match opcode {
Trunc =>
translate_trunc(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
SExt =>
translate_sext(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
ZExt =>
translate_zext(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
FPTrunc =>
translate_fptrunc(
from_ty_size, to_ty_size, from_op, dst_op, builder, func,
)
FPExt =>
translate_fpext(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
UIToFP =>
translate_uitofp(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
SIToFP =>
translate_sitofp(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
FPToUI =>
translate_fptoui(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
FPToSI =>
translate_fptosi(from_ty_size, to_ty_size, from_op, dst_op, builder, func)
PtrToInt => translate_bitcast(64, from_op, dst_op, builder, func)
IntToPtr => translate_bitcast(64, from_op, dst_op, builder, func)
BitCast => translate_bitcast(from_ty_size, from_op, dst_op, builder, func)
}
}
///|
pub fn translate_trunc(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), IRegister(src)) =>
[builder.build_trunc(from_ty_size~, to_ty_size~, dst~, src~)]
(IRegister(dst), Imm(src_imm)) => {
let src = func.new_virtual_reg()
[
builder.build_imove_imm(from_ty_size, dst=src, src=src_imm),
builder.build_trunc(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"Trunc: Unsupported operand types for truncation, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_sext(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), IRegister(src)) =>
[builder.build_sext(from_ty_size~, to_ty_size~, dst~, src~)]
(IRegister(dst), Imm(src_imm)) => {
let src = func.new_virtual_reg()
[
builder.build_imove_imm(from_ty_size, dst=src, src=src_imm),
builder.build_sext(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"SExt: Unsupported operand types for sign extension, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_zext(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), IRegister(src)) =>
[builder.build_zext(from_ty_size~, to_ty_size~, dst~, src~)]
(IRegister(dst), Imm(src_imm)) => {
let src = func.new_virtual_reg()
[
builder.build_imove_imm(from_ty_size, dst=src, src=src_imm),
builder.build_zext(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"ZExt: Unsupported operand types for zero extension, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_fptrunc(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(FRegister(dst), FRegister(src)) =>
[builder.build_fptrunc(from_ty_size~, to_ty_size~, dst~, src~)]
(FRegister(dst), FImm(src_imm)) => {
let src = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
from_ty_size,
dst=ireg,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(from_ty_size, dst=src, src=ireg),
builder.build_fptrunc(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"FPTrunc: Unsupported operand types for floating-point truncation, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_fpext(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(FRegister(dst), FRegister(src)) =>
[builder.build_fpext(from_ty_size~, to_ty_size~, dst~, src~)]
(FRegister(dst), FImm(src_imm)) => {
let src = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
from_ty_size,
dst=ireg,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(from_ty_size, dst=src, src=ireg),
builder.build_fpext(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"FPExt: Unsupported operand types for floating-point extension, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_uitofp(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(FRegister(dst), IRegister(src)) =>
[builder.build_ui_to_fp(from_ty_size~, to_ty_size~, dst~, src~)]
(FRegister(dst), Imm(src_imm)) => {
let src = func.new_virtual_reg()
[
builder.build_imove_imm(from_ty_size, dst=src, src=src_imm),
builder.build_ui_to_fp(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"UIToFP: Unsupported operand types for unsigned integer to floating-point conversion, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_sitofp(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(FRegister(dst), IRegister(src)) =>
[builder.build_si_to_fp(from_ty_size~, to_ty_size~, dst~, src~)]
(FRegister(dst), Imm(src_imm)) => {
let src = func.new_virtual_reg()
[
builder.build_imove_imm(from_ty_size, dst=src, src=src_imm),
builder.build_si_to_fp(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"SIToFP: Unsupported operand types for signed integer to floating-point conversion, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_fptoui(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), FRegister(src)) =>
[builder.build_fp_to_ui(from_ty_size~, to_ty_size~, dst~, src~)]
(IRegister(dst), FImm(src_imm)) => {
let src = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
from_ty_size,
dst=ireg,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(from_ty_size, dst=src, src=ireg),
builder.build_fp_to_ui(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"FPToUI: Unsupported operand types for floating-point to unsigned integer conversion, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_fptosi(
from_ty_size : Int,
to_ty_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), FRegister(src)) =>
[builder.build_fp_to_si(from_ty_size~, to_ty_size~, dst~, src~)]
(IRegister(dst), FImm(src_imm)) => {
let src = func.new_virtual_freg()
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
from_ty_size,
dst=ireg,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(from_ty_size, dst=src, src=ireg),
builder.build_fp_to_si(from_ty_size~, to_ty_size~, dst~, src~),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"FPToSI: Unsupported operand types for floating-point to signed integer conversion, got \{dst_op}, \{from_op}",
)
}
}
// MIR has no bitcast instruction
// Use Move, FMoveI, IMoveF, Load, Store, etc. to implement BitCast
///|
pub fn translate_bitcast(
data_size : Int,
from_op : Operand,
dst_op : Operand,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
match (dst_op, from_op) {
(IRegister(dst), IRegister(src)) =>
[builder.build_imove(data_size, dst~, src~)]
(IRegister(dst), Imm(src_imm)) =>
[builder.build_imove_imm(data_size, dst~, src=src_imm)]
(FRegister(dst), FRegister(src)) =>
[builder.build_fmove(data_size, dst~, src~)]
(FRegister(dst), FImm(src_imm)) => {
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(
data_size,
dst=ireg,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_from_ireg(data_size, dst~, src=ireg),
]
}
(FRegister(dst), IRegister(src)) =>
[builder.build_fmove_from_ireg(data_size, dst~, src~)]
(IRegister(dst), FRegister(src)) =>
[builder.build_fmove_to_ireg(data_size, dst~, src~)]
(FRegister(dst), Imm(src_imm)) => {
let ireg = func.new_virtual_reg()
[
builder.build_imove_imm(data_size, dst=ireg, src=src_imm),
builder.build_fmove_from_ireg(data_size, dst~, src=ireg),
]
}
(IRegister(dst), FImm(src_imm)) => {
let freg = func.new_virtual_freg()
[
builder.build_imove_imm(
data_size,
dst~,
src=src_imm.reinterpret_as_int64(),
),
builder.build_fmove_to_ireg(data_size, dst~, src=freg),
]
}
(dst_op, from_op) =>
raise IRTranslateError(
"BitCast: Unsupported operand types for bitcast, got \{dst_op}, \{from_op}",
)
}
}
///|
pub fn translate_llvm_gep_inst(
llvm_inst : @IR.GetElementPtrInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
let { ptr, indices, pointeeType, .. } = llvm_inst
guard func.get_operand_from_llvm_value(ptr) is Some(ptr_op) else {
raise IRTranslateError(
"GetElementPtrInst: pointer operand not found in value_map",
)
}
guard func.get_operand_from_llvm_value(llvm_inst) is Some(dst_op) else {
// If the destination operand is not found, it means this GEP instruction
// was skipped because its result is never used (dead code)
return []
}
guard dst_op is IRegister(ptr_reg) else {
raise IRTranslateError(
"GetElementPtrInst: destination operand is not an integer register",
)
}
let minsts : Array[Instruction] = []
match ptr_op {
IRegister(r) => {
let inst = builder.build_imove(64, dst=ptr_reg, src=r)
minsts.push(inst)
}
MemLoc(base, 0L) => {
let inst = builder.build_imove(64, dst=ptr_reg, src=base)
minsts.push(inst)
}
MemLoc(base, offset) => {
let inst = builder.build_ibinary_imm(
Add,
64,
dst=ptr_reg,
src1=base,
src2=offset,
)
minsts.push(inst)
}
Label(label) => {
let inst = builder.build_load_addr(ptr_reg, label)
minsts.push(inst)
}
op =>
raise IRTranslateError(
"GetElementPtrInst: unsupported pointer operand type: \{op}",
)
}
let indices_ops : Array[Operand] = []
for idx_val in indices {
guard func.get_operand_from_llvm_value(idx_val) is Some(idx_op) else {
raise IRTranslateError(
"GetElementPtrInst: index operand not found in value_map",
)
}
indices_ops.push(idx_op)
}
guard indices_ops is [first_idx_op, .. indices_ops]
match first_idx_op {
Imm(0) => ()
Imm(i) => {
let offset = data_layout.getTypeAllocSize(pointeeType).to_int64() * i
let inst = builder.build_ibinary_imm(
Add,
64,
dst=ptr_reg,
src1=ptr_reg,
src2=offset,
)
minsts.push(inst)
}
IRegister(idx_r) => {
let offset = data_layout.getTypeAllocSize(pointeeType).to_int64()
let scaled_idx = func.new_virtual_reg()
let inst1 = builder.build_ibinary_imm(
Mul,
64,
dst=scaled_idx,
src1=idx_r,
src2=offset,
)
let inst2 = builder.build_ibinary(
Add,
64,
dst=ptr_reg,
src1=ptr_reg,
src2=scaled_idx,
)
minsts..push(inst1)..push(inst2)
}
_ =>
raise IRTranslateError(
"GetElementPtrInst: unsupported first index operand in GEP translation",
)
}
loop (pointeeType.asTypeEnum(), indices_ops) {
(_, []) => break
(_, [Imm(0)]) => break
(ArrayType({ elementType, .. }), [Imm(0), .. rest_indices]) =>
continue (elementType.asTypeEnum(), rest_indices)
(ArrayType({ elementType, .. }), [Imm(i), .. rest_indices]) => {
let elem_size = data_layout.getTypeAllocSize(elementType).to_int64()
let offset = elem_size * i
let inst = builder.build_ibinary_imm(
Add,
64,
dst=ptr_reg,
src1=ptr_reg,
src2=offset,
)
minsts.push(inst)
continue (elementType.asTypeEnum(), rest_indices)
}
(ArrayType({ elementType, .. }), [IRegister(idx_r), .. rest_indices]) => {
let offset = data_layout.getTypeAllocSize(elementType).to_int64()
let scaled_idx = func.new_virtual_reg()
let inst1 = builder.build_ibinary_imm(
Mul,
64,
dst=scaled_idx,
src1=idx_r,
src2=offset,
)
let inst2 = builder.build_ibinary(
Add,
64,
dst=ptr_reg,
src1=ptr_reg,
src2=scaled_idx,
)
minsts.push(inst1)
minsts.push(inst2)
continue (elementType.asTypeEnum(), rest_indices)
}
(StructType({ elements, .. }), [Imm(0), .. rest_indices]) => {
guard elements.get(0) is Some(elem_type) else {
raise IRTranslateError("GetElementPtrInst: StructType has no elements")
}
continue (elem_type.asTypeEnum(), rest_indices)
}
(StructType({ elements, .. } as sty), [Imm(i), .. rest_indices]) => {
let i_int = i.to_int()
guard elements.get(i_int) is Some(elem_type) else {
raise IRTranslateError(
"GetElementPtrInst: StructType index out of bounds",
)
}
let offset = data_layout.getStructTypeOffset(sty, i_int)
let offset = offset.to_int64()
let inst = builder.build_ibinary_imm(
Add,
64,
dst=ptr_reg,
src1=ptr_reg,
src2=offset,
)
minsts.push(inst)
continue (elem_type.asTypeEnum(), rest_indices)
}
(t, indices) =>
raise IRTranslateError(
"GetElementPtrInst: unsupported type or indices in GEP translation: \{t}, \{indices}",
)
}
minsts
}
///|
pub fn translate_llvm_select_inst(
llvm_inst : @IR.SelectInst,
builder : IRBuilder,
func : Function,
data_layout : @IR.DataLayout,
) -> Array[Instruction] raise MIRError {
raise IRTranslateError("SelectInst translation not implemented yet")
ignore(llvm_inst)
ignore(builder)
ignore(func)
ignore(data_layout)
[]
}
///|
pub fn translate_llvm_return_inst(
llvm_inst : @IR.ReturnInst,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
func.terminal_blocks.push(builder.bb)
let { retVal, .. } = llvm_inst
guard retVal is Some(retVal) else {
let minst = builder.build_ret()
return [minst]
}
// Check if return type is void
if retVal.getType().asTypeEnum() is VoidType(_) {
let minst = builder.build_ret()
return [minst]
}
guard func.get_operand_from_llvm_value(retVal) is Some(ret_op) else {
raise IRTranslateError(
"ReturnInst: return value operand not found in value_map",
)
}
match ret_op {
IRegister(ret) if !(ret is AReg(0)) => {
let minst = builder.build_imove(64, dst=AReg(0), src=ret)
return [minst, builder.build_ret()]
}
IRegister(_) => [builder.build_ret()]
FRegister(ret) if !(ret is FAReg(0)) => {
let minst = builder.build_fmove(64, dst=FAReg(0), src=ret)
return [minst, builder.build_ret()]
}
FRegister(_) => [builder.build_ret()]
Imm(ret) => {
let minst = builder.build_imove_imm(64, dst=AReg(0), src=ret)
return [minst, builder.build_ret()]
}
FImm(ret) => {
let ireg = func.new_virtual_reg()
let freg = func.new_virtual_freg()
[
builder.build_imove_imm(64, dst=ireg, src=ret.reinterpret_as_int64()),
builder.build_fmove_from_ireg(64, dst=freg, src=ireg),
builder.build_fmove(64, dst=FAReg(0), src=freg),
builder.build_ret(),
]
}
Label(label) => {
let minst = builder.build_load_addr(AReg(0), label)
[minst, builder.build_ret()]
}
ret_op =>
raise IRTranslateError(
"ReturnInst: Unsupported operand type for return value \{ret_op}",
)
}
}
///|
/// IRTranslate from LLVM IR to MoonMIR for CallInst.
pub fn translate_llvm_call_inst(
llvm_inst : @IR.CallInst,
builder : IRBuilder,
func : Function,
) -> Array[Instruction] raise MIRError {
let { callee, args, .. } = llvm_inst
let { num_arg_regs, num_arg_fregs, arch_name, .. } = func.mod.arch_config
// Handle both direct function calls and indirect function pointer calls
let (is_indirect_call, callee_op, callee_name_opt) : (Bool, Operand?, String?) = match
callee {
Left(llvm_func) => {
let name = llvm_func.getName().unwrap()
(false, None, Some(name))
}
Right(func_ptr) => {
// Function pointer call - get the operand for the function pointer
guard func.get_operand_from_llvm_value(func_ptr) is Some(op) else {
raise IRTranslateError(
"CallInst: function pointer operand not found in value_map",
)
}
match op {
IRegister(_) => (true, Some(op), None)
_ =>
raise IRTranslateError(
"CallInst: function pointer must be in an integer register, got \{op}",
)
}
}
}
// Check if the callee is a variadic function
// For indirect calls, we cannot determine if it's variadic, so assume non-variadic
let is_variadic_call = llvm_inst.getFunctionType().isVarArg
// For AArch64 variadic functions, arguments after the first one should be on stack only
// The first argument (format string) still goes in register x0
let is_aarch64_variadic = is_variadic_call && arch_name == "aarch64"
let mut call_ireg_cnt = 0
let mut call_freg_cnt = 0
let mut variadic_stack_offset = 0L // Stack offset for variadic arguments
let minsts : Array[Instruction] = Array::new()
for arg_idx, arg in args.iter2() {
guard func.get_operand_from_llvm_value(arg) is Some(arg_op) else {
raise IRTranslateError(
"CallInst: argument operand not found in value_map",
)
}
let arg_is_fp = arg.getType().isIEEELikeFPTy()
// For AArch64 variadic functions, arguments after index 0 go on stack only
let force_stack = is_aarch64_variadic && arg_idx > 0
match (arg_op, arg_is_fp) {
(IRegister(src), false) if !force_stack && call_ireg_cnt < num_arg_regs => {
let dst = AReg(call_ireg_cnt)
let inst = builder.build_imove(64, dst~, src~)
if src != dst {
minsts.push(inst)
}
call_ireg_cnt += 1
}
(IRegister(src), false) if force_stack => {
// For variadic functions on AArch64, put on stack at variadic_stack_offset
let _ = func.extend_var_stack(8L, 8L)
let inst = builder.build_storei(
64,
src~,
base=StackPtr,
offset=variadic_stack_offset,
)
minsts.push(inst)
variadic_stack_offset += 8
}
(IRegister(src), false) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_ireg_cnt - num_arg_regs).to_int64()
let inst = builder.build_storei(
64,
src~,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst)
call_ireg_cnt += 1
}
(FRegister(src), true) if call_freg_cnt < num_arg_fregs => {
let dst = FAReg(call_freg_cnt)
// Get the argument type width
let arg_ty = arg.getType()
let arg_width = match arg_ty.asTypeEnum() {
FloatType(_) => 32
DoubleType(_) => 64
_ => 64 // Default to 64 for other types
}
let inst = builder.build_fmove(arg_width, dst~, src~)
if src != dst {
minsts.push(inst)
}
call_freg_cnt += 1
}
(FRegister(src), true) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_freg_cnt - num_arg_fregs).to_int64()
let inst = builder.build_storef(
64,
src~,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst)
call_freg_cnt += 1
}
(Imm(src), false) if !force_stack && call_ireg_cnt < num_arg_regs => {
let dst = AReg(call_ireg_cnt)
let inst = builder.build_imove_imm(64, dst~, src~)
minsts.push(inst)
call_ireg_cnt += 1
}
(Imm(src), false) if force_stack => {
// For variadic functions on AArch64, put on stack at variadic_stack_offset
let _ = func.extend_var_stack(8L, 8L)
let vreg = func.new_virtual_reg()
let inst1 = builder.build_imove_imm(64, dst=vreg, src~)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=variadic_stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
variadic_stack_offset += 8
}
(Imm(src), false) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_ireg_cnt - num_arg_regs).to_int64()
let vreg = func.new_virtual_reg()
let inst1 = builder.build_imove_imm(64, dst=vreg, src~)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
call_ireg_cnt += 1
}
(FImm(src), true) if call_freg_cnt < num_arg_fregs => {
let dst = FAReg(call_freg_cnt)
// Get the argument type width
let arg_ty = arg.getType()
let arg_width = match arg_ty.asTypeEnum() {
FloatType(_) => 32
DoubleType(_) => 64
_ => 64 // Default to 64 for other types
}
let ireg = func.new_virtual_reg()
// Convert double bit representation to appropriate type
let bit_value = match arg_width {
32 => {
// Convert double to float bit representation
let float_val = src.to_float()
float_val.reinterpret_as_int().to_int64()
}
64 => src.reinterpret_as_int64()
_ => src.reinterpret_as_int64()
}
let inst1 = builder.build_imove_imm(arg_width, dst=ireg, src=bit_value)
let inst2 = builder.build_fmove_from_ireg(arg_width, dst~, src=ireg)
minsts.push(inst1)
minsts.push(inst2)
call_freg_cnt += 1
}
(FImm(src), true) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_freg_cnt - num_arg_fregs).to_int64()
let ireg = func.new_virtual_reg()
let inst1 = builder.build_imove_imm(
64,
dst=ireg,
src=src.reinterpret_as_int64(),
)
let inst2 = builder.build_storei(
64,
src=ireg,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
call_freg_cnt += 1
}
(Label(label), false) if !force_stack && call_ireg_cnt < num_arg_regs => {
let dst = AReg(call_ireg_cnt)
let inst = builder.build_load_addr(dst, label)
minsts.push(inst)
call_ireg_cnt += 1
}
(Label(label), false) if force_stack => {
// For variadic functions on AArch64, put on stack at variadic_stack_offset
let _ = func.extend_var_stack(8L, 8L)
let vreg = func.new_virtual_reg()
let inst1 = builder.build_load_addr(vreg, label)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=variadic_stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
variadic_stack_offset += 8
}
(Label(label), false) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_ireg_cnt - num_arg_regs).to_int64()
let vreg = func.new_virtual_reg()
let inst1 = builder.build_load_addr(vreg, label)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
call_ireg_cnt += 1
}
(MemLoc(base, 0L), false) if !force_stack && call_ireg_cnt < num_arg_regs => {
let dst = AReg(call_ireg_cnt)
let inst = builder.build_imove(64, dst~, src=base)
minsts.push(inst)
call_ireg_cnt += 1
}
(MemLoc(base, 0L), false) if force_stack => {
// For variadic functions on AArch64, put on stack at variadic_stack_offset
let _ = func.extend_var_stack(8L, 8L)
let inst = builder.build_storei(
64,
src=base,
base=StackPtr,
offset=variadic_stack_offset,
)
minsts.push(inst)
variadic_stack_offset += 8
}
(MemLoc(base, offset), false) if !force_stack &&
call_ireg_cnt < num_arg_regs => {
let dst = AReg(call_ireg_cnt)
let vreg = func.new_virtual_reg()
let inst1 = builder.build_ibinary_imm(
Add,
64,
dst=vreg,
src1=base,
src2=offset,
)
let inst2 = builder.build_imove(64, dst~, src=vreg)
minsts.push(inst1)
minsts.push(inst2)
call_ireg_cnt += 1
}
(MemLoc(base, offset), false) if force_stack => {
// For variadic functions on AArch64, put on stack at variadic_stack_offset
let _ = func.extend_var_stack(8L, 8L)
let vreg = func.new_virtual_reg()
let inst1 = builder.build_ibinary_imm(
Add,
64,
dst=vreg,
src1=base,
src2=offset,
)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=variadic_stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
variadic_stack_offset += 8
}
(MemLoc(base, 0L), false) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_ireg_cnt - num_arg_regs).to_int64()
let inst = builder.build_storei(
64,
src=base,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst)
call_ireg_cnt += 1
}
(MemLoc(base, offset), false) => {
let _ = func.extend_var_stack(8L, 8L)
let stack_offset = 8L * (call_ireg_cnt - num_arg_regs).to_int64()
let vreg = func.new_virtual_reg()
let inst1 = builder.build_ibinary_imm(
Add,
64,
dst=vreg,
src1=base,
src2=offset,
)
let inst2 = builder.build_storei(
64,
src=vreg,
base=StackPtr,
offset=stack_offset,
)
minsts.push(inst1)
minsts.push(inst2)
call_ireg_cnt += 1
}
arg_op =>
raise IRTranslateError(
"CallInst: Unsupported argument operand type: \{arg_op}",
)
}
}
// Generate the call instruction
let call_inst = match (is_indirect_call, callee_op, callee_name_opt) {
(false, _, Some(callee_name)) => builder.build_call(callee_name)
(true, Some(op), _) =>
// For indirect calls, create a Call instruction with the function pointer register as the first use
Instruction::new(Call, [], [op], builder.bb)
_ =>
raise IRTranslateError(
"CallInst: invalid state in call instruction generation",
)
}
// Add argument registers to the call instruction uses
for i = 0; i < call_ireg_cnt && i < num_arg_regs; i = i + 1 {
call_inst.uses.push(IRegister(AReg(i)))
}
for i = 0; i < call_freg_cnt && i < num_arg_fregs; i = i + 1 {
call_inst.uses.push(FRegister(FAReg(i)))
}
minsts.push(call_inst)
if !(llvm_inst.getType().asTypeEnum() is VoidType(_)) {
guard func.get_operand_from_llvm_value(llvm_inst) is Some(ret_op) else {
return []
}
// Get the return type width
let ret_ty = llvm_inst.getType()
let ret_width = match ret_ty.asTypeEnum() {
Int1Type(_) | Int8Type(_) | Int16Type(_) | Int32Type(_) | FloatType(_) =>
32
Int64Type(_) | PointerType(_) | DoubleType(_) => 64
_ => 64 // Default to 64 for other types
}
match ret_op {
IRegister(dst) => {
let inst = builder.build_imove(ret_width, dst~, src=AReg(0))
minsts.push(inst)
// need extra inst `move v, fa0` to avoid fa0 being used after call
let tmp_freg = func.new_virtual_freg()
let mv_fa0 = builder.build_fmove(64, dst=tmp_freg, src=FAReg(0))
minsts.push(mv_fa0)
}
FRegister(dst) => {
let inst = builder.build_fmove(ret_width, dst~, src=FAReg(0))
minsts.push(inst)
// need extra inst `move v, a0` to avoid a0 being used after call
let tmp_reg = func.new_virtual_reg()
let mv_a0 = builder.build_imove(64, dst=tmp_reg, src=AReg(0))
minsts.push(mv_a0)
}
_ => ()
}
} else {
// Even the call inst has no return value, still need a move from a0/fa0
// Why we do that?
// Because we need to ensure in register allocation phase
// others varible not use a0/fa0 after the call instruction
let tmp_reg = func.new_virtual_reg()
let tmp_freg = func.new_virtual_freg()
let mv_a0 = builder.build_imove(64, dst=tmp_reg, src=AReg(0))
let mv_fa0 = builder.build_fmove(64, dst=tmp_freg, src=FAReg(0))
minsts.push(mv_a0)
minsts.push(mv_fa0)
}
minsts
}
///|
pub fn translate_llvm_phi_node(
llvm_inst : @IR.PHINode,
func : Function,
) -> Array[Instruction] raise MIRError {
let { incomings, .. } = llvm_inst
for imcoming in incomings {
let (value, llvm_bb) = imcoming
guard func.get_operand_from_llvm_value(value) is Some(value_op) else {
raise IRTranslateError(
"PHINode: incoming value operand not found in value_map",
)
}
guard func.get_operand_from_llvm_value(llvm_bb) is Some(bb_op) else {
raise IRTranslateError(
"PHINode: incoming basic block operand not found in value_map",
)
}
guard bb_op is Label(bb_label) else {
raise IRTranslateError(
"PHINode: incoming basic block operand is not a label",
)
}
let machine_bb = func.bbmap.get(bb_label).unwrap()
machine_bb.phi_nodes.push((llvm_inst, value_op))
}
[]
}
///|
pub fn Function::phi_elimination(func : Self) -> Unit raise MIRError {
func.body.each(bb => bb.phi_elimination())
}
///|
pub fn BasicBlock::phi_elimination(self : Self) -> Unit raise MIRError {
let func = self.parent
let data_layout = func.llvm_func.unwrap().getDataLayout()
let builder = IRBuilder::new(func, self)
for pair in self.phi_nodes {
let (llvm_phi_node, value_op) = pair
guard func.get_operand_from_llvm_value(llvm_phi_node) is Some(dst_op) else {
raise IRTranslateError(
"PHINode: destination operand not found in value_map",
)
}
let data_size = data_layout.getTypeAllocSizeInBits(llvm_phi_node.getType())
match (dst_op, value_op) {
(IRegister(dst), IRegister(src)) => {
let inst = builder.build_imove(data_size, dst~, src~)
self.push_inst_before_terminator(inst)
}
(IRegister(dst), Imm(src)) => {
let inst = builder.build_imove_imm(data_size, dst~, src~)
self.push_inst_before_terminator(inst)
}
(FRegister(dst), FRegister(src)) => {
let inst = builder.build_fmove(data_size, dst~, src~)
self.push_inst_before_terminator(inst)
}
(FRegister(dst), FImm(src)) => {
let inst = builder.build_fmove_imm(data_size, dst~, src~)
self.push_inst_before_terminator(inst)
}
phi =>
raise IRTranslateError(
"PHINode: Unsupported operand types for phi elimination \{phi}",
)
}
}
}