Stream: compiler development

Topic: gen llvm NumF32ToParts


view this post on Zulip Anton (Mar 23 2024 at 18:40):

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",
            )
        }

branch for the rest

view this post on Zulip Folkert de Vries (Mar 25 2024 at 09:08):

it would be fine to just call zig to get these values

view this post on Zulip Folkert de Vries (Mar 25 2024 at 09:09):

manual llvm is just extra work, for such a niche function it's not really worth it I think

view this post on Zulip Folkert de Vries (Mar 25 2024 at 09:10):

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

view this post on Zulip Folkert de Vries (Mar 25 2024 at 09:10):

and then the zig struct should be structurally the same as the roc record

view this post on Zulip Anton (Mar 25 2024 at 09:24):

Cool, thanks Folkert :heart:

view this post on Zulip Fabian Schmalzried (Mar 26 2024 at 10:02):

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.

view this post on Zulip Fabian Schmalzried (Mar 26 2024 at 10:04):

When I have to extract the values again aniway, I think it might be worth to actually wirte the llvm by hand?

view this post on Zulip Folkert de Vries (Mar 26 2024 at 10:16):

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

view this post on Zulip Fabian Schmalzried (Mar 26 2024 at 13:37):

I think I managed to do it.

view this post on Zulip Fabian Schmalzried (Mar 26 2024 at 13:38):

Thanks for all the help, let me know what I can clean up!

view this post on Zulip Anton (Mar 26 2024 at 18:47):

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