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");
1515const Inst = ir.Inst;
1616
1717pub 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
2020pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void {
2121 const word_count = arg_count + 1;
......@@ -27,24 +27,34 @@ pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const
2727 try code.appendSlice(args);
2828}
2929
30/// This structure represents a SPIR-V binary module being compiled, and keeps track of relevant information
31/// such as code for the different logical sections, and the next result-id.
30/// This structure represents a SPIR-V (binary) module being compiled, and keeps track of all relevant information.
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.
3233pub const SPIRVModule = struct {
3334 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 {
3844 return .{
3945 .next_result_id = 1, // 0 is an invalid SPIR-V result ID.
40 .types_globals_constants = std.ArrayList(u32).init(allocator),
41 .fn_decls = std.ArrayList(u32).init(allocator),
46 .binary = .{
47 .types_globals_constants = std.ArrayList(u32).init(gpa),
48 .fn_decls = std.ArrayList(u32).init(gpa),
49 },
50 .types = TypeMap.init(gpa),
4251 };
4352 }
4453
4554 pub fn deinit(self: *SPIRVModule) void {
46 self.types_globals_constants.deinit();
47 self.fn_decls.deinit();
55 self.binary.types_globals_constants.deinit();
56 self.binary.fn_decls.deinit();
57 self.types.deinit();
4858 }
4959
5060 pub fn allocResultId(self: *SPIRVModule) u32 {
......@@ -59,18 +69,29 @@ pub const SPIRVModule = struct {
5969
6070/// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
6171pub const DeclGen = struct {
72 /// The parent module.
6273 module: *Module,
74
75 /// The SPIR-V module code should be put in.
6376 spv: *SPIRVModule,
6477
78 /// An array of function argument result-ids. Each index corresponds with the function argument of the same index.
6579 args: std.ArrayList(u32),
80
81 /// A counter to keep track of how many `arg` instructions we've seen yet.
6682 next_arg_index: u32,
6783
68 types: TypeMap,
69 values: ValueMap,
84 /// A map keeping track of which instruction generated which result-id.
85 inst_results: InstMap,
7086
87 /// The decl we are currently generating code for.
7188 decl: *Decl,
89
90 /// If `gen` returned `Error.AnalysisFail`, this contains an explanatory message. Memory is owned by
91 /// `module.gpa`.
7292 error_msg: ?*Module.ErrorMsg,
7393
94 /// Possible errors the `gen` function may return.
7495 const Error = error{ AnalysisFail, OutOfMemory };
7596
7697 /// This structure is used to return information about a type typically used for arithmetic operations.
......@@ -129,7 +150,7 @@ pub const DeclGen = struct {
129150 return self.genConstant(inst.ty, val);
130151 }
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!
133154 }
134155
135156 /// 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 {
145166 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {
146167 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.
149170 std.debug.assert(bits != 0);
150171
151172 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
......@@ -194,7 +215,6 @@ pub const DeclGen = struct {
194215
195216 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {
196217 const target = self.module.getTarget();
197
198218 return switch (ty.zigTypeTag()) {
199219 .Bool => ArithmeticTypeInfo{
200220 .bits = 1, // Doesn't matter for this class.
......@@ -231,7 +251,7 @@ pub const DeclGen = struct {
231251 /// TODO: Deduplication?
232252 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 {
233253 const target = self.module.getTarget();
234 const code = &self.spv.types_globals_constants;
254 const code = &self.spv.binary.types_globals_constants;
235255 const result_id = self.spv.allocResultId();
236256 const result_type_id = try self.getOrGenType(ty);
237257
......@@ -276,12 +296,12 @@ pub const DeclGen = struct {
276296
277297 fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 {
278298 // 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| {
280300 return already_generated;
281301 }
282302
283303 const target = self.module.getTarget();
284 const code = &self.spv.types_globals_constants;
304 const code = &self.spv.binary.types_globals_constants;
285305 const result_id = self.spv.allocResultId();
286306
287307 switch (ty.zigTypeTag()) {
......@@ -345,7 +365,7 @@ pub const DeclGen = struct {
345365
346366 i = 0;
347367 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)).?;
349369 try code.append(param_type_id);
350370 }
351371 },
......@@ -373,10 +393,11 @@ pub const DeclGen = struct {
373393 else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type {}s", .{tag}),
374394 }
375395
376 try self.types.putNoClobber(ty, result_id);
396 try self.spv.types.putNoClobber(ty, result_id);
377397 return result_id;
378398 }
379399
400<<<<<<< HEAD
380401 pub fn gen(self: *DeclGen) !void {
381402 const decl = self.decl;
382403 const result_id = decl.fn_link.spirv.id;
......@@ -386,6 +407,17 @@ pub const DeclGen = struct {
386407 const prototype_id = try self.getOrGenType(decl.ty);
387408 try writeInstruction(&self.spv.fn_decls, .OpFunction, &[_]u32{
388409 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)
389421 result_id,
390422 @bitCast(u32, spec.FunctionControl{}), // TODO: We can set inline here if the type requires it.
391423 prototype_id,
......@@ -396,18 +428,22 @@ pub const DeclGen = struct {
396428
397429 try self.args.ensureCapacity(params);
398430 while (i < params) : (i += 1) {
431<<<<<<< HEAD
399432 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)
400436 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 });
402438 self.args.appendAssumeCapacity(arg_result_id);
403439 }
404440
405441 // TODO: This could probably be done in a better way...
406442 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});
408444 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{});
411447 } else {
412448 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});
413449 }
......@@ -417,7 +453,7 @@ pub const DeclGen = struct {
417453 for (body.instructions) |inst| {
418454 const maybe_result_id = try self.genInst(inst);
419455 if (maybe_result_id) |result_id|
420 try self.values.putNoClobber(inst, result_id);
456 try self.inst_results.putNoClobber(inst, result_id);
421457 }
422458 }
423459
......@@ -510,7 +546,7 @@ pub const DeclGen = struct {
510546 else => unreachable,
511547 };
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
515551 // TODO: Trap on overflow? Probably going to be annoying.
516552 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
......@@ -535,7 +571,7 @@ pub const DeclGen = struct {
535571 else => unreachable,
536572 };
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
540576 return result_id;
541577 }
......@@ -548,19 +584,19 @@ pub const DeclGen = struct {
548584 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?u32 {
549585 const operand_id = try self.resolve(inst.operand);
550586 // 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});
552588 return null;
553589 }
554590
555591 fn genRetVoid(self: *DeclGen) !?u32 {
556592 // 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{});
558594 return null;
559595 }
560596
561597 fn genUnreach(self: *DeclGen) !?u32 {
562598 // 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{});
564600 return null;
565601 }
566602};
src/link/SpirV.zig+5-6
......@@ -157,20 +157,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
157157 .spv = &spv,
158158 .args = std.ArrayList(u32).init(self.base.allocator),
159159 .next_arg_index = undefined,
160 .types = codegen.TypeMap.init(self.base.allocator),
161 .values = codegen.ValueMap.init(self.base.allocator),
160 .inst_results = codegen.InstMap.init(self.base.allocator),
162161 .decl = undefined,
163162 .error_msg = undefined,
164163 };
165164
166 defer decl_gen.values.deinit();
167 defer decl_gen.types.deinit();
165 defer decl_gen.inst_results.deinit();
168166 defer decl_gen.args.deinit();
169167
170168 for (self.decl_table.items()) |entry| {
171169 const decl = entry.key;
172170 if (!decl.has_tv) continue;
173171
172 // Reset the decl_gen, but retain allocated resources.
174173 decl_gen.args.items.len = 0;
175174 decl_gen.next_arg_index = 0;
176175 decl_gen.decl = decl;
......@@ -204,8 +203,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
204203 // follows the SPIR-V logical module format!
205204 var all_buffers = [_]std.os.iovec_const{
206205 wordsToIovConst(binary.items),
207 wordsToIovConst(spv.types_globals_constants.items),
208 wordsToIovConst(spv.fn_decls.items),
206 wordsToIovConst(spv.binary.types_globals_constants.items),
207 wordsToIovConst(spv.binary.fn_decls.items),
209208 };
210209
211210 const file = self.base.file.?;