authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-18 13:22:44+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-22 16:11:56+02:00
log9ddd7f4a60c70c1bf146c2fd0c35b32098755f77
tree2b40d94ef8bf05aea3adfb8abf9ef7d7ada885a3
parentbcda3c5b828fcb631e739e9c820a56aaaf0fd4df

SPIR-V: Put types in SPIRVModule, some general restructuring


2 files changed, 70 insertions(+), 35 deletions(-)

src/codegen/spirv.zig+65-29
...@@ -15,7 +15,7 @@ const ir = @import("../ir.zig");...@@ -15,7 +15,7 @@ const ir = @import("../ir.zig");
15const Inst = ir.Inst;15const Inst = ir.Inst;
1616
17pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);17pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
18pub const ValueMap = std.AutoHashMap(*Inst, u32);18pub const InstMap = std.AutoHashMap(*Inst, u32);
1919
20pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void {20pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void {
21 const word_count = arg_count + 1;21 const word_count = arg_count + 1;
...@@ -27,24 +27,34 @@ pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const...@@ -27,24 +27,34 @@ pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const
27 try code.appendSlice(args);27 try code.appendSlice(args);
28}28}
2929
30/// This structure represents a SPIR-V binary module being compiled, and keeps track of relevant information30/// This structure represents a SPIR-V (binary) module being compiled, and keeps track of all relevant information.
31/// such as code for the different logical sections, and the next result-id.31/// That includes the actual instructions, the current result-id bound, and data structures for querying result-id's
32/// of data which needs to be persistent over different calls to Decl code generation.
32pub const SPIRVModule = struct {33pub const SPIRVModule = struct {
33 next_result_id: u32,34 next_result_id: u32,
34 types_globals_constants: std.ArrayList(u32),
35 fn_decls: std.ArrayList(u32),
3635
37 pub fn init(allocator: *Allocator) SPIRVModule {36 binary: struct {
37 types_globals_constants: std.ArrayList(u32),
38 fn_decls: std.ArrayList(u32),
39 },
40
41 types: TypeMap,
42
43 pub fn init(gpa: *Allocator) SPIRVModule {
38 return .{44 return .{
39 .next_result_id = 1, // 0 is an invalid SPIR-V result ID.45 .next_result_id = 1, // 0 is an invalid SPIR-V result ID.
40 .types_globals_constants = std.ArrayList(u32).init(allocator),46 .binary = .{
41 .fn_decls = std.ArrayList(u32).init(allocator),47 .types_globals_constants = std.ArrayList(u32).init(gpa),
48 .fn_decls = std.ArrayList(u32).init(gpa),
49 },
50 .types = TypeMap.init(gpa),
42 };51 };
43 }52 }
4453
45 pub fn deinit(self: *SPIRVModule) void {54 pub fn deinit(self: *SPIRVModule) void {
46 self.types_globals_constants.deinit();55 self.binary.types_globals_constants.deinit();
47 self.fn_decls.deinit();56 self.binary.fn_decls.deinit();
57 self.types.deinit();
48 }58 }
4959
50 pub fn allocResultId(self: *SPIRVModule) u32 {60 pub fn allocResultId(self: *SPIRVModule) u32 {
...@@ -59,18 +69,29 @@ pub const SPIRVModule = struct {...@@ -59,18 +69,29 @@ pub const SPIRVModule = struct {
5969
60/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.70/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
61pub const DeclGen = struct {71pub const DeclGen = struct {
72 /// The parent module.
62 module: *Module,73 module: *Module,
74
75 /// The SPIR-V module code should be put in.
63 spv: *SPIRVModule,76 spv: *SPIRVModule,
6477
78 /// An array of function argument result-ids. Each index corresponds with the function argument of the same index.
65 args: std.ArrayList(u32),79 args: std.ArrayList(u32),
80
81 /// A counter to keep track of how many `arg` instructions we've seen yet.
66 next_arg_index: u32,82 next_arg_index: u32,
6783
68 types: TypeMap,84 /// A map keeping track of which instruction generated which result-id.
69 values: ValueMap,85 inst_results: InstMap,
7086
87 /// The decl we are currently generating code for.
71 decl: *Decl,88 decl: *Decl,
89
90 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message. Memory is owned by
91 /// `module.gpa`.
72 error_msg: ?*Module.ErrorMsg,92 error_msg: ?*Module.ErrorMsg,
7393
94 /// Possible errors the `gen` function may return.
74 const Error = error{ AnalysisFail, OutOfMemory };95 const Error = error{ AnalysisFail, OutOfMemory };
7596
76 /// This structure is used to return information about a type typically used for arithmetic operations.97 /// This structure is used to return information about a type typically used for arithmetic operations.
...@@ -129,7 +150,7 @@ pub const DeclGen = struct {...@@ -129,7 +150,7 @@ pub const DeclGen = struct {
129 return self.genConstant(inst.ty, val);150 return self.genConstant(inst.ty, val);
130 }151 }
131152
132 return self.values.get(inst).?; // Instruction does not dominate all uses!153 return self.inst_results.get(inst).?; // Instruction does not dominate all uses!
133 }154 }
134155
135 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need156 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need
...@@ -145,7 +166,7 @@ pub const DeclGen = struct {...@@ -145,7 +166,7 @@ pub const DeclGen = struct {
145 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {166 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {
146 const target = self.module.getTarget();167 const target = self.module.getTarget();
147168
148 // TODO: Figure out what to do with u0/i0.169 // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
149 std.debug.assert(bits != 0);170 std.debug.assert(bits != 0);
150171
151 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.172 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
...@@ -194,7 +215,6 @@ pub const DeclGen = struct {...@@ -194,7 +215,6 @@ pub const DeclGen = struct {
194215
195 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {216 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {
196 const target = self.module.getTarget();217 const target = self.module.getTarget();
197
198 return switch (ty.zigTypeTag()) {218 return switch (ty.zigTypeTag()) {
199 .Bool => ArithmeticTypeInfo{219 .Bool => ArithmeticTypeInfo{
200 .bits = 1, // Doesn't matter for this class.220 .bits = 1, // Doesn't matter for this class.
...@@ -231,7 +251,7 @@ pub const DeclGen = struct {...@@ -231,7 +251,7 @@ pub const DeclGen = struct {
231 /// TODO: Deduplication?251 /// TODO: Deduplication?
232 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 {252 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 {
233 const target = self.module.getTarget();253 const target = self.module.getTarget();
234 const code = &self.spv.types_globals_constants;254 const code = &self.spv.binary.types_globals_constants;
235 const result_id = self.spv.allocResultId();255 const result_id = self.spv.allocResultId();
236 const result_type_id = try self.getOrGenType(ty);256 const result_type_id = try self.getOrGenType(ty);
237257
...@@ -276,12 +296,12 @@ pub const DeclGen = struct {...@@ -276,12 +296,12 @@ pub const DeclGen = struct {
276296
277 fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 {297 fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 {
278 // We can't use getOrPut here so we can recursively generate types.298 // We can't use getOrPut here so we can recursively generate types.
279 if (self.types.get(ty)) |already_generated| {299 if (self.spv.types.get(ty)) |already_generated| {
280 return already_generated;300 return already_generated;
281 }301 }
282302
283 const target = self.module.getTarget();303 const target = self.module.getTarget();
284 const code = &self.spv.types_globals_constants;304 const code = &self.spv.binary.types_globals_constants;
285 const result_id = self.spv.allocResultId();305 const result_id = self.spv.allocResultId();
286306
287 switch (ty.zigTypeTag()) {307 switch (ty.zigTypeTag()) {
...@@ -345,7 +365,7 @@ pub const DeclGen = struct {...@@ -345,7 +365,7 @@ pub const DeclGen = struct {
345365
346 i = 0;366 i = 0;
347 while (i < params) : (i += 1) {367 while (i < params) : (i += 1) {
348 const param_type_id = self.types.get(ty.fnParamType(i)).?;368 const param_type_id = self.spv.types.get(ty.fnParamType(i)).?;
349 try code.append(param_type_id);369 try code.append(param_type_id);
350 }370 }
351 },371 },
...@@ -373,10 +393,11 @@ pub const DeclGen = struct {...@@ -373,10 +393,11 @@ pub const DeclGen = struct {
373 else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type {}s", .{tag}),393 else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type {}s", .{tag}),
374 }394 }
375395
376 try self.types.putNoClobber(ty, result_id);396 try self.spv.types.putNoClobber(ty, result_id);
377 return result_id;397 return result_id;
378 }398 }
379399
400<<<<<<< HEAD
380 pub fn gen(self: *DeclGen) !void {401 pub fn gen(self: *DeclGen) !void {
381 const decl = self.decl;402 const decl = self.decl;
382 const result_id = decl.fn_link.spirv.id;403 const result_id = decl.fn_link.spirv.id;
...@@ -386,6 +407,17 @@ pub const DeclGen = struct {...@@ -386,6 +407,17 @@ pub const DeclGen = struct {
386 const prototype_id = try self.getOrGenType(decl.ty);407 const prototype_id = try self.getOrGenType(decl.ty);
387 try writeInstruction(&self.spv.fn_decls, .OpFunction, &[_]u32{408 try writeInstruction(&self.spv.fn_decls, .OpFunction, &[_]u32{
388 self.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.409 self.types.get(decl.ty.fnReturnType()).?, // This type should be generated along with the prototype.
410=======
411 pub fn gen(self: *DeclGen) Error!void {
412 const result_id = self.decl.fn_link.spirv.id;
413 const tv = self.decl.typed_value.most_recent.typed_value;
414
415 if (tv.val.castTag(.function)) |func_payload| {
416 std.debug.assert(tv.ty.zigTypeTag() == .Fn);
417 const prototype_id = try self.getOrGenType(tv.ty);
418 try writeInstruction(&self.spv.binary.fn_decls, .OpFunction, &[_]u32{
419 self.spv.types.get(tv.ty.fnReturnType()).?, // This type should be generated along with the prototype.
420>>>>>>> 09e563b75 (SPIR-V: Put types in SPIRVModule, some general restructuring)
389 result_id,421 result_id,
390 @bitCast(u32, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it.422 @bitCast(u32, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it.
391 prototype_id,423 prototype_id,
...@@ -396,18 +428,22 @@ pub const DeclGen = struct {...@@ -396,18 +428,22 @@ pub const DeclGen = struct {
396428
397 try self.args.ensureCapacity(params);429 try self.args.ensureCapacity(params);
398 while (i < params) : (i += 1) {430 while (i < params) : (i += 1) {
431<<<<<<< HEAD
399 const param_type_id = self.types.get(decl.ty.fnParamType(i)).?;432 const param_type_id = self.types.get(decl.ty.fnParamType(i)).?;
433=======
434 const param_type_id = self.spv.types.get(tv.ty.fnParamType(i)).?;
435>>>>>>> 09e563b75 (SPIR-V: Put types in SPIRVModule, some general restructuring)
400 const arg_result_id = self.spv.allocResultId();436 const arg_result_id = self.spv.allocResultId();
401 try writeInstruction(&self.spv.fn_decls, .OpFunctionParameter, &[_]u32{ param_type_id, arg_result_id });437 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionParameter, &[_]u32{ param_type_id, arg_result_id });
402 self.args.appendAssumeCapacity(arg_result_id);438 self.args.appendAssumeCapacity(arg_result_id);
403 }439 }
404440
405 // TODO: This could probably be done in a better way...441 // TODO: This could probably be done in a better way...
406 const root_block_id = self.spv.allocResultId();442 const root_block_id = self.spv.allocResultId();
407 _ = try writeInstruction(&self.spv.fn_decls, .OpLabel, &[_]u32{root_block_id});443 _ = try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]u32{root_block_id});
408 try self.genBody(func_payload.data.body);444 try self.genBody(func_payload.data.body);
409445
410 try writeInstruction(&self.spv.fn_decls, .OpFunctionEnd, &[_]u32{});446 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]u32{});
411 } else {447 } else {
412 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});448 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});
413 }449 }
...@@ -417,7 +453,7 @@ pub const DeclGen = struct {...@@ -417,7 +453,7 @@ pub const DeclGen = struct {
417 for (body.instructions) |inst| {453 for (body.instructions) |inst| {
418 const maybe_result_id = try self.genInst(inst);454 const maybe_result_id = try self.genInst(inst);
419 if (maybe_result_id) |result_id|455 if (maybe_result_id) |result_id|
420 try self.values.putNoClobber(inst, result_id);456 try self.inst_results.putNoClobber(inst, result_id);
421 }457 }
422 }458 }
423459
...@@ -510,7 +546,7 @@ pub const DeclGen = struct {...@@ -510,7 +546,7 @@ pub const DeclGen = struct {
510 else => unreachable,546 else => unreachable,
511 };547 };
512548
513 try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, result_id, lhs_id, rhs_id });549 try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]u32{ result_type_id, result_id, lhs_id, rhs_id });
514550
515 // TODO: Trap on overflow? Probably going to be annoying.551 // TODO: Trap on overflow? Probably going to be annoying.
516 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.552 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
...@@ -535,7 +571,7 @@ pub const DeclGen = struct {...@@ -535,7 +571,7 @@ pub const DeclGen = struct {
535 else => unreachable,571 else => unreachable,
536 };572 };
537573
538 try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, result_id, operand_id });574 try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]u32{ result_type_id, result_id, operand_id });
539575
540 return result_id;576 return result_id;
541 }577 }
...@@ -548,19 +584,19 @@ pub const DeclGen = struct {...@@ -548,19 +584,19 @@ pub const DeclGen = struct {
548 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?u32 {584 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?u32 {
549 const operand_id = try self.resolve(inst.operand);585 const operand_id = try self.resolve(inst.operand);
550 // TODO: This instruction needs to be the last in a block. Is that guaranteed?586 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
551 try writeInstruction(&self.spv.fn_decls, .OpReturnValue, &[_]u32{operand_id});587 try writeInstruction(&self.spv.binary.fn_decls, .OpReturnValue, &[_]u32{operand_id});
552 return null;588 return null;
553 }589 }
554590
555 fn genRetVoid(self: *DeclGen) !?u32 {591 fn genRetVoid(self: *DeclGen) !?u32 {
556 // TODO: This instruction needs to be the last in a block. Is that guaranteed?592 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
557 try writeInstruction(&self.spv.fn_decls, .OpReturn, &[_]u32{});593 try writeInstruction(&self.spv.binary.fn_decls, .OpReturn, &[_]u32{});
558 return null;594 return null;
559 }595 }
560596
561 fn genUnreach(self: *DeclGen) !?u32 {597 fn genUnreach(self: *DeclGen) !?u32 {
562 // TODO: This instruction needs to be the last in a block. Is that guaranteed?598 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
563 try writeInstruction(&self.spv.fn_decls, .OpUnreachable, &[_]u32{});599 try writeInstruction(&self.spv.binary.fn_decls, .OpUnreachable, &[_]u32{});
564 return null;600 return null;
565 }601 }
566};602};
src/link/SpirV.zig+5-6
...@@ -157,20 +157,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -157,20 +157,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
157 .spv = &spv,157 .spv = &spv,
158 .args = std.ArrayList(u32).init(self.base.allocator),158 .args = std.ArrayList(u32).init(self.base.allocator),
159 .next_arg_index = undefined,159 .next_arg_index = undefined,
160 .types = codegen.TypeMap.init(self.base.allocator),160 .inst_results = codegen.InstMap.init(self.base.allocator),
161 .values = codegen.ValueMap.init(self.base.allocator),
162 .decl = undefined,161 .decl = undefined,
163 .error_msg = undefined,162 .error_msg = undefined,
164 };163 };
165164
166 defer decl_gen.values.deinit();165 defer decl_gen.inst_results.deinit();
167 defer decl_gen.types.deinit();
168 defer decl_gen.args.deinit();166 defer decl_gen.args.deinit();
169167
170 for (self.decl_table.items()) |entry| {168 for (self.decl_table.items()) |entry| {
171 const decl = entry.key;169 const decl = entry.key;
172 if (!decl.has_tv) continue;170 if (!decl.has_tv) continue;
173171
172 // Reset the decl_gen, but retain allocated resources.
174 decl_gen.args.items.len = 0;173 decl_gen.args.items.len = 0;
175 decl_gen.next_arg_index = 0;174 decl_gen.next_arg_index = 0;
176 decl_gen.decl = decl;175 decl_gen.decl = decl;
...@@ -204,8 +203,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -204,8 +203,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
204 // follows the SPIR-V logical module format!203 // follows the SPIR-V logical module format!
205 var all_buffers = [_]std.os.iovec_const{204 var all_buffers = [_]std.os.iovec_const{
206 wordsToIovConst(binary.items),205 wordsToIovConst(binary.items),
207 wordsToIovConst(spv.types_globals_constants.items),206 wordsToIovConst(spv.binary.types_globals_constants.items),
208 wordsToIovConst(spv.fn_decls.items),207 wordsToIovConst(spv.binary.fn_decls.items),
209 };208 };
210209
211 const file = self.base.file.?;210 const file = self.base.file.?;