authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-15 09:43:57+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 14:13:23+02:00
logcbf5280f54509e7aa58d8fd14258274a12efeee1
tree8acf462cd3b0a94b13057b06f7748d7906bd107a
parentda0cc732ea899d2284200faf54c3c12e8c798b7f

SPIR-V: Some instructions + constant generation setup


2 files changed, 135 insertions(+), 21 deletions(-)

src/codegen/spirv.zig+130-20
......@@ -1,16 +1,19 @@
11const std = @import("std");
22const Allocator = std.mem.Allocator;
3const log = std.log.scoped(.codegen);
4
53const Target = std.Target;
4const log = std.log.scoped(.codegen);
65
76const spec = @import("spirv/spec.zig");
87const Module = @import("../Module.zig");
98const Decl = Module.Decl;
109const Type = @import("../type.zig").Type;
10const Value = @import("../value.zig").Value;
1111const LazySrcLoc = Module.LazySrcLoc;
12const ir = @import("../ir.zig");
13const Inst = ir.Inst;
1214
1315pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
16pub const ValueMap = std.AutoHashMap(*Inst, u32);
1417
1518pub fn writeOpcode(code: *std.ArrayList(u32), opcode: spec.Opcode, arg_count: u32) !void {
1619 const word_count = arg_count + 1;
......@@ -26,19 +29,19 @@ pub fn writeInstruction(code: *std.ArrayList(u32), opcode: spec.Opcode, args: []
2629/// such as code for the different logical sections, and the next result-id.
2730pub const SPIRVModule = struct {
2831 next_result_id: u32,
29 types_and_globals: std.ArrayList(u32),
32 types_globals_constants: std.ArrayList(u32),
3033 fn_decls: std.ArrayList(u32),
3134
3235 pub fn init(allocator: *Allocator) SPIRVModule {
3336 return .{
3437 .next_result_id = 1, // 0 is an invalid SPIR-V result ID.
35 .types_and_globals = std.ArrayList(u32).init(allocator),
38 .types_globals_constants = std.ArrayList(u32).init(allocator),
3639 .fn_decls = std.ArrayList(u32).init(allocator),
3740 };
3841 }
3942
4043 pub fn deinit(self: *SPIRVModule) void {
41 self.types_and_globals.deinit();
44 self.types_globals_constants.deinit();
4245 self.fn_decls.deinit();
4346 }
4447
......@@ -58,7 +61,10 @@ pub const DeclGen = struct {
5861 spv: *SPIRVModule,
5962
6063 args: std.ArrayList(u32),
64 next_arg_index: u32,
65
6166 types: TypeMap,
67 values: ValueMap,
6268
6369 decl: *Decl,
6470 error_msg: ?*Module.ErrorMsg,
......@@ -75,6 +81,14 @@ pub const DeclGen = struct {
7581 return error.AnalysisFail;
7682 }
7783
84 fn resolve(self: *DeclGen, inst: *Inst) !u32 {
85 if (inst.value()) |val| {
86 return self.genConstant(inst.ty, val);
87 }
88
89 return self.values.get(inst).?; // Instruction does not dominate all uses!
90 }
91
7892 /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need
7993 /// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign
8094 /// included), the width of the underlying type which represents it, given the enabled features for the current target.
......@@ -82,13 +96,16 @@ pub const DeclGen = struct {
8296 /// that size. In this case, multiple elements of the largest type should be used.
8397 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
8498 /// The result is valid to be used with OpTypeInt.
99 /// asserts `ty` is an integer.
85100 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
86101 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
87 fn backingIntBits(self: *DeclGen, bits: u32) ?u32 {
88 // TODO: Figure out what to do with u0/i0.
89 std.debug.assert(bits != 0);
90
102 /// TODO: Should the result of this function be cached?
103 fn backingIntBits(self: *DeclGen, ty: Type) ?u32 {
91104 const target = self.module.getTarget();
105 const int_info = ty.intInfo(target);
106
107 // TODO: Figure out what to do with u0/i0.
108 std.debug.assert(int_info.bits != 0);
92109
93110 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
94111 const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } {
......@@ -104,7 +121,7 @@ pub const DeclGen = struct {
104121 else
105122 true;
106123
107 if (bits <= int.bits and has_feature) {
124 if (int_info.bits <= int.bits and has_feature) {
108125 return int.bits;
109126 }
110127 }
......@@ -112,6 +129,43 @@ pub const DeclGen = struct {
112129 return null;
113130 }
114131
132 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if
133 /// the Int64 capability is enabled).
134 /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
135 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there
136 /// is no way of knowing whether those are actually supported.
137 /// TODO: Maybe this should be cached?
138 fn largestSupportedIntBits(self: *DeclGen) u32 {
139 const target = self.module.getTarget();
140 return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))
141 64
142 else
143 32;
144 }
145
146 /// Generate a constant representing `val`.
147 /// TODO: Deduplication?
148 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 {
149 const code = &self.spv.types_globals_constants;
150 const result_id = self.spv.allocResultId();
151 const result_type_id = try self.getOrGenType(ty);
152
153 if (val.isUndef()) {
154 try writeInstruction(code, .OpUndef, &[_]u32{ result_type_id, result_id });
155 return result_id;
156 }
157
158 switch (ty.zigTypeTag()) {
159 .Bool => {
160 const opcode: spec.Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;
161 try writeInstruction(code, opcode, &[_]u32{ result_type_id, result_id });
162 },
163 else => return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: constant generation of type {s}\n", .{ ty.zigTypeTag() }),
164 }
165
166 return result_id;
167 }
168
115169 fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 {
116170 // We can't use getOrPut here so we can recursively generate types.
117171 if (self.types.get(ty)) |already_generated| {
......@@ -119,24 +173,21 @@ pub const DeclGen = struct {
119173 }
120174
121175 const target = self.module.getTarget();
122 const code = &self.spv.types_and_globals;
176 const code = &self.spv.types_globals_constants;
123177 const result_id = self.spv.allocResultId();
124178
125179 switch (ty.zigTypeTag()) {
126180 .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{ result_id }),
127181 .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{ result_id }),
128182 .Int => {
129 const int_info = ty.intInfo(self.module.getTarget());
130 const backing_bits = self.backingIntBits(int_info.bits) orelse
183 const backing_bits = self.backingIntBits(ty) orelse
131184 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement fallback for {}", .{ ty });
132185
186 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.
133187 try writeInstruction(code, .OpTypeInt, &[_]u32{
134188 result_id,
135189 backing_bits,
136 switch (int_info.signedness) {
137 .unsigned => 0,
138 .signed => 1,
139 },
190 @boolToInt(ty.isSignedInt()),
140191 });
141192 },
142193 .Float => {
......@@ -183,6 +234,15 @@ pub const DeclGen = struct {
183234 try code.append(param_type_id);
184235 }
185236 },
237 .Vector => {
238 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations
239 // which work on them), so simply use those.
240 // Note: SPIR-V vectors only support bools, ints and floats, so pointer vectors need to be supported another way.
241 // "big integers" (larger than the largest supported native type) can probably be represented by an array of vectors.
242
243 // TODO: Vectors are not yet supported by the self-hosted compiler itself it seems.
244 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement type Vector", .{});
245 },
186246 .Null,
187247 .Undefined,
188248 .EnumLiteral,
......@@ -193,10 +253,10 @@ pub const DeclGen = struct {
193253
194254 .BoundFn => unreachable, // this type will be deleted from the language.
195255
196 else => |tag| return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement type {}", .{ tag }),
256 else => |tag| return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement type {}s", .{ tag }),
197257 }
198258
199 try self.types.put(ty, result_id);
259 try self.types.putNoClobber(ty, result_id);
200260 return result_id;
201261 }
202262
......@@ -225,11 +285,61 @@ pub const DeclGen = struct {
225285 self.args.appendAssumeCapacity(arg_result_id);
226286 }
227287
228 // TODO: Body
288 // TODO: This could probably be done in a better way...
289 const root_block_id = self.spv.allocResultId();
290 _ = try writeInstruction(&self.spv.fn_decls, .OpLabel, &[_]u32{root_block_id});
291 try self.genBody(func_payload.data.body);
229292
230293 try writeInstruction(&self.spv.fn_decls, .OpFunctionEnd, &[_]u32{});
231294 } else {
232295 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: generate decl type {}", .{ tv.ty.zigTypeTag() });
233296 }
234297 }
298
299 fn genBody(self: *DeclGen, body: ir.Body) !void {
300 for (body.instructions) |inst| {
301 const maybe_result_id = try self.genInst(inst);
302 if (maybe_result_id) |result_id|
303 try self.values.putNoClobber(inst, result_id);
304 }
305 }
306
307 fn genInst(self: *DeclGen, inst: *Inst) !?u32 {
308 return switch (inst.tag) {
309 .arg => self.genArg(),
310 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
311 // throughout the IR.
312 .breakpoint => null,
313 // TODO: What does this entail?
314 .dbg_stmt => null,
315 .ret => self.genRet(inst.castTag(.ret).?),
316 .retvoid => self.genRetVoid(),
317 .unreach => self.genUnreach(),
318 else => self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement inst {}", .{inst.tag}),
319 };
320 }
321
322 fn genArg(self: *DeclGen) u32 {
323 defer self.next_arg_index += 1;
324 return self.args.items[self.next_arg_index];
325 }
326
327 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?u32 {
328 const operand_id = try self.resolve(inst.operand);
329 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
330 try writeInstruction(&self.spv.fn_decls, .OpReturnValue, &[_]u32{ operand_id });
331 return null;
332 }
333
334 fn genRetVoid(self: *DeclGen) !?u32 {
335 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
336 try writeInstruction(&self.spv.fn_decls, .OpReturn, &[_]u32{});
337 return null;
338 }
339
340 fn genUnreach(self: *DeclGen) !?u32 {
341 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
342 try writeInstruction(&self.spv.fn_decls, .OpUnreachable, &[_]u32{});
343 return null;
344 }
235345};
src/link/SpirV.zig+5-1
......@@ -146,11 +146,14 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
146146 .module = module,
147147 .spv = &spv,
148148 .args = std.ArrayList(u32).init(self.base.allocator),
149 .next_arg_index = undefined,
149150 .types = codegen.TypeMap.init(self.base.allocator),
151 .values = codegen.ValueMap.init(self.base.allocator),
150152 .decl = undefined,
151153 .error_msg = undefined,
152154 };
153155
156 defer decl_gen.values.deinit();
154157 defer decl_gen.types.deinit();
155158 defer decl_gen.args.deinit();
156159
......@@ -160,6 +163,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
160163 continue;
161164
162165 decl_gen.args.items.len = 0;
166 decl_gen.next_arg_index = 0;
163167 decl_gen.decl = decl;
164168 decl_gen.error_msg = null;
165169
......@@ -191,7 +195,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
191195 // follows the SPIR-V logical module format!
192196 var all_buffers = [_]std.os.iovec_const{
193197 wordsToIovConst(binary.items),
194 wordsToIovConst(spv.types_and_globals.items),
198 wordsToIovConst(spv.types_globals_constants.items),
195199 wordsToIovConst(spv.fn_decls.items),
196200 };
197201