authorgravatar for gwenzek@users.noreply.github.comgwenzek <gwenzek@users.noreply.github.com> 2022-02-05 15:33:00+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-05 16:33:00+02:00
log0e1afb4d986c3316c6f024be50612b0a6c66777b
tree7b2bc69ae80604165ce4d11bf72ef104d4b51f86
parentfbc06f9c9151205896fb167b087506d6580946c4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

stage2: add support for Nvptx target

sample command: /home/guw/github/zig/stage2/bin/zig build-obj cuda_kernel.zig -target nvptx64-cuda -O ReleaseSafe this will create a kernel.ptx expose PtxKernel call convention from LLVM kernels are `export fn f() callconv(.PtxKernel)`

12 files changed, 185 insertions(+), 8 deletions(-)

lib/std/builtin.zig+1
......@@ -147,6 +147,7 @@ pub const CallingConvention = enum {
147147 AAPCS,
148148 AAPCSVFP,
149149 SysV,
150 PtxKernel,
150151};
151152
152153/// This data structure is used by the Zig language code generation and
lib/std/target.zig+4
......@@ -579,6 +579,8 @@ pub const Target = struct {
579579 raw,
580580 /// Plan 9 from Bell Labs
581581 plan9,
582 /// Nvidia PTX format
583 nvptx,
582584
583585 pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 {
584586 return switch (of) {
......@@ -589,6 +591,7 @@ pub const Target = struct {
589591 .hex => ".ihex",
590592 .raw => ".bin",
591593 .plan9 => plan9Ext(cpu_arch),
594 .nvptx => ".ptx",
592595 };
593596 }
594597 };
......@@ -1388,6 +1391,7 @@ pub const Target = struct {
13881391 else => return switch (cpu_arch) {
13891392 .wasm32, .wasm64 => .wasm,
13901393 .spirv32, .spirv64 => .spirv,
1394 .nvptx, .nvptx64 => .nvptx,
13911395 else => .elf,
13921396 },
13931397 };
lib/std/zig.zig+1
......@@ -181,6 +181,7 @@ pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error
181181 .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, ofmt.fileExt(target.cpu.arch) }),
182182 .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ target.libPrefix(), root_name }),
183183 },
184 .nvptx => return std.fmt.allocPrint(allocator, "{s}", .{root_name}),
184185 }
185186}
186187
src/Module.zig+5-1
......@@ -4242,7 +4242,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
42424242 // in `Decl` to notice that the line number did not change.
42434243 mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
42444244 },
4245 .c, .wasm, .spirv => {},
4245 .c, .wasm, .spirv, .nvptx => {},
42464246 }
42474247 }
42484248}
......@@ -4316,6 +4316,7 @@ pub fn clearDecl(
43164316 .c => .{ .c = {} },
43174317 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
43184318 .spirv => .{ .spirv = {} },
4319 .nvptx => .{ .nvptx = {} },
43194320 };
43204321 decl.fn_link = switch (mod.comp.bin_file.tag) {
43214322 .coff => .{ .coff = {} },
......@@ -4325,6 +4326,7 @@ pub fn clearDecl(
43254326 .c => .{ .c = {} },
43264327 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
43274328 .spirv => .{ .spirv = .{} },
4329 .nvptx => .{ .nvptx = .{} },
43284330 };
43294331 }
43304332 if (decl.getInnerNamespace()) |namespace| {
......@@ -4652,6 +4654,7 @@ pub fn allocateNewDecl(
46524654 .c => .{ .c = {} },
46534655 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
46544656 .spirv => .{ .spirv = {} },
4657 .nvptx => .{ .nvptx = {} },
46554658 },
46564659 .fn_link = switch (mod.comp.bin_file.tag) {
46574660 .coff => .{ .coff = {} },
......@@ -4661,6 +4664,7 @@ pub fn allocateNewDecl(
46614664 .c => .{ .c = {} },
46624665 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
46634666 .spirv => .{ .spirv = .{} },
4667 .nvptx => .{ .nvptx = .{} },
46644668 },
46654669 .generation = 0,
46664670 .is_pub = false,
src/Sema.zig+1
......@@ -3724,6 +3724,7 @@ pub fn analyzeExport(
37243724 .c => .{ .c = {} },
37253725 .wasm => .{ .wasm = {} },
37263726 .spirv => .{ .spirv = {} },
3727 .nvptx => .{ .nvptx = {} },
37273728 },
37283729 .owner_decl = owner_decl,
37293730 .src_decl = src_decl,
src/codegen/llvm.zig+5-1
......@@ -378,7 +378,7 @@ pub const Object = struct {
378378 const mod = comp.bin_file.options.module.?;
379379 const cache_dir = mod.zig_cache_artifact_directory;
380380
381 const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
381 var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit|
382382 try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?))
383383 else
384384 null;
......@@ -5078,6 +5078,10 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
50785078 },
50795079 .Signal => .AVR_SIGNAL,
50805080 .SysV => .X86_64_SysV,
5081 .PtxKernel => return switch (target.cpu.arch) {
5082 .nvptx, .nvptx64 => .PTX_Kernel,
5083 else => unreachable,
5084 },
50815085 };
50825086}
50835087
src/link.zig+25-5
......@@ -215,6 +215,7 @@ pub const File = struct {
215215 c: void,
216216 wasm: Wasm.DeclBlock,
217217 spirv: void,
218 nvptx: void,
218219 };
219220
220221 pub const LinkFn = union {
......@@ -225,6 +226,7 @@ pub const File = struct {
225226 c: void,
226227 wasm: Wasm.FnData,
227228 spirv: SpirV.FnData,
229 nvptx: void,
228230 };
229231
230232 pub const Export = union {
......@@ -235,6 +237,7 @@ pub const File = struct {
235237 c: void,
236238 wasm: void,
237239 spirv: void,
240 nvptx: void,
238241 };
239242
240243 /// For DWARF .debug_info.
......@@ -274,6 +277,7 @@ pub const File = struct {
274277 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,
275278 .c => unreachable, // Reported error earlier.
276279 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
280 .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
277281 .hex => return error.HexObjectFormatUnimplemented,
278282 .raw => return error.RawObjectFormatUnimplemented,
279283 };
......@@ -292,6 +296,7 @@ pub const File = struct {
292296 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
293297 .c => unreachable, // Reported error earlier.
294298 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
299 .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
295300 .hex => return error.HexObjectFormatUnimplemented,
296301 .raw => return error.RawObjectFormatUnimplemented,
297302 };
......@@ -312,6 +317,7 @@ pub const File = struct {
312317 .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base,
313318 .c => &(try C.openPath(allocator, sub_path, options)).base,
314319 .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base,
320 .nvptx => &(try NvPtx.openPath(allocator, sub_path, options)).base,
315321 .hex => return error.HexObjectFormatUnimplemented,
316322 .raw => return error.RawObjectFormatUnimplemented,
317323 };
......@@ -344,7 +350,7 @@ pub const File = struct {
344350 .mode = determineMode(base.options),
345351 });
346352 },
347 .c, .wasm, .spirv => {},
353 .c, .wasm, .spirv, .nvptx => {},
348354 }
349355 }
350356
......@@ -389,7 +395,7 @@ pub const File = struct {
389395 f.close();
390396 base.file = null;
391397 },
392 .c, .wasm, .spirv => {},
398 .c, .wasm, .spirv, .nvptx => {},
393399 }
394400 }
395401
......@@ -437,6 +443,7 @@ pub const File = struct {
437443 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl),
438444 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
439445 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl),
446 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl),
440447 // zig fmt: on
441448 }
442449 }
......@@ -456,6 +463,7 @@ pub const File = struct {
456463 .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func, air, liveness),
457464 .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func, air, liveness),
458465 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func, air, liveness),
466 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func, air, liveness),
459467 // zig fmt: on
460468 }
461469 }
......@@ -471,7 +479,7 @@ pub const File = struct {
471479 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
472480 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
473481 .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"),
474 .wasm, .spirv => {},
482 .wasm, .spirv, .nvptx => {},
475483 }
476484 }
477485
......@@ -493,7 +501,7 @@ pub const File = struct {
493501 },
494502 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
495503 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
496 .c, .spirv => {},
504 .c, .spirv, .nvptx => {},
497505 }
498506 }
499507
......@@ -551,6 +559,11 @@ pub const File = struct {
551559 parent.deinit();
552560 base.allocator.destroy(parent);
553561 },
562 .nvptx => {
563 const parent = @fieldParentPtr(NvPtx, "base", base);
564 parent.deinit();
565 base.allocator.destroy(parent);
566 },
554567 }
555568 }
556569
......@@ -584,6 +597,7 @@ pub const File = struct {
584597 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp),
585598 .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp),
586599 .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp),
600 .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp),
587601 }
588602 }
589603
......@@ -598,6 +612,7 @@ pub const File = struct {
598612 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp),
599613 .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp),
600614 .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp),
615 .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp),
601616 }
602617 }
603618
......@@ -612,6 +627,7 @@ pub const File = struct {
612627 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
613628 .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl),
614629 .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl),
630 .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl),
615631 }
616632 }
617633
......@@ -622,7 +638,7 @@ pub const File = struct {
622638 .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
623639 .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags,
624640 .c => return .{ .no_entry_point_found = false },
625 .wasm, .spirv => return ErrorFlags{},
641 .wasm, .spirv, .nvptx => return ErrorFlags{},
626642 }
627643 }
628644
......@@ -644,6 +660,7 @@ pub const File = struct {
644660 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
645661 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports),
646662 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports),
663 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl, exports),
647664 }
648665 }
649666
......@@ -656,6 +673,7 @@ pub const File = struct {
656673 .c => unreachable,
657674 .wasm => unreachable,
658675 .spirv => unreachable,
676 .nvptx => unreachable,
659677 }
660678 }
661679
......@@ -851,6 +869,7 @@ pub const File = struct {
851869 wasm,
852870 spirv,
853871 plan9,
872 nvptx,
854873 };
855874
856875 pub const ErrorFlags = struct {
......@@ -864,6 +883,7 @@ pub const File = struct {
864883 pub const MachO = @import("link/MachO.zig");
865884 pub const SpirV = @import("link/SpirV.zig");
866885 pub const Wasm = @import("link/Wasm.zig");
886 pub const NvPtx = @import("link/NvPtx.zig");
867887};
868888
869889pub fn determineMode(options: Options) fs.File.Mode {
src/link/NvPtx.zig created+122
......@@ -0,0 +1,122 @@
1//! NVidia PTX (Paralle Thread Execution)
2//! https://docs.nvidia.com/cuda/parallel-thread-execution/index.html
3//! For this we rely on the nvptx backend of LLVM
4//! Kernel functions need to be marked both as "export" and "callconv(.PtxKernel)"
5
6const NvPtx = @This();
7
8const std = @import("std");
9const builtin = @import("builtin");
10
11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;
13const log = std.log.scoped(.link);
14
15const Module = @import("../Module.zig");
16const Compilation = @import("../Compilation.zig");
17const link = @import("../link.zig");
18const trace = @import("../tracy.zig").trace;
19const build_options = @import("build_options");
20const Air = @import("../Air.zig");
21const Liveness = @import("../Liveness.zig");
22const LlvmObject = @import("../codegen/llvm.zig").Object;
23
24base: link.File,
25llvm_object: *LlvmObject,
26
27pub fn createEmpty(gpa: Allocator, options: link.Options) !*NvPtx {
28 if (!build_options.have_llvm) return error.TODOArchNotSupported;
29
30 const nvptx = try gpa.create(NvPtx);
31 nvptx.* = .{
32 .base = .{
33 .tag = .nvptx,
34 .options = options,
35 .file = null,
36 .allocator = gpa,
37 },
38 .llvm_object = undefined,
39 };
40
41 switch (options.target.cpu.arch) {
42 .nvptx, .nvptx64 => {},
43 else => return error.TODOArchNotSupported,
44 }
45
46 switch (options.target.os.tag) {
47 // TODO: does it also work with nvcl ?
48 .cuda => {},
49 else => return error.TODOOsNotSupported,
50 }
51
52 return nvptx;
53}
54
55pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*NvPtx {
56 if (!build_options.have_llvm) @panic("nvptx target requires a zig compiler with llvm enabled.");
57 if (!options.use_llvm) return error.TODOArchNotSupported;
58 assert(options.object_format == .nvptx);
59
60 const nvptx = try createEmpty(allocator, options);
61 errdefer nvptx.base.destroy();
62 log.info("Opening .ptx target file {s}", .{sub_path});
63 nvptx.llvm_object = try LlvmObject.create(allocator, options);
64 return nvptx;
65}
66
67pub fn deinit(self: *NvPtx) void {
68 if (!build_options.have_llvm) return;
69 self.llvm_object.destroy(self.base.allocator);
70}
71
72pub fn updateFunc(self: *NvPtx, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
73 if (!build_options.have_llvm) return;
74 try self.llvm_object.updateFunc(module, func, air, liveness);
75}
76
77pub fn updateDecl(self: *NvPtx, module: *Module, decl: *Module.Decl) !void {
78 if (!build_options.have_llvm) return;
79 return self.llvm_object.updateDecl(module, decl);
80}
81
82pub fn updateDeclExports(
83 self: *NvPtx,
84 module: *Module,
85 decl: *const Module.Decl,
86 exports: []const *Module.Export,
87) !void {
88 if (!build_options.have_llvm) return;
89 if (build_options.skip_non_native and builtin.object_format != .nvptx) {
90 @panic("Attempted to compile for object format that was disabled by build configuration");
91 }
92 return self.llvm_object.updateDeclExports(module, decl, exports);
93}
94
95pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void {
96 if (!build_options.have_llvm) return;
97 return self.llvm_object.freeDecl(decl);
98}
99
100pub fn flush(self: *NvPtx, comp: *Compilation) !void {
101 return self.flushModule(comp);
102}
103
104pub fn flushModule(self: *NvPtx, comp: *Compilation) !void {
105 if (!build_options.have_llvm) return;
106 if (build_options.skip_non_native) {
107 @panic("Attempted to compile for architecture that was disabled by build configuration");
108 }
109 const tracy = trace(@src());
110 defer tracy.end();
111
112 var hack_comp = comp;
113 if (comp.bin_file.options.emit) |emit| {
114 hack_comp.emit_asm = .{
115 .directory = emit.directory,
116 .basename = comp.bin_file.intermediary_basename.?,
117 };
118 hack_comp.bin_file.options.emit = null;
119 }
120
121 return try self.llvm_object.flushModule(hack_comp);
122}
src/stage1/all_types.hpp+2-1
......@@ -83,7 +83,8 @@ enum CallingConvention {
8383 CallingConventionAPCS,
8484 CallingConventionAAPCS,
8585 CallingConventionAAPCSVFP,
86 CallingConventionSysV
86 CallingConventionSysV,
87 CallingConventionPtxKernel
8788};
8889
8990// Stage 1 supports only the generic address space
src/stage1/analyze.cpp+12
......@@ -991,6 +991,7 @@ const char *calling_convention_name(CallingConvention cc) {
991991 case CallingConventionAAPCSVFP: return "AAPCSVFP";
992992 case CallingConventionInline: return "Inline";
993993 case CallingConventionSysV: return "SysV";
994 case CallingConventionPtxKernel: return "PtxKernel";
994995 }
995996 zig_unreachable();
996997}
......@@ -1000,6 +1001,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
10001001 case CallingConventionUnspecified:
10011002 case CallingConventionAsync:
10021003 case CallingConventionInline:
1004 case CallingConventionPtxKernel:
10031005 return true;
10041006 case CallingConventionC:
10051007 case CallingConventionNaked:
......@@ -2006,6 +2008,15 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
20062008 case CallingConventionSysV:
20072009 if (g->zig_target->arch != ZigLLVM_x86_64)
20082010 allowed_platforms = "x86_64";
2011 break;
2012 case CallingConventionPtxKernel:
2013 if (g->zig_target->arch != ZigLLVM_nvptx
2014 && g->zig_target->arch != ZigLLVM_nvptx64)
2015 {
2016 allowed_platforms = "nvptx and nvptx64";
2017 }
2018 break;
2019
20092020 }
20102021 if (allowed_platforms != nullptr) {
20112022 add_node_error(g, source_node, buf_sprintf(
......@@ -3827,6 +3838,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
38273838 case CallingConventionAAPCS:
38283839 case CallingConventionAAPCSVFP:
38293840 case CallingConventionSysV:
3841 case CallingConventionPtxKernel:
38303842 add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name),
38313843 GlobalLinkageIdStrong, fn_cc);
38323844 break;
src/stage1/codegen.cpp+6
......@@ -209,6 +209,11 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
209209 case CallingConventionSysV:
210210 assert(g->zig_target->arch == ZigLLVM_x86_64);
211211 return ZigLLVM_X86_64_SysV;
212 case CallingConventionPtxKernel:
213 assert(g->zig_target->arch == ZigLLVM_nvptx ||
214 g->zig_target->arch == ZigLLVM_nvptx64);
215 return ZigLLVM_PTX_Kernel;
216
212217 }
213218 zig_unreachable();
214219}
......@@ -354,6 +359,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
354359 case CallingConventionAAPCS:
355360 case CallingConventionAAPCSVFP:
356361 case CallingConventionSysV:
362 case CallingConventionPtxKernel:
357363 return true;
358364 case CallingConventionAsync:
359365 case CallingConventionUnspecified:
src/stage1/ir.cpp+1
......@@ -11666,6 +11666,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns
1166611666 case CallingConventionAAPCS:
1166711667 case CallingConventionAAPCSVFP:
1166811668 case CallingConventionSysV:
11669 case CallingConventionPtxKernel:
1166911670 add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc);
1167011671 fn_entry->section_name = section_name;
1167111672 break;