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 {
29122912
29132913 return switch (t.toIntern()) {
29142914 .u0_type => unreachable, // no runtime bits
2915 .u1_type => try o.intType(1, repr),
2915 .u1_type, .bool_type => try o.intType(1, repr),
29162916 .u8_type, .i8_type => try o.intType(8, repr),
29172917 .u16_type, .i16_type => try o.intType(16, repr),
29182918 .u29_type => try o.intType(29, repr),
......@@ -2983,7 +2983,6 @@ pub const Object = struct {
29832983 // @foo = external global i8
29842984 return .i8;
29852985 },
2986 .bool_type => .i1,
29872986 .anyerror_type => try o.errorIntType(repr),
29882987 .void_type => unreachable, // no runtime bits
29892988 .type_type => unreachable, // no runtime bits
......@@ -3472,8 +3471,14 @@ pub const Object = struct {
34723471 .null => unreachable, // non-runtime value
34733472 .@"unreachable" => unreachable, // non-runtime value
34743473
3475 .false => .false,
3476 .true => .true,
3474 .false => switch (repr) {
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 },
34773482 },
34783483 .enum_literal => unreachable, // non-runtime value
34793484 .@"extern" => unreachable, // non-runtime value
src/codegen/llvm/FuncGen.zig+11-6
......@@ -2950,12 +2950,11 @@ fn airIsErr(
29502950 if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal;
29512951
29522952 if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
2953 const val: Builder.Constant = switch (cond) {
2953 return switch (cond) {
29542954 .eq => .true, // 0 == 0
29552955 .ne => .false, // 0 != 0
29562956 else => unreachable,
29572957 };
2958 return val.toValue();
29592958 }
29602959
29612960 if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu));
......@@ -6666,7 +6665,10 @@ fn load(
66666665 const llvm_value_ty = try o.lowerType(load_ty, .as_value);
66676666
66686667 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 };
66706672 // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special
66716673 // handling for these, as LLVM's documented semantics are a valid implementation of Zig's
66726674 // semantics. However:
......@@ -6685,7 +6687,7 @@ fn load(
66856687 // implemented, but until then, do a normal trunc for packed types.
66866688 return fg.wip.cast(switch (load_ty.zigTypeTag(zcu)) {
66876689 .@"struct", .@"union" => .trunc,
6688 else => switch (load_ty.intInfo(zcu).signedness) {
6690 else => switch (signedness) {
66896691 .unsigned => .@"trunc nuw",
66906692 .signed => .@"trunc nsw",
66916693 },
......@@ -6740,10 +6742,13 @@ fn store(
67406742 const llvm_value_ty = try o.lowerType(elem_ty, .as_value);
67416743
67426744 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 };
67446749 // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see
67456750 // 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) {
67476752 .unsigned => .zext,
67486753 .signed => .sext,
67496754 }, elem, llvm_access_ty, "");
test/llvm_ir.zig+20
......@@ -116,6 +116,26 @@ pub fn addCases(cases: *tests.LlvmIrContext) void {
116116 "null_pointer_is_valid",
117117 "store i16 42, ptr",
118118 }, .{});
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 });
119139}
120140
121141const std = @import("std");