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 {...@@ -1494,7 +1494,6 @@ pub const DeclGen = struct {
1494 .id_result = decl_id,1494 .id_result = decl_id,
1495 .storage_class = actual_storage_class,1495 .storage_class = actual_storage_class,
1496 });1496 });
1497 self.spv.globalPtr(spv_decl_index).?.result_id = decl_id;
14981497
1499 // Now emit the instructions that initialize the variable.1498 // Now emit the instructions that initialize the variable.
1500 const initializer_id = self.spv.allocId();1499 const initializer_id = self.spv.allocId();
...@@ -1517,14 +1516,12 @@ pub const DeclGen = struct {...@@ -1517,14 +1516,12 @@ pub const DeclGen = struct {
1517 });1516 });
15181517
1519 // TODO: We should be able to get rid of this by now...1518 // 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
1522 try self.func.body.emit(self.spv.gpa, .OpReturn, {});1521 try self.func.body.emit(self.spv.gpa, .OpReturn, {});
1523 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});1522 try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
1524 try self.spv.addFunction(spv_decl_index, self.func);1523 try self.spv.addFunction(spv_decl_index, self.func);
15251524
1526 try self.spv.initializers.append(self.spv.gpa, initializer_id);
1527
1528 const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module));1525 const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module));
1529 try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{1526 try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{
1530 .target = decl_id,1527 .target = decl_id,
src/codegen/spirv/Module.zig+95-21
...@@ -94,6 +94,8 @@ pub const Global = struct {...@@ -94,6 +94,8 @@ pub const Global = struct {
94 begin_inst: u32,94 begin_inst: u32,
95 /// The past-end offset into `self.flobals.section`.95 /// The past-end offset into `self.flobals.section`.
96 end_inst: u32,96 end_inst: u32,
97 /// The result-id of the function that initializes this value.
98 initializer_id: IdRef,
97};99};
98100
99/// This models a kernel entry point.101/// This models a kernel entry point.
...@@ -174,9 +176,6 @@ globals: struct {...@@ -174,9 +176,6 @@ globals: struct {
174 section: Section = .{},176 section: Section = .{},
175} = .{},177} = .{},
176178
177/// The function IDs of global variable initializers
178initializers: std.ArrayListUnmanaged(IdRef) = .{},
179
180pub fn init(gpa: Allocator, arena: Allocator) Module {179pub fn init(gpa: Allocator, arena: Allocator) Module {
181 return .{180 return .{
182 .gpa = gpa,181 .gpa = gpa,
...@@ -205,8 +204,6 @@ pub fn deinit(self: *Module) void {...@@ -205,8 +204,6 @@ pub fn deinit(self: *Module) void {
205 self.globals.globals.deinit(self.gpa);204 self.globals.globals.deinit(self.gpa);
206 self.globals.section.deinit(self.gpa);205 self.globals.section.deinit(self.gpa);
207206
208 self.initializers.deinit(self.gpa);
209
210 self.* = undefined;207 self.* = undefined;
211}208}
212209
...@@ -289,6 +286,10 @@ fn addEntryPointDeps(...@@ -289,6 +286,10 @@ fn addEntryPointDeps(
289 const decl = self.declPtr(decl_index);286 const decl = self.declPtr(decl_index);
290 const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep];287 const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep];
291288
289 if (seen.isSet(@intFromEnum(decl_index))) {
290 return;
291 }
292
292 seen.set(@intFromEnum(decl_index));293 seen.set(@intFromEnum(decl_index));
293294
294 if (self.globalPtr(decl_index)) |global| {295 if (self.globalPtr(decl_index)) |global| {
...@@ -296,9 +297,7 @@ fn addEntryPointDeps(...@@ -296,9 +297,7 @@ fn addEntryPointDeps(
296 }297 }
297298
298 for (deps) |dep| {299 for (deps) |dep| {
299 if (!seen.isSet(@intFromEnum(dep))) {300 try self.addEntryPointDeps(dep, seen, interface);
300 try self.addEntryPointDeps(dep, seen, interface);
301 }
302 }301 }
303}302}
304303
...@@ -330,20 +329,76 @@ fn entryPoints(self: *Module) !Section {...@@ -330,20 +329,76 @@ fn entryPoints(self: *Module) !Section {
330 return entry_points;329 return entry_points;
331}330}
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
333/// Emit this module as a spir-v binary.398/// Emit this module as a spir-v binary.
334pub fn flush(self: *Module, file: std.fs.File) !void {399pub fn flush(self: *Module, file: std.fs.File) !void {
335 // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"400 // 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
347 // TODO: Perform topological sort on the globals.402 // TODO: Perform topological sort on the globals.
348 var globals = try self.orderGlobals();403 var globals = try self.orderGlobals();
349 defer globals.deinit(self.gpa);404 defer globals.deinit(self.gpa);
...@@ -354,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void {...@@ -354,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
354 var types_constants = try self.cache.materialize(self);409 var types_constants = try self.cache.materialize(self);
355 defer types_constants.deinit(self.gpa);410 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
357 // Note: needs to be kept in order according to section 2.3!425 // Note: needs to be kept in order according to section 2.3!
358 const buffers = &[_][]const Word{426 const buffers = &[_][]const Word{
359 &header,427 &header,
...@@ -368,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {...@@ -368,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
368 self.sections.types_globals_constants.toWords(),436 self.sections.types_globals_constants.toWords(),
369 globals.toWords(),437 globals.toWords(),
370 self.sections.functions.toWords(),438 self.sections.functions.toWords(),
439 init_func.toWords(),
371 };440 };
372441
373 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;442 var iovc_buffers: [buffers.len]std.os.iovec_const = undefined;
...@@ -529,6 +598,7 @@ pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index {...@@ -529,6 +598,7 @@ pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index {
529 .result_id = undefined,598 .result_id = undefined,
530 .begin_inst = undefined,599 .begin_inst = undefined,
531 .end_inst = undefined,600 .end_inst = undefined,
601 .initializer_id = undefined,
532 }),602 }),
533 }603 }
534604
...@@ -558,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 {...@@ -558,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 {
558 return @as(u32, @intCast(self.globals.section.instructions.items.len));628 return @as(u32, @intCast(self.globals.section.instructions.items.len));
559}629}
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 {
562 const global = self.globalPtr(global_index).?;632 const global = self.globalPtr(global_index).?;
563 global.begin_inst = begin_inst;633 global.* = .{
564 global.end_inst = @as(u32, @intCast(self.globals.section.instructions.items.len));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 };
565}639}
566640
567pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8) !void {641pub 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 {...@@ -48,7 +48,6 @@ fn getArrayLen(a: []const u32) usize {
48test "array concat with undefined" {48test "array concat with undefined" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5251
53 const S = struct {52 const S = struct {
54 fn doTheTest() !void {53 fn doTheTest() !void {
...@@ -88,7 +87,6 @@ test "array concat with tuple" {...@@ -88,7 +87,6 @@ test "array concat with tuple" {
8887
89test "array init with concat" {88test "array init with concat" {
90 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO89 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
91 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9290
93 const a = 'a';91 const a = 'a';
94 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };92 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };
...@@ -98,7 +96,6 @@ test "array init with concat" {...@@ -98,7 +96,6 @@ test "array init with concat" {
98test "array init with mult" {96test "array init with mult" {
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;97 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO98 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10299
103 const a = 'a';100 const a = 'a';
104 var i: [8]u8 = [2]u8{ a, 'b' } ** 4;101 var i: [8]u8 = [2]u8{ a, 'b' } ** 4;
...@@ -241,7 +238,6 @@ fn plusOne(x: u32) u32 {...@@ -241,7 +238,6 @@ fn plusOne(x: u32) u32 {
241test "single-item pointer to array indexing and slicing" {238test "single-item pointer to array indexing and slicing" {
242 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;239 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
243 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO240 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
244 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
245241
246 try testSingleItemPtrArrayIndexSlice();242 try testSingleItemPtrArrayIndexSlice();
247 try comptime testSingleItemPtrArrayIndexSlice();243 try comptime testSingleItemPtrArrayIndexSlice();
...@@ -384,7 +380,6 @@ test "runtime initialize array elem and then implicit cast to slice" {...@@ -384,7 +380,6 @@ test "runtime initialize array elem and then implicit cast to slice" {
384test "array literal as argument to function" {380test "array literal as argument to function" {
385 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;381 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
386 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO382 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
387 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
388383
389 const S = struct {384 const S = struct {
390 fn entry(two: i32) !void {385 fn entry(two: i32) !void {
...@@ -413,7 +408,6 @@ test "double nested array to const slice cast in array literal" {...@@ -413,7 +408,6 @@ test "double nested array to const slice cast in array literal" {
413 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO409 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
415 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO410 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
417411
418 const S = struct {412 const S = struct {
419 fn entry(two: i32) !void {413 fn entry(two: i32) !void {
...@@ -651,7 +645,6 @@ test "tuple to array handles sentinel" {...@@ -651,7 +645,6 @@ test "tuple to array handles sentinel" {
651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO645 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
653 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO647 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
654 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
655648
656 const S = struct {649 const S = struct {
657 const a = .{ 1, 2, 3 };650 const a = .{ 1, 2, 3 };
test/behavior/basic.zig+1-7
...@@ -330,7 +330,6 @@ const FnPtrWrapper = struct {...@@ -330,7 +330,6 @@ const FnPtrWrapper = struct {
330330
331test "const ptr from var variable" {331test "const ptr from var variable" {
332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;332 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
333 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
334333
335 var x: u64 = undefined;334 var x: u64 = undefined;
336 var y: u64 = undefined;335 var y: u64 = undefined;
...@@ -581,7 +580,7 @@ test "comptime cast fn to ptr" {...@@ -581,7 +580,7 @@ test "comptime cast fn to ptr" {
581}580}
582581
583test "equality compare fn ptrs" {582test "equality compare fn ptrs" {
584 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // Test passes but should not583 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
585584
586 var a = &emptyFn;585 var a = &emptyFn;
587 try expect(a == a);586 try expect(a == a);
...@@ -639,7 +638,6 @@ test "global constant is loaded with a runtime-known index" {...@@ -639,7 +638,6 @@ test "global constant is loaded with a runtime-known index" {
639638
640test "multiline string literal is null terminated" {639test "multiline string literal is null terminated" {
641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;640 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
642 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
643641
644 const s1 =642 const s1 =
645 \\one643 \\one
...@@ -711,7 +709,6 @@ test "comptime manyptr concatenation" {...@@ -711,7 +709,6 @@ test "comptime manyptr concatenation" {
711 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;709 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
712 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
713 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO711 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
714 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
715712
716 const s = "epic";713 const s = "epic";
717 const actual = manyptrConcat(s);714 const actual = manyptrConcat(s);
...@@ -1027,7 +1024,6 @@ comptime {...@@ -1027,7 +1024,6 @@ comptime {
10271024
1028test "switch inside @as gets correct type" {1025test "switch inside @as gets correct type" {
1029 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1026 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1030 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10311027
1032 var a: u32 = 0;1028 var a: u32 = 0;
1033 var b: [2]u32 = undefined;1029 var b: [2]u32 = undefined;
...@@ -1136,8 +1132,6 @@ test "orelse coercion as function argument" {...@@ -1136,8 +1132,6 @@ test "orelse coercion as function argument" {
1136}1132}
11371133
1138test "runtime-known globals initialized with undefined" {1134test "runtime-known globals initialized with undefined" {
1139 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1140
1141 const S = struct {1135 const S = struct {
1142 var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };1136 var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
1143 var vp: [*]u32 = undefined;1137 var vp: [*]u32 = undefined;