authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-10-15 13:53:04-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-10-15 13:53:04-04:00
logfeab1ebe1bfea3955320a6acba69f0c6c79c0730
tree023f71b0c26e3a0ea63b607aedb97b132c9c114e
parent65f860bef7995a6120e49606d549bdf154bca150
parentc289794f0db3e06c568450cc6c646a0ba63a73ba
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12878 from gwenzek/ptx

Update Nvptx backend for Zig 0.10

9 files changed, 91 insertions(+), 31 deletions(-)

lib/std/builtin.zig+1
...@@ -833,6 +833,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr...@@ -833,6 +833,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
833 // Didn't have boot_services, just fallback to whatever.833 // Didn't have boot_services, just fallback to whatever.
834 std.os.abort();834 std.os.abort();
835 },835 },
836 .cuda => std.os.abort(),
836 else => {837 else => {
837 const first_trace_addr = ret_addr orelse @returnAddress();838 const first_trace_addr = ret_addr orelse @returnAddress();
838 std.debug.panicImpl(error_return_trace, first_trace_addr, msg);839 std.debug.panicImpl(error_return_trace, first_trace_addr, msg);
lib/std/os.zig+6
...@@ -500,10 +500,16 @@ pub fn abort() noreturn {...@@ -500,10 +500,16 @@ pub fn abort() noreturn {
500 @breakpoint();500 @breakpoint();
501 exit(1);501 exit(1);
502 }502 }
503 if (builtin.os.tag == .cuda) {
504 // TODO: introduce `@trap` instead of abusing https://github.com/ziglang/zig/issues/2291
505 @"llvm.trap"();
506 }
503507
504 system.abort();508 system.abort();
505}509}
506510
511extern fn @"llvm.trap"() noreturn;
512
507pub const RaiseError = UnexpectedError;513pub const RaiseError = UnexpectedError;
508514
509pub fn raise(sig: u8) RaiseError!void {515pub fn raise(sig: u8) RaiseError!void {
lib/std/target.zig+7
...@@ -951,6 +951,13 @@ pub const Target = struct {...@@ -951,6 +951,13 @@ pub const Target = struct {
951 };951 };
952 }952 }
953953
954 pub fn isNvptx(arch: Arch) bool {
955 return switch (arch) {
956 .nvptx, .nvptx64 => true,
957 else => false,
958 };
959 }
960
954 pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model {961 pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model {
955 for (arch.allCpuModels()) |cpu| {962 for (arch.allCpuModels()) |cpu| {
956 if (mem.eql(u8, cpu_name, cpu.name)) {963 if (mem.eql(u8, cpu_name, cpu.name)) {
src/Module.zig+9
...@@ -720,6 +720,15 @@ pub const Decl = struct {...@@ -720,6 +720,15 @@ pub const Decl = struct {
720 var buffer = std.ArrayList(u8).init(mod.gpa);720 var buffer = std.ArrayList(u8).init(mod.gpa);
721 defer buffer.deinit();721 defer buffer.deinit();
722 try decl.renderFullyQualifiedName(mod, buffer.writer());722 try decl.renderFullyQualifiedName(mod, buffer.writer());
723
724 // Sanitize the name for nvptx which is more restrictive.
725 if (mod.comp.bin_file.options.target.cpu.arch.isNvptx()) {
726 for (buffer.items) |*byte| switch (byte.*) {
727 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_',
728 else => {},
729 };
730 }
731
723 return buffer.toOwnedSliceSentinel(0);732 return buffer.toOwnedSliceSentinel(0);
724 }733 }
725734
src/Sema.zig+6-7
...@@ -18202,12 +18202,6 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst...@@ -18202,12 +18202,6 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
18202 else18202 else
18203 dest_ptr_ty;18203 dest_ptr_ty;
1820418204
18205 if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |val| {
18206 // Pointer value should compatible with both address spaces.
18207 // TODO: Figure out why this generates an invalid bitcast.
18208 return sema.addConstant(dest_ty, val);
18209 }
18210
18211 try sema.requireRuntimeBlock(block, src, ptr_src);18205 try sema.requireRuntimeBlock(block, src, ptr_src);
18212 // TODO: Address space cast safety?18206 // TODO: Address space cast safety?
1821318207
...@@ -21397,7 +21391,12 @@ fn validateExternType(...@@ -21397,7 +21391,12 @@ fn validateExternType(
21397 },21391 },
21398 .Fn => {21392 .Fn => {
21399 if (position != .other) return false;21393 if (position != .other) return false;
21400 return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention());21394 return switch (ty.fnCallingConvention()) {
21395 // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
21396 // The goal is to experiment with more integrated CPU/GPU code.
21397 .PtxKernel => true,
21398 else => !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()),
21399 };
21401 },21400 },
21402 .Enum => {21401 .Enum => {
21403 var buf: Type.Payload.Bits = undefined;21402 var buf: Type.Payload.Bits = undefined;
src/link/NvPtx.zig+17-15
...@@ -28,10 +28,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*NvPtx {...@@ -28,10 +28,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*NvPtx {
28 if (!build_options.have_llvm) return error.PtxArchNotSupported;28 if (!build_options.have_llvm) return error.PtxArchNotSupported;
29 if (!options.use_llvm) return error.PtxArchNotSupported;29 if (!options.use_llvm) return error.PtxArchNotSupported;
3030
31 switch (options.target.cpu.arch) {31 if (!options.target.cpu.arch.isNvptx()) return error.PtxArchNotSupported;
32 .nvptx, .nvptx64 => {},
33 else => return error.PtxArchNotSupported,
34 }
3532
36 switch (options.target.os.tag) {33 switch (options.target.os.tag) {
37 // TODO: does it also work with nvcl ?34 // TODO: does it also work with nvcl ?
...@@ -59,9 +56,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -59,9 +56,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
59 if (!options.use_llvm) return error.PtxArchNotSupported;56 if (!options.use_llvm) return error.PtxArchNotSupported;
60 assert(options.target.ofmt == .nvptx);57 assert(options.target.ofmt == .nvptx);
6158
62 const nvptx = try createEmpty(allocator, options);59 log.debug("Opening .ptx target file {s}", .{sub_path});
63 log.info("Opening .ptx target file {s}", .{sub_path});60 return createEmpty(allocator, options);
64 return nvptx;
65}61}
6662
67pub fn deinit(self: *NvPtx) void {63pub fn deinit(self: *NvPtx) void {
...@@ -109,13 +105,19 @@ pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.No...@@ -109,13 +105,19 @@ pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.No
109 const tracy = trace(@src());105 const tracy = trace(@src());
110 defer tracy.end();106 defer tracy.end();
111107
112 var hack_comp = comp;108 const outfile = comp.bin_file.options.emit.?;
113 if (comp.bin_file.options.emit) |emit| {109 // We modify 'comp' before passing it to LLVM, but restore value afterwards.
114 hack_comp.emit_asm = .{110 // We tell LLVM to not try to build a .o, only an "assembly" file.
115 .directory = emit.directory,111 // This is required by the LLVM PTX backend.
116 .basename = comp.bin_file.intermediary_basename.?,112 comp.bin_file.options.emit = null;
117 };113 comp.emit_asm = .{
118 hack_comp.bin_file.options.emit = null;114 .directory = outfile.directory,
115 .basename = comp.bin_file.intermediary_basename.?,
116 };
117 defer {
118 comp.bin_file.options.emit = outfile;
119 comp.emit_asm = null;
119 }120 }
120 return try self.llvm_object.flushModule(hack_comp, prog_node);121
122 try self.llvm_object.flushModule(comp, prog_node);
121}123}
src/target.zig+6-2
...@@ -411,7 +411,11 @@ pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerR...@@ -411,7 +411,11 @@ pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerR
411}411}
412412
413pub fn hasDebugInfo(target: std.Target) bool {413pub fn hasDebugInfo(target: std.Target) bool {
414 _ = target;414 if (target.cpu.arch.isNvptx()) {
415 // TODO: not sure how to test "ptx >= 7.5" with featureset
416 return std.Target.nvptx.featureSetHas(target.cpu.features, .ptx75);
417 }
418
415 return true;419 return true;
416}420}
417421
...@@ -651,7 +655,7 @@ pub fn addrSpaceCastIsValid(...@@ -651,7 +655,7 @@ pub fn addrSpaceCastIsValid(
651 const arch = target.cpu.arch;655 const arch = target.cpu.arch;
652 switch (arch) {656 switch (arch) {
653 .x86_64, .i386 => return arch.supportsAddressSpace(from) and arch.supportsAddressSpace(to),657 .x86_64, .i386 => return arch.supportsAddressSpace(from) and arch.supportsAddressSpace(to),
654 .amdgcn => {658 .nvptx64, .nvptx, .amdgcn => {
655 const to_generic = arch.supportsAddressSpace(from) and to == .generic;659 const to_generic = arch.supportsAddressSpace(from) and to == .generic;
656 const from_generic = arch.supportsAddressSpace(to) and from == .generic;660 const from_generic = arch.supportsAddressSpace(to) and from == .generic;
657 return to_generic or from_generic;661 return to_generic or from_generic;
test/cases.zig+1-2
...@@ -4,6 +4,5 @@ const TestContext = @import("../src/test.zig").TestContext;...@@ -4,6 +4,5 @@ const TestContext = @import("../src/test.zig").TestContext;
4pub fn addCases(ctx: *TestContext) !void {4pub fn addCases(ctx: *TestContext) !void {
5 try @import("compile_errors.zig").addCases(ctx);5 try @import("compile_errors.zig").addCases(ctx);
6 try @import("stage2/cbe.zig").addCases(ctx);6 try @import("stage2/cbe.zig").addCases(ctx);
7 // https://github.com/ziglang/zig/issues/109687 try @import("stage2/nvptx.zig").addCases(ctx);
8 //try @import("stage2/nvptx.zig").addCases(ctx);
9}8}
test/stage2/nvptx.zig+38-5
...@@ -23,11 +23,10 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -23,11 +23,10 @@ pub fn addCases(ctx: *TestContext) !void {
23 var case = addPtx(ctx, "nvptx: read special registers");23 var case = addPtx(ctx, "nvptx: read special registers");
2424
25 case.compiles(25 case.compiles(
26 \\fn threadIdX() usize {26 \\fn threadIdX() u32 {
27 \\ var tid = asm volatile ("mov.u32 \t$0, %tid.x;"27 \\ return asm ("mov.u32 \t%[r], %tid.x;"
28 \\ : [ret] "=r" (-> u32),28 \\ : [r] "=r" (-> u32),
29 \\ );29 \\ );
30 \\ return @as(usize, tid);
31 \\}30 \\}
32 \\31 \\
33 \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void {32 \\pub export fn special_reg(a: []const i32, out: []i32) callconv(.PtxKernel) void {
...@@ -49,6 +48,38 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -49,6 +48,38 @@ pub fn addCases(ctx: *TestContext) !void {
49 \\}48 \\}
50 );49 );
51 }50 }
51
52 {
53 var case = addPtx(ctx, "nvptx: reduce in shared mem");
54 case.compiles(
55 \\fn threadIdX() u32 {
56 \\ return asm ("mov.u32 \t%[r], %tid.x;"
57 \\ : [r] "=r" (-> u32),
58 \\ );
59 \\}
60 \\
61 \\ var _sdata: [1024]f32 addrspace(.shared) = undefined;
62 \\ pub export fn reduceSum(d_x: []const f32, out: *f32) callconv(.PtxKernel) void {
63 \\ var sdata = @addrSpaceCast(.generic, &_sdata);
64 \\ const tid: u32 = threadIdX();
65 \\ var sum = d_x[tid];
66 \\ sdata[tid] = sum;
67 \\ asm volatile ("bar.sync \t0;");
68 \\ var s: u32 = 512;
69 \\ while (s > 0) : (s = s >> 1) {
70 \\ if (tid < s) {
71 \\ sum += sdata[tid + s];
72 \\ sdata[tid] = sum;
73 \\ }
74 \\ asm volatile ("bar.sync \t0;");
75 \\ }
76 \\
77 \\ if (tid == 0) {
78 \\ out.* = sum;
79 \\ }
80 \\ }
81 );
82 }
52}83}
5384
54const nvptx_target = std.zig.CrossTarget{85const nvptx_target = std.zig.CrossTarget{
...@@ -68,6 +99,8 @@ pub fn addPtx(...@@ -68,6 +99,8 @@ pub fn addPtx(
68 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),99 .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator),
69 .link_libc = false,100 .link_libc = false,
70 .backend = .llvm,101 .backend = .llvm,
102 // Bug in Debug mode
103 .optimize_mode = .ReleaseSafe,
71 }) catch @panic("out of memory");104 }) catch @panic("out of memory");
72 return &ctx.cases.items[ctx.cases.items.len - 1];105 return &ctx.cases.items[ctx.cases.items.len - 1];
73}106}