| ... | ... | @@ -12,9 +12,13 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 12 | 12 | |
| 13 | 13 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); |
| 14 | 14 | |
| 15 | | pub fn writeInstruction(code: *std.ArrayList(u32), instr: spec.Opcode, args: []const u32) !void { |
| 16 | | const word_count = @intCast(u32, args.len + 1); |
| 17 | | try code.append((word_count << 16) | @enumToInt(instr)); |
| 15 | pub fn writeOpcode(code: *std.ArrayList(u32), opcode: spec.Opcode, arg_count: u32) !void { |
| 16 | const word_count = arg_count + 1; |
| 17 | try code.append((word_count << 16) | @enumToInt(opcode)); |
| 18 | } |
| 19 | |
| 20 | pub fn writeInstruction(code: *std.ArrayList(u32), opcode: spec.Opcode, args: []const u32) !void { |
| 21 | try writeOpcode(code, opcode, @intCast(u32, args.len)); |
| 18 | 22 | try code.appendSlice(args); |
| 19 | 23 | } |
| 20 | 24 | |
| ... | ... | @@ -58,7 +62,12 @@ pub const DeclGen = struct { |
| 58 | 62 | decl: *Decl, |
| 59 | 63 | error_msg: ?*Module.ErrorMsg, |
| 60 | 64 | |
| 61 | | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 65 | const Error = error{ |
| 66 | AnalysisFail, |
| 67 | OutOfMemory |
| 68 | }; |
| 69 | |
| 70 | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) Error { |
| 62 | 71 | @setCold(true); |
| 63 | 72 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| 64 | 73 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); |
| ... | ... | @@ -102,24 +111,25 @@ pub const DeclGen = struct { |
| 102 | 111 | return null; |
| 103 | 112 | } |
| 104 | 113 | |
| 105 | | pub fn getOrGenType(self: *DeclGen, t: Type) !u32 { |
| 114 | fn getOrGenType(self: *DeclGen, ty: Type) Error!u32 { |
| 106 | 115 | // We can't use getOrPut here so we can recursively generate types. |
| 107 | | if (self.types.get(t)) |already_generated| { |
| 116 | if (self.types.get(ty)) |already_generated| { |
| 108 | 117 | return already_generated; |
| 109 | 118 | } |
| 110 | 119 | |
| 111 | | const result = self.spv.allocResultId(); |
| 120 | const code = &self.spv.types_and_globals; |
| 121 | const result_id = self.spv.allocResultId(); |
| 112 | 122 | |
| 113 | | switch (t.zigTypeTag()) { |
| 114 | | .Void => try writeInstruction(&self.spv.types_and_globals, .OpTypeVoid, &[_]u32{ result }), |
| 115 | | .Bool => try writeInstruction(&self.spv.types_and_globals, .OpTypeBool, &[_]u32{ result }), |
| 123 | switch (ty.zigTypeTag()) { |
| 124 | .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{ result_id }), |
| 125 | .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{ result_id }), |
| 116 | 126 | .Int => { |
| 117 | | const int_info = t.intInfo(self.module.getTarget()); |
| 127 | const int_info = ty.intInfo(self.module.getTarget()); |
| 118 | 128 | const backing_bits = self.backingIntBits(int_info.bits) orelse |
| 119 | 129 | return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement fallback for integer of {} bits", .{ int_info.bits }); |
| 120 | 130 | |
| 121 | | try writeInstruction(&self.spv.types_and_globals, .OpTypeInt, &[_]u32{ |
| 122 | | result, |
| 131 | try writeInstruction(code, .OpTypeInt, &[_]u32{ |
| 132 | result_id, |
| 123 | 133 | backing_bits, |
| 124 | 134 | switch (int_info.signedness) { |
| 125 | 135 | .unsigned => 0, |
| ... | ... | @@ -128,7 +138,34 @@ pub const DeclGen = struct { |
| 128 | 138 | }); |
| 129 | 139 | }, |
| 130 | 140 | // TODO: Capabilities. |
| 131 | | .Float => try writeInstruction(&self.spv.types_and_globals, .OpTypeFloat, &[_]u32{ result, t.floatBits(self.module.getTarget()) }), |
| 141 | .Float => try writeInstruction(code, .OpTypeFloat, &[_]u32{ result_id, ty.floatBits(self.module.getTarget()) }), |
| 142 | .Fn => { |
| 143 | // We only support zig-calling-convention functions, no varargs. |
| 144 | if (ty.fnCallingConvention() != .Unspecified) |
| 145 | return self.fail(.{.node_offset = 0}, "Invalid calling convention for SPIR-V", .{}); |
| 146 | if (ty.fnIsVarArgs()) |
| 147 | return self.fail(.{.node_offset = 0}, "VarArgs are not supported for SPIR-V", .{}); |
| 148 | |
| 149 | // In order to avoid a temporary here, first generate all the required types and then simply look them up |
| 150 | // when generating the function type. |
| 151 | const params = ty.fnParamLen(); |
| 152 | var i: usize = 0; |
| 153 | while (i < params) : (i += 1) { |
| 154 | _ = try self.getOrGenType(ty.fnParamType(i)); |
| 155 | } |
| 156 | |
| 157 | const return_type_id = try self.getOrGenType(ty.fnReturnType()); |
| 158 | |
| 159 | // result id + result type id + parameter type ids. |
| 160 | try writeOpcode(code, .OpTypeFunction, 2 + @intCast(u32, ty.fnParamLen()) ); |
| 161 | try code.appendSlice(&.{ result_id, return_type_id }); |
| 162 | |
| 163 | i = 0; |
| 164 | while (i < params) : (i += 1) { |
| 165 | const param_type_id = self.types.get(ty.fnParamType(i)).?; |
| 166 | try code.append(param_type_id); |
| 167 | } |
| 168 | }, |
| 132 | 169 | .Null, |
| 133 | 170 | .Undefined, |
| 134 | 171 | .EnumLiteral, |
| ... | ... | @@ -139,23 +176,21 @@ pub const DeclGen = struct { |
| 139 | 176 | |
| 140 | 177 | .BoundFn => unreachable, // this type will be deleted from the language. |
| 141 | 178 | |
| 142 | | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type with tag {}", .{ tag }), |
| 179 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement type {}", .{ tag }), |
| 143 | 180 | } |
| 144 | 181 | |
| 145 | | try self.types.put(t, result); |
| 146 | | return result; |
| 182 | try self.types.put(ty, result_id); |
| 183 | return result_id; |
| 147 | 184 | } |
| 148 | 185 | |
| 149 | 186 | pub fn gen(self: *DeclGen) !void { |
| 150 | | const typed_value = self.decl.typed_value.most_recent.typed_value; |
| 151 | | |
| 152 | | switch (typed_value.ty.zigTypeTag()) { |
| 153 | | .Fn => { |
| 154 | | log.debug("Generating code for function '{s}'", .{ std.mem.spanZ(self.decl.name) }); |
| 187 | const tv = self.decl.typed_value.most_recent.typed_value; |
| 155 | 188 | |
| 156 | | _ = try self.getOrGenType(typed_value.ty.fnReturnType()); |
| 157 | | }, |
| 158 | | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl with tag {}", .{ tag }), |
| 189 | if (tv.val.castTag(.function)) |func_payload| { |
| 190 | std.debug.assert(tv.ty.zigTypeTag() == .Fn); |
| 191 | _ = try self.getOrGenType(tv.ty); |
| 192 | } else { |
| 193 | return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: generate decl type {}", .{ tv.ty.zigTypeTag() }); |
| 159 | 194 | } |
| 160 | 195 | } |
| 161 | 196 | }; |