authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-06 08:07:53+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-07 09:32:22+02:00
log17e07ffc6381a1650b6bac5948b9f22d24411982
tree3d72ff4335360dd314567cfaf822deec99a472dd
parent21f23814383ff8060a6b983c5ff9e47b5f1853ba

llvm: represent bool as i8 in memory

Follow-up to https://codeberg.org/ziglang/zig/pulls/35711

3 files changed, 40 insertions(+), 10 deletions(-)

src/codegen/llvm.zig+9-4
...@@ -2912,7 +2912,7 @@ pub const Object = struct {...@@ -2912,7 +2912,7 @@ pub const Object = struct {
29122912
2913 return switch (t.toIntern()) {2913 return switch (t.toIntern()) {
2914 .u0_type => unreachable, // no runtime bits2914 .u0_type => unreachable, // no runtime bits
2915 .u1_type => try o.intType(1, repr),2915 .u1_type, .bool_type => try o.intType(1, repr),
2916 .u8_type, .i8_type => try o.intType(8, repr),2916 .u8_type, .i8_type => try o.intType(8, repr),
2917 .u16_type, .i16_type => try o.intType(16, repr),2917 .u16_type, .i16_type => try o.intType(16, repr),
2918 .u29_type => try o.intType(29, repr),2918 .u29_type => try o.intType(29, repr),
...@@ -2983,7 +2983,6 @@ pub const Object = struct {...@@ -2983,7 +2983,6 @@ pub const Object = struct {
2983 // @foo = external global i82983 // @foo = external global i8
2984 return .i8;2984 return .i8;
2985 },2985 },
2986 .bool_type => .i1,
2987 .anyerror_type => try o.errorIntType(repr),2986 .anyerror_type => try o.errorIntType(repr),
2988 .void_type => unreachable, // no runtime bits2987 .void_type => unreachable, // no runtime bits
2989 .type_type => unreachable, // no runtime bits2988 .type_type => unreachable, // no runtime bits
...@@ -3472,8 +3471,14 @@ pub const Object = struct {...@@ -3472,8 +3471,14 @@ pub const Object = struct {
3472 .null => unreachable, // non-runtime value3471 .null => unreachable, // non-runtime value
3473 .@"unreachable" => unreachable, // non-runtime value3472 .@"unreachable" => unreachable, // non-runtime value
34743473
3475 .false => .false,3474 .false => switch (repr) {
3476 .true => .true,3475 .as_value => .false,
3476 .in_memory, .memory_access => try o.builder.intConst(.i8, 0),
3477 },
3478 .true => switch (repr) {
3479 .as_value => .true,
3480 .in_memory, .memory_access => try o.builder.intConst(.i8, 1),
3481 },
3477 },3482 },
3478 .enum_literal => unreachable, // non-runtime value3483 .enum_literal => unreachable, // non-runtime value
3479 .@"extern" => unreachable, // non-runtime value3484 .@"extern" => unreachable, // non-runtime value
src/codegen/llvm/FuncGen.zig+11-6
...@@ -2950,12 +2950,11 @@ fn airIsErr(...@@ -2950,12 +2950,11 @@ fn airIsErr(
2950 if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;2950 if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
29512951
2952 if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {2952 if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
2953 const val: Builder.Constant = switch (cond) {2953 return switch (cond) {
2954 .eq => .true, // 0 == 02954 .eq => .true, // 0 == 0
2955 .ne => .false, // 0 != 02955 .ne => .false, // 0 != 0
2956 else => unreachable,2956 else => unreachable,
2957 };2957 };
2958 return val.toValue();
2959 }2958 }
29602959
2961 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));2960 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
...@@ -6666,7 +6665,10 @@ fn load(...@@ -6666,7 +6665,10 @@ fn load(
6666 const llvm_value_ty = try o.lowerType(load_ty, .as_value);6665 const llvm_value_ty = try o.lowerType(load_ty, .as_value);
66676666
6668 if (llvm_access_ty != llvm_value_ty) {6667 if (llvm_access_ty != llvm_value_ty) {
6669 assert(load_ty.isAbiInt(zcu));6668 const signedness: std.lang.Signedness = switch (load_ty.toIntern()) {
6669 .bool_type => .unsigned,
6670 else => load_ty.intInfo(zcu).signedness,
6671 };
6670 // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special6672 // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special
6671 // handling for these, as LLVM's documented semantics are a valid implementation of Zig's6673 // handling for these, as LLVM's documented semantics are a valid implementation of Zig's
6672 // semantics. However:6674 // semantics. However:
...@@ -6685,7 +6687,7 @@ fn load(...@@ -6685,7 +6687,7 @@ fn load(
6685 // implemented, but until then, do a normal trunc for packed types.6687 // implemented, but until then, do a normal trunc for packed types.
6686 return fg.wip.cast(switch (load_ty.zigTypeTag(zcu)) {6688 return fg.wip.cast(switch (load_ty.zigTypeTag(zcu)) {
6687 .@"struct", .@"union" => .trunc,6689 .@"struct", .@"union" => .trunc,
6688 else => switch (load_ty.intInfo(zcu).signedness) {6690 else => switch (signedness) {
6689 .unsigned => .@"trunc nuw",6691 .unsigned => .@"trunc nuw",
6690 .signed => .@"trunc nsw",6692 .signed => .@"trunc nsw",
6691 },6693 },
...@@ -6740,10 +6742,13 @@ fn store(...@@ -6740,10 +6742,13 @@ fn store(
6740 const llvm_value_ty = try o.lowerType(elem_ty, .as_value);6742 const llvm_value_ty = try o.lowerType(elem_ty, .as_value);
67416743
6742 if (llvm_access_ty != llvm_value_ty) {6744 if (llvm_access_ty != llvm_value_ty) {
6743 assert(elem_ty.isAbiInt(zcu));6745 const signedness: std.lang.Signedness = switch (elem_ty.toIntern()) {
6746 .bool_type => .unsigned,
6747 else => elem_ty.intInfo(zcu).signedness,
6748 };
6744 // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see6749 // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see
6745 // the corresponding comment in `FuncGen.load` for more details.6750 // the corresponding comment in `FuncGen.load` for more details.
6746 const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) {6751 const extended = try fg.wip.cast(switch (signedness) {
6747 .unsigned => .zext,6752 .unsigned => .zext,
6748 .signed => .sext,6753 .signed => .sext,
6749 }, elem, llvm_access_ty, "");6754 }, elem, llvm_access_ty, "");
test/llvm_ir.zig+20
...@@ -116,6 +116,26 @@ pub fn addCases(cases: *tests.LlvmIrContext) void {...@@ -116,6 +116,26 @@ pub fn addCases(cases: *tests.LlvmIrContext) void {
116 "null_pointer_is_valid",116 "null_pointer_is_valid",
117 "store i16 42, ptr",117 "store i16 42, ptr",
118 }, .{});118 }, .{});
119
120 cases.addMatches("load and store bool",
121 \\export fn foo(a: *bool, b: *align(2) bool) void {
122 \\ const tmp = a.*;
123 \\ a.* = b.*;
124 \\ b.* = tmp;
125 \\}
126 , &.{
127 // TODO: this should all be one multiline string literal, but `-femit-llvm-ir` is currently
128 // emitting CRLF on Windows, which is a pain to handle here. In future that option will emit
129 // unoptimized LLVM IR emitted directly from Zig, so that bug will go away.
130 " %3 = load i8, ptr %0, align 1",
131 " %4 = trunc nuw i8 %3 to i1",
132 " %5 = load i8, ptr %1, align 2",
133 " %6 = trunc nuw i8 %5 to i1",
134 " %7 = zext i1 %6 to i8",
135 " store i8 %7, ptr %0, align 1",
136 " %8 = zext i1 %4 to i8",
137 " store i8 %8, ptr %1, align 2",
138 }, .{ .strip = true });
119}139}
120140
121const std = @import("std");141const std = @import("std");