authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-16 00:53:39+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 12:36:44-07:00
logece52640ebeac867e0f69116f99bc14f5590bb9a
treeadb1d60ec933acd300c9721a8317549e99cf2299
parentced8a2c3a650fdddc489f97a1d12dd029856fe9e

spirv: construct error union at runtime


1 files changed, 49 insertions(+), 7 deletions(-)

src/codegen/spirv.zig+49-7
...@@ -1021,9 +1021,10 @@ pub const DeclGen = struct {...@@ -1021,9 +1021,10 @@ pub const DeclGen = struct {
1021 }1021 }
10221022
1023 /// This function generates a load for a constant in direct (ie, non-memory) representation.1023 /// This function generates a load for a constant in direct (ie, non-memory) representation.
1024 /// When the constant is simple, it can be generated directly using OpConstant instructions. When1024 /// When the constant is simple, it can be generated directly using OpConstant instructions.
1025 /// the constant is more complicated however, it needs to be lowered to an indirect constant, which1025 /// When the constant is more complicated however, it needs to be constructed using multiple values. This
1026 /// is then loaded using OpLoad. Such values are loaded into the UniformConstant storage class by default.1026 /// is done by emitting a sequence of instructions that initialize the value.
1027 //
1027 /// This function should only be called during function code generation.1028 /// This function should only be called during function code generation.
1028 fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef {1029 fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef {
1029 const mod = self.module;1030 const mod = self.module;
...@@ -1037,7 +1038,7 @@ pub const DeclGen = struct {...@@ -1037,7 +1038,7 @@ pub const DeclGen = struct {
1037 }1038 }
10381039
1039 log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) });1040 log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) });
1040 if (val.isUndef(mod)) {1041 if (val.isUndefDeep(mod)) {
1041 return self.spv.constUndef(result_ty_ref);1042 return self.spv.constUndef(result_ty_ref);
1042 }1043 }
10431044
...@@ -1060,8 +1061,7 @@ pub const DeclGen = struct {...@@ -1060,8 +1061,7 @@ pub const DeclGen = struct {
1060 .inferred_error_set_type,1061 .inferred_error_set_type,
1061 => unreachable, // types, not values1062 => unreachable, // types, not values
10621063
1063 .undef => unreachable, // handled above1064 .undef, .runtime_value => unreachable, // handled above
1064 .runtime_value => unreachable, // ???
10651065
1066 .variable,1066 .variable,
1067 .extern_func,1067 .extern_func,
...@@ -1103,6 +1103,49 @@ pub const DeclGen = struct {...@@ -1103,6 +1103,49 @@ pub const DeclGen = struct {
1103 const value = try mod.getErrorValue(err.name);1103 const value = try mod.getErrorValue(err.name);
1104 return try self.spv.constInt(result_ty_ref, value);1104 return try self.spv.constInt(result_ty_ref, value);
1105 },1105 },
1106 .error_union => |error_union| {
1107 // TODO: Error unions may be constructed with constant instructions if the payload type
1108 // allows it. For now, just generate it here regardless.
1109 const err_ty = switch (error_union.val) {
1110 .err_name => ty.errorUnionSet(mod),
1111 .payload => Type.err_int,
1112 };
1113 const err_val = switch (error_union.val) {
1114 .err_name => |err_name| (try mod.intern(.{ .err = .{
1115 .ty = ty.errorUnionSet(mod).toIntern(),
1116 .name = err_name,
1117 } })).toValue(),
1118 .payload => try mod.intValue(Type.err_int, 0),
1119 };
1120 const payload_ty = ty.errorUnionPayload(mod);
1121 const eu_layout = self.errorUnionLayout(payload_ty);
1122 if (!eu_layout.payload_has_bits) {
1123 // We use the error type directly as the type.
1124 return try self.constant(err_ty, err_val, .indirect);
1125 }
1126
1127 const payload_val = switch (error_union.val) {
1128 .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }),
1129 .payload => |payload| payload,
1130 }.toValue();
1131
1132 var constituents: [2]IdRef = undefined;
1133 if (eu_layout.error_first) {
1134 constituents[0] = try self.constant(err_ty, err_val, .indirect);
1135 constituents[1] = try self.constant(payload_ty, payload_val, .indirect);
1136 } else {
1137 constituents[0] = try self.constant(payload_ty, payload_val, .indirect);
1138 constituents[1] = try self.constant(err_ty, err_val, .indirect);
1139 }
1140
1141 const result_id = self.spv.allocId();
1142 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
1143 .id_result_type = self.typeId(result_ty_ref),
1144 .id_result = result_id,
1145 .constituents = &constituents,
1146 });
1147 return result_id;
1148 },
1106 // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra1149 // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra
1107 // OpVariable that is not really required.1150 // OpVariable that is not really required.
1108 else => {1151 else => {
...@@ -1316,7 +1359,6 @@ pub const DeclGen = struct {...@@ -1316,7 +1359,6 @@ pub const DeclGen = struct {
1316 const entry = try self.type_map.getOrPut(self.gpa, ty.toIntern());1359 const entry = try self.type_map.getOrPut(self.gpa, ty.toIntern());
1317 if (entry.found_existing) return entry.value_ptr.ty_ref;1360 if (entry.found_existing) return entry.value_ptr.ty_ref;
13181361
1319 const ip = &mod.intern_pool;
1320 const fn_info = mod.typeToFunc(ty).?;1362 const fn_info = mod.typeToFunc(ty).?;
1321 // TODO: Put this somewhere in Sema.zig1363 // TODO: Put this somewhere in Sema.zig
1322 if (fn_info.is_var_args)1364 if (fn_info.is_var_args)