I've cobbled something together for the llvm codegen of NumF32ToParts
, this currently panics with LLVM errors when defining function "Num_f32ToParts_ec2bd03bf86b935fa34d71ad7ebb49f1f10f87d343e521511d8f9e6625620cd"
.
I think the returning sign field needs to be changed so it matches the Roc Bool representation and the field order needs to be changed. Can someone tell me in broad strokes what those should be?
NumF32ToParts => {
// Splits a [F32] into its components according to IEEE 754 standard.
// F32 -> { sign : Bool, exponent : U8, fraction : U32 }
// temp from zig code for inspiration
/*pub fn f32ToParts(self: f32) callconv(.C) F32Parts {
const u32Value = @as(u32, @bitCast(self));
return F32Parts{
.fraction = u32Value & 0x7fffff,
.exponent = @truncate(u32Value >> 23 & 0xff),
.sign = u32Value >> 31 & 1 == 1,
};
} */
arguments!(arg);
let float_val = arg.into_float_value();
let float_bits = env.builder.new_build_bitcast(
float_val,
env.context.i32_type(),
"float_as_int"
).into_int_value();
let sign_bit = env.builder.new_build_right_shift(
float_bits,
env.context.i32_type().const_int(31, false),
false,
"sign_bit"
);
let exponent_mask = env.context.i32_type().const_int(0x7F80_0000, false);
let exponent_bits = env.builder.new_build_and(
float_bits,
exponent_mask,
"exponent_bits"
);
let exponent = env.builder.new_build_right_shift(
exponent_bits,
env.context.i32_type().const_int(23, false),
false,
"exponent"
);
let fraction_mask = env.context.i32_type().const_int(0x007F_FFFF, false);
let fraction = env.builder.new_build_and(
float_bits,
fraction_mask,
"fraction"
);
// Not sure if order is correct
let fields = [Layout::BOOL, Layout::U16, Layout::U64];
// Not sure if order is correct
let (sign_index, exponent_index, fraction_index) = (0, 1, 2);
let result_layout = LayoutRepr::Struct(env.arena.alloc(fields));
let result_struct_type =
basic_type_from_layout(env, layout_interner, result_layout).into_struct_type();
let result = result_struct_type.const_zero();
// We have sign_bit, not sure how to convert it to Roc Bool representation
let sign = sign_bit;
let result = env
.builder
.build_insert_value(result, sign, sign_index, "insert_sign")
.unwrap();
let result = env
.builder
.build_insert_value(result, exponent, exponent_index, "insert_exponent")
.unwrap();
let result = env
.builder
.build_insert_value(result, fraction, fraction_index, "insert_fraction")
.unwrap();
use_roc_value(
env,
layout_interner,
result_layout,
result.into_struct_value().into(),
"use_f32_to_parts_result_record",
)
}
it would be fine to just call zig to get these values
manual llvm is just extra work, for such a niche function it's not really worth it I think
you can order the fields on the zig side first by size (big to small) and then use the name (alphabetic) as a tie-breaker
and then the zig struct should be structurally the same as the roc record
Cool, thanks Folkert :heart:
Thanks for all the help!
I tried to call the zig code, but the return types do not match:
define internal { i64, i24 } @roc_builtins.num.f64_to_parts(double %0) local_unnamed_addr #9 {
...
}
define internal fastcc { i64, i16, i1 } @Num_f64ToParts_ec2bd03bf86b935fa34d71ad7ebb49f1f10f87d343e521511d8f9e6625620cd(double %"#arg1") !dbg !1037 {
...
}
I tried to bitcast this, but llvm will not allow me to do this.
When I have to extract the values again aniway, I think it might be worth to actually wirte the llvm by hand?
bitcast only works for integer types. The trick here is to crate a new alloca
, write the zig value into it, then read it back as the type you want
I think I managed to do it.
Thanks for all the help, let me know what I can clean up!
Can someone check the zig code and crates/compiler/gen_llvm/src/llvm/lowlevel.rs?
All the rest looks good!
Last updated: Jul 06 2025 at 12:14 UTC