| author | |
| committer | |
| log | b845c9d5326bc83691edcb483ac44793b88afe75 |
| tree | a965db64c6d8a6c657d46ab9983cdf88fb0c853f |
| parent | 5d844faf7c5c30555664b4161e5f9a903daaf562 |
4 files changed, 97 insertions(+), 39 deletions(-)
src/codegen/spirv.zig+1-4| ... | ... | @@ -1494,7 +1494,6 @@ pub const DeclGen = struct { |
| 1494 | 1494 | .id_result = decl_id, |
| 1495 | 1495 | .storage_class = actual_storage_class, |
| 1496 | 1496 | }); |
| 1497 | self.spv.globalPtr(spv_decl_index).?.result_id = decl_id; | |
| 1498 | 1497 | |
| 1499 | 1498 | // Now emit the instructions that initialize the variable. |
| 1500 | 1499 | const initializer_id = self.spv.allocId(); |
| ... | ... | @@ -1517,14 +1516,12 @@ pub const DeclGen = struct { |
| 1517 | 1516 | }); |
| 1518 | 1517 | |
| 1519 | 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); | |
| 1521 | 1520 | |
| 1522 | 1521 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| 1523 | 1522 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 1524 | 1523 | try self.spv.addFunction(spv_decl_index, self.func); |
| 1525 | 1524 | |
| 1526 | try self.spv.initializers.append(self.spv.gpa, initializer_id); | |
| 1527 | ||
| 1528 | 1525 | const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); |
| 1529 | 1526 | try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ |
| 1530 | 1527 | .target = decl_id, |
src/codegen/spirv/Module.zig+95-21| ... | ... | @@ -94,6 +94,8 @@ pub const Global = struct { |
| 94 | 94 | begin_inst: u32, |
| 95 | 95 | /// The past-end offset into `self.flobals.section`. |
| 96 | 96 | end_inst: u32, |
| 97 | /// The result-id of the function that initializes this value. | |
| 98 | initializer_id: IdRef, | |
| 97 | 99 | }; |
| 98 | 100 | |
| 99 | 101 | /// This models a kernel entry point. |
| ... | ... | @@ -174,9 +176,6 @@ globals: struct { |
| 174 | 176 | section: Section = .{}, |
| 175 | 177 | } = .{}, |
| 176 | 178 | |
| 177 | /// The function IDs of global variable initializers | |
| 178 | initializers: std.ArrayListUnmanaged(IdRef) = .{}, | |
| 179 | ||
| 180 | 179 | pub fn init(gpa: Allocator, arena: Allocator) Module { |
| 181 | 180 | return .{ |
| 182 | 181 | .gpa = gpa, |
| ... | ... | @@ -205,8 +204,6 @@ pub fn deinit(self: *Module) void { |
| 205 | 204 | self.globals.globals.deinit(self.gpa); |
| 206 | 205 | self.globals.section.deinit(self.gpa); |
| 207 | 206 | |
| 208 | self.initializers.deinit(self.gpa); | |
| 209 | ||
| 210 | 207 | self.* = undefined; |
| 211 | 208 | } |
| 212 | 209 | |
| ... | ... | @@ -289,6 +286,10 @@ fn addEntryPointDeps( |
| 289 | 286 | const decl = self.declPtr(decl_index); |
| 290 | 287 | const deps = self.decl_deps.items[decl.begin_dep..decl.end_dep]; |
| 291 | 288 | |
| 289 | if (seen.isSet(@intFromEnum(decl_index))) { | |
| 290 | return; | |
| 291 | } | |
| 292 | ||
| 292 | 293 | seen.set(@intFromEnum(decl_index)); |
| 293 | 294 | |
| 294 | 295 | if (self.globalPtr(decl_index)) |global| { |
| ... | ... | @@ -296,9 +297,7 @@ fn addEntryPointDeps( |
| 296 | 297 | } |
| 297 | 298 | |
| 298 | 299 | for (deps) |dep| { |
| 299 | if (!seen.isSet(@intFromEnum(dep))) { | |
| 300 | try self.addEntryPointDeps(dep, seen, interface); | |
| 301 | } | |
| 300 | try self.addEntryPointDeps(dep, seen, interface); | |
| 302 | 301 | } |
| 303 | 302 | } |
| 304 | 303 | |
| ... | ... | @@ -330,20 +329,76 @@ fn entryPoints(self: *Module) !Section { |
| 330 | 329 | return entry_points; |
| 331 | 330 | } |
| 332 | 331 | |
| 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 | |
| 340 | fn 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 | 398 | /// Emit this module as a spir-v binary. |
| 334 | 399 | pub fn flush(self: *Module, file: std.fs.File) !void { |
| 335 | 400 | // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" |
| 336 | 401 | |
| 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 | 402 | // TODO: Perform topological sort on the globals. |
| 348 | 403 | var globals = try self.orderGlobals(); |
| 349 | 404 | defer globals.deinit(self.gpa); |
| ... | ... | @@ -354,6 +409,19 @@ pub fn flush(self: *Module, file: std.fs.File) !void { |
| 354 | 409 | var types_constants = try self.cache.materialize(self); |
| 355 | 410 | defer types_constants.deinit(self.gpa); |
| 356 | 411 | |
| 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 | 425 | // Note: needs to be kept in order according to section 2.3! |
| 358 | 426 | const buffers = &[_][]const Word{ |
| 359 | 427 | &header, |
| ... | ... | @@ -368,6 +436,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void { |
| 368 | 436 | self.sections.types_globals_constants.toWords(), |
| 369 | 437 | globals.toWords(), |
| 370 | 438 | self.sections.functions.toWords(), |
| 439 | init_func.toWords(), | |
| 371 | 440 | }; |
| 372 | 441 | |
| 373 | 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 | 598 | .result_id = undefined, |
| 530 | 599 | .begin_inst = undefined, |
| 531 | 600 | .end_inst = undefined, |
| 601 | .initializer_id = undefined, | |
| 532 | 602 | }), |
| 533 | 603 | } |
| 534 | 604 | |
| ... | ... | @@ -558,10 +628,14 @@ pub fn beginGlobal(self: *Module) u32 { |
| 558 | 628 | return @as(u32, @intCast(self.globals.section.instructions.items.len)); |
| 559 | 629 | } |
| 560 | 630 | |
| 561 | pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32) void { | |
| 631 | pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, result_id: IdRef, initializer_id: IdRef) void { | |
| 562 | 632 | 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 | }; | |
| 565 | 639 | } |
| 566 | 640 | |
| 567 | 641 | pub 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 | 48 | test "array concat with undefined" { |
| 49 | 49 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 50 | 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 51 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 52 | 51 | |
| 53 | 52 | const S = struct { |
| 54 | 53 | fn doTheTest() !void { |
| ... | ... | @@ -88,7 +87,6 @@ test "array concat with tuple" { |
| 88 | 87 | |
| 89 | 88 | test "array init with concat" { |
| 90 | 89 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 91 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 92 | 90 | |
| 93 | 91 | const a = 'a'; |
| 94 | 92 | var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; |
| ... | ... | @@ -98,7 +96,6 @@ test "array init with concat" { |
| 98 | 96 | test "array init with mult" { |
| 99 | 97 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 100 | 98 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 101 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 102 | 99 | |
| 103 | 100 | const a = 'a'; |
| 104 | 101 | var i: [8]u8 = [2]u8{ a, 'b' } ** 4; |
| ... | ... | @@ -241,7 +238,6 @@ fn plusOne(x: u32) u32 { |
| 241 | 238 | test "single-item pointer to array indexing and slicing" { |
| 242 | 239 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 243 | 240 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 244 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 245 | 241 | |
| 246 | 242 | try testSingleItemPtrArrayIndexSlice(); |
| 247 | 243 | try comptime testSingleItemPtrArrayIndexSlice(); |
| ... | ... | @@ -384,7 +380,6 @@ test "runtime initialize array elem and then implicit cast to slice" { |
| 384 | 380 | test "array literal as argument to function" { |
| 385 | 381 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 386 | 382 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 387 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 388 | 383 | |
| 389 | 384 | const S = struct { |
| 390 | 385 | fn entry(two: i32) !void { |
| ... | ... | @@ -413,7 +408,6 @@ test "double nested array to const slice cast in array literal" { |
| 413 | 408 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 414 | 409 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 415 | 410 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 416 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 417 | 411 | |
| 418 | 412 | const S = struct { |
| 419 | 413 | fn entry(two: i32) !void { |
| ... | ... | @@ -651,7 +645,6 @@ test "tuple to array handles sentinel" { |
| 651 | 645 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 652 | 646 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 653 | 647 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 654 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 655 | 648 | |
| 656 | 649 | const S = struct { |
| 657 | 650 | const a = .{ 1, 2, 3 }; |
test/behavior/basic.zig+1-7| ... | ... | @@ -330,7 +330,6 @@ const FnPtrWrapper = struct { |
| 330 | 330 | |
| 331 | 331 | test "const ptr from var variable" { |
| 332 | 332 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 333 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 334 | 333 | |
| 335 | 334 | var x: u64 = undefined; |
| 336 | 335 | var y: u64 = undefined; |
| ... | ... | @@ -581,7 +580,7 @@ test "comptime cast fn to ptr" { |
| 581 | 580 | } |
| 582 | 581 | |
| 583 | 582 | test "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; | |
| 585 | 584 | |
| 586 | 585 | var a = &emptyFn; |
| 587 | 586 | try expect(a == a); |
| ... | ... | @@ -639,7 +638,6 @@ test "global constant is loaded with a runtime-known index" { |
| 639 | 638 | |
| 640 | 639 | test "multiline string literal is null terminated" { |
| 641 | 640 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 642 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 643 | 641 | |
| 644 | 642 | const s1 = |
| 645 | 643 | \\one |
| ... | ... | @@ -711,7 +709,6 @@ test "comptime manyptr concatenation" { |
| 711 | 709 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 712 | 710 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 713 | 711 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 714 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 715 | 712 | |
| 716 | 713 | const s = "epic"; |
| 717 | 714 | const actual = manyptrConcat(s); |
| ... | ... | @@ -1027,7 +1024,6 @@ comptime { |
| 1027 | 1024 | |
| 1028 | 1025 | test "switch inside @as gets correct type" { |
| 1029 | 1026 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1030 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 1031 | 1027 | |
| 1032 | 1028 | var a: u32 = 0; |
| 1033 | 1029 | var b: [2]u32 = undefined; |
| ... | ... | @@ -1136,8 +1132,6 @@ test "orelse coercion as function argument" { |
| 1136 | 1132 | } |
| 1137 | 1133 | |
| 1138 | 1134 | test "runtime-known globals initialized with undefined" { |
| 1139 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 1140 | ||
| 1141 | 1135 | const S = struct { |
| 1142 | 1136 | var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; |
| 1143 | 1137 | var vp: [*]u32 = undefined; |