diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 049f7bfad6..431d158293 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6459,6 +6459,34 @@ pub const FuncGen = struct { return vector; }, .Struct => { + if (result_ty.containerLayout() == .Packed) { + const struct_obj = result_ty.castTag(.@"struct").?.data; + const big_bits = struct_obj.packedIntegerBits(target); + const int_llvm_ty = self.dg.context.intType(big_bits); + const fields = struct_obj.fields.values(); + comptime assert(Type.packed_struct_layout_version == 2); + var running_int: *const llvm.Value = int_llvm_ty.constNull(); + var running_bits: u16 = 0; + for (elements) |elem, i| { + const field = fields[i]; + if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; + + const non_int_val = try self.resolveInst(elem); + const ty_bit_size = @intCast(u16, field.ty.bitSize(target)); + const small_int_ty = self.dg.context.intType(ty_bit_size); + const small_int_val = self.builder.buildBitCast(non_int_val, small_int_ty, ""); + const shift_rhs = int_llvm_ty.constInt(running_bits, .False); + // If the field is as large as the entire packed struct, this + // zext would go from, e.g. i16 to i16. This is legal with + // constZExtOrBitCast but not legal with constZExt. + const extended_int_val = self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, ""); + const shifted = self.builder.buildShl(extended_int_val, shift_rhs, ""); + running_int = self.builder.buildOr(running_int, shifted, ""); + running_bits += ty_bit_size; + } + return running_int; + } + var ptr_ty_buf: Type.Payload.Pointer = undefined; if (isByRef(result_ty)) { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index b7a3ff7230..33b862499c 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -448,6 +448,14 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildZExtOrBitCast = LLVMBuildZExtOrBitCast; + extern fn LLVMBuildZExtOrBitCast( + *const Builder, + Val: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildSExt = LLVMBuildSExt; extern fn LLVMBuildSExt( *const Builder, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 4bed1f56cc..a46605c424 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1294,3 +1294,24 @@ test "loading a struct pointer perfoms a copy" { try expect(s2.b == 2); try expect(s2.c == 3); } + +test "packed struct aggregate init" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(a: i2, b: i6) u8 { + return @bitCast(u8, P{ .a = a, .b = b }); + } + + const P = packed struct { + a: i2, + b: i6, + }; + }; + const result = @bitCast(u8, S.foo(1, 2)); + try expect(result == 9); +}