diff --git a/spec/compiler/codegen/union_type_spec.cr b/spec/compiler/codegen/union_type_spec.cr index eb561a92dbdd..8ea7d058bff9 100644 --- a/spec/compiler/codegen/union_type_spec.cr +++ b/spec/compiler/codegen/union_type_spec.cr @@ -215,4 +215,23 @@ describe "Code gen: union type" do Union(Nil, Int32).foo )).to_string.should eq("TupleLiteral") end + + it "respects union payload alignment when upcasting Bool (#14898)" do + mod = codegen(<<-CRYSTAL) + x = uninitialized Bool | UInt8[64] + x = true + CRYSTAL + + str = mod.to_s + {% if LibLLVM::IS_LT_150 %} + str.should contain("store i512 1, i512* %2, align 8") + {% else %} + str.should contain("store i512 1, ptr %1, align 8") + {% end %} + + # an i512 store defaults to 16-byte alignment, which is undefined behavior + # as it overestimates the actual alignment of `x`'s data field (x86 in + # particular segfaults on misaligned 16-byte stores) + str.should_not contain("align 16") + end end diff --git a/src/compiler/crystal/codegen/unions.cr b/src/compiler/crystal/codegen/unions.cr index b2b63a17c5ab..fdf1d81a4c95 100644 --- a/src/compiler/crystal/codegen/unions.cr +++ b/src/compiler/crystal/codegen/unions.cr @@ -81,16 +81,19 @@ module Crystal def store_bool_in_union(target_type, union_pointer, value) struct_type = llvm_type(target_type) + union_value_type = struct_type.struct_element_types[1] store type_id(value, @program.bool), union_type_id(struct_type, union_pointer) # To store a boolean in a union - # we sign-extend it to the size in bits of the union - union_size = @llvm_typer.size_of(struct_type.struct_element_types[1]) + # we zero-extend it to the size in bits of the union + union_size = @llvm_typer.size_of(union_value_type) int_type = llvm_context.int((union_size * 8).to_i32) bool_as_extended_int = builder.zext(value, int_type) casted_value_ptr = pointer_cast(union_value(struct_type, union_pointer), int_type.pointer) - store bool_as_extended_int, casted_value_ptr + inst = store bool_as_extended_int, casted_value_ptr + set_alignment(inst, @llvm_typer.align_of(union_value_type)) + inst end def store_nil_in_union(target_type, union_pointer)