| ... | ... | @@ -272,7 +272,7 @@ pub const DeclGen = struct { |
| 272 | 272 | |
| 273 | 273 | if (!entry.found_existing) { |
| 274 | 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 | 276 | } else { |
| 277 | 277 | entry.value_ptr.* = .{ .global = try self.spv.allocGlobal() }; |
| 278 | 278 | } |
| ... | ... | @@ -418,11 +418,7 @@ pub const DeclGen = struct { |
| 418 | 418 | |
| 419 | 419 | fn genUndef(self: *DeclGen, ty_ref: SpvType.Ref) Error!IdRef { |
| 420 | 420 | const result_id = self.spv.allocId(); |
| 421 | | try self.spv.sections.types_globals_constants.emit( |
| 422 | | self.spv.gpa, |
| 423 | | .OpUndef, |
| 424 | | .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id } |
| 425 | | ); |
| 421 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id }); |
| 426 | 422 | return result_id; |
| 427 | 423 | } |
| 428 | 424 | |
| ... | ... | @@ -899,11 +895,12 @@ pub const DeclGen = struct { |
| 899 | 895 | .initializer = constant_struct_id, |
| 900 | 896 | }); |
| 901 | 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 | 900 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ |
| 904 | 901 | .id_result_type = self.typeId(ptr_ty_ref), |
| 905 | 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 | 1264 | // Similar to unions, we're going to put the most aligned member first. |
| 1268 | 1265 | if (error_align > payload_align) { |
| 1269 | 1266 | // Put the error first |
| 1270 | | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1271 | | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1267 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1268 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1272 | 1269 | // TODO: ABI padding? |
| 1273 | 1270 | } else { |
| 1274 | 1271 | // Put the payload first. |
| 1275 | | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1276 | | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1272 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| 1273 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1277 | 1274 | // TODO: ABI padding? |
| 1278 | 1275 | } |
| 1279 | 1276 | |
| ... | ... | @@ -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 | 1371 | fn genDecl(self: *DeclGen) !void { |
| 1306 | 1372 | const decl = self.module.declPtr(self.decl_index); |
| 1307 | 1373 | const link = try self.resolveDecl(self.decl_index); |
| 1308 | 1374 | |
| 1309 | 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 | 1378 | assert(decl.ty.zigTypeTag() == .Fn); |
| 1313 | 1379 | const prototype_id = try self.resolveTypeId(decl.ty); |
| ... | ... | @@ -1356,6 +1422,10 @@ pub const DeclGen = struct { |
| 1356 | 1422 | .target = link.func.result_id, |
| 1357 | 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 | 1429 | } else { |
| 1360 | 1430 | const init_val = if (decl.val.castTag(.variable)) |payload| |
| 1361 | 1431 | payload.data.init |
| ... | ... | @@ -1396,6 +1466,7 @@ pub const DeclGen = struct { |
| 1396 | 1466 | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1397 | 1467 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| 1398 | 1468 | // TODO: Can we eliminate this cast? |
| 1469 | // TODO: Const-wash pointer |
| 1399 | 1470 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1400 | 1471 | .id_result_type = self.typeId(ptr_ty_ref), |
| 1401 | 1472 | .id_result = global_result_id, |
| ... | ... | @@ -2036,6 +2107,24 @@ pub const DeclGen = struct { |
| 2036 | 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 | 2128 | fn variable( |
| 2040 | 2129 | self: *DeclGen, |
| 2041 | 2130 | comptime context: enum { function, global }, |
| ... | ... | @@ -2088,11 +2177,14 @@ pub const DeclGen = struct { |
| 2088 | 2177 | .pointer = alloc_result_id, |
| 2089 | 2178 | }), |
| 2090 | 2179 | // TODO: Can we do without this cast or move it to runtime? |
| 2091 | | else => try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2092 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 2093 | | .id_result = result_id, |
| 2094 | | .pointer = alloc_result_id, |
| 2095 | | }), |
| 2180 | else => { |
| 2181 | const const_ptr_id = try self.makePointerConstant(section, actual_ptr_ty_ref, alloc_result_id); |
| 2182 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2183 | .id_result_type = self.typeId(ptr_ty_ref), |
| 2184 | .id_result = result_id, |
| 2185 | .pointer = const_ptr_id, |
| 2186 | }); |
| 2187 | }, |
| 2096 | 2188 | } |
| 2097 | 2189 | } |
| 2098 | 2190 | |