From b26e732bd0a33161b079202e9df9dda4b918b2bb Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 27 Jul 2025 08:00:57 -0400 Subject: [PATCH] aarch64: fix error union constants --- src/codegen/aarch64/Select.zig | 87 ++++++++++++++++++++++++---------- test/behavior/enum.zig | 1 - test/behavior/error.zig | 12 ----- test/behavior/while.zig | 2 - 4 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 0b60f26b02..d030eab471 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -10414,11 +10414,12 @@ pub const Value = struct { } }, .error_union => |error_union| { const error_union_type = ip.indexToKey(error_union.ty).error_union_type; + const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type); const payload_ty: ZigType = .fromInterned(error_union_type.payload_type); - if (!ip.isNoReturn(error_union_type.error_set_type) and - offset == codegen.errUnionErrorOffset(payload_ty, zcu)) - { - offset = 0; + const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu); + const error_set_size = error_set_ty.abiSize(zcu); + if (offset >= error_set_offset and offset + size <= error_set_offset + error_set_size) { + offset -= error_set_offset; continue :constant_key switch (error_union.val) { .err_name => |err_name| .{ .err = .{ .ty = error_union_type.error_set_type, @@ -10430,15 +10431,18 @@ pub const Value = struct { } }, }; } - assert(payload_ty.hasRuntimeBitsIgnoreComptime(zcu)); - offset -= @intCast(codegen.errUnionPayloadOffset(payload_ty, zcu)); - switch (error_union.val) { - .err_name => continue :constant_key .{ .undef = error_union_type.payload_type }, - .payload => |payload| { - constant = payload; - constant_key = ip.indexToKey(payload); - continue :constant_key constant_key; - }, + const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu); + const payload_size = payload_ty.abiSize(zcu); + if (offset >= payload_offset and offset + size <= payload_offset + payload_size) { + offset -= payload_offset; + switch (error_union.val) { + .err_name => continue :constant_key .{ .undef = error_union_type.payload_type }, + .payload => |payload| { + constant = payload; + constant_key = ip.indexToKey(payload); + continue :constant_key constant_key; + }, + } } }, .enum_tag => |enum_tag| continue :constant_key .{ .int = ip.indexToKey(enum_tag.int).int }, @@ -10975,7 +10979,17 @@ fn hasRepeatedByteRepr(isel: *Select, constant: Constant) error{OutOfMemory}!?u8 fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMemory}!bool { const zcu = isel.pt.zcu; const ip = &zcu.intern_pool; - switch (ip.indexToKey(constant.toIntern())) { + if (try isel.writeKeyToMemory(ip.indexToKey(constant.toIntern()), buffer)) return true; + constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false, + }; + return true; +} +fn writeKeyToMemory(isel: *Select, constant_key: InternPool.Key, buffer: []u8) error{OutOfMemory}!bool { + const zcu = isel.pt.zcu; + const ip = &zcu.intern_pool; + switch (constant_key) { .int_type, .ptr_type, .array_type, @@ -10997,6 +11011,37 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem .empty_enum_value, .memoized_call, => unreachable, // not a runtime value + .err => |err| { + const error_int = ip.getErrorValueIfExists(err.name).?; + switch (buffer.len) { + else => unreachable, + inline 1...4 => |size| std.mem.writeInt( + @Type(.{ .int = .{ .signedness = .unsigned, .bits = 8 * size } }), + buffer[0..size], + @intCast(error_int), + isel.target.cpu.arch.endian(), + ), + } + }, + .error_union => |error_union| { + const error_union_type = ip.indexToKey(error_union.ty).error_union_type; + const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type); + const payload_ty: ZigType = .fromInterned(error_union_type.payload_type); + const error_set = buffer[@intCast(codegen.errUnionErrorOffset(payload_ty, zcu))..][0..@intCast(error_set_ty.abiSize(zcu))]; + switch (error_union.val) { + .err_name => |err_name| if (!try isel.writeKeyToMemory(.{ .err = .{ + .ty = error_set_ty.toIntern(), + .name = err_name, + } }, error_set)) return false, + .payload => |payload| { + if (!try isel.writeToMemory( + .fromInterned(payload), + buffer[@intCast(codegen.errUnionPayloadOffset(payload_ty, zcu))..][0..@intCast(payload_ty.abiSize(zcu))], + )) return false; + @memset(error_set, 0); + }, + } + }, .opt => |opt| { const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu)); switch (opt.val) { @@ -11008,7 +11053,6 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) buffer[child_size] = @intFromBool(true); }, } - return true; }, .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) { else => unreachable, @@ -11027,9 +11071,8 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem elem_offset += elem_size; }, } - return true; }, - .vector_type => {}, + .vector_type => return false, .struct_type => { const loaded_struct = ip.loadStructType(aggregate.ty); switch (loaded_struct.layout) { @@ -11052,9 +11095,8 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false; field_offset += field_size; } - return true; }, - .@"extern", .@"packed" => {}, + .@"extern", .@"packed" => return false, } }, .tuple_type => |tuple_type| { @@ -11071,15 +11113,10 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false; field_offset += field_size; } - return true; }, }, - else => {}, + else => return false, } - constant.writeToMemory(isel.pt, buffer) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.ReinterpretDeclRef, error.Unimplemented, error.IllDefinedMemoryLayout => return false, - }; return true; } diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 2d9d41d7b2..d719a611e6 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -926,7 +926,6 @@ test "enum literal casting to tagged union" { const Bar = enum { A, B, C, D }; test "enum literal casting to error union with payload enum" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var bar: error{B}!Bar = undefined; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ae99c0a7e8..4ce94bb43b 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -145,14 +145,11 @@ test "implicit cast to optional to error union to return result loc" { } test "fn returning empty error set can be passed as fn returning any error" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - entry(); comptime entry(); } test "fn returning empty error set can be passed as fn returning any error - pointer" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; entryPtr(); @@ -404,7 +401,6 @@ fn intLiteral(str: []const u8) !?i64 { } test "nested error union function call in optional unwrap" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -482,7 +478,6 @@ test "optional error set is the same size as error set" { } test "nested catch" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -698,7 +693,6 @@ test "coerce error set to the current inferred error set" { } test "error union payload is properly aligned" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; @@ -757,7 +751,6 @@ test "simple else prong allowed even when all errors handled" { } test "pointer to error union payload" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; @@ -845,7 +838,6 @@ test "alignment of wrapping an error union payload" { } test "compare error union and error set" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var a: anyerror = error.Foo; @@ -1034,8 +1026,6 @@ test "errorCast to adhoc inferred error set" { } test "@errorCast from error set to error union" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - const S = struct { fn doTheTest(set: error{ A, B }) error{A}!i32 { return @errorCast(set); @@ -1046,8 +1036,6 @@ test "@errorCast from error set to error union" { } test "@errorCast from error union to error union" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - const S = struct { fn doTheTest(set: error{ A, B }!i32) error{A}!i32 { return @errorCast(set); diff --git a/test/behavior/while.zig b/test/behavior/while.zig index d6323babf5..7a177d5690 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -174,7 +174,6 @@ test "while with optional as condition with else" { } test "while with error union condition" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; @@ -306,7 +305,6 @@ test "while optional 2 break statements and an else" { } test "while error 2 break statements and an else" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO