| ... | ... | @@ -19,6 +19,7 @@ const Word = spec.Word; |
| 19 | 19 | const IdRef = spec.IdRef; |
| 20 | 20 | const IdResult = spec.IdResult; |
| 21 | 21 | const IdResultType = spec.IdResultType; |
| 22 | const StorageClass = spec.StorageClass; |
| 22 | 23 | |
| 23 | 24 | const SpvModule = @import("spirv/Module.zig"); |
| 24 | 25 | const SpvSection = @import("spirv/Section.zig"); |
| ... | ... | @@ -37,23 +38,8 @@ const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| 37 | 38 | incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), |
| 38 | 39 | }); |
| 39 | 40 | |
| 40 | | /// Linking information about a particular decl. |
| 41 | | /// The active field of this enum depends on the type of the corresponding decl. |
| 42 | | const DeclLink = union { |
| 43 | | /// Linking information about a function. |
| 44 | | /// Active when the decl is a function. |
| 45 | | func: struct { |
| 46 | | /// Result-id of the OpFunction instruction. |
| 47 | | result_id: IdResult, |
| 48 | | }, |
| 49 | | /// Linking information about a global. This index points into the |
| 50 | | /// SPIR-V module's `globals` array. |
| 51 | | /// Active when the decl is a variable. |
| 52 | | global: SpvModule.Global.Index, |
| 53 | | }; |
| 54 | | |
| 55 | 41 | /// Maps Zig decl indices to linking SPIR-V linking information. |
| 56 | | pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, DeclLink); |
| 42 | pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index); |
| 57 | 43 | |
| 58 | 44 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. |
| 59 | 45 | pub const DeclGen = struct { |
| ... | ... | @@ -251,8 +237,8 @@ pub const DeclGen = struct { |
| 251 | 237 | .function => val.castTag(.function).?.data.owner_decl, |
| 252 | 238 | else => unreachable, |
| 253 | 239 | }; |
| 254 | | const link = try self.resolveDecl(fn_decl_index); |
| 255 | | return link.func.result_id; |
| 240 | const spv_decl_index = try self.resolveDecl(fn_decl_index); |
| 241 | return self.spv.declPtr(spv_decl_index).result_id; |
| 256 | 242 | } |
| 257 | 243 | |
| 258 | 244 | return try self.constant(ty, val); |
| ... | ... | @@ -263,19 +249,19 @@ pub const DeclGen = struct { |
| 263 | 249 | |
| 264 | 250 | /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. |
| 265 | 251 | /// Note: Function does not actually generate the decl. |
| 266 | | fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !DeclLink { |
| 252 | fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !SpvModule.Decl.Index { |
| 267 | 253 | const decl = self.module.declPtr(decl_index); |
| 268 | 254 | self.module.markDeclAlive(decl); |
| 269 | 255 | |
| 270 | 256 | const entry = try self.decl_link.getOrPut(decl_index); |
| 271 | | const result_id = self.spv.allocId(); |
| 272 | | |
| 273 | 257 | if (!entry.found_existing) { |
| 274 | | if (decl.val.castTag(.function)) |_| { |
| 275 | | entry.value_ptr.* = .{ .func = .{ .result_id = result_id } }; |
| 276 | | } else { |
| 277 | | entry.value_ptr.* = .{ .global = try self.spv.allocGlobal() }; |
| 278 | | } |
| 258 | // TODO: Extern fn? |
| 259 | const kind: SpvModule.DeclKind = if (decl.val.tag() == .function) |
| 260 | .func |
| 261 | else |
| 262 | .global; |
| 263 | |
| 264 | entry.value_ptr.* = try self.spv.allocDecl(kind); |
| 279 | 265 | } |
| 280 | 266 | |
| 281 | 267 | return entry.value_ptr.*; |
| ... | ... | @@ -440,6 +426,8 @@ pub const DeclGen = struct { |
| 440 | 426 | /// The partially filled last constant. |
| 441 | 427 | /// If full, its flushed. |
| 442 | 428 | partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{}, |
| 429 | /// The declaration dependencies of the constant we are lowering. |
| 430 | decl_deps: std.ArrayList(SpvModule.Decl.Index), |
| 443 | 431 | |
| 444 | 432 | /// Utility function to get the section that instructions should be lowered to. |
| 445 | 433 | fn section(self: *@This()) *SpvSection { |
| ... | ... | @@ -554,7 +542,7 @@ pub const DeclGen = struct { |
| 554 | 542 | const ty_id = dg.typeId(ty_ref); |
| 555 | 543 | |
| 556 | 544 | const decl = dg.module.declPtr(decl_index); |
| 557 | | const link = try dg.resolveDecl(decl_index); |
| 545 | const spv_decl_index = try dg.resolveDecl(decl_index); |
| 558 | 546 | |
| 559 | 547 | switch (decl.val.tag()) { |
| 560 | 548 | .function => { |
| ... | ... | @@ -569,14 +557,15 @@ pub const DeclGen = struct { |
| 569 | 557 | const result_id = dg.spv.allocId(); |
| 570 | 558 | log.debug("addDeclRef {s} = {}", .{ decl.name, result_id.id }); |
| 571 | 559 | |
| 572 | | const global = dg.spv.globalPtr(link.global); |
| 573 | | try dg.spv.addGlobalDependency(link.global); |
| 560 | try self.decl_deps.append(spv_decl_index); |
| 561 | |
| 562 | const decl_id = dg.spv.declPtr(spv_decl_index).result_id; |
| 574 | 563 | // TODO: Do we need a storage class cast here? |
| 575 | 564 | // TODO: We can probably eliminate these casts |
| 576 | 565 | try dg.spv.globals.section.emitSpecConstantOp(dg.spv.gpa, .OpBitcast, .{ |
| 577 | 566 | .id_result_type = ty_id, |
| 578 | 567 | .id_result = result_id, |
| 579 | | .operand = global.result_id, |
| 568 | .operand = decl_id, |
| 580 | 569 | }); |
| 581 | 570 | |
| 582 | 571 | try self.addPtr(ty_ref, result_id); |
| ... | ... | @@ -810,10 +799,11 @@ pub const DeclGen = struct { |
| 810 | 799 | /// pointer points to. Note: result is not necessarily an OpVariable instruction! |
| 811 | 800 | fn lowerIndirectConstant( |
| 812 | 801 | self: *DeclGen, |
| 813 | | result_id: IdRef, |
| 802 | spv_decl_index: SpvModule.Decl.Index, |
| 814 | 803 | ty: Type, |
| 815 | 804 | val: Value, |
| 816 | | storage_class: spec.StorageClass, |
| 805 | storage_class: StorageClass, |
| 806 | cast_to_generic: bool, |
| 817 | 807 | alignment: u32, |
| 818 | 808 | ) Error!void { |
| 819 | 809 | // To simplify constant generation, we're going to generate constants as a word-array, and |
| ... | ... | @@ -844,23 +834,27 @@ pub const DeclGen = struct { |
| 844 | 834 | const ty_ref = try self.resolveType(ty, .indirect); |
| 845 | 835 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, alignment); |
| 846 | 836 | |
| 847 | | const target = self.getTarget(); |
| 848 | | |
| 849 | | if (val.isUndef()) { |
| 850 | | // Special case: the entire value is undefined. In this case, we can just |
| 851 | | // generate an OpVariable with no initializer. |
| 852 | | return try section.emit(self.spv.gpa, .OpVariable, .{ |
| 853 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 854 | | .id_result = result_id, |
| 855 | | .storage_class = storage_class, |
| 856 | | }); |
| 857 | | } else if (ty.abiSize(target) == 0) { |
| 858 | | // Special case: if the type has no size, then return an undefined pointer. |
| 859 | | return try section.emit(self.spv.gpa, .OpUndef, .{ |
| 860 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 861 | | .id_result = result_id, |
| 862 | | }); |
| 863 | | } |
| 837 | // const target = self.getTarget(); |
| 838 | |
| 839 | // TODO: Fix the resulting global linking for these paths. |
| 840 | // if (val.isUndef()) { |
| 841 | // // Special case: the entire value is undefined. In this case, we can just |
| 842 | // // generate an OpVariable with no initializer. |
| 843 | // return try section.emit(self.spv.gpa, .OpVariable, .{ |
| 844 | // .id_result_type = self.typeId(ptr_ty_ref), |
| 845 | // .id_result = result_id, |
| 846 | // .storage_class = storage_class, |
| 847 | // }); |
| 848 | // } else if (ty.abiSize(target) == 0) { |
| 849 | // // Special case: if the type has no size, then return an undefined pointer. |
| 850 | // return try section.emit(self.spv.gpa, .OpUndef, .{ |
| 851 | // .id_result_type = self.typeId(ptr_ty_ref), |
| 852 | // .id_result = result_id, |
| 853 | // }); |
| 854 | // } |
| 855 | |
| 856 | // TODO: Capture the above stuff in here as well... |
| 857 | const begin_inst = self.spv.beginGlobal(); |
| 864 | 858 | |
| 865 | 859 | const u32_ty_ref = try self.intType(.unsigned, 32); |
| 866 | 860 | var icl = IndirectConstantLowering{ |
| ... | ... | @@ -869,10 +863,12 @@ pub const DeclGen = struct { |
| 869 | 863 | .u32_ty_id = self.typeId(u32_ty_ref), |
| 870 | 864 | .members = std.ArrayList(SpvType.Payload.Struct.Member).init(self.gpa), |
| 871 | 865 | .initializers = std.ArrayList(IdRef).init(self.gpa), |
| 866 | .decl_deps = std.ArrayList(SpvModule.Decl.Index).init(self.gpa), |
| 872 | 867 | }; |
| 873 | 868 | |
| 874 | 869 | defer icl.members.deinit(); |
| 875 | 870 | defer icl.initializers.deinit(); |
| 871 | defer icl.decl_deps.deinit(); |
| 876 | 872 | |
| 877 | 873 | try icl.lower(ty, val); |
| 878 | 874 | try icl.flush(); |
| ... | ... | @@ -888,6 +884,7 @@ pub const DeclGen = struct { |
| 888 | 884 | }); |
| 889 | 885 | |
| 890 | 886 | const var_id = self.spv.allocId(); |
| 887 | self.spv.globalPtr(spv_decl_index).?.result_id = var_id; |
| 891 | 888 | try section.emit(self.spv.gpa, .OpVariable, .{ |
| 892 | 889 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), |
| 893 | 890 | .id_result = var_id, |
| ... | ... | @@ -896,12 +893,32 @@ pub const DeclGen = struct { |
| 896 | 893 | }); |
| 897 | 894 | // TODO: Set alignment of OpVariable. |
| 898 | 895 | // TODO: We may be able to eliminate these casts. |
| 896 | |
| 899 | 897 | const const_ptr_id = try self.makePointerConstant(section, ptr_constant_struct_ty_ref, var_id); |
| 898 | const result_id = self.spv.declPtr(spv_decl_index).result_id; |
| 899 | |
| 900 | const bitcast_result_id = if (cast_to_generic) |
| 901 | self.spv.allocId() |
| 902 | else |
| 903 | result_id; |
| 904 | |
| 900 | 905 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ |
| 901 | 906 | .id_result_type = self.typeId(ptr_ty_ref), |
| 902 | | .id_result = result_id, |
| 907 | .id_result = bitcast_result_id, |
| 903 | 908 | .operand = const_ptr_id, |
| 904 | 909 | }); |
| 910 | |
| 911 | if (cast_to_generic) { |
| 912 | const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, alignment); |
| 913 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 914 | .id_result_type = self.typeId(generic_ptr_ty_ref), |
| 915 | .id_result = result_id, |
| 916 | .pointer = bitcast_result_id, |
| 917 | }); |
| 918 | } |
| 919 | |
| 920 | try self.spv.declareDeclDeps(spv_decl_index, icl.decl_deps.items); |
| 921 | self.spv.endGlobal(spv_decl_index, begin_inst); |
| 905 | 922 | } |
| 906 | 923 | |
| 907 | 924 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| ... | ... | @@ -940,19 +957,28 @@ pub const DeclGen = struct { |
| 940 | 957 | try section.emit(self.spv.gpa, .OpConstantFalse, operands); |
| 941 | 958 | } |
| 942 | 959 | }, |
| 960 | // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra |
| 961 | // OpVariable that is not really required. |
| 943 | 962 | else => { |
| 944 | 963 | // The value cannot be generated directly, so generate it as an indirect constant, |
| 945 | 964 | // and then perform an OpLoad. |
| 946 | 965 | const alignment = ty.abiAlignment(target); |
| 947 | | const global_index = try self.spv.allocGlobal(); |
| 948 | | log.debug("constant {}", .{global_index}); |
| 949 | | const ptr_id = self.spv.beginGlobal(global_index); |
| 950 | | defer self.spv.endGlobal(); |
| 951 | | try self.lowerIndirectConstant(ptr_id, ty, val, .UniformConstant, alignment); |
| 966 | const spv_decl_index = try self.spv.allocDecl(.global); |
| 967 | |
| 968 | try self.lowerIndirectConstant( |
| 969 | spv_decl_index, |
| 970 | ty, |
| 971 | val, |
| 972 | .UniformConstant, |
| 973 | false, |
| 974 | alignment, |
| 975 | ); |
| 976 | try self.func.decl_deps.append(self.spv.gpa, spv_decl_index); |
| 977 | |
| 952 | 978 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 953 | 979 | .id_result_type = result_ty_id, |
| 954 | 980 | .id_result = result_id, |
| 955 | | .pointer = ptr_id, |
| 981 | .pointer = self.spv.declPtr(spv_decl_index).result_id, |
| 956 | 982 | }); |
| 957 | 983 | // TODO: Convert bools? This logic should hook into `load`. It should be a dead |
| 958 | 984 | // path though considering .Bool is handled above. |
| ... | ... | @@ -1289,7 +1315,7 @@ pub const DeclGen = struct { |
| 1289 | 1315 | } |
| 1290 | 1316 | } |
| 1291 | 1317 | |
| 1292 | | fn spvStorageClass(as: std.builtin.AddressSpace) spec.StorageClass { |
| 1318 | fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass { |
| 1293 | 1319 | return switch (as) { |
| 1294 | 1320 | .generic => .Generic, // TODO: Disallow? |
| 1295 | 1321 | .gs, .fs, .ss => unreachable, |
| ... | ... | @@ -1370,16 +1396,17 @@ pub const DeclGen = struct { |
| 1370 | 1396 | |
| 1371 | 1397 | fn genDecl(self: *DeclGen) !void { |
| 1372 | 1398 | const decl = self.module.declPtr(self.decl_index); |
| 1373 | | const link = try self.resolveDecl(self.decl_index); |
| 1399 | const spv_decl_index = try self.resolveDecl(self.decl_index); |
| 1374 | 1400 | |
| 1375 | | if (decl.val.castTag(.function)) |_| { |
| 1376 | | log.debug("genDecl function {s} = {}", .{ decl.name, link.func.result_id.id }); |
| 1401 | const decl_id = self.spv.declPtr(spv_decl_index).result_id; |
| 1402 | log.debug("genDecl {s} = {}", .{ decl.name, decl_id }); |
| 1377 | 1403 | |
| 1404 | if (decl.val.castTag(.function)) |_| { |
| 1378 | 1405 | assert(decl.ty.zigTypeTag() == .Fn); |
| 1379 | 1406 | const prototype_id = try self.resolveTypeId(decl.ty); |
| 1380 | 1407 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 1381 | 1408 | .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()), |
| 1382 | | .id_result = link.func.result_id, |
| 1409 | .id_result = decl_id, |
| 1383 | 1410 | .function_control = .{}, // TODO: We can set inline here if the type requires it. |
| 1384 | 1411 | .function_type = prototype_id, |
| 1385 | 1412 | }); |
| ... | ... | @@ -1413,18 +1440,18 @@ pub const DeclGen = struct { |
| 1413 | 1440 | |
| 1414 | 1441 | // Append the actual code into the functions section. |
| 1415 | 1442 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 1416 | | try self.spv.addFunction(self.func); |
| 1443 | try self.spv.addFunction(spv_decl_index, self.func); |
| 1417 | 1444 | |
| 1418 | 1445 | const fqn = try decl.getFullyQualifiedName(self.module); |
| 1419 | 1446 | defer self.module.gpa.free(fqn); |
| 1420 | 1447 | |
| 1421 | 1448 | try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ |
| 1422 | | .target = link.func.result_id, |
| 1449 | .target = decl_id, |
| 1423 | 1450 | .name = fqn, |
| 1424 | 1451 | }); |
| 1425 | 1452 | |
| 1426 | 1453 | if (self.module.test_functions.contains(self.decl_index)) { |
| 1427 | | try self.generateTestEntryPoint(fqn, link.func.result_id); |
| 1454 | try self.generateTestEntryPoint(fqn, decl_id); |
| 1428 | 1455 | } |
| 1429 | 1456 | } else { |
| 1430 | 1457 | const init_val = if (decl.val.castTag(.variable)) |payload| |
| ... | ... | @@ -1438,41 +1465,33 @@ pub const DeclGen = struct { |
| 1438 | 1465 | |
| 1439 | 1466 | // TODO: integrate with variable(). |
| 1440 | 1467 | |
| 1441 | | const storage_class = spvStorageClass(decl.@"addrspace"); |
| 1442 | | const actual_storage_class = switch (storage_class) { |
| 1468 | const final_storage_class = spvStorageClass(decl.@"addrspace"); |
| 1469 | const actual_storage_class = switch (final_storage_class) { |
| 1443 | 1470 | .Generic => .CrossWorkgroup, |
| 1444 | | else => storage_class, |
| 1445 | | }; |
| 1446 | | |
| 1447 | | const global_result_id = self.spv.beginGlobal(link.global); |
| 1448 | | defer self.spv.endGlobal(); |
| 1449 | | log.debug("genDecl {}", .{link.global}); |
| 1450 | | |
| 1451 | | const var_result_id = switch (storage_class) { |
| 1452 | | .Generic => self.spv.allocId(), |
| 1453 | | else => global_result_id, |
| 1471 | else => final_storage_class, |
| 1454 | 1472 | }; |
| 1455 | 1473 | |
| 1456 | 1474 | try self.lowerIndirectConstant( |
| 1457 | | var_result_id, |
| 1475 | spv_decl_index, |
| 1458 | 1476 | decl.ty, |
| 1459 | 1477 | init_val, |
| 1460 | 1478 | actual_storage_class, |
| 1479 | final_storage_class == .Generic, |
| 1461 | 1480 | decl.@"align", |
| 1462 | 1481 | ); |
| 1463 | 1482 | |
| 1464 | | if (storage_class == .Generic) { |
| 1465 | | const section = &self.spv.globals.section; |
| 1466 | | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1467 | | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| 1468 | | // TODO: Can we eliminate this cast? |
| 1469 | | // TODO: Const-wash pointer |
| 1470 | | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1471 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 1472 | | .id_result = global_result_id, |
| 1473 | | .pointer = var_result_id, |
| 1474 | | }); |
| 1475 | | } |
| 1483 | // if (storage_class == .Generic) { |
| 1484 | // const section = &self.spv.globals.section; |
| 1485 | // const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1486 | // const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| 1487 | // // TODO: Can we eliminate this cast? |
| 1488 | // // TODO: Const-wash pointer? |
| 1489 | // try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1490 | // .id_result_type = self.typeId(ptr_ty_ref), |
| 1491 | // .id_result = global_result_id, |
| 1492 | // .pointer = casted_result_id, |
| 1493 | // }); |
| 1494 | // } |
| 1476 | 1495 | } |
| 1477 | 1496 | } |
| 1478 | 1497 | |