From 886df772f06377df95f867e8b18ee47bbd0fcd8b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Dec 2021 16:59:26 -0700 Subject: [PATCH] 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. --- src/codegen/llvm.zig | 10 +++--- src/codegen/llvm/bindings.zig | 5 +++ test/behavior.zig | 2 +- test/behavior/enum_llvm.zig | 43 ++++++++++++++++++++++++++ test/behavior/enum_stage1.zig | 57 ----------------------------------- 5 files changed, 55 insertions(+), 62 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 22361a1c6e..2ec1cb4708 100644 --- a/src/codegen/llvm.zig +++ b/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 { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index e6a61ca883..3c85524cc1 100644 --- a/src/codegen/llvm/bindings.zig +++ b/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 { diff --git a/test/behavior.zig b/test/behavior.zig index 8a7e44c784..46b4cf083d 100644 --- a/test/behavior.zig +++ b/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"); diff --git a/test/behavior/enum_llvm.zig b/test/behavior/enum_llvm.zig index bd93f48db7..c0d536c029 100644 --- a/test/behavior/enum_llvm.zig +++ b/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; +} diff --git a/test/behavior/enum_stage1.zig b/test/behavior/enum_stage1.zig index 791f636633..59c1b93586 100644 --- a/test/behavior/enum_stage1.zig +++ b/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 {