From 17e07ffc6381a1650b6bac5948b9f22d24411982 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Thu, 6 Aug 2026 08:07:53 +0100 Subject: [PATCH] llvm: represent bool as i8 in memory Follow-up to https://codeberg.org/ziglang/zig/pulls/35711 --- src/codegen/llvm.zig | 13 +++++++++---- src/codegen/llvm/FuncGen.zig | 17 +++++++++++------ test/llvm_ir.zig | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2f73d371ea2ed35e82d6923c917581e89ae708c1..255b2f6bb38d215ce826b67ca2d6b71d38c45de5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2912,7 +2912,7 @@ pub const Object = struct { return switch (t.toIntern()) { .u0_type => unreachable, // no runtime bits - .u1_type => try o.intType(1, repr), + .u1_type, .bool_type => try o.intType(1, repr), .u8_type, .i8_type => try o.intType(8, repr), .u16_type, .i16_type => try o.intType(16, repr), .u29_type => try o.intType(29, repr), @@ -2983,7 +2983,6 @@ pub const Object = struct { // @foo = external global i8 return .i8; }, - .bool_type => .i1, .anyerror_type => try o.errorIntType(repr), .void_type => unreachable, // no runtime bits .type_type => unreachable, // no runtime bits @@ -3472,8 +3471,14 @@ pub const Object = struct { .null => unreachable, // non-runtime value .@"unreachable" => unreachable, // non-runtime value - .false => .false, - .true => .true, + .false => switch (repr) { + .as_value => .false, + .in_memory, .memory_access => try o.builder.intConst(.i8, 0), + }, + .true => switch (repr) { + .as_value => .true, + .in_memory, .memory_access => try o.builder.intConst(.i8, 1), + }, }, .enum_literal => unreachable, // non-runtime value .@"extern" => unreachable, // non-runtime value diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 676fad2f1036445d01e1774b223e1af849d01f82..14e31fd1c09f846fcef7417b46b7681423880cfd 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -2950,12 +2950,11 @@ fn airIsErr( if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) { - const val: Builder.Constant = switch (cond) { + return switch (cond) { .eq => .true, // 0 == 0 .ne => .false, // 0 != 0 else => unreachable, }; - return val.toValue(); } if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); @@ -6666,7 +6665,10 @@ fn load( const llvm_value_ty = try o.lowerType(load_ty, .as_value); if (llvm_access_ty != llvm_value_ty) { - assert(load_ty.isAbiInt(zcu)); + const signedness: std.lang.Signedness = switch (load_ty.toIntern()) { + .bool_type => .unsigned, + else => load_ty.intInfo(zcu).signedness, + }; // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special // handling for these, as LLVM's documented semantics are a valid implementation of Zig's // semantics. However: @@ -6685,7 +6687,7 @@ fn load( // implemented, but until then, do a normal trunc for packed types. return fg.wip.cast(switch (load_ty.zigTypeTag(zcu)) { .@"struct", .@"union" => .trunc, - else => switch (load_ty.intInfo(zcu).signedness) { + else => switch (signedness) { .unsigned => .@"trunc nuw", .signed => .@"trunc nsw", }, @@ -6740,10 +6742,13 @@ fn store( const llvm_value_ty = try o.lowerType(elem_ty, .as_value); if (llvm_access_ty != llvm_value_ty) { - assert(elem_ty.isAbiInt(zcu)); + const signedness: std.lang.Signedness = switch (elem_ty.toIntern()) { + .bool_type => .unsigned, + else => elem_ty.intInfo(zcu).signedness, + }; // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see // the corresponding comment in `FuncGen.load` for more details. - const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) { + const extended = try fg.wip.cast(switch (signedness) { .unsigned => .zext, .signed => .sext, }, elem, llvm_access_ty, ""); diff --git a/test/llvm_ir.zig b/test/llvm_ir.zig index 32221d82878f48be7159a28404dfe3719650c6d9..7949ddab0d19abb763440cdca0d5112cef974da9 100644 --- a/test/llvm_ir.zig +++ b/test/llvm_ir.zig @@ -116,6 +116,26 @@ pub fn addCases(cases: *tests.LlvmIrContext) void { "null_pointer_is_valid", "store i16 42, ptr", }, .{}); + + cases.addMatches("load and store bool", + \\export fn foo(a: *bool, b: *align(2) bool) void { + \\ const tmp = a.*; + \\ a.* = b.*; + \\ b.* = tmp; + \\} + , &.{ + // TODO: this should all be one multiline string literal, but `-femit-llvm-ir` is currently + // emitting CRLF on Windows, which is a pain to handle here. In future that option will emit + // unoptimized LLVM IR emitted directly from Zig, so that bug will go away. + " %3 = load i8, ptr %0, align 1", + " %4 = trunc nuw i8 %3 to i1", + " %5 = load i8, ptr %1, align 2", + " %6 = trunc nuw i8 %5 to i1", + " %7 = zext i1 %6 to i8", + " store i8 %7, ptr %0, align 1", + " %8 = zext i1 %4 to i8", + " store i8 %8, ptr %1, align 2", + }, .{ .strip = true }); } const std = @import("std"); -- 2.54.0