Commit 886df772f0

Andrew Kelley <andrew@ziglang.org>
2021-12-28 00:59:26
stage2: LLVM backend: fix const packed structs
When doing LLVM const bit shifting we must make sure the integer bit sizes are wide enough or else LLVM gives us a poison result.
1 parent 3abe464
Changed files (5)
src/codegen/llvm/bindings.zig
@@ -201,6 +201,11 @@ pub const Value = opaque {
 
     pub const addCase = LLVMAddCase;
     extern fn LLVMAddCase(Switch: *const Value, OnVal: *const Value, Dest: *const BasicBlock) void;
+
+    pub inline fn isPoison(Val: *const Value) bool {
+        return LLVMIsPoison(Val).toBool();
+    }
+    extern fn LLVMIsPoison(Val: *const Value) Bool;
 };
 
 pub const Type = opaque {
src/codegen/llvm.zig
@@ -1361,10 +1361,12 @@ pub const DeclGen = struct {
                                 .val = field_val,
                             });
                             const ty_bit_size = @intCast(u16, field.ty.bitSize(target));
-                            const llvm_int_ty = dg.context.intType(ty_bit_size);
-                            const int_val = non_int_val.constBitCast(llvm_int_ty);
-                            const shift_rhs = llvm_int_ty.constInt(running_bits, .False);
-                            const shifted = int_val.constShl(shift_rhs);
+                            const small_int_ty = dg.context.intType(ty_bit_size);
+                            const small_int_val = non_int_val.constBitCast(small_int_ty);
+                            const big_int_ty = running_int.typeOf();
+                            const shift_rhs = big_int_ty.constInt(running_bits, .False);
+                            const extended_int_val = small_int_val.constZExt(big_int_ty);
+                            const shifted = extended_int_val.constShl(shift_rhs);
                             running_int = running_int.constOr(shifted);
                             running_bits += ty_bit_size;
                         } else {
test/behavior/enum_llvm.zig
@@ -47,3 +47,46 @@ test "enum literal casting to optional" {
 
     try expect(bar.? == Bar.B);
 }
+
+const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
+const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
+const C = enum(u2) { One4, Two4, Three4, Four4 };
+
+const BitFieldOfEnums = packed struct {
+    a: A,
+    b: B,
+    c: C,
+};
+
+const bit_field_1 = BitFieldOfEnums{
+    .a = A.Two,
+    .b = B.Three3,
+    .c = C.Four4,
+};
+
+test "bit field access with enum fields" {
+    var data = bit_field_1;
+    try expect(getA(&data) == A.Two);
+    try expect(getB(&data) == B.Three3);
+    try expect(getC(&data) == C.Four4);
+    comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
+
+    data.b = B.Four3;
+    try expect(data.b == B.Four3);
+
+    data.a = A.Three;
+    try expect(data.a == A.Three);
+    try expect(data.b == B.Four3);
+}
+
+fn getA(data: *const BitFieldOfEnums) A {
+    return data.a;
+}
+
+fn getB(data: *const BitFieldOfEnums) B {
+    return data.b;
+}
+
+fn getC(data: *const BitFieldOfEnums) C {
+    return data.c;
+}
test/behavior/enum_stage1.zig
@@ -2,63 +2,6 @@ const expect = @import("std").testing.expect;
 const mem = @import("std").mem;
 const Tag = @import("std").meta.Tag;
 
-const Small2 = enum(u2) { One, Two };
-
-const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 };
-const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 };
-const C = enum(u2) { One4, Two4, Three4, Four4 };
-
-const BitFieldOfEnums = packed struct {
-    a: A,
-    b: B,
-    c: C,
-};
-
-const bit_field_1 = BitFieldOfEnums{
-    .a = A.Two,
-    .b = B.Three3,
-    .c = C.Four4,
-};
-
-test "bit field access with enum fields" {
-    var data = bit_field_1;
-    try expect(getA(&data) == A.Two);
-    try expect(getB(&data) == B.Three3);
-    try expect(getC(&data) == C.Four4);
-    comptime try expect(@sizeOf(BitFieldOfEnums) == 1);
-
-    data.b = B.Four3;
-    try expect(data.b == B.Four3);
-
-    data.a = A.Three;
-    try expect(data.a == A.Three);
-    try expect(data.b == B.Four3);
-}
-
-fn getA(data: *const BitFieldOfEnums) A {
-    return data.a;
-}
-
-fn getB(data: *const BitFieldOfEnums) B {
-    return data.b;
-}
-
-fn getC(data: *const BitFieldOfEnums) C {
-    return data.c;
-}
-
-const MultipleChoice2 = enum(u32) {
-    Unspecified1,
-    A = 20,
-    Unspecified2,
-    B = 40,
-    Unspecified3,
-    C = 60,
-    Unspecified4,
-    D = 1000,
-    Unspecified5,
-};
-
 const EnumWithOneMember = enum { Eof };
 
 fn doALoopThing(id: EnumWithOneMember) void {
test/behavior.zig
@@ -21,7 +21,7 @@ test {
     _ = @import("behavior/usingnamespace.zig");
 
     // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
-    if (!builtin.zig_is_stage2 or (builtin.zig_is_stage2 and builtin.stage2_arch != .wasm32)) {
+    if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
         _ = @import("behavior/align.zig");
         _ = @import("behavior/bool.zig");
         _ = @import("behavior/bugs/704.zig");