From 0a3aec020a6c24e82e8b0accfd8d1a78286f4eda Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 18 May 2019 17:20:20 +0200 Subject: [PATCH] Fix load/store of non-integer fields in packed struct --- src/codegen.cpp | 34 ++++++++++++++++++++++----------- test/stage1/behavior/struct.zig | 28 ++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c8903b7dbf..3a34894bb3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1972,13 +1972,18 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - size_in_bits : bit_offset; LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); - LLVMValueRef mask_val = LLVMConstAllOnes(get_llvm_type(g, child_type)); + // Convert to equally-sized integer type in order to perform the bit + // operations on the value to store + LLVMTypeRef value_bits_type = LLVMIntType(size_in_bits); + LLVMValueRef value_bits = LLVMBuildBitCast(g->builder, value, value_bits_type, ""); + + LLVMValueRef mask_val = LLVMConstAllOnes(value_bits_type); mask_val = LLVMConstZExt(mask_val, LLVMTypeOf(containing_int)); mask_val = LLVMConstShl(mask_val, shift_amt_val); mask_val = LLVMConstNot(mask_val); LLVMValueRef anded_containing_int = LLVMBuildAnd(g->builder, containing_int, mask_val, ""); - LLVMValueRef extended_value = LLVMBuildZExt(g->builder, value, LLVMTypeOf(containing_int), ""); + LLVMValueRef extended_value = LLVMBuildZExt(g->builder, value_bits, LLVMTypeOf(containing_int), ""); LLVMValueRef shifted_value = LLVMBuildShl(g->builder, extended_value, shift_amt_val, ""); LLVMValueRef ored_value = LLVMBuildOr(g->builder, shifted_value, anded_containing_int, ""); @@ -3388,16 +3393,23 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); - if (!handle_is_ptr(child_type)) - return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); + if (handle_is_ptr(child_type)) { + assert(instruction->tmp_ptr != nullptr); + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, + LLVMPointerType(same_size_int, 0), ""); + LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); + return instruction->tmp_ptr; + } - assert(instruction->tmp_ptr != nullptr); - LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); - LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); - LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, - LLVMPointerType(same_size_int, 0), ""); - LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); - return instruction->tmp_ptr; + if (child_type->id == ZigTypeIdFloat) { + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); + } + + return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); } static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) { diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index 114f06982b..1812642857 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -1,8 +1,9 @@ const std = @import("std"); const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; const expectEqualSlices = std.testing.expectEqualSlices; const builtin = @import("builtin"); -const maxInt = std.math.maxInt; +const maxInt = std.math.maxInt; const StructWithNoFields = struct { fn add(a: i32, b: i32) i32 { return a + b; @@ -505,10 +506,10 @@ test "packed struct with u0 field access" { comptime expect(s.f0 == 0); } -const S0 = struct{ +const S0 = struct { bar: S1, - pub const S1 = struct{ + pub const S1 = struct { value: u8, }; @@ -523,3 +524,24 @@ test "access to global struct fields" { g_foo.bar.value = 42; expect(g_foo.bar.value == 42); } + +test "packed struct with fp fields" { + const S = packed struct { + data: [3]f32, + + pub fn frob(self: *@This()) void { + self.data[0] += self.data[1] + self.data[2]; + self.data[1] += self.data[0] + self.data[2]; + self.data[2] += self.data[0] + self.data[1]; + } + }; + + var s: S = undefined; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.frob(); + expectEqual(f32(6.0), s.data[0]); + expectEqual(f32(11.0), s.data[1]); + expectEqual(f32(20.0), s.data[2]); +}