authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-18 22:39:44+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 12:36:56-07:00
logb845c9d5326bc83691edcb483ac44793b88afe75
treea965db64c6d8a6c657d46ab9983cdf88fb0c853f
parent5d844faf7c5c30555664b4161e5f9a903daaf562

spirv: generate module initializer


4 files changed, 97 insertions(+), 39 deletions(-)

src/codegen/spirv.zig+1-4
......@@ -1494,7 +1494,6 @@ pub const DeclGen = struct {
14941494 .id_result = decl_id,
14951495 .storage_class = actual_storage_class,
14961496 });
1497 self.spv.globalPtr(spv_decl_index).?.result_id = decl_id;
14981497
14991498 // Now emit the instructions that initialize the variable.
15001499 const initializer_id = self.spv.allocId();
......@@ -1517,14 +1516,12 @@ pub const DeclGen = struct {
15171516 });
15181517
15191518 // TODO: We should be able to get rid of this by now...
1520 self.spv.endGlobal(spv_decl_index, begin);
1519 self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id);
15211520
15221521 try self.func.body.emit(self.spv.gpa, .OpReturn, {});
15231522 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
15241523 try self.spv.addFunction(spv_decl_index, self.func);
15251524
1526 try self.spv.initializers.append(self.spv.gpa, initializer_id);
1527
15281525 const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module));
15291526 try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{
15301527 .target = decl_id,
src/codegen/spirv/Module.zig+95-21
......@@ -94,6 +94,8 @@ pub const Global = struct {
9494 begin_inst: u32,
9595 /// The past-end offset into `self.flobals.section`.
9696 end_inst: u32,
97 /// The result-id of the function that initializes this value.
98 initializer_id: IdRef,
9799};
98100
99101/// This models a kernel entry point.
......@@ -174,9 +176,6 @@ globals: struct {
174176 section: Section = .{},
175177} = .{},
176178
177/// The function IDs of global variable initializers
178initializers: std.ArrayListUnmanaged(IdRef) = .{},
179
180179pub fn init(gpa: Allocator, arena: Allocator) Module {
181180 return .{
182181 .gpa = gpa,
......@@ -205,8 +204,6 @@ pub fn deinit(self: *Module) void {
205204 self.globals.globals.deinit(self.gpa);
206205 self.globals.section.deinit(self.gpa);
207206
208 self.initializers.deinit(self.gpa);
209
210207 self.* = undefined;
211208}
212209
......@@ -289,6 +286,10 @@ fn addEntryPointDeps(
289286 const decl = self.declPtr(decl_index);
290287 const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep];
291288
289 if (seen.isSet(@intFromEnum(decl_index))) {
290 return;
291 }
292
292293 seen.set(@intFromEnum(decl_index));
293294
294295 if (self.globalPtr(decl_index)) |global| {
......@@ -296,9 +297,7 @@ fn addEntryPointDeps(
296297 }
297298
298299 for (deps) |dep| {
299 if (!seen.isSet(@intFromEnum(dep))) {
300 try self.addEntryPointDeps(dep, seen, interface);
301 }
300 try self.addEntryPointDeps(dep, seen, interface);
302301 }
303302}
304303
......@@ -330,20 +329,76 @@ fn entryPoints(self: *Module) !Section {
330329 return entry_points;
331330}
332331
332/// Generate a function that calls all initialization functions,
333/// in unspecified order (an order should not be required here).
334/// It generated as follows:
335/// %init = OpFunction %void None
336/// foreach %initializer:
337/// OpFunctionCall %initializer
338/// OpReturn
339/// OpFunctionEnd
340fn initializer(self: *Module, entry_points: *Section) !Section {
341 var section = Section{};
342 errdefer section.deinit(self.gpa);
343
344 // const void_ty_ref = try self.resolveType(Type.void, .direct);
345 const void_ty_ref = try self.resolve(.void_type);
346 const void_ty_id = self.resultId(void_ty_ref);
347 const init_proto_ty_ref = try self.resolve(.{ .function_type = .{
348 .return_type = void_ty_ref,
349 .parameters = &.{},
350 } });
351
352 const init_id = self.allocId();
353 try section.emit(self.gpa, .OpFunction, .{
354 .id_result_type = void_ty_id,
355 .id_result = init_id,
356 .function_control = .{},
357 .function_type = self.resultId(init_proto_ty_ref),
358 });
359 try section.emit(self.gpa, .OpLabel, .{
360 .id_result = self.allocId(),
361 });
362
363 var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
364 defer seen.deinit(self.gpa);
365
366 var interface = std.ArrayList(IdRef).init(self.gpa);
367 defer interface.deinit();
368
369 for (self.globals.globals.keys(), self.globals.globals.values()) |decl_index, global| {
370 try self.addEntryPointDeps(decl_index, &seen, &interface);
371 try section.emit(self.gpa, .OpFunctionCall, .{
372 .id_result_type = void_ty_id,
373 .id_result = self.allocId(),
374 .function = global.initializer_id,
375 });
376 }
377
378 try section.emit(self.gpa, .OpReturn, {});
379 try section.emit(self.gpa, .OpFunctionEnd, {});
380
381 try entry_points.emit(self.gpa, .OpEntryPoint, .{
382 // TODO: Rusticl does not support this because its poorly defined.
383 // Do we need to generate a workaround here?
384 .execution_model = .Kernel,
385 .entry_point = init_id,
386 .name = "zig global initializer",
387 .interface = interface.items,
388 });
389
390 try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{
391 .entry_point = init_id,
392 .mode = .Initializer,
393 });
394
395 return section;
396}
397
333398/// Emit this module as a spir-v binary.
334399pub fn flush(self: *Module, file: std.fs.File) !void {
335400 // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"
336401
337 const header = [_]Word{
338 spec.magic_number,
339 // TODO: From cpu features
340 // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports.
341 (1 << 16) | (4 << 8),
342 0, // TODO: Register Zig compiler magic number.
343 self.idBound(),
344 0, // Schema (currently reserved for future use)
345 };
346
347402 // TODO: Perform topological sort on the globals.
348403 var globals = try self.orderGlobals();
349404 defer globals.deinit(self.gpa);
......@@ -354,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
354409 var types_constants = try self.cache.materialize(self);
355410 defer types_constants.deinit(self.gpa);
356411
412 var init_func = try self.initializer(&entry_points);
413 defer init_func.deinit(self.gpa);
414
415 const header = [_]Word{
416 spec.magic_number,
417 // TODO: From cpu features
418 // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports.
419 (1 << 16) | (4 << 8),
420 0, // TODO: Register Zig compiler magic number.
421 self.idBound(),
422 0, // Schema (currently reserved for future use)
423 };
424
357425 // Note: needs to be kept in order according to section 2.3!
358426 const buffers = &[_][]const Word{
359427 &header,
......@@ -368,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
368436 self.sections.types_globals_constants.toWords(),
369437 globals.toWords(),
370438 self.sections.functions.toWords(),
439 init_func.toWords(),
371440 };
372441
373442 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;
......@@ -529,6 +598,7 @@ pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index {
529598 .result_id = undefined,
530599 .begin_inst = undefined,
531600 .end_inst = undefined,
601 .initializer_id = undefined,
532602 }),
533603 }
534604
......@@ -558,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 {
558628 return @as(u32, @intCast(self.globals.section.instructions.items.len));
559629}
560630
561pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32) void {
631pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, result_id: IdRef, initializer_id: IdRef) void {
562632 const global = self.globalPtr(global_index).?;
563 global.begin_inst = begin_inst;
564 global.end_inst = @as(u32, @intCast(self.globals.section.instructions.items.len));
633 global.* = .{
634 .result_id = result_id,
635 .begin_inst = begin_inst,
636 .end_inst = @intCast(self.globals.section.instructions.items.len),
637 .initializer_id = initializer_id,
638 };
565639}
566640
567641pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8) !void {
test/behavior/array.zig-7
......@@ -48,7 +48,6 @@ fn getArrayLen(a: []const u32) usize {
4848test "array concat with undefined" {
4949 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5050 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5251
5352 const S = struct {
5453 fn doTheTest() !void {
......@@ -88,7 +87,6 @@ test "array concat with tuple" {
8887
8988test "array init with concat" {
9089 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
91 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9290
9391 const a = 'a';
9492 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };
......@@ -98,7 +96,6 @@ test "array init with concat" {
9896test "array init with mult" {
9997 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10098 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10299
103100 const a = 'a';
104101 var i: [8]u8 = [2]u8{ a, 'b' } ** 4;
......@@ -241,7 +238,6 @@ fn plusOne(x: u32) u32 {
241238test "single-item pointer to array indexing and slicing" {
242239 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
243240 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
244 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
245241
246242 try testSingleItemPtrArrayIndexSlice();
247243 try comptime testSingleItemPtrArrayIndexSlice();
......@@ -384,7 +380,6 @@ test "runtime initialize array elem and then implicit cast to slice" {
384380test "array literal as argument to function" {
385381 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
386382 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
387 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
388383
389384 const S = struct {
390385 fn entry(two: i32) !void {
......@@ -413,7 +408,6 @@ test "double nested array to const slice cast in array literal" {
413408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
414409 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
415410 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
417411
418412 const S = struct {
419413 fn entry(two: i32) !void {
......@@ -651,7 +645,6 @@ test "tuple to array handles sentinel" {
651645 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
653647 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
654 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
655648
656649 const S = struct {
657650 const a = .{ 1, 2, 3 };
test/behavior/basic.zig+1-7
......@@ -330,7 +330,6 @@ const FnPtrWrapper = struct {
330330
331331test "const ptr from var variable" {
332332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
333 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
334333
335334 var x: u64 = undefined;
336335 var y: u64 = undefined;
......@@ -581,7 +580,7 @@ test "comptime cast fn to ptr" {
581580}
582581
583582test "equality compare fn ptrs" {
584 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // Test passes but should not
583 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
585584
586585 var a = &emptyFn;
587586 try expect(a == a);
......@@ -639,7 +638,6 @@ test "global constant is loaded with a runtime-known index" {
639638
640639test "multiline string literal is null terminated" {
641640 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
642 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
643641
644642 const s1 =
645643 \\one
......@@ -711,7 +709,6 @@ test "comptime manyptr concatenation" {
711709 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
712710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
713711 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
714 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
715712
716713 const s = "epic";
717714 const actual = manyptrConcat(s);
......@@ -1027,7 +1024,6 @@ comptime {
10271024
10281025test "switch inside @as gets correct type" {
10291026 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1030 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10311027
10321028 var a: u32 = 0;
10331029 var b: [2]u32 = undefined;
......@@ -1136,8 +1132,6 @@ test "orelse coercion as function argument" {
11361132}
11371133
11381134test "runtime-known globals initialized with undefined" {
1139 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1140
11411135 const S = struct {
11421136 var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
11431137 var vp: [*]u32 = undefined;