Merge pull request #16284 from Snektron/spirv-internpool-fixes

SPIR-V InternPool aftermath damage control
This commit is contained in:
Andrew Kelley 2023-07-01 14:21:04 -07:00 committed by GitHub
commit 309aacfc89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 191 additions and 103 deletions

View File

@ -741,7 +741,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
builtin.zig_backend == .stage2_x86_64 or
builtin.zig_backend == .stage2_x86 or
builtin.zig_backend == .stage2_riscv64 or
builtin.zig_backend == .stage2_sparc64)
builtin.zig_backend == .stage2_sparc64 or
builtin.zig_backend == .stage2_spirv64)
{
while (true) {
@breakpoint();

View File

@ -2,7 +2,6 @@ const std = @import("std.zig");
const builtin = @import("builtin");
const math = std.math;
const print = std.debug.print;
pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator;
@ -22,15 +21,22 @@ pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
/// TODO https://github.com/ziglang/zig/issues/5738
pub var log_level = std.log.Level.warn;
fn print(comptime fmt: []const u8, args: anytype) void {
// Disable printing in tests for simple backends.
if (builtin.zig_backend == .stage2_spirv64) return;
std.debug.print(fmt, args);
}
/// This function is intended to be used only in tests. It prints diagnostics to stderr
/// and then returns a test failure error when actual_error_union is not expected_error.
pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void {
if (actual_error_union) |actual_payload| {
std.debug.print("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload });
print("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload });
return error.TestUnexpectedError;
} else |actual_error| {
if (expected_error != actual_error) {
std.debug.print("expected error.{s}, found error.{s}\n", .{
print("expected error.{s}, found error.{s}\n", .{
@errorName(expected_error),
@errorName(actual_error),
});
@ -58,7 +64,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
.Type => {
if (actual != expected) {
std.debug.print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
return error.TestExpectedEqual;
}
},
@ -74,7 +80,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
.ErrorSet,
=> {
if (actual != expected) {
std.debug.print("expected {}, found {}\n", .{ expected, actual });
print("expected {}, found {}\n", .{ expected, actual });
return error.TestExpectedEqual;
}
},
@ -83,17 +89,17 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
switch (pointer.size) {
.One, .Many, .C => {
if (actual != expected) {
std.debug.print("expected {*}, found {*}\n", .{ expected, actual });
print("expected {*}, found {*}\n", .{ expected, actual });
return error.TestExpectedEqual;
}
},
.Slice => {
if (actual.ptr != expected.ptr) {
std.debug.print("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr });
print("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr });
return error.TestExpectedEqual;
}
if (actual.len != expected.len) {
std.debug.print("expected slice len {}, found {}\n", .{ expected.len, actual.len });
print("expected slice len {}, found {}\n", .{ expected.len, actual.len });
return error.TestExpectedEqual;
}
},
@ -106,7 +112,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
var i: usize = 0;
while (i < info.len) : (i += 1) {
if (!std.meta.eql(expected[i], actual[i])) {
std.debug.print("index {} incorrect. expected {}, found {}\n", .{
print("index {} incorrect. expected {}, found {}\n", .{
i, expected[i], actual[i],
});
return error.TestExpectedEqual;
@ -151,12 +157,12 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
if (actual) |actual_payload| {
try expectEqual(expected_payload, actual_payload);
} else {
std.debug.print("expected {any}, found null\n", .{expected_payload});
print("expected {any}, found null\n", .{expected_payload});
return error.TestExpectedEqual;
}
} else {
if (actual) |actual_payload| {
std.debug.print("expected null, found {any}\n", .{actual_payload});
print("expected null, found {any}\n", .{actual_payload});
return error.TestExpectedEqual;
}
}
@ -167,12 +173,12 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
if (actual) |actual_payload| {
try expectEqual(expected_payload, actual_payload);
} else |actual_err| {
std.debug.print("expected {any}, found {}\n", .{ expected_payload, actual_err });
print("expected {any}, found {}\n", .{ expected_payload, actual_err });
return error.TestExpectedEqual;
}
} else |expected_err| {
if (actual) |actual_payload| {
std.debug.print("expected {}, found {any}\n", .{ expected_err, actual_payload });
print("expected {}, found {any}\n", .{ expected_err, actual_payload });
return error.TestExpectedEqual;
} else |actual_err| {
try expectEqual(expected_err, actual_err);
@ -219,7 +225,7 @@ pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance
switch (@typeInfo(T)) {
.Float => if (!math.approxEqAbs(T, expected, actual, tolerance)) {
std.debug.print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });
print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });
return error.TestExpectedApproxEqAbs;
},
@ -251,7 +257,7 @@ pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance
switch (@typeInfo(T)) {
.Float => if (!math.approxEqRel(T, expected, actual, tolerance)) {
std.debug.print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });
print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });
return error.TestExpectedApproxEqRel;
},
@ -294,7 +300,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
break :diff_index if (expected.len == actual.len) return else shortest;
};
std.debug.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
// TODO: Should this be configurable by the caller?
const max_lines: usize = 16;
@ -329,12 +335,12 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
// that is usually useful.
const index_fmt = if (T == u8) "0x{X}" else "{}";
std.debug.print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len });
print("\n============ expected this output: ============= len: {} (0x{X})\n\n", .{ expected.len, expected.len });
if (window_start > 0) {
if (T == u8) {
std.debug.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
} else {
std.debug.print("... truncated ...\n", .{});
print("... truncated ...\n", .{});
}
}
differ.write(stderr.writer()) catch {};
@ -342,21 +348,21 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
const end_offset = window_start + expected_window.len;
const num_missing_items = expected.len - (window_start + expected_window.len);
if (T == u8) {
std.debug.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
} else {
std.debug.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
}
}
// now reverse expected/actual and print again
differ.expected = actual_window;
differ.actual = expected_window;
std.debug.print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len });
print("\n============= instead found this: ============== len: {} (0x{X})\n\n", .{ actual.len, actual.len });
if (window_start > 0) {
if (T == u8) {
std.debug.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
} else {
std.debug.print("... truncated ...\n", .{});
print("... truncated ...\n", .{});
}
}
differ.write(stderr.writer()) catch {};
@ -364,12 +370,12 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
const end_offset = window_start + actual_window.len;
const num_missing_items = actual.len - (window_start + actual_window.len);
if (T == u8) {
std.debug.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
} else {
std.debug.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
}
}
std.debug.print("\n================================================\n\n", .{});
print("\n================================================\n\n", .{});
return error.TestExpectedEqual;
}
@ -493,12 +499,12 @@ pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:s
};
if (!std.meta.eql(sentinel, expected_value_sentinel)) {
std.debug.print("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });
print("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });
return error.TestExpectedEqual;
}
if (!std.meta.eql(sentinel, actual_value_sentinel)) {
std.debug.print("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });
print("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });
return error.TestExpectedEqual;
}
}
@ -697,7 +703,7 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
.Type => {
if (actual != expected) {
std.debug.print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
return error.TestExpectedEqual;
}
},
@ -713,7 +719,7 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
.ErrorSet,
=> {
if (actual != expected) {
std.debug.print("expected {}, found {}\n", .{ expected, actual });
print("expected {}, found {}\n", .{ expected, actual });
return error.TestExpectedEqual;
}
},
@ -723,7 +729,7 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
// We have no idea what is behind those pointers, so the best we can do is `==` check.
.C, .Many => {
if (actual != expected) {
std.debug.print("expected {*}, found {*}\n", .{ expected, actual });
print("expected {*}, found {*}\n", .{ expected, actual });
return error.TestExpectedEqual;
}
},
@ -732,7 +738,7 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
switch (@typeInfo(pointer.child)) {
.Fn, .Opaque => {
if (actual != expected) {
std.debug.print("expected {*}, found {*}\n", .{ expected, actual });
print("expected {*}, found {*}\n", .{ expected, actual });
return error.TestExpectedEqual;
}
},
@ -741,13 +747,13 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
},
.Slice => {
if (expected.len != actual.len) {
std.debug.print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
return error.TestExpectedEqual;
}
var i: usize = 0;
while (i < expected.len) : (i += 1) {
expectEqualDeep(expected[i], actual[i]) catch |e| {
std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
print("index {d} incorrect. expected {any}, found {any}\n", .{
i, expected[i], actual[i],
});
return e;
@ -759,13 +765,13 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
.Array => |_| {
if (expected.len != actual.len) {
std.debug.print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
return error.TestExpectedEqual;
}
var i: usize = 0;
while (i < expected.len) : (i += 1) {
expectEqualDeep(expected[i], actual[i]) catch |e| {
std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
print("index {d} incorrect. expected {any}, found {any}\n", .{
i, expected[i], actual[i],
});
return e;
@ -775,13 +781,13 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
.Vector => |info| {
if (info.len != @typeInfo(@TypeOf(actual)).Vector.len) {
std.debug.print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).Vector.len });
print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).Vector.len });
return error.TestExpectedEqual;
}
var i: usize = 0;
while (i < info.len) : (i += 1) {
expectEqualDeep(expected[i], actual[i]) catch |e| {
std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
print("index {d} incorrect. expected {any}, found {any}\n", .{
i, expected[i], actual[i],
});
return e;
@ -792,7 +798,7 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
.Struct => |structType| {
inline for (structType.fields) |field| {
expectEqualDeep(@field(expected, field.name), @field(actual, field.name)) catch |e| {
std.debug.print("Field {s} incorrect. expected {any}, found {any}\n", .{ field.name, @field(expected, field.name), @field(actual, field.name) });
print("Field {s} incorrect. expected {any}, found {any}\n", .{ field.name, @field(expected, field.name), @field(actual, field.name) });
return e;
};
}
@ -823,12 +829,12 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
if (actual) |actual_payload| {
try expectEqualDeep(expected_payload, actual_payload);
} else {
std.debug.print("expected {any}, found null\n", .{expected_payload});
print("expected {any}, found null\n", .{expected_payload});
return error.TestExpectedEqual;
}
} else {
if (actual) |actual_payload| {
std.debug.print("expected null, found {any}\n", .{actual_payload});
print("expected null, found {any}\n", .{actual_payload});
return error.TestExpectedEqual;
}
}
@ -839,12 +845,12 @@ pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
if (actual) |actual_payload| {
try expectEqualDeep(expected_payload, actual_payload);
} else |actual_err| {
std.debug.print("expected {any}, found {any}\n", .{ expected_payload, actual_err });
print("expected {any}, found {any}\n", .{ expected_payload, actual_err });
return error.TestExpectedEqual;
}
} else |expected_err| {
if (actual) |actual_payload| {
std.debug.print("expected {any}, found {any}\n", .{ expected_err, actual_payload });
print("expected {any}, found {any}\n", .{ expected_err, actual_payload });
return error.TestExpectedEqual;
} else |actual_err| {
try expectEqualDeep(expected_err, actual_err);

View File

@ -537,6 +537,12 @@ pub const DeclGen = struct {
fn addInt(self: *@This(), ty: Type, val: Value) !void {
const mod = self.dg.module;
const len = ty.abiSize(mod);
if (val.isUndef(mod)) {
try self.addUndef(len);
return;
}
const int_info = ty.intInfo(mod);
const int_bits = switch (int_info.signedness) {
.signed => @as(u64, @bitCast(val.toSignedInt(mod))),
@ -544,7 +550,6 @@ pub const DeclGen = struct {
};
// TODO: Swap endianess if the compiler is big endian.
const len = ty.abiSize(mod);
try self.addBytes(std.mem.asBytes(&int_bits)[0..@as(usize, @intCast(len))]);
}
@ -667,31 +672,41 @@ pub const DeclGen = struct {
try self.addConstInt(u16, @as(u16, @intCast(int)));
},
.error_union => |error_union| {
const err_ty = switch (error_union.val) {
.err_name => ty.errorUnionSet(mod),
.payload => Type.err_int,
};
const err_val = switch (error_union.val) {
.err_name => |err_name| (try mod.intern(.{ .err = .{
.ty = ty.errorUnionSet(mod).toIntern(),
.name = err_name,
} })).toValue(),
.payload => try mod.intValue(Type.err_int, 0),
};
const payload_ty = ty.errorUnionPayload(mod);
const is_pl = val.errorUnionIsPayload(mod);
const error_val = if (!is_pl) val else try mod.intValue(Type.anyerror, 0);
const eu_layout = dg.errorUnionLayout(payload_ty);
if (!eu_layout.payload_has_bits) {
return try self.lower(Type.anyerror, error_val);
// We use the error type directly as the type.
try self.lower(err_ty, err_val);
return;
}
const payload_size = payload_ty.abiSize(mod);
const error_size = Type.anyerror.abiAlignment(mod);
const error_size = err_ty.abiSize(mod);
const ty_size = ty.abiSize(mod);
const padding = ty_size - payload_size - error_size;
const payload_val = switch (error_union.val) {
.err_name => try mod.intern(.{ .undef = payload_ty.ip_index }),
.err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }),
.payload => |payload| payload,
}.toValue();
if (eu_layout.error_first) {
try self.lower(Type.anyerror, error_val);
try self.lower(err_ty, err_val);
try self.lower(payload_ty, payload_val);
} else {
try self.lower(payload_ty, payload_val);
try self.lower(Type.anyerror, error_val);
try self.lower(err_ty, err_val);
}
try self.addUndef(padding);
@ -705,9 +720,14 @@ pub const DeclGen = struct {
},
.float => try self.addFloat(ty, val),
.ptr => |ptr| {
const ptr_ty = switch (ptr.len) {
.none => ty,
else => ty.slicePtrFieldType(mod),
};
switch (ptr.addr) {
.decl => |decl| try self.addDeclRef(ty, decl),
.mut_decl => |mut_decl| try self.addDeclRef(ty, mut_decl.decl),
.decl => |decl| try self.addDeclRef(ptr_ty, decl),
.mut_decl => |mut_decl| try self.addDeclRef(ptr_ty, mut_decl.decl),
.int => |int| try self.addInt(Type.usize, int.toValue()),
else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}),
}
if (ptr.len != .none) {
@ -979,38 +999,84 @@ pub const DeclGen = struct {
/// the constant is more complicated however, it needs to be lowered to an indirect constant, which
/// is then loaded using OpLoad. Such values are loaded into the UniformConstant storage class by default.
/// This function should only be called during function code generation.
fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef {
fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef {
const mod = self.module;
const target = self.getTarget();
const result_ty_ref = try self.resolveType(ty, repr);
log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) });
var val = arg_val;
switch (mod.intern_pool.indexToKey(val.toIntern())) {
.runtime_value => |rt| val = rt.val.toValue(),
else => {},
}
log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) });
if (val.isUndef(mod)) {
return self.spv.constUndef(result_ty_ref);
}
switch (ty.zigTypeTag(mod)) {
.Int => {
switch (mod.intern_pool.indexToKey(val.toIntern())) {
.int_type,
.ptr_type,
.array_type,
.vector_type,
.opt_type,
.anyframe_type,
.error_union_type,
.simple_type,
.struct_type,
.anon_struct_type,
.union_type,
.opaque_type,
.enum_type,
.func_type,
.error_set_type,
.inferred_error_set_type,
=> unreachable, // types, not values
.undef => unreachable, // handled above
.runtime_value => unreachable, // ???
.variable,
.extern_func,
.func,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.simple_value => |simple_value| switch (simple_value) {
.undefined,
.void,
.null,
.empty_struct,
.@"unreachable",
.generic_poison,
=> unreachable, // non-runtime values
.false, .true => switch (repr) {
.direct => return try self.spv.constBool(result_ty_ref, val.toBool()),
.indirect => return try self.spv.constInt(result_ty_ref, @intFromBool(val.toBool())),
},
},
.int => {
if (ty.isSignedInt(mod)) {
return try self.spv.constInt(result_ty_ref, val.toSignedInt(mod));
} else {
return try self.spv.constInt(result_ty_ref, val.toUnsignedInt(mod));
}
},
.Bool => switch (repr) {
.direct => return try self.spv.constBool(result_ty_ref, val.toBool()),
.indirect => return try self.spv.constInt(result_ty_ref, @intFromBool(val.toBool())),
},
.Float => return switch (ty.floatBits(target)) {
.float => return switch (ty.floatBits(target)) {
16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }),
32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }),
64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }),
80, 128 => unreachable, // TODO
else => unreachable,
},
.ErrorSet => @panic("TODO"),
.ErrorUnion => @panic("TODO"),
.err => |err| {
const value = try mod.getErrorValue(err.name);
return try self.spv.constInt(result_ty_ref, value);
},
// TODO: We can handle most pointers here (decl refs etc), because now they emit an extra
// OpVariable that is not really required.
else => {
@ -1263,51 +1329,53 @@ pub const DeclGen = struct {
} });
},
.Struct => {
const struct_ty = mod.typeToStruct(ty).?;
const fields = struct_ty.fields.values();
const struct_ty = switch (mod.intern_pool.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| {
const member_types = try self.gpa.alloc(CacheRef, tuple.values.len);
defer self.gpa.free(member_types);
if (ty.isSimpleTupleOrAnonStruct(mod)) {
const member_types = try self.gpa.alloc(CacheRef, fields.len);
defer self.gpa.free(member_types);
var member_index: usize = 0;
for (tuple.types, tuple.values) |field_ty, field_val| {
if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
var member_index: usize = 0;
for (fields) |field| {
if (field.ty.ip_index != .unreachable_value or !field.ty.hasRuntimeBits(mod)) continue;
member_types[member_index] = try self.resolveType(field_ty.toType(), .indirect);
member_index += 1;
}
member_types[member_index] = try self.resolveType(field.ty, .indirect);
member_index += 1;
}
return try self.spv.resolve(.{ .struct_type = .{
.member_types = member_types[0..member_index],
} });
},
.struct_type => |struct_ty| struct_ty,
else => unreachable,
};
return try self.spv.resolve(.{ .struct_type = .{
.member_types = member_types[0..member_index],
} });
const struct_obj = mod.structPtrUnwrap(struct_ty.index).?;
if (struct_obj.layout == .Packed) {
return try self.resolveType(struct_obj.backing_int_ty, .direct);
}
if (struct_ty.layout == .Packed) {
return try self.resolveType(struct_ty.backing_int_ty, .direct);
var member_types = std.ArrayList(CacheRef).init(self.gpa);
defer member_types.deinit();
var member_names = std.ArrayList(CacheString).init(self.gpa);
defer member_names.deinit();
var it = struct_obj.runtimeFieldIterator(mod);
while (it.next()) |field_and_index| {
const field = field_and_index.field;
const index = field_and_index.index;
const field_name = mod.intern_pool.stringToSlice(struct_obj.fields.keys()[index]);
try member_types.append(try self.resolveType(field.ty, .indirect));
try member_names.append(try self.spv.resolveString(field_name));
}
const member_types = try self.gpa.alloc(CacheRef, fields.len);
defer self.gpa.free(member_types);
const member_names = try self.gpa.alloc(CacheString, fields.len);
defer self.gpa.free(member_names);
var member_index: usize = 0;
for (fields, 0..) |field, i| {
if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue;
member_types[member_index] = try self.resolveType(field.ty, .indirect);
member_names[member_index] = try self.spv.resolveString(mod.intern_pool.stringToSlice(struct_ty.fields.keys()[i]));
member_index += 1;
}
const name = mod.intern_pool.stringToSlice(try struct_ty.getFullyQualifiedName(self.module));
const name = mod.intern_pool.stringToSlice(try struct_obj.getFullyQualifiedName(self.module));
return try self.spv.resolve(.{ .struct_type = .{
.name = try self.spv.resolveString(name),
.member_types = member_types[0..member_index],
.member_names = member_names[0..member_index],
.member_types = member_types.items,
.member_names = member_names.items,
} });
},
.Optional => {
@ -2512,9 +2580,9 @@ pub const DeclGen = struct {
// just an element.
var elem_ptr_info = ptr_ty.ptrInfo(mod);
elem_ptr_info.flags.size = .One;
const elem_ptr_ty = elem_ptr_info.child.toType();
const elem_ptr_ty = try mod.intern_pool.get(mod.gpa, .{ .ptr_type = elem_ptr_info });
return try self.load(elem_ptr_ty, elem_ptr_id);
return try self.load(elem_ptr_ty.toType(), elem_ptr_id);
}
fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {

View File

@ -1,10 +1,13 @@
const std = @import("std");
const builtin = @import("builtin");
const expectEqual = std.testing.expectEqual;
const c = @cImport({
@cInclude("limits.h");
});
test "c_char signedness" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
try expectEqual(@as(c_char, c.CHAR_MIN), std.math.minInt(c_char));
try expectEqual(@as(c_char, c.CHAR_MAX), std.math.maxInt(c_char));
}

View File

@ -417,6 +417,8 @@ test "inline while with @call" {
}
test "method call as parameter type" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const S = struct {
fn foo(x: anytype, y: @TypeOf(x).Inner()) @TypeOf(y) {
return y;

View File

@ -433,6 +433,8 @@ test "dereference undefined pointer to zero-bit type" {
}
test "type pun extern struct" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const S = extern struct { f: u8 };
comptime var s = S{ .f = 123 };
@as(*u8, @ptrCast(&s)).* = 72;

View File

@ -1199,6 +1199,8 @@ test "enum tag from a local variable" {
}
test "auto-numbered enum with signed tag type" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const E = enum(i32) { a, b };
try std.testing.expectEqual(@as(i32, 0), @intFromEnum(E.a));

View File

@ -297,6 +297,8 @@ test "@min/@max notices bounds from vector types when element of comptime-known
}
test "@min/@max of signed and unsigned runtime integers" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
var x: i32 = -1;
var y: u31 = 1;

View File

@ -33,6 +33,7 @@ test "@ptrFromInt creates null pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const ptr = @as(?*u32, @ptrFromInt(0));
try expectEqual(@as(?*u32, null), ptr);
@ -42,6 +43,7 @@ test "@ptrFromInt creates allowzero zero pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const ptr = @as(*allowzero u32, @ptrFromInt(0));
try expectEqual(@as(usize, 0), @intFromPtr(ptr));