| ... | @@ -272,7 +272,7 @@ pub const DeclGen = struct { | ... | @@ -272,7 +272,7 @@ pub const DeclGen = struct { |
| 272 | | 272 | |
| 273 | if (!entry.found_existing) { | 273 | if (!entry.found_existing) { |
| 274 | if (decl.val.castTag(.function)) |_| { | 274 | if (decl.val.castTag(.function)) |_| { |
| 275 | entry.value_ptr.* = .{.func = .{ .result_id = result_id }}; | 275 | entry.value_ptr.* = .{ .func = .{ .result_id = result_id } }; |
| 276 | } else { | 276 | } else { |
| 277 | entry.value_ptr.* = .{ .global = try self.spv.allocGlobal() }; | 277 | entry.value_ptr.* = .{ .global = try self.spv.allocGlobal() }; |
| 278 | } | 278 | } |
| ... | @@ -418,11 +418,7 @@ pub const DeclGen = struct { | ... | @@ -418,11 +418,7 @@ pub const DeclGen = struct { |
| 418 | | 418 | |
| 419 | fn genUndef(self: *DeclGen, ty_ref: SpvType.Ref) Error!IdRef { | 419 | fn genUndef(self: *DeclGen, ty_ref: SpvType.Ref) Error!IdRef { |
| 420 | const result_id = self.spv.allocId(); | 420 | const result_id = self.spv.allocId(); |
| 421 | try self.spv.sections.types_globals_constants.emit( | 421 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id }); |
| 422 | self.spv.gpa, | | |
| 423 | .OpUndef, | | |
| 424 | .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id } | | |
| 425 | ); | | |
| 426 | return result_id; | 422 | return result_id; |
| 427 | } | 423 | } |
| 428 | | 424 | |
| ... | @@ -899,11 +895,12 @@ pub const DeclGen = struct { | ... | @@ -899,11 +895,12 @@ pub const DeclGen = struct { |
| 899 | .initializer = constant_struct_id, | 895 | .initializer = constant_struct_id, |
| 900 | }); | 896 | }); |
| 901 | // TODO: Set alignment of OpVariable. | 897 | // TODO: Set alignment of OpVariable. |
| 902 | // TODO: We may be able to eliminate this cast. | 898 | // TODO: We may be able to eliminate these casts. |
| | 899 | const const_ptr_id = try self.makePointerConstant(section, ptr_constant_struct_ty_ref, var_id); |
| 903 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ | 900 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ |
| 904 | .id_result_type = self.typeId(ptr_ty_ref), | 901 | .id_result_type = self.typeId(ptr_ty_ref), |
| 905 | .id_result = result_id, | 902 | .id_result = result_id, |
| 906 | .operand = var_id, | 903 | .operand = const_ptr_id, |
| 907 | }); | 904 | }); |
| 908 | } | 905 | } |
| 909 | | 906 | |
| ... | @@ -1267,13 +1264,13 @@ pub const DeclGen = struct { | ... | @@ -1267,13 +1264,13 @@ pub const DeclGen = struct { |
| 1267 | // Similar to unions, we're going to put the most aligned member first. | 1264 | // Similar to unions, we're going to put the most aligned member first. |
| 1268 | if (error_align > payload_align) { | 1265 | if (error_align > payload_align) { |
| 1269 | // Put the error first | 1266 | // Put the error first |
| 1270 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); | 1267 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1271 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); | 1268 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1272 | // TODO: ABI padding? | 1269 | // TODO: ABI padding? |
| 1273 | } else { | 1270 | } else { |
| 1274 | // Put the payload first. | 1271 | // Put the payload first. |
| 1275 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); | 1272 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1276 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); | 1273 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1277 | // TODO: ABI padding? | 1274 | // TODO: ABI padding? |
| 1278 | } | 1275 | } |
| 1279 | | 1276 | |
| ... | @@ -1302,12 +1299,81 @@ pub const DeclGen = struct { | ... | @@ -1302,12 +1299,81 @@ pub const DeclGen = struct { |
| 1302 | }; | 1299 | }; |
| 1303 | } | 1300 | } |
| 1304 | | 1301 | |
| | 1302 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. |
| | 1303 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- |
| | 1304 | /// points. The test executor will then be able to invoke these to run the tests. |
| | 1305 | /// Note that tests are lowered according to std.builtin.TestFn, which is `fn () anyerror!void`. |
| | 1306 | /// (anyerror!void has the same layout as anyerror). |
| | 1307 | /// Each test declaration generates a function like. |
| | 1308 | /// %anyerror = OpTypeInt 0 16 |
| | 1309 | /// %p_anyerror = OpTypePointer CrossWorkgroup %anyerror |
| | 1310 | /// %K = OpTypeFunction %void %p_anyerror |
| | 1311 | /// |
| | 1312 | /// %test = OpFunction %void %K |
| | 1313 | /// %p_err = OpFunctionParameter %p_anyerror |
| | 1314 | /// %lbl = OpLabel |
| | 1315 | /// %result = OpFunctionCall %anyerror %func |
| | 1316 | /// OpStore %p_err %result |
| | 1317 | /// OpFunctionEnd |
| | 1318 | /// TODO is to also write out the error as a function call parameter, and to somehow fetch |
| | 1319 | /// the name of an error in the text executor. |
| | 1320 | fn generateTestEntryPoint(self: *DeclGen, name: []const u8, func: IdResult) !void { |
| | 1321 | const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct); |
| | 1322 | const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup, null); |
| | 1323 | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| | 1324 | |
| | 1325 | const kernel_proto_ty_ref = blk: { |
| | 1326 | const proto_payload = try self.spv.arena.create(SpvType.Payload.Function); |
| | 1327 | proto_payload.* = .{ |
| | 1328 | .return_type = void_ty_ref, |
| | 1329 | .parameters = try self.spv.arena.dupe(SpvType.Ref, &.{ptr_anyerror_ty_ref}), |
| | 1330 | }; |
| | 1331 | break :blk try self.spv.resolveType(SpvType.initPayload(&proto_payload.base)); |
| | 1332 | }; |
| | 1333 | |
| | 1334 | const kernel_id = self.spv.allocId(); |
| | 1335 | const error_id = self.spv.allocId(); |
| | 1336 | const p_error_id = self.spv.allocId(); |
| | 1337 | |
| | 1338 | const section = &self.spv.sections.functions; |
| | 1339 | try section.emit(self.spv.gpa, .OpFunction, .{ |
| | 1340 | .id_result_type = self.typeId(void_ty_ref), |
| | 1341 | .id_result = kernel_id, |
| | 1342 | .function_control = .{}, |
| | 1343 | .function_type = self.typeId(kernel_proto_ty_ref), |
| | 1344 | }); |
| | 1345 | try section.emit(self.spv.gpa, .OpFunctionParameter, .{ |
| | 1346 | .id_result_type = self.typeId(ptr_anyerror_ty_ref), |
| | 1347 | .id_result = p_error_id, |
| | 1348 | }); |
| | 1349 | try section.emit(self.spv.gpa, .OpLabel, .{ |
| | 1350 | .id_result = self.spv.allocId(), |
| | 1351 | }); |
| | 1352 | try section.emit(self.spv.gpa, .OpFunctionCall, .{ |
| | 1353 | .id_result_type = self.typeId(anyerror_ty_ref), |
| | 1354 | .id_result = error_id, |
| | 1355 | .function = func, |
| | 1356 | }); |
| | 1357 | try section.emit(self.spv.gpa, .OpStore, .{ |
| | 1358 | .pointer = p_error_id, |
| | 1359 | .object = error_id, |
| | 1360 | }); |
| | 1361 | try section.emit(self.spv.gpa, .OpReturn, {}); |
| | 1362 | try section.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| | 1363 | |
| | 1364 | try self.spv.sections.entry_points.emit(self.spv.gpa, .OpEntryPoint, .{ |
| | 1365 | .execution_model = .Kernel, |
| | 1366 | .entry_point = kernel_id, |
| | 1367 | .name = name, |
| | 1368 | }); |
| | 1369 | } |
| | 1370 | |
| 1305 | fn genDecl(self: *DeclGen) !void { | 1371 | fn genDecl(self: *DeclGen) !void { |
| 1306 | const decl = self.module.declPtr(self.decl_index); | 1372 | const decl = self.module.declPtr(self.decl_index); |
| 1307 | const link = try self.resolveDecl(self.decl_index); | 1373 | const link = try self.resolveDecl(self.decl_index); |
| 1308 | | 1374 | |
| 1309 | if (decl.val.castTag(.function)) |_| { | 1375 | if (decl.val.castTag(.function)) |_| { |
| 1310 | log.debug("genDecl function {s} = {}", .{decl.name, link.func.result_id.id}); | 1376 | log.debug("genDecl function {s} = {}", .{ decl.name, link.func.result_id.id }); |
| 1311 | | 1377 | |
| 1312 | assert(decl.ty.zigTypeTag() == .Fn); | 1378 | assert(decl.ty.zigTypeTag() == .Fn); |
| 1313 | const prototype_id = try self.resolveTypeId(decl.ty); | 1379 | const prototype_id = try self.resolveTypeId(decl.ty); |
| ... | @@ -1356,6 +1422,10 @@ pub const DeclGen = struct { | ... | @@ -1356,6 +1422,10 @@ pub const DeclGen = struct { |
| 1356 | .target = link.func.result_id, | 1422 | .target = link.func.result_id, |
| 1357 | .name = fqn, | 1423 | .name = fqn, |
| 1358 | }); | 1424 | }); |
| | 1425 | |
| | 1426 | if (self.module.test_functions.contains(self.decl_index)) { |
| | 1427 | try self.generateTestEntryPoint(fqn, link.func.result_id); |
| | 1428 | } |
| 1359 | } else { | 1429 | } else { |
| 1360 | const init_val = if (decl.val.castTag(.variable)) |payload| | 1430 | const init_val = if (decl.val.castTag(.variable)) |payload| |
| 1361 | payload.data.init | 1431 | payload.data.init |
| ... | @@ -1396,6 +1466,7 @@ pub const DeclGen = struct { | ... | @@ -1396,6 +1466,7 @@ pub const DeclGen = struct { |
| 1396 | const ty_ref = try self.resolveType(decl.ty, .indirect); | 1466 | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1397 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); | 1467 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| 1398 | // TODO: Can we eliminate this cast? | 1468 | // TODO: Can we eliminate this cast? |
| | 1469 | // TODO: Const-wash pointer |
| 1399 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | 1470 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1400 | .id_result_type = self.typeId(ptr_ty_ref), | 1471 | .id_result_type = self.typeId(ptr_ty_ref), |
| 1401 | .id_result = global_result_id, | 1472 | .id_result = global_result_id, |
| ... | @@ -2036,6 +2107,24 @@ pub const DeclGen = struct { | ... | @@ -2036,6 +2107,24 @@ pub const DeclGen = struct { |
| 2036 | return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); | 2107 | return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); |
| 2037 | } | 2108 | } |
| 2038 | | 2109 | |
| | 2110 | /// We cannot use an OpVariable directly in an OpSpecConstantOp, but we can |
| | 2111 | /// after we insert a dummy AccessChain... |
| | 2112 | /// TODO: Get rid of this |
| | 2113 | fn makePointerConstant( |
| | 2114 | self: *DeclGen, |
| | 2115 | section: *SpvSection, |
| | 2116 | ptr_ty_ref: SpvType.Ref, |
| | 2117 | ptr_id: IdRef, |
| | 2118 | ) !IdRef { |
| | 2119 | const result_id = self.spv.allocId(); |
| | 2120 | try section.emitSpecConstantOp(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| | 2121 | .id_result_type = self.typeId(ptr_ty_ref), |
| | 2122 | .id_result = result_id, |
| | 2123 | .base = ptr_id, |
| | 2124 | }); |
| | 2125 | return result_id; |
| | 2126 | } |
| | 2127 | |
| 2039 | fn variable( | 2128 | fn variable( |
| 2040 | self: *DeclGen, | 2129 | self: *DeclGen, |
| 2041 | comptime context: enum { function, global }, | 2130 | comptime context: enum { function, global }, |
| ... | @@ -2088,11 +2177,14 @@ pub const DeclGen = struct { | ... | @@ -2088,11 +2177,14 @@ pub const DeclGen = struct { |
| 2088 | .pointer = alloc_result_id, | 2177 | .pointer = alloc_result_id, |
| 2089 | }), | 2178 | }), |
| 2090 | // TODO: Can we do without this cast or move it to runtime? | 2179 | // TODO: Can we do without this cast or move it to runtime? |
| 2091 | else => try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | 2180 | else => { |
| 2092 | .id_result_type = self.typeId(ptr_ty_ref), | 2181 | const const_ptr_id = try self.makePointerConstant(section, actual_ptr_ty_ref, alloc_result_id); |
| 2093 | .id_result = result_id, | 2182 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2094 | .pointer = alloc_result_id, | 2183 | .id_result_type = self.typeId(ptr_ty_ref), |
| 2095 | }), | 2184 | .id_result = result_id, |
| | 2185 | .pointer = const_ptr_id, |
| | 2186 | }); |
| | 2187 | }, |
| 2096 | } | 2188 | } |
| 2097 | } | 2189 | } |
| 2098 | | 2190 | |