From b30cd679878ab0fab7f1e1589c348a2477d542aa Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sat, 16 Sep 2023 13:14:38 +0200 Subject: spirv: put global var initializers in functions --- src/codegen/spirv/Module.zig | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/codegen/spirv/Module.zig') diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index e61ac754ee..cafc2f0662 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -174,6 +174,9 @@ globals: struct { section: Section = .{}, } = .{}, +/// The function IDs of global variable initializers +initializers: std.ArrayListUnmanaged(IdRef) = .{}, + pub fn init(gpa: Allocator, arena: Allocator) Module { return .{ .gpa = gpa, @@ -202,6 +205,8 @@ pub fn deinit(self: *Module) void { self.globals.globals.deinit(self.gpa); self.globals.section.deinit(self.gpa); + self.initializers.deinit(self.gpa); + self.* = undefined; } -- cgit v1.2.3 From b845c9d5326bc83691edcb483ac44793b88afe75 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 18 Sep 2023 22:39:44 +0200 Subject: spirv: generate module initializer --- src/codegen/spirv.zig | 5 +- src/codegen/spirv/Module.zig | 116 +++++++++++++++++++++++++++++++++++-------- test/behavior/array.zig | 7 --- test/behavior/basic.zig | 8 +-- 4 files changed, 97 insertions(+), 39 deletions(-) (limited to 'src/codegen/spirv/Module.zig') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e4ce3e5f3f..9d44b8eb8f 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1494,7 +1494,6 @@ pub const DeclGen = struct { .id_result = decl_id, .storage_class = actual_storage_class, }); - self.spv.globalPtr(spv_decl_index).?.result_id = decl_id; // Now emit the instructions that initialize the variable. const initializer_id = self.spv.allocId(); @@ -1517,14 +1516,12 @@ pub const DeclGen = struct { }); // TODO: We should be able to get rid of this by now... - self.spv.endGlobal(spv_decl_index, begin); + self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id); try self.func.body.emit(self.spv.gpa, .OpReturn, {}); try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); try self.spv.addFunction(spv_decl_index, self.func); - try self.spv.initializers.append(self.spv.gpa, initializer_id); - const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ .target = decl_id, diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index cafc2f0662..b6ed381360 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -94,6 +94,8 @@ pub const Global = struct { begin_inst: u32, /// The past-end offset into `self.flobals.section`. end_inst: u32, + /// The result-id of the function that initializes this value. + initializer_id: IdRef, }; /// This models a kernel entry point. @@ -174,9 +176,6 @@ globals: struct { section: Section = .{}, } = .{}, -/// The function IDs of global variable initializers -initializers: std.ArrayListUnmanaged(IdRef) = .{}, - pub fn init(gpa: Allocator, arena: Allocator) Module { return .{ .gpa = gpa, @@ -205,8 +204,6 @@ pub fn deinit(self: *Module) void { self.globals.globals.deinit(self.gpa); self.globals.section.deinit(self.gpa); - self.initializers.deinit(self.gpa); - self.* = undefined; } @@ -289,6 +286,10 @@ fn addEntryPointDeps( const decl = self.declPtr(decl_index); const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep]; + if (seen.isSet(@intFromEnum(decl_index))) { + return; + } + seen.set(@intFromEnum(decl_index)); if (self.globalPtr(decl_index)) |global| { @@ -296,9 +297,7 @@ fn addEntryPointDeps( } for (deps) |dep| { - if (!seen.isSet(@intFromEnum(dep))) { - try self.addEntryPointDeps(dep, seen, interface); - } + try self.addEntryPointDeps(dep, seen, interface); } } @@ -330,20 +329,76 @@ fn entryPoints(self: *Module) !Section { return entry_points; } +/// Generate a function that calls all initialization functions, +/// in unspecified order (an order should not be required here). +/// It generated as follows: +/// %init = OpFunction %void None +/// foreach %initializer: +/// OpFunctionCall %initializer +/// OpReturn +/// OpFunctionEnd +fn initializer(self: *Module, entry_points: *Section) !Section { + var section = Section{}; + errdefer section.deinit(self.gpa); + + // const void_ty_ref = try self.resolveType(Type.void, .direct); + const void_ty_ref = try self.resolve(.void_type); + const void_ty_id = self.resultId(void_ty_ref); + const init_proto_ty_ref = try self.resolve(.{ .function_type = .{ + .return_type = void_ty_ref, + .parameters = &.{}, + } }); + + const init_id = self.allocId(); + try section.emit(self.gpa, .OpFunction, .{ + .id_result_type = void_ty_id, + .id_result = init_id, + .function_control = .{}, + .function_type = self.resultId(init_proto_ty_ref), + }); + try section.emit(self.gpa, .OpLabel, .{ + .id_result = self.allocId(), + }); + + var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len); + defer seen.deinit(self.gpa); + + var interface = std.ArrayList(IdRef).init(self.gpa); + defer interface.deinit(); + + for (self.globals.globals.keys(), self.globals.globals.values()) |decl_index, global| { + try self.addEntryPointDeps(decl_index, &seen, &interface); + try section.emit(self.gpa, .OpFunctionCall, .{ + .id_result_type = void_ty_id, + .id_result = self.allocId(), + .function = global.initializer_id, + }); + } + + try section.emit(self.gpa, .OpReturn, {}); + try section.emit(self.gpa, .OpFunctionEnd, {}); + + try entry_points.emit(self.gpa, .OpEntryPoint, .{ + // TODO: Rusticl does not support this because its poorly defined. + // Do we need to generate a workaround here? + .execution_model = .Kernel, + .entry_point = init_id, + .name = "zig global initializer", + .interface = interface.items, + }); + + try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{ + .entry_point = init_id, + .mode = .Initializer, + }); + + return section; +} + /// Emit this module as a spir-v binary. pub fn flush(self: *Module, file: std.fs.File) !void { // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" - const header = [_]Word{ - spec.magic_number, - // TODO: From cpu features - // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. - (1 << 16) | (4 << 8), - 0, // TODO: Register Zig compiler magic number. - self.idBound(), - 0, // Schema (currently reserved for future use) - }; - // TODO: Perform topological sort on the globals. var globals = try self.orderGlobals(); defer globals.deinit(self.gpa); @@ -354,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void { var types_constants = try self.cache.materialize(self); defer types_constants.deinit(self.gpa); + var init_func = try self.initializer(&entry_points); + defer init_func.deinit(self.gpa); + + const header = [_]Word{ + spec.magic_number, + // TODO: From cpu features + // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. + (1 << 16) | (4 << 8), + 0, // TODO: Register Zig compiler magic number. + self.idBound(), + 0, // Schema (currently reserved for future use) + }; + // Note: needs to be kept in order according to section 2.3! const buffers = &[_][]const Word{ &header, @@ -368,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void { self.sections.types_globals_constants.toWords(), globals.toWords(), self.sections.functions.toWords(), + init_func.toWords(), }; var iovc_buffers: [buffers.len]std.os.iovec_const = undefined; @@ -529,6 +598,7 @@ pub fn allocDecl(self: *Module, kind: DeclKind) !Decl.Index { .result_id = undefined, .begin_inst = undefined, .end_inst = undefined, + .initializer_id = undefined, }), } @@ -558,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 { return @as(u32, @intCast(self.globals.section.instructions.items.len)); } -pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32) void { +pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, result_id: IdRef, initializer_id: IdRef) void { const global = self.globalPtr(global_index).?; - global.begin_inst = begin_inst; - global.end_inst = @as(u32, @intCast(self.globals.section.instructions.items.len)); + global.* = .{ + .result_id = result_id, + .begin_inst = begin_inst, + .end_inst = @intCast(self.globals.section.instructions.items.len), + .initializer_id = initializer_id, + }; } pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8) !void { diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 7edef09578..6fa93b4657 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -48,7 +48,6 @@ fn getArrayLen(a: []const u32) usize { test "array concat with undefined" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -88,7 +87,6 @@ test "array concat with tuple" { test "array init with concat" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const a = 'a'; var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; @@ -98,7 +96,6 @@ test "array init with concat" { test "array init with mult" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const a = 'a'; var i: [8]u8 = [2]u8{ a, 'b' } ** 4; @@ -241,7 +238,6 @@ fn plusOne(x: u32) u32 { test "single-item pointer to array indexing and slicing" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; try testSingleItemPtrArrayIndexSlice(); try comptime testSingleItemPtrArrayIndexSlice(); @@ -384,7 +380,6 @@ test "runtime initialize array elem and then implicit cast to slice" { test "array literal as argument to function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { fn entry(two: i32) !void { @@ -413,7 +408,6 @@ test "double nested array to const slice cast in array literal" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { fn entry(two: i32) !void { @@ -651,7 +645,6 @@ test "tuple to array handles sentinel" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { const a = .{ 1, 2, 3 }; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 57a579cfdb..9a75a8495d 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -330,7 +330,6 @@ const FnPtrWrapper = struct { test "const ptr from var variable" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; var x: u64 = undefined; var y: u64 = undefined; @@ -581,7 +580,7 @@ test "comptime cast fn to ptr" { } test "equality compare fn ptrs" { - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // Test passes but should not + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; var a = &emptyFn; try expect(a == a); @@ -639,7 +638,6 @@ test "global constant is loaded with a runtime-known index" { test "multiline string literal is null terminated" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const s1 = \\one @@ -711,7 +709,6 @@ test "comptime manyptr concatenation" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const s = "epic"; const actual = manyptrConcat(s); @@ -1027,7 +1024,6 @@ comptime { test "switch inside @as gets correct type" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; var a: u32 = 0; var b: [2]u32 = undefined; @@ -1136,8 +1132,6 @@ test "orelse coercion as function argument" { } test "runtime-known globals initialized with undefined" { - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - const S = struct { var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var vp: [*]u32 = undefined; -- cgit v1.2.3 From 572517376a7695693c59a0ec4ec6cc5442003e3a Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Thu, 21 Sep 2023 23:02:53 +0200 Subject: spirv: air dbg_var_val and dbg_var_ptr --- src/codegen/spirv.zig | 34 ++++++++++++++-------------------- src/codegen/spirv/Cache.zig | 12 ++++++------ src/codegen/spirv/Module.zig | 10 ++++++---- 3 files changed, 26 insertions(+), 30 deletions(-) (limited to 'src/codegen/spirv/Module.zig') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index b7d749e141..97574961bc 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1474,10 +1474,7 @@ pub const DeclGen = struct { try self.spv.addFunction(spv_decl_index, self.func); const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); - try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ - .target = decl_id, - .name = fqn, - }); + try self.spv.debugName(decl_id, fqn); // Temporarily generate a test kernel declaration if this is a test function. if (self.module.test_functions.contains(self.decl_index)) { @@ -1548,17 +1545,8 @@ pub const DeclGen = struct { try self.spv.addFunction(spv_decl_index, self.func); const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); - try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ - .target = decl_id, - .name = fqn, - }); - - const init_name = try std.fmt.allocPrint(self.gpa, "initializer of {s}", .{fqn}); - defer self.gpa.free(init_name); - try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ - .target = initializer_id, - .name = init_name, - }); + try self.spv.debugName(decl_id, fqn); + try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn}); } } @@ -1756,11 +1744,10 @@ pub const DeclGen = struct { .switch_br => return self.airSwitchBr(inst), .unreach, .trap => return self.airUnreach(), - .dbg_stmt => return self.airDbgStmt(inst), - .dbg_inline_begin => return self.airDbgInlineBegin(inst), - .dbg_inline_end => return self.airDbgInlineEnd(inst), - .dbg_var_ptr => return, - .dbg_var_val => return, + .dbg_stmt => return self.airDbgStmt(inst), + .dbg_inline_begin => return self.airDbgInlineBegin(inst), + .dbg_inline_end => return self.airDbgInlineEnd(inst), + .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst), .dbg_block_begin => return, .dbg_block_end => return, @@ -3566,6 +3553,13 @@ pub const DeclGen = struct { _ = self.base_line_stack.pop(); } + fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const target_id = try self.resolve(pl_op.operand); + const name = self.air.nullTerminatedString(pl_op.payload); + try self.spv.debugName(target_id, name); + } + fn airAssembly(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { const mod = self.module; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; diff --git a/src/codegen/spirv/Cache.zig b/src/codegen/spirv/Cache.zig index 7a3b6f61f5..68fea5c47a 100644 --- a/src/codegen/spirv/Cache.zig +++ b/src/codegen/spirv/Cache.zig @@ -462,11 +462,11 @@ fn emit( switch (key) { .void_type => { try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); - try spv.debugName(result_id, "void", .{}); + try spv.debugName(result_id, "void"); }, .bool_type => { try section.emit(spv.gpa, .OpTypeBool, .{ .id_result = result_id }); - try spv.debugName(result_id, "bool", .{}); + try spv.debugName(result_id, "bool"); }, .int_type => |int| { try section.emit(spv.gpa, .OpTypeInt, .{ @@ -481,14 +481,14 @@ fn emit( .unsigned => "u", .signed => "i", }; - try spv.debugName(result_id, "{s}{}", .{ ui, int.bits }); + try spv.debugNameFmt(result_id, "{s}{}", .{ ui, int.bits }); }, .float_type => |float| { try section.emit(spv.gpa, .OpTypeFloat, .{ .id_result = result_id, .width = float.bits, }); - try spv.debugName(result_id, "f{}", .{float.bits}); + try spv.debugNameFmt(result_id, "f{}", .{float.bits}); }, .vector_type => |vector| { try section.emit(spv.gpa, .OpTypeVector, .{ @@ -530,11 +530,11 @@ fn emit( section.writeOperand(IdResult, self.resultId(member_type)); } if (self.getString(struct_type.name)) |name| { - try spv.debugName(result_id, "{s}", .{name}); + try spv.debugName(result_id, name); } for (struct_type.memberNames(), 0..) |member_name, i| { if (self.getString(member_name)) |name| { - try spv.memberDebugName(result_id, @as(u32, @intCast(i)), "{s}", .{name}); + try spv.memberDebugName(result_id, @as(u32, @intCast(i)), name); } } // TODO: Decorations? diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index b6ed381360..81b97ebae5 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -645,18 +645,20 @@ pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8 }); } -pub fn debugName(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { - const name = try std.fmt.allocPrint(self.gpa, fmt, args); - defer self.gpa.free(name); +pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void { try self.sections.debug_names.emit(self.gpa, .OpName, .{ .target = target, .name = name, }); } -pub fn memberDebugName(self: *Module, target: IdResult, member: u32, comptime fmt: []const u8, args: anytype) !void { +pub fn debugNameFmt(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { const name = try std.fmt.allocPrint(self.gpa, fmt, args); defer self.gpa.free(name); + try self.debugName(target, name); +} + +pub fn memberDebugName(self: *Module, target: IdResult, member: u32, name: []const u8) !void { try self.sections.debug_names.emit(self.gpa, .OpMemberName, .{ .type = target, .member = member, -- cgit v1.2.3