Commit 0a3aec020a

LemonBoy <thatlemon@gmail.com>
2019-05-18 17:20:20
Fix load/store of non-integer fields in packed struct
1 parent 51aaa02
Changed files (2)
src
test
stage1
behavior
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) {
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]);
+}