authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-08 02:44:52+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-15 14:00:04+02:00
logd2692af8e2c6e4397f9be94b2b6625ba0e6e725f
tree305ecdd1eb88c38dfe9b51cf279c232f8d1f5453
parentae3efab2260ce84fa25dd874e0c193d919eb80c1
signaturebadge-check Signed by SSH key SHA256:CQ99aPxq+RueiL9u7z0FEki5Fm7V6T8q4PrEGmINrA4

spirv: override function return type to void if it has no runtime bits


2 files changed, 57 insertions(+), 22 deletions(-)

src/codegen/spirv.zig+57-18
...@@ -1180,6 +1180,22 @@ const DeclGen = struct {...@@ -1180,6 +1180,22 @@ const DeclGen = struct {
1180 return ty_ref;1180 return ty_ref;
1181 }1181 }
11821182
1183 fn resolveFnReturnType(self: *DeclGen, ret_ty: Type) !CacheRef {
1184 const mod = self.module;
1185 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
1186 // If the return type is an error set or an error union, then we make this
1187 // anyerror return type instead, so that it can be coerced into a function
1188 // pointer type which has anyerror as the return type.
1189 if (ret_ty.isError(mod)) {
1190 return self.resolveType(Type.anyerror, .direct);
1191 } else {
1192 return self.resolveType(Type.void, .direct);
1193 }
1194 }
1195
1196 return try self.resolveType(ret_ty, .direct);
1197 }
1198
1183 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.1199 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
1184 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef {1200 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef {
1185 const mod = self.module;1201 const mod = self.module;
...@@ -1260,7 +1276,7 @@ const DeclGen = struct {...@@ -1260,7 +1276,7 @@ const DeclGen = struct {
1260 param_ty_refs[param_index] = try self.resolveType(param_ty, .direct);1276 param_ty_refs[param_index] = try self.resolveType(param_ty, .direct);
1261 param_index += 1;1277 param_index += 1;
1262 }1278 }
1263 const return_ty_ref = try self.resolveType(fn_info.return_type.toType(), .direct);1279 const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType());
12641280
1265 const ty_ref = try self.spv.resolve(.{ .function_type = .{1281 const ty_ref = try self.spv.resolve(.{ .function_type = .{
1266 .return_type = return_ty_ref,1282 .return_type = return_ty_ref,
...@@ -1449,9 +1465,11 @@ const DeclGen = struct {...@@ -1449,9 +1465,11 @@ const DeclGen = struct {
1449 return ty_ref;1465 return ty_ref;
1450 },1466 },
1451 .Opaque => {1467 .Opaque => {
1452 return try self.spv.resolve(.{ .opaque_type = .{1468 return try self.spv.resolve(.{
1453 .name = .none, // TODO1469 .opaque_type = .{
1454 } });1470 .name = .none, // TODO
1471 },
1472 });
1455 },1473 },
14561474
1457 .Null,1475 .Null,
...@@ -1677,16 +1695,17 @@ const DeclGen = struct {...@@ -1677,16 +1695,17 @@ const DeclGen = struct {
16771695
1678 if (decl.val.getFunction(mod)) |_| {1696 if (decl.val.getFunction(mod)) |_| {
1679 assert(decl.ty.zigTypeTag(mod) == .Fn);1697 assert(decl.ty.zigTypeTag(mod) == .Fn);
1698 const fn_info = mod.typeToFunc(decl.ty).?;
1699 const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType());
1700
1680 const prototype_id = try self.resolveTypeId(decl.ty);1701 const prototype_id = try self.resolveTypeId(decl.ty);
1681 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{1702 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
1682 .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType(mod)),1703 .id_result_type = self.typeId(return_ty_ref),
1683 .id_result = decl_id,1704 .id_result = decl_id,
1684 .function_control = .{}, // TODO: We can set inline here if the type requires it.1705 .function_control = .{}, // TODO: We can set inline here if the type requires it.
1685 .function_type = prototype_id,1706 .function_type = prototype_id,
1686 });1707 });
16871708
1688 const fn_info = mod.typeToFunc(decl.ty).?;
1689
1690 try self.args.ensureUnusedCapacity(self.gpa, fn_info.param_types.len);1709 try self.args.ensureUnusedCapacity(self.gpa, fn_info.param_types.len);
1691 for (fn_info.param_types.get(ip)) |param_ty_index| {1710 for (fn_info.param_types.get(ip)) |param_ty_index| {
1692 const param_ty = param_ty_index.toType();1711 const param_ty = param_ty_index.toType();
...@@ -3385,15 +3404,25 @@ const DeclGen = struct {...@@ -3385,15 +3404,25 @@ const DeclGen = struct {
33853404
3386 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {3405 fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
3387 const operand = self.air.instructions.items(.data)[inst].un_op;3406 const operand = self.air.instructions.items(.data)[inst].un_op;
3388 const operand_ty = self.typeOf(operand);3407 const ret_ty = self.typeOf(operand);
3389 const mod = self.module;3408 const mod = self.module;
3390 if (operand_ty.hasRuntimeBits(mod)) {3409 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3391 // TODO: If we return an empty struct, this branch is also hit incorrectly.3410 const decl = mod.declPtr(self.decl_index);
3392 const operand_id = try self.resolve(operand);3411 const fn_info = mod.typeToFunc(decl.ty).?;
3393 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id });3412 if (fn_info.return_type.toType().isError(mod)) {
3394 } else {3413 // Functions with an empty error set are emitted with an error code
3395 try self.func.body.emit(self.spv.gpa, .OpReturn, {});3414 // return type and return zero so they can be function pointers coerced
3415 // to functions that return anyerror.
3416 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
3417 const no_err_id = try self.constInt(err_ty_ref, 0);
3418 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
3419 } else {
3420 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
3421 }
3396 }3422 }
3423
3424 const operand_id = try self.resolve(operand);
3425 try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id });
3397 }3426 }
33983427
3399 fn airRetLoad(self: *DeclGen, inst: Air.Inst.Index) !void {3428 fn airRetLoad(self: *DeclGen, inst: Air.Inst.Index) !void {
...@@ -3403,8 +3432,18 @@ const DeclGen = struct {...@@ -3403,8 +3432,18 @@ const DeclGen = struct {
3403 const ret_ty = ptr_ty.childType(mod);3432 const ret_ty = ptr_ty.childType(mod);
34043433
3405 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {3434 if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3406 try self.func.body.emit(self.spv.gpa, .OpReturn, {});3435 const decl = mod.declPtr(self.decl_index);
3407 return;3436 const fn_info = mod.typeToFunc(decl.ty).?;
3437 if (fn_info.return_type.toType().isError(mod)) {
3438 // Functions with an empty error set are emitted with an error code
3439 // return type and return zero so they can be function pointers coerced
3440 // to functions that return anyerror.
3441 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
3442 const no_err_id = try self.constInt(err_ty_ref, 0);
3443 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
3444 } else {
3445 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
3446 }
3408 }3447 }
34093448
3410 const ptr = try self.resolve(un_op);3449 const ptr = try self.resolve(un_op);
...@@ -3991,7 +4030,7 @@ const DeclGen = struct {...@@ -3991,7 +4030,7 @@ const DeclGen = struct {
3991 const fn_info = mod.typeToFunc(zig_fn_ty).?;4030 const fn_info = mod.typeToFunc(zig_fn_ty).?;
3992 const return_type = fn_info.return_type;4031 const return_type = fn_info.return_type;
39934032
3994 const result_type_id = try self.resolveTypeId(return_type.toType());4033 const result_type_ref = try self.resolveFnReturnType(return_type.toType());
3995 const result_id = self.spv.allocId();4034 const result_id = self.spv.allocId();
3996 const callee_id = try self.resolve(pl_op.operand);4035 const callee_id = try self.resolve(pl_op.operand);
39974036
...@@ -4012,7 +4051,7 @@ const DeclGen = struct {...@@ -4012,7 +4051,7 @@ const DeclGen = struct {
4012 }4051 }
40134052
4014 try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{4053 try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{
4015 .id_result_type = result_type_id,4054 .id_result_type = self.typeId(result_type_ref),
4016 .id_result = result_id,4055 .id_result = result_id,
4017 .function = callee_id,4056 .function = callee_id,
4018 .id_ref_3 = params[0..n_params],4057 .id_ref_3 = params[0..n_params],
test/behavior/optional.zig-4
...@@ -27,7 +27,6 @@ pub const EmptyStruct = struct {};...@@ -27,7 +27,6 @@ pub const EmptyStruct = struct {};
2727
28test "optional pointer to size zero struct" {28test "optional pointer to size zero struct" {
29 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO29 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
30 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
3130
32 var e = EmptyStruct{};31 var e = EmptyStruct{};
33 var o: ?*EmptyStruct = &e;32 var o: ?*EmptyStruct = &e;
...@@ -152,7 +151,6 @@ fn test_cmp_optional_non_optional() !void {...@@ -152,7 +151,6 @@ fn test_cmp_optional_non_optional() !void {
152test "unwrap function call with optional pointer return value" {151test "unwrap function call with optional pointer return value" {
153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO152 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO153 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
155 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
156154
157 const S = struct {155 const S = struct {
158 fn entry() !void {156 fn entry() !void {
...@@ -400,7 +398,6 @@ const NoReturn = struct {...@@ -400,7 +398,6 @@ const NoReturn = struct {
400398
401test "optional of noreturn used with if" {399test "optional of noreturn used with if" {
402 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO400 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
403 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
404401
405 NoReturn.a = 64;402 NoReturn.a = 64;
406 if (NoReturn.loop()) |_| {403 if (NoReturn.loop()) |_| {
...@@ -412,7 +409,6 @@ test "optional of noreturn used with if" {...@@ -412,7 +409,6 @@ test "optional of noreturn used with if" {
412409
413test "optional of noreturn used with orelse" {410test "optional of noreturn used with orelse" {
414 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO411 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
415 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
416412
417 NoReturn.a = 64;413 NoReturn.a = 64;
418 const val = NoReturn.testOrelse();414 const val = NoReturn.testOrelse();