// Function Builder - Convenient API for constructing IR
// SSA function builder
///|
/// FunctionBuilder - helps construct IR functions
/// Tracks the current block and provides methods for emitting instructions
struct FunctionBuilder {
func : Function
mut current_block : Block
}
///|
pub fn FunctionBuilder::FunctionBuilder(name : String) -> FunctionBuilder {
let func = Function::new_empty(name)
let entry = func.new_block0()
{ func, current_block: entry }
}
///|
/// Get the function being built
pub fn FunctionBuilder::get_function(self : FunctionBuilder) -> Function {
self.func
}
///|
/// Finish building and return the constructed function.
pub fn FunctionBuilder::finalize(
self : FunctionBuilder,
) -> Function raise VerifyError {
self.func.verify()
self.func
}
///|
/// Add a parameter to the function
pub fn FunctionBuilder::add_param(self : FunctionBuilder, ty : Type) -> Value {
self.func.add_param(ty)
}
///|
/// Add a result type to the function
pub fn FunctionBuilder::add_result(self : FunctionBuilder, ty : Type) -> Unit {
self.func.add_result(ty)
}
///|
/// Create a new block.
pub fn FunctionBuilder::create_block(self : FunctionBuilder) -> Block {
let block = self.func.new_block0()
block
}
///|
fn FunctionBuilder::require_block(
self : FunctionBuilder,
block : Block,
) -> Bool {
if !self.func.owns_block(block) {
self.func.record_construction_error(ForeignBlock(block_id=block.id))
return false
}
true
}
///|
fn FunctionBuilder::require_values(
self : FunctionBuilder,
values : Array[Value],
) -> Bool {
for value in values {
if !self.func.owns_value(value) {
self.func.record_construction_error(ForeignValue(value_id=value.id))
return false
}
}
true
}
///|
/// Switch to a different block for emitting instructions
pub fn FunctionBuilder::switch_to_block(
self : FunctionBuilder,
block : Block,
) -> Unit {
if !self.require_block(block) {
return
}
self.current_block = block
}
///|
/// Get the current block
pub fn FunctionBuilder::current_block(self : FunctionBuilder) -> Block {
self.current_block
}
///|
/// Add a block parameter (for SSA phi nodes)
pub fn FunctionBuilder::add_block_param(
self : FunctionBuilder,
block : Block,
ty : Type,
) -> Value {
if !self.require_block(block) {
return self.func.new_value(ty)
}
let v = self.func.new_value(ty)
block.add_param(v, ty)
v
}
///|
/// Append block parameters matching the function parameter types.
pub fn FunctionBuilder::append_block_params_for_function_params(
self : FunctionBuilder,
block : Block,
) -> Unit {
for item in self.func.params {
let (_, ty) = item
self.add_block_param(block, ty) |> ignore
}
}
///|
/// Append block parameters matching the function result types.
pub fn FunctionBuilder::append_block_params_for_function_returns(
self : FunctionBuilder,
block : Block,
) -> Unit {
for ty in self.func.results {
self.add_block_param(block, ty) |> ignore
}
}
///|
/// Return the SSA values used as block parameters.
pub fn FunctionBuilder::block_params(
self : FunctionBuilder,
block : Block,
) -> Array[Value] {
if !self.require_block(block) {
return []
}
block.params.map(fn(item) {
let (value, _) = item
value
})
}
///|
/// Emit an instruction that produces a result
pub fn FunctionBuilder::emit_inst(
self : FunctionBuilder,
ty : Type,
opcode : Opcode,
operands : Array[Value],
) -> Value {
let result = self.func.new_value(ty)
if !self.require_values(operands) {
return result
}
let block = self.current_block
let inst = self.func.new_inst(opcode, operands, [result])
block.add_inst(inst)
result
}
///|
/// Emit an instruction without a result
pub fn FunctionBuilder::emit_void_inst(
self : FunctionBuilder,
opcode : Opcode,
operands : Array[Value],
) -> Unit {
if !self.require_values(operands) {
return
}
let block = self.current_block
let inst = self.func.new_inst(opcode, operands, [])
block.add_inst(inst)
}
///|
/// Emit an instruction with multiple results.
pub fn FunctionBuilder::emit_multi_inst(
self : FunctionBuilder,
result_types : Array[Type],
opcode : Opcode,
operands : Array[Value],
) -> Array[Value] {
let results : Array[Value] = []
for ty in result_types {
results.push(self.func.new_value(ty))
}
if !self.require_values(operands) {
return results
}
if result_types.length() == 0 {
self.emit_void_inst(opcode, operands)
return []
}
let block = self.current_block
let inst = self.func.new_inst(opcode, operands, results)
block.add_inst(inst)
results
}
///|
pub fn FunctionBuilder::emit_ext_inst(
self : FunctionBuilder,
ty : Type,
opcode : ExtOp,
operands : Array[Value],
) -> Value {
self.emit_inst(
ty,
Ext(opcode, Signature(operands.map(value => value.ty), [ty])),
operands,
)
}
///|
pub fn FunctionBuilder::emit_void_ext_inst(
self : FunctionBuilder,
opcode : ExtOp,
operands : Array[Value],
) -> Unit {
self.emit_void_inst(
Ext(opcode, Signature(operands.map(value => value.ty), [])),
operands,
)
}
///|
pub fn FunctionBuilder::emit_multi_ext_inst(
self : FunctionBuilder,
result_types : Array[Type],
opcode : ExtOp,
operands : Array[Value],
) -> Array[Value] {
self.emit_multi_inst(
result_types,
Ext(opcode, Signature(operands.map(value => value.ty), result_types.copy())),
operands,
)
}
// ============ Constants ============
///|
/// Get the constant value if a Value was defined by an Iconst instruction.
/// Searches all blocks in the function to find the defining instruction.
/// Returns None if the value is not a constant or not found.
pub fn FunctionBuilder::get_const_value(
self : FunctionBuilder,
v : Value,
) -> Int64? {
if !self.require_values([v]) {
return None
}
for block in self.func.blocks {
for inst in block.instructions {
if inst.first_result() is Some(r) && r.id == v.id {
if inst.opcode is Scalar(IntConst(c)) {
return Some(c)
}
return None // Found defining instruction but not a constant
}
}
}
None
}
///|
/// Emit an integer constant
pub fn FunctionBuilder::iconst(
self : FunctionBuilder,
ty : Type,
value : Int64,
) -> Value {
self.emit_inst(ty, Scalar(IntConst(value)), [])
}
///|
/// Emit an i32 constant
pub fn FunctionBuilder::iconst_i32(
self : FunctionBuilder,
value : Int,
) -> Value {
self.iconst(I32, value.to_int64())
}
///|
/// Emit an i64 constant
pub fn FunctionBuilder::iconst_i64(
self : FunctionBuilder,
value : Int64,
) -> Value {
self.iconst(I64, value)
}
///|
/// Emit an f32 constant
/// Note: We pack the f32 bits into the Double's bit representation to preserve
/// NaN payloads. Using value.to_double() would go through the FPU and convert
/// signaling NaNs to quiet NaNs.
pub fn FunctionBuilder::fconst_f32(
self : FunctionBuilder,
value : Float,
) -> Value {
self.emit_inst(F32, Scalar(FloatConst32(value.reinterpret_as_uint())), [])
}
///|
/// Emit an f64 constant
pub fn FunctionBuilder::fconst_f64(
self : FunctionBuilder,
value : Double,
) -> Value {
self.emit_inst(F64, Scalar(FloatConst64(value.reinterpret_as_uint64())), [])
}
// ============ Integer Arithmetic ============
///|
/// Integer add
pub fn FunctionBuilder::iadd(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(Add)), [a, b])
}
///|
/// Integer subtract
pub fn FunctionBuilder::isub(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(Sub)), [a, b])
}
///|
/// Integer multiply
pub fn FunctionBuilder::imul(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(Mul)), [a, b])
}
///|
/// Unsigned multiply high (i64 only)
pub fn FunctionBuilder::umulh(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(UnsignedMulHigh)), [a, b])
}
///|
/// Signed multiply high (i64 only)
pub fn FunctionBuilder::smulh(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(SignedMulHigh)), [a, b])
}
///|
/// Signed integer divide
pub fn FunctionBuilder::sdiv(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(SignedDiv)), [a, b])
}
///|
/// Unsigned integer divide
pub fn FunctionBuilder::udiv(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(UnsignedDiv)), [a, b])
}
///|
/// Signed integer remainder
pub fn FunctionBuilder::srem(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(SignedRem)), [a, b])
}
///|
/// Unsigned integer remainder
pub fn FunctionBuilder::urem(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(UnsignedRem)), [a, b])
}
// ============ Bitwise Operations ============
///|
/// Bitwise and
pub fn FunctionBuilder::band(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(And)), [a, b])
}
///|
/// Bitwise or
pub fn FunctionBuilder::bor(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(Or)), [a, b])
}
///|
/// Bitwise xor
pub fn FunctionBuilder::bxor(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(Xor)), [a, b])
}
///|
/// Bitwise not
pub fn FunctionBuilder::bnot(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(IntUnary(Not)), [a])
}
///|
/// Shift left
pub fn FunctionBuilder::ishl(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(ShiftLeft)), [a, b])
}
///|
/// Signed shift right
pub fn FunctionBuilder::sshr(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(SignedShiftRight)), [a, b])
}
///|
/// Unsigned shift right
pub fn FunctionBuilder::ushr(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(UnsignedShiftRight)), [a, b])
}
///|
/// Rotate left
pub fn FunctionBuilder::rotl(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(RotateLeft)), [a, b])
}
///|
/// Rotate right
pub fn FunctionBuilder::rotr(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(IntBinary(RotateRight)), [a, b])
}
///|
/// Count leading zeros
pub fn FunctionBuilder::clz(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(IntUnary(CountLeadingZeros)), [a])
}
///|
/// Count trailing zeros
pub fn FunctionBuilder::ctz(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(IntUnary(CountTrailingZeros)), [a])
}
///|
/// Population count (count number of 1 bits)
pub fn FunctionBuilder::popcnt(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(IntUnary(PopulationCount)), [a])
}
// ============ Integer Comparisons ============
///|
/// Integer comparison (returns i32 0 or 1)
pub fn FunctionBuilder::icmp(
self : FunctionBuilder,
cc : IntCC,
a : Value,
b : Value,
) -> Value {
self.emit_inst(I32, Scalar(IntCompare(cc)), [a, b])
}
///|
/// Integer equal
pub fn FunctionBuilder::icmp_eq(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Eq, a, b)
}
///|
/// Integer not equal
pub fn FunctionBuilder::icmp_ne(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Ne, a, b)
}
///|
/// Signed less than
pub fn FunctionBuilder::icmp_slt(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Slt, a, b)
}
///|
/// Signed less than or equal
pub fn FunctionBuilder::icmp_sle(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Sle, a, b)
}
///|
/// Signed greater than
pub fn FunctionBuilder::icmp_sgt(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Sgt, a, b)
}
///|
/// Signed greater than or equal
pub fn FunctionBuilder::icmp_sge(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Sge, a, b)
}
///|
/// Unsigned less than
pub fn FunctionBuilder::icmp_ult(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Ult, a, b)
}
///|
/// Unsigned less than or equal
pub fn FunctionBuilder::icmp_ule(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Ule, a, b)
}
///|
/// Unsigned greater than
pub fn FunctionBuilder::icmp_ugt(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Ugt, a, b)
}
///|
/// Unsigned greater than or equal
pub fn FunctionBuilder::icmp_uge(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.icmp(Uge, a, b)
}
// ============ Floating Point Arithmetic ============
///|
/// Float add
pub fn FunctionBuilder::fadd(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Add)), [a, b])
}
///|
/// Float subtract
pub fn FunctionBuilder::fsub(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Sub)), [a, b])
}
///|
/// Float multiply
pub fn FunctionBuilder::fmul(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Mul)), [a, b])
}
///|
/// Float divide
pub fn FunctionBuilder::fdiv(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Div)), [a, b])
}
///|
/// Float minimum
pub fn FunctionBuilder::fmin(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Min)), [a, b])
}
///|
/// Float maximum
pub fn FunctionBuilder::fmax(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(FloatBinary(Max)), [a, b])
}
// ============ Float Comparisons ============
///|
/// Float comparison (returns i32 0 or 1)
pub fn FunctionBuilder::fcmp(
self : FunctionBuilder,
cc : FloatCC,
a : Value,
b : Value,
) -> Value {
self.emit_inst(I32, Scalar(FloatCompare(cc)), [a, b])
}
// ============ Float Unary Operations ============
///|
/// Float negate
pub fn FunctionBuilder::fneg(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Neg)), [a])
}
///|
/// Float absolute value
pub fn FunctionBuilder::fabs(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Abs)), [a])
}
///|
/// Float square root
pub fn FunctionBuilder::fsqrt(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Sqrt)), [a])
}
///|
/// Float ceiling
pub fn FunctionBuilder::fceil(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Ceil)), [a])
}
///|
/// Float floor
pub fn FunctionBuilder::ffloor(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Floor)), [a])
}
///|
/// Float truncate
pub fn FunctionBuilder::ftrunc(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Trunc)), [a])
}
///|
/// Float nearest (round to nearest even)
pub fn FunctionBuilder::fnearest(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(FloatUnary(Nearest)), [a])
}
// ============ Conversions ============
///|
/// Reduce integer width (e.g., i64 -> i32)
pub fn FunctionBuilder::ireduce(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(IntReduce)), [a])
}
///|
/// Sign extend (e.g., i32 -> i64)
pub fn FunctionBuilder::sextend(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(SignedExtend)), [a])
}
///|
/// Zero extend (e.g., i32 -> i64)
pub fn FunctionBuilder::uextend(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(UnsignedExtend)), [a])
}
///|
/// Sign extend from 8 bits (in-place, keeps the same type)
/// Similar to ireduce(I8) + sextend(ty)
pub fn FunctionBuilder::sextend8(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(SignExtendFrom(8)), [a])
}
///|
/// Sign extend from 16 bits (in-place, keeps the same type)
/// Similar to ireduce(I16) + sextend(ty)
pub fn FunctionBuilder::sextend16(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(SignExtendFrom(16)), [a])
}
///|
/// Sign extend from 32 bits to 64 bits
/// Similar to ireduce(I32) + sextend(I64)
pub fn FunctionBuilder::sextend32(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(I64, Scalar(SignExtendFrom(32)), [a])
}
///|
/// Promote float (f32 -> f64)
pub fn FunctionBuilder::fpromote(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(F64, Scalar(Convert(FloatPromote)), [a])
}
///|
/// Demote float (f64 -> f32)
pub fn FunctionBuilder::fdemote(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(F32, Scalar(Convert(FloatDemote)), [a])
}
///|
/// Float to signed int
pub fn FunctionBuilder::fcvt_to_sint(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(FloatToSignedInt)), [a])
}
///|
/// Float to unsigned int
pub fn FunctionBuilder::fcvt_to_uint(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(FloatToUnsignedInt)), [a])
}
///|
/// Float to signed int (saturating - NaN->0, overflow->max/min)
pub fn FunctionBuilder::fcvt_to_sint_sat(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(FloatToSignedIntSaturating)), [a])
}
///|
/// Float to unsigned int (saturating - NaN->0, overflow->max, negative->0)
pub fn FunctionBuilder::fcvt_to_uint_sat(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(FloatToUnsignedIntSaturating)), [a])
}
///|
/// Signed int to float
pub fn FunctionBuilder::sint_to_fcvt(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(SignedIntToFloat)), [a])
}
///|
/// Unsigned int to float
pub fn FunctionBuilder::uint_to_fcvt(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(UnsignedIntToFloat)), [a])
}
///|
/// Bitcast (reinterpret bits)
pub fn FunctionBuilder::bitcast(
self : FunctionBuilder,
ty : Type,
a : Value,
) -> Value {
self.emit_inst(ty, Scalar(Convert(Bitcast)), [a])
}
// ============ Misc Operations ============
///|
/// Conditional select: cond ? a : b
pub fn FunctionBuilder::select(
self : FunctionBuilder,
cond : Value,
a : Value,
b : Value,
) -> Value {
self.emit_inst(a.ty, Scalar(Select), [cond, a, b])
}
///|
/// Copy value (for register allocation)
pub fn FunctionBuilder::copy(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(a.ty, Scalar(Copy), [a])
}
// ============ Function Calls ============
///|
pub fn FunctionBuilder::call_symbol(
self : FunctionBuilder,
symbol : ExternalSymbol,
result_ty : Type?,
args : Array[Value],
) -> Value? {
let param_types = args.map(value => value.ty)
match result_ty {
Some(ty) =>
Some(
self.emit_inst(
ty,
Call(Direct(symbol, Signature(param_types, [ty]))),
args,
),
)
None => {
self.emit_void_inst(
Call(Direct(symbol, Signature(param_types, []))),
args,
)
None
}
}
}
///|
pub fn FunctionBuilder::call_symbol_multi(
self : FunctionBuilder,
symbol : ExternalSymbol,
result_types : Array[Type],
args : Array[Value],
) -> Array[Value] {
self.emit_multi_inst(
result_types,
Call(
Direct(
symbol,
Signature(args.map(value => value.ty), result_types.copy()),
),
),
args,
)
}
// ============ Terminators ============
///|
/// Unconditional jump
pub fn FunctionBuilder::jump(
self : FunctionBuilder,
target : Block,
args : Array[Value],
) -> Unit {
if !self.require_block(target) || !self.require_values(args) {
return
}
let block = self.current_block
block.set_terminator(Jump(target.id, args))
}
///|
/// Conditional branch (branch if zero)
pub fn FunctionBuilder::brz(
self : FunctionBuilder,
cond : Value,
then_block : Block,
else_block : Block,
) -> Unit {
if !self.require_values([cond]) ||
!self.require_block(then_block) ||
!self.require_block(else_block) {
return
}
let block = self.current_block
block.set_terminator(Brz(cond, then_block.id, else_block.id))
}
///|
/// Conditional branch (branch if non-zero)
pub fn FunctionBuilder::brnz(
self : FunctionBuilder,
cond : Value,
then_block : Block,
else_block : Block,
) -> Unit {
if !self.require_values([cond]) ||
!self.require_block(then_block) ||
!self.require_block(else_block) {
return
}
let block = self.current_block
block.set_terminator(Brnz(cond, then_block.id, else_block.id))
}
///|
/// Branch table (switch)
pub fn FunctionBuilder::br_table(
self : FunctionBuilder,
index : Value,
targets : Array[Block],
default_target : Block,
) -> Unit {
if !self.require_values([index]) {
return
}
for target in targets {
if !self.require_block(target) {
return
}
}
if !self.require_block(default_target) {
return
}
let target_ids = targets.map(fn(b) { b.id })
let block = self.current_block
block.set_terminator(BrTable(index, target_ids, default_target.id))
}
///|
/// Return from function
pub fn FunctionBuilder::return_(
self : FunctionBuilder,
values : Array[Value],
) -> Unit {
if !self.require_values(values) {
return
}
let block = self.current_block
block.set_terminator(Return(values))
}
///|
/// Trap/unreachable
pub fn FunctionBuilder::trap(self : FunctionBuilder, reason : String) -> Unit {
let block = self.current_block
block.set_terminator(Trap(reason))
}
// ============ Raw Pointer Operations (for trampolines) ============
///|
/// Load from raw pointer (no bounds checking)
/// For trampoline code that operates on host memory
pub fn FunctionBuilder::load_ptr(
self : FunctionBuilder,
ty : Type,
base : Value,
offset : Value,
) -> Value {
self.emit_inst(ty, Memory(Load(ty)), [base, offset])
}
///|
/// Store to raw pointer (no bounds checking)
/// For trampoline code that operates on host memory
pub fn FunctionBuilder::store_ptr(
self : FunctionBuilder,
ty : Type,
base : Value,
value : Value,
offset : Value,
) -> Unit {
self.emit_void_inst(Memory(Store(ty)), [base, value, offset])
}
///|
/// Load narrow value from raw pointer (no bounds checking)
/// Loads 'bits' bits from memory and extends to result_ty
pub fn FunctionBuilder::load_ptr_narrow(
self : FunctionBuilder,
result_ty : Type,
bits : Int,
signed : Bool,
base : Value,
offset : Value,
) -> Value {
self.emit_inst(result_ty, Memory(LoadNarrow(result_ty, bits, signed)), [
base, offset,
])
}
///|
/// Store narrow value to raw pointer (no bounds checking)
/// Stores the low 'bits' bits of value to memory
pub fn FunctionBuilder::store_ptr_narrow(
self : FunctionBuilder,
bits : Int,
base : Value,
value : Value,
offset : Value,
) -> Unit {
self.emit_void_inst(Memory(StoreNarrow(bits)), [base, value, offset])
}
///|
/// Call a function pointer with ordinary arguments.
pub fn FunctionBuilder::call_pointer(
self : FunctionBuilder,
func_ptr : Value,
args : Array[Value],
result_types : Array[Type],
) -> Array[Value] {
let operands : Array[Value] = [func_ptr]
for arg in args {
operands.push(arg)
}
let num_args = args.length()
let num_results = result_types.length()
self.emit_multi_inst(
result_types,
Call(Pointer(num_args, num_results)),
operands,
)
}
// ============ SIMD Operations ============
///|
/// v128_const - emit a V128 constant
pub fn FunctionBuilder::v128_const(
self : FunctionBuilder,
bytes : Bytes,
) -> Value {
self.emit_inst(V128, Vector(Const(bytes)), [])
}
///|
/// v128_splat - broadcast a scalar to all lanes
pub fn FunctionBuilder::v128_splat8(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(I8)), [val])
}
///|
pub fn FunctionBuilder::v128_splat16(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(I16)), [val])
}
///|
pub fn FunctionBuilder::v128_splat32(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(I32)), [val])
}
///|
pub fn FunctionBuilder::v128_splat64(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(I64)), [val])
}
///|
pub fn FunctionBuilder::v128_splat_f32(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(F32)), [val])
}
///|
pub fn FunctionBuilder::v128_splat_f64(
self : FunctionBuilder,
val : Value,
) -> Value {
self.emit_inst(V128, Vector(Splat(F64)), [val])
}
///|
/// Extract a lane from a v128 value
pub fn FunctionBuilder::v128_extract8s(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I32, Vector(ExtractLane(I8, Signed, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract8u(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I32, Vector(ExtractLane(I8, Unsigned, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract16s(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I32, Vector(ExtractLane(I16, Signed, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract16u(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I32, Vector(ExtractLane(I16, Unsigned, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract32(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I32, Vector(ExtractLane(I32, None, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract64(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(I64, Vector(ExtractLane(I64, None, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract_f32(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(F32, Vector(ExtractLane(F32, None, lane)), [vec])
}
///|
pub fn FunctionBuilder::v128_extract_f64(
self : FunctionBuilder,
vec : Value,
lane : Int,
) -> Value {
self.emit_inst(F64, Vector(ExtractLane(F64, None, lane)), [vec])
}
///|
/// Replace a lane in a v128 value
pub fn FunctionBuilder::v128_replace8(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(I8, lane)), [vec, val])
}
///|
pub fn FunctionBuilder::v128_replace16(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(I16, lane)), [vec, val])
}
///|
pub fn FunctionBuilder::v128_replace32(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(I32, lane)), [vec, val])
}
///|
pub fn FunctionBuilder::v128_replace64(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(I64, lane)), [vec, val])
}
///|
pub fn FunctionBuilder::v128_replace_f32(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(F32, lane)), [vec, val])
}
///|
pub fn FunctionBuilder::v128_replace_f64(
self : FunctionBuilder,
vec : Value,
val : Value,
lane : Int,
) -> Value {
self.emit_inst(V128, Vector(ReplaceLane(F64, lane)), [vec, val])
}
///|
/// Shuffle lanes from two v128 values
pub fn FunctionBuilder::v128_shuffle(
self : FunctionBuilder,
a : Value,
b : Value,
lanes : FixedArray[Int],
) -> Value {
self.emit_inst(V128, Vector(Shuffle(lanes)), [a, b])
}
///|
/// Swizzle lanes using indices from another v128
pub fn FunctionBuilder::v128_swizzle(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(V128, Vector(Swizzle), [a, b])
}
///|
/// Bitwise operations on v128
pub fn FunctionBuilder::v128_not(self : FunctionBuilder, a : Value) -> Value {
self.emit_inst(V128, Vector(Bitwise(Not)), [a])
}
///|
pub fn FunctionBuilder::v128_and(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(V128, Vector(Bitwise(And)), [a, b])
}
///|
pub fn FunctionBuilder::v128_andnot(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(V128, Vector(Bitwise(AndNot)), [a, b])
}
///|
pub fn FunctionBuilder::v128_or(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(V128, Vector(Bitwise(Or)), [a, b])
}
///|
pub fn FunctionBuilder::v128_xor(
self : FunctionBuilder,
a : Value,
b : Value,
) -> Value {
self.emit_inst(V128, Vector(Bitwise(Xor)), [a, b])
}
///|
pub fn FunctionBuilder::v128_bitselect(
self : FunctionBuilder,
a : Value,
b : Value,
c : Value,
) -> Value {
self.emit_inst(V128, Vector(Bitwise(Bitselect)), [a, b, c])
}
///|
pub fn FunctionBuilder::v128_anytrue(
self : FunctionBuilder,
a : Value,
) -> Value {
self.emit_inst(I32, Vector(Predicate(AnyTrue)), [a])
}
///|
/// SIMD load with effective address (for complex SIMD loads)
pub fn FunctionBuilder::v128_load_with_addr(
self : FunctionBuilder,
opcode : VectorMemoryOp,
effective_addr : Value,
) -> Value {
self.emit_inst(V128, Memory(Vector(opcode)), [effective_addr])
}
///|
/// SIMD load lane with effective address and existing vector
pub fn FunctionBuilder::v128_load_lane_with_addr(
self : FunctionBuilder,
opcode : VectorMemoryOp,
effective_addr : Value,
vec : Value,
) -> Value {
self.emit_inst(V128, Memory(Vector(opcode)), [effective_addr, vec])
}
///|
/// SIMD store lane with effective address and vector (void)
pub fn FunctionBuilder::v128_store_lane_with_addr(
self : FunctionBuilder,
opcode : VectorMemoryOp,
effective_addr : Value,
vec : Value,
) -> Unit {
self.emit_void_inst(Memory(Vector(opcode)), [effective_addr, vec])
}