aarch64: fix error union constants

This commit is contained in:
Jacob Young 2025-07-27 08:00:57 -04:00
parent 771523c675
commit b26e732bd0
4 changed files with 62 additions and 40 deletions

View File

@ -10414,11 +10414,12 @@ pub const Value = struct {
} }, } },
.error_union => |error_union| { .error_union => |error_union| {
const error_union_type = ip.indexToKey(error_union.ty).error_union_type; 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 payload_ty: ZigType = .fromInterned(error_union_type.payload_type);
if (!ip.isNoReturn(error_union_type.error_set_type) and const error_set_offset = codegen.errUnionErrorOffset(payload_ty, zcu);
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 = 0; offset -= error_set_offset;
continue :constant_key switch (error_union.val) { continue :constant_key switch (error_union.val) {
.err_name => |err_name| .{ .err = .{ .err_name => |err_name| .{ .err = .{
.ty = error_union_type.error_set_type, .ty = error_union_type.error_set_type,
@ -10430,15 +10431,18 @@ pub const Value = struct {
} }, } },
}; };
} }
assert(payload_ty.hasRuntimeBitsIgnoreComptime(zcu)); const payload_offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
offset -= @intCast(codegen.errUnionPayloadOffset(payload_ty, zcu)); const payload_size = payload_ty.abiSize(zcu);
switch (error_union.val) { if (offset >= payload_offset and offset + size <= payload_offset + payload_size) {
.err_name => continue :constant_key .{ .undef = error_union_type.payload_type }, offset -= payload_offset;
.payload => |payload| { switch (error_union.val) {
constant = payload; .err_name => continue :constant_key .{ .undef = error_union_type.payload_type },
constant_key = ip.indexToKey(payload); .payload => |payload| {
continue :constant_key constant_key; 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 }, .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 { fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMemory}!bool {
const zcu = isel.pt.zcu; const zcu = isel.pt.zcu;
const ip = &zcu.intern_pool; 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, .int_type,
.ptr_type, .ptr_type,
.array_type, .array_type,
@ -10997,6 +11011,37 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
.empty_enum_value, .empty_enum_value,
.memoized_call, .memoized_call,
=> unreachable, // not a runtime value => 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| { .opt => |opt| {
const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu)); const child_size: usize = @intCast(ZigType.fromInterned(ip.indexToKey(opt.ty).opt_type).abiSize(zcu));
switch (opt.val) { 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); if (!ZigType.fromInterned(opt.ty).optionalReprIsPayload(zcu)) buffer[child_size] = @intFromBool(true);
}, },
} }
return true;
}, },
.aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) { .aggregate => |aggregate| switch (ip.indexToKey(aggregate.ty)) {
else => unreachable, else => unreachable,
@ -11027,9 +11071,8 @@ fn writeToMemory(isel: *Select, constant: Constant, buffer: []u8) error{OutOfMem
elem_offset += elem_size; elem_offset += elem_size;
}, },
} }
return true;
}, },
.vector_type => {}, .vector_type => return false,
.struct_type => { .struct_type => {
const loaded_struct = ip.loadStructType(aggregate.ty); const loaded_struct = ip.loadStructType(aggregate.ty);
switch (loaded_struct.layout) { 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; }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
field_offset += field_size; field_offset += field_size;
} }
return true;
}, },
.@"extern", .@"packed" => {}, .@"extern", .@"packed" => return false,
} }
}, },
.tuple_type => |tuple_type| { .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; }), buffer[@intCast(field_offset)..][0..@intCast(field_size)])) return false;
field_offset += field_size; 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; return true;
} }

View File

@ -926,7 +926,6 @@ test "enum literal casting to tagged union" {
const Bar = enum { A, B, C, D }; const Bar = enum { A, B, C, D };
test "enum literal casting to error union with payload enum" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var bar: error{B}!Bar = undefined; var bar: error{B}!Bar = undefined;

View File

@ -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" { test "fn returning empty error set can be passed as fn returning any error" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
entry(); entry();
comptime entry(); comptime entry();
} }
test "fn returning empty error set can be passed as fn returning any error - pointer" { 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; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
entryPtr(); entryPtr();
@ -404,7 +401,6 @@ fn intLiteral(str: []const u8) !?i64 {
} }
test "nested error union function call in optional unwrap" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) 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" { test "nested catch" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
const S = struct { const S = struct {
@ -698,7 +693,6 @@ test "coerce error set to the current inferred error set" {
} }
test "error union payload is properly aligned" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var a: anyerror = error.Foo; var a: anyerror = error.Foo;
@ -1034,8 +1026,6 @@ test "errorCast to adhoc inferred error set" {
} }
test "@errorCast from error set to error union" { test "@errorCast from error set to error union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
const S = struct { const S = struct {
fn doTheTest(set: error{ A, B }) error{A}!i32 { fn doTheTest(set: error{ A, B }) error{A}!i32 {
return @errorCast(set); return @errorCast(set);
@ -1046,8 +1036,6 @@ test "@errorCast from error set to error union" {
} }
test "@errorCast from error union to error union" { test "@errorCast from error union to error union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
const S = struct { const S = struct {
fn doTheTest(set: error{ A, B }!i32) error{A}!i32 { fn doTheTest(set: error{ A, B }!i32) error{A}!i32 {
return @errorCast(set); return @errorCast(set);

View File

@ -174,7 +174,6 @@ test "while with optional as condition with else" {
} }
test "while with error union condition" { 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_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv) 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" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO