authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-08 00:55:18+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:53+02:00
log405f7298acaa4818a26fdc93991c48705c19de15
tree5aada5fd3997215cfbdb51f3dcb37225cf0e7bbf
parentefe7fae6afe1ecdfc3838a97651dc617c4c747c2
signaturelock-open Commit is signed but in an unrecognized format.

spirv: add decl dependencies for functions also

Entry points need to be attributed with a complete list of global variables that they use. To that end, the global dependencies mechanism is extended to also allow functions - when flushing the module, the list of dependencies is examined to generate this list of global variable result-ids.

2 files changed, 205 insertions(+), 150 deletions(-)

src/codegen/spirv.zig+108-89
......@@ -19,6 +19,7 @@ const Word = spec.Word;
1919const IdRef = spec.IdRef;
2020const IdResult = spec.IdResult;
2121const IdResultType = spec.IdResultType;
22const StorageClass = spec.StorageClass;
2223
2324const SpvModule = @import("spirv/Module.zig");
2425const SpvSection = @import("spirv/Section.zig");
......@@ -37,23 +38,8 @@ const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
3738 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
3839});
3940
40/// Linking information about a particular decl.
41/// The active field of this enum depends on the type of the corresponding decl.
42const 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
5541/// Maps Zig decl indices to linking SPIR-V linking information.
56pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, DeclLink);
42pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index);
5743
5844/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
5945pub const DeclGen = struct {
......@@ -251,8 +237,8 @@ pub const DeclGen = struct {
251237 .function => val.castTag(.function).?.data.owner_decl,
252238 else => unreachable,
253239 };
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;
256242 }
257243
258244 return try self.constant(ty, val);
......@@ -263,19 +249,19 @@ pub const DeclGen = struct {
263249
264250 /// Fetch or allocate a result id for decl index. This function also marks the decl as alive.
265251 /// 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 {
267253 const decl = self.module.declPtr(decl_index);
268254 self.module.markDeclAlive(decl);
269255
270256 const entry = try self.decl_link.getOrPut(decl_index);
271 const result_id = self.spv.allocId();
272
273257 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);
279265 }
280266
281267 return entry.value_ptr.*;
......@@ -440,6 +426,8 @@ pub const DeclGen = struct {
440426 /// The partially filled last constant.
441427 /// If full, its flushed.
442428 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),
443431
444432 /// Utility function to get the section that instructions should be lowered to.
445433 fn section(self: *@This()) *SpvSection {
......@@ -554,7 +542,7 @@ pub const DeclGen = struct {
554542 const ty_id = dg.typeId(ty_ref);
555543
556544 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);
558546
559547 switch (decl.val.tag()) {
560548 .function => {
......@@ -569,14 +557,15 @@ pub const DeclGen = struct {
569557 const result_id = dg.spv.allocId();
570558 log.debug("addDeclRef {s} = {}", .{ decl.name, result_id.id });
571559
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;
574563 // TODO: Do we need a storage class cast here?
575564 // TODO: We can probably eliminate these casts
576565 try dg.spv.globals.section.emitSpecConstantOp(dg.spv.gpa, .OpBitcast, .{
577566 .id_result_type = ty_id,
578567 .id_result = result_id,
579 .operand = global.result_id,
568 .operand = decl_id,
580569 });
581570
582571 try self.addPtr(ty_ref, result_id);
......@@ -810,10 +799,11 @@ pub const DeclGen = struct {
810799 /// pointer points to. Note: result is not necessarily an OpVariable instruction!
811800 fn lowerIndirectConstant(
812801 self: *DeclGen,
813 result_id: IdRef,
802 spv_decl_index: SpvModule.Decl.Index,
814803 ty: Type,
815804 val: Value,
816 storage_class: spec.StorageClass,
805 storage_class: StorageClass,
806 cast_to_generic: bool,
817807 alignment: u32,
818808 ) Error!void {
819809 // To simplify constant generation, we're going to generate constants as a word-array, and
......@@ -844,23 +834,27 @@ pub const DeclGen = struct {
844834 const ty_ref = try self.resolveType(ty, .indirect);
845835 const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, alignment);
846836
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();
864858
865859 const u32_ty_ref = try self.intType(.unsigned, 32);
866860 var icl = IndirectConstantLowering{
......@@ -869,10 +863,12 @@ pub const DeclGen = struct {
869863 .u32_ty_id = self.typeId(u32_ty_ref),
870864 .members = std.ArrayList(SpvType.Payload.Struct.Member).init(self.gpa),
871865 .initializers = std.ArrayList(IdRef).init(self.gpa),
866 .decl_deps = std.ArrayList(SpvModule.Decl.Index).init(self.gpa),
872867 };
873868
874869 defer icl.members.deinit();
875870 defer icl.initializers.deinit();
871 defer icl.decl_deps.deinit();
876872
877873 try icl.lower(ty, val);
878874 try icl.flush();
......@@ -888,6 +884,7 @@ pub const DeclGen = struct {
888884 });
889885
890886 const var_id = self.spv.allocId();
887 self.spv.globalPtr(spv_decl_index).?.result_id = var_id;
891888 try section.emit(self.spv.gpa, .OpVariable, .{
892889 .id_result_type = self.typeId(ptr_constant_struct_ty_ref),
893890 .id_result = var_id,
......@@ -896,12 +893,32 @@ pub const DeclGen = struct {
896893 });
897894 // TODO: Set alignment of OpVariable.
898895 // TODO: We may be able to eliminate these casts.
896
899897 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
900905 try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{
901906 .id_result_type = self.typeId(ptr_ty_ref),
902 .id_result = result_id,
907 .id_result = bitcast_result_id,
903908 .operand = const_ptr_id,
904909 });
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);
905922 }
906923
907924 /// This function generates a load for a constant in direct (ie, non-memory) representation.
......@@ -940,19 +957,28 @@ pub const DeclGen = struct {
940957 try section.emit(self.spv.gpa, .OpConstantFalse, operands);
941958 }
942959 },
960 // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra
961 // OpVariable that is not really required.
943962 else => {
944963 // The value cannot be generated directly, so generate it as an indirect constant,
945964 // and then perform an OpLoad.
946965 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
952978 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
953979 .id_result_type = result_ty_id,
954980 .id_result = result_id,
955 .pointer = ptr_id,
981 .pointer = self.spv.declPtr(spv_decl_index).result_id,
956982 });
957983 // TODO: Convert bools? This logic should hook into `load`. It should be a dead
958984 // path though considering .Bool is handled above.
......@@ -1289,7 +1315,7 @@ pub const DeclGen = struct {
12891315 }
12901316 }
12911317
1292 fn spvStorageClass(as: std.builtin.AddressSpace) spec.StorageClass {
1318 fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass {
12931319 return switch (as) {
12941320 .generic => .Generic, // TODO: Disallow?
12951321 .gs, .fs, .ss => unreachable,
......@@ -1370,16 +1396,17 @@ pub const DeclGen = struct {
13701396
13711397 fn genDecl(self: *DeclGen) !void {
13721398 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);
13741400
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 });
13771403
1404 if (decl.val.castTag(.function)) |_| {
13781405 assert(decl.ty.zigTypeTag() == .Fn);
13791406 const prototype_id = try self.resolveTypeId(decl.ty);
13801407 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
13811408 .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()),
1382 .id_result = link.func.result_id,
1409 .id_result = decl_id,
13831410 .function_control = .{}, // TODO: We can set inline here if the type requires it.
13841411 .function_type = prototype_id,
13851412 });
......@@ -1413,18 +1440,18 @@ pub const DeclGen = struct {
14131440
14141441 // Append the actual code into the functions section.
14151442 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);
14171444
14181445 const fqn = try decl.getFullyQualifiedName(self.module);
14191446 defer self.module.gpa.free(fqn);
14201447
14211448 try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{
1422 .target = link.func.result_id,
1449 .target = decl_id,
14231450 .name = fqn,
14241451 });
14251452
14261453 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);
14281455 }
14291456 } else {
14301457 const init_val = if (decl.val.castTag(.variable)) |payload|
......@@ -1438,41 +1465,33 @@ pub const DeclGen = struct {
14381465
14391466 // TODO: integrate with variable().
14401467
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) {
14431470 .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,
14541472 };
14551473
14561474 try self.lowerIndirectConstant(
1457 var_result_id,
1475 spv_decl_index,
14581476 decl.ty,
14591477 init_val,
14601478 actual_storage_class,
1479 final_storage_class == .Generic,
14611480 decl.@"align",
14621481 );
14631482
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 // }
14761495 }
14771496 }
14781497
src/codegen/spirv/Module.zig+97-61
......@@ -39,41 +39,58 @@ pub const Fn = struct {
3939 /// This section should also contain the OpFunctionEnd instruction marking
4040 /// the end of this function definition.
4141 body: Section = .{},
42 /// The decl dependencies that this function depends on.
43 decl_deps: std.ArrayListUnmanaged(Decl.Index) = .{},
4244
4345 /// Reset this function without deallocating resources, so that
4446 /// it may be used to emit code for another function.
4547 pub fn reset(self: *Fn) void {
4648 self.prologue.reset();
4749 self.body.reset();
50 self.decl_deps.items.len = 0;
4851 }
4952
5053 /// Free the resources owned by this function.
5154 pub fn deinit(self: *Fn, a: Allocator) void {
5255 self.prologue.deinit(a);
5356 self.body.deinit(a);
57 self.decl_deps.deinit(a);
5458 self.* = undefined;
5559 }
5660};
5761
62/// Declarations, both functions and globals, can have dependencies. These are used for 2 things:
63/// - Globals must be declared before they are used, also between globals. The compiler processes
64/// globals unordered, so we must use the dependencies here to figure out how to order the globals
65/// in the final module. The Globals structure is also used for that.
66/// - Entry points must declare the complete list of OpVariable instructions that they access.
67/// For these we use the same dependency structure.
68/// In this mechanism, globals will only depend on other globals, while functions may depend on
69/// globals or other functions.
70pub const Decl = struct {
71 /// Index to refer to a Decl by.
72 pub const Index = enum(u32) { _ };
73
74 /// The result-id to be used for this declaration. This is the final result-id
75 /// of the decl, which may be an OpFunction, OpVariable, or the result of a sequence
76 /// of OpSpecConstantOp operations.
77 result_id: IdRef,
78 /// The offset of the first dependency of this decl in the `decl_deps` array.
79 begin_dep: u32,
80 /// The past-end offset of the dependencies of this decl in the `decl_deps` array.
81 end_dep: u32,
82};
83
5884/// Globals must be kept in order: operations involving globals must be ordered
5985/// so that the global declaration precedes any usage.
6086pub const Global = struct {
61 /// Index type to refer to a global by.
62 pub const Index = enum(u32) { _ };
63
64 /// The result-id to be used for this global declaration. Note that this does not
65 /// necessarily refer to an OpVariable instruction - it may also be the final result
66 /// id of a number of OpSpecConstantOp instructions.
87 /// This is the result-id of the OpVariable instruction that declares the global.
6788 result_id: IdRef,
6889 /// The offset into `self.globals.section` of the first instruction of this global
6990 /// declaration.
7091 begin_inst: u32,
7192 /// The past-end offset into `self.flobals.section`.
7293 end_inst: u32,
73 /// The first dependency in the `self.globals.dependencies` array list.
74 begin_dep: u32,
75 /// The past-end dependency in `self.globals.dependencies`.
76 end_dep: u32,
7794};
7895
7996/// A general-purpose allocator which may be used to allocate resources for this module
......@@ -123,18 +140,19 @@ source_file_names: std.StringHashMapUnmanaged(IdRef) = .{},
123140/// Note: Uses ArrayHashMap which is insertion ordered, so that we may refer to other types by index (Type.Ref).
124141type_cache: TypeCache = .{},
125142
143/// Set of Decls, referred to by Decl.Index.
144decls: std.ArrayListUnmanaged(Decl) = .{},
145
146decl_deps: std.ArrayListUnmanaged(Decl.Index) = .{},
147
126148/// The fields in this structure help to maintain the required order for global variables.
127149globals: struct {
128 /// The graph nodes of global variables present in the module.
129 nodes: std.ArrayListUnmanaged(Global) = .{},
150 /// Set of globals, referred to by Decl.Index.
151 globals: std.AutoArrayHashMapUnmanaged(Decl.Index, Global) = .{},
130152 /// This pseudo-section contains the initialization code for all the globals. Instructions from
131153 /// here are reordered when flushing the module. Its contents should be part of the
132154 /// `types_globals_constants` SPIR-V section.
133155 section: Section = .{},
134 /// Holds a list of dependent global variables for each global variable.
135 dependencies: std.ArrayListUnmanaged(Global.Index) = .{},
136 /// The global that initialization code/dependencies are currently being generated for, if any.
137 current_global: ?Global.Index = null,
138156} = .{},
139157
140158pub fn init(gpa: Allocator, arena: Allocator) Module {
......@@ -159,9 +177,11 @@ pub fn deinit(self: *Module) void {
159177 self.source_file_names.deinit(self.gpa);
160178 self.type_cache.deinit(self.gpa);
161179
162 self.globals.nodes.deinit(self.gpa);
180 self.decls.deinit(self.gpa);
181 self.decl_deps.deinit(self.gpa);
182
183 self.globals.globals.deinit(self.gpa);
163184 self.globals.section.deinit(self.gpa);
164 self.globals.dependencies.deinit(self.gpa);
165185
166186 self.* = undefined;
167187}
......@@ -181,16 +201,17 @@ pub fn idBound(self: Module) Word {
181201}
182202
183203fn orderGlobalsInto(
184 self: Module,
185 global_index: Global.Index,
204 self: *Module,
205 index: Decl.Index,
186206 section: *Section,
187207 seen: *std.DynamicBitSetUnmanaged,
188208) !void {
189 const node = self.globals.nodes.items[@enumToInt(global_index)];
190 const deps = self.globals.dependencies.items[node.begin_dep..node.end_dep];
191 const insts = self.globals.section.instructions.items[node.begin_inst..node.end_inst];
209 const decl = self.declPtr(index);
210 const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep];
211 const global = self.globalPtr(index).?;
212 const insts = self.globals.section.instructions.items[global.begin_inst..global.end_inst];
192213
193 seen.set(@enumToInt(global_index));
214 seen.set(@enumToInt(index));
194215
195216 for (deps) |dep| {
196217 if (!seen.isSet(@enumToInt(dep))) {
......@@ -201,17 +222,16 @@ fn orderGlobalsInto(
201222 try section.instructions.appendSlice(self.gpa, insts);
202223}
203224
204fn orderGlobals(self: Module) !Section {
205 const nodes = self.globals.nodes.items;
225fn orderGlobals(self: *Module) !Section {
226 const globals = self.globals.globals.keys();
206227
207 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, nodes.len);
228 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
208229 defer seen.deinit(self.gpa);
209230
210231 var ordered_globals = Section{};
211
212 for (0..nodes.len) |global_index| {
213 if (!seen.isSet(global_index)) {
214 try self.orderGlobalsInto(@intToEnum(Global.Index, @intCast(u32, global_index)), &ordered_globals, &seen);
232 for (globals) |decl_index| {
233 if (!seen.isSet(@enumToInt(decl_index))) {
234 try self.orderGlobalsInto(decl_index, &ordered_globals, &seen);
215235 }
216236 }
217237
......@@ -219,12 +239,14 @@ fn orderGlobals(self: Module) !Section {
219239}
220240
221241/// Emit this module as a spir-v binary.
222pub fn flush(self: Module, file: std.fs.File) !void {
242pub fn flush(self: *Module, file: std.fs.File) !void {
223243 // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"
224244
225245 const header = [_]Word{
226246 spec.magic_number,
227 (1 << 16) | (4 << 8), // TODO: From cpu features
247 // TODO: From cpu features
248 // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports.
249 (1 << 16) | (4 << 8),
228250 0, // TODO: Register Zig compiler magic number.
229251 self.idBound(),
230252 0, // Schema (currently reserved for future use)
......@@ -265,9 +287,10 @@ pub fn flush(self: Module, file: std.fs.File) !void {
265287}
266288
267289/// Merge the sections making up a function declaration into this module.
268pub fn addFunction(self: *Module, func: Fn) !void {
290pub fn addFunction(self: *Module, decl_index: Decl.Index, func: Fn) !void {
269291 try self.sections.functions.append(self.gpa, func.prologue);
270292 try self.sections.functions.append(self.gpa, func.body);
293 try self.declareDeclDeps(decl_index, func.decl_deps.items);
271294}
272295
273296/// Fetch the result-id of an OpString instruction that encodes the path of the source
......@@ -719,43 +742,56 @@ pub fn decorateMember(
719742 });
720743}
721744
722pub fn allocGlobal(self: *Module) !Global.Index {
723 try self.globals.nodes.append(self.gpa, .{
745pub const DeclKind = enum {
746 func,
747 global,
748};
749
750pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index {
751 try self.decls.append(self.gpa, .{
724752 .result_id = self.allocId(),
725 .begin_inst = undefined,
726 .end_inst = undefined,
727753 .begin_dep = undefined,
728754 .end_dep = undefined,
729755 });
730 return @intToEnum(Global.Index, @intCast(u32, self.globals.nodes.items.len - 1));
756 const index = @intToEnum(Decl.Index, @intCast(u32, self.decls.items.len - 1));
757 switch (kind) {
758 .func => {},
759 // If the decl represents a global, also allocate a global node.
760 .global => try self.globals.globals.putNoClobber(self.gpa, index, .{
761 .result_id = undefined,
762 .begin_inst = undefined,
763 .end_inst = undefined,
764 }),
765 }
766
767 return index;
731768}
732769
733pub fn globalPtr(self: *Module, index: Global.Index) *Global {
734 return &self.globals.nodes.items[@enumToInt(index)];
770pub fn declPtr(self: *Module, index: Decl.Index) *Decl {
771 return &self.decls.items[@enumToInt(index)];
735772}
736773
737/// Begin generating the global for `index`. The previous global is finalized
738/// at this point, and the global for `index` is made active. Any new calls to
739/// `addGlobalDependency` will affect this global. After a new call to this function,
740/// the prior active global cannot be modified again.
741pub fn beginGlobal(self: *Module, index: Global.Index) IdRef {
742 const global = self.globalPtr(index);
743 global.begin_inst = @intCast(u32, self.globals.section.instructions.items.len);
744 global.begin_dep = @intCast(u32, self.globals.dependencies.items.len);
745 self.globals.current_global = index;
746 return global.result_id;
774pub fn globalPtr(self: *Module, index: Decl.Index) ?*Global {
775 return self.globals.globals.getPtr(index);
747776}
748777
749/// Finalize the global. After this point, the current global cannot be modified anymore.
750pub fn endGlobal(self: *Module) void {
751 const global = self.globalPtr(self.globals.current_global.?);
752 global.end_inst = @intCast(u32, self.globals.section.instructions.items.len);
753 global.end_dep = @intCast(u32, self.globals.dependencies.items.len);
754 self.globals.current_global = null;
778/// Declare ALL dependencies for a decl.
779pub fn declareDeclDeps(self: *Module, decl_index: Decl.Index, deps: []const Decl.Index) !void {
780 const begin_dep = @intCast(u32, self.decl_deps.items.len);
781 try self.decl_deps.appendSlice(self.gpa, deps);
782 const end_dep = @intCast(u32, self.decl_deps.items.len);
783
784 const decl = self.declPtr(decl_index);
785 decl.begin_dep = begin_dep;
786 decl.end_dep = end_dep;
755787}
756788
757pub fn addGlobalDependency(self: *Module, dependency: Global.Index) !void {
758 assert(self.globals.current_global != null);
759 assert(self.globals.current_global.? != dependency);
760 try self.globals.dependencies.append(self.gpa, dependency);
789pub fn beginGlobal(self: *Module) u32 {
790 return @intCast(u32, self.globals.section.instructions.items.len);
791}
792
793pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32) void {
794 const global = self.globalPtr(global_index).?;
795 global.begin_inst = begin_inst;
796 global.end_inst = @intCast(u32, self.globals.section.instructions.items.len);
761797}