| ... | @@ -12,9 +12,13 @@ const LazySrcLoc = Module.LazySrcLoc; | ... | @@ -12,9 +12,13 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 12 | | 12 | |
| 13 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); | 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 { | 15 | pub fn writeOpcode(code: *std.ArrayList(u32), opcode: spec.Opcode, arg_count: u32) !void { |
| 16 | const word_count = @intCast(u32, args.len + 1); | 16 | const word_count = arg_count + 1; |
| 17 | try code.append((word_count << 16) | @enumToInt(instr)); | 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 | try code.appendSlice(args); | 22 | try code.appendSlice(args); |
| 19 | } | 23 | } |
| 20 | | 24 | |
| ... | @@ -58,7 +62,12 @@ pub const DeclGen = struct { | ... | @@ -58,7 +62,12 @@ pub const DeclGen = struct { |
| 58 | decl: *Decl, | 62 | decl: *Decl, |
| 59 | error_msg: ?*Module.ErrorMsg, | 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 | @setCold(true); | 71 | @setCold(true); |
| 63 | const src_loc = src.toSrcLocWithDecl(self.decl); | 72 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| 64 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); | 73 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); |
| ... | @@ -102,24 +111,25 @@ pub const DeclGen = struct { | ... | @@ -102,24 +111,25 @@ pub const DeclGen = struct { |
| 102 | return null; | 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 | // We can't use getOrPut here so we can recursively generate types. | 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 | return already_generated; | 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()) { | 123 | switch (ty.zigTypeTag()) { |
| 114 | .Void => try writeInstruction(&self.spv.types_and_globals, .OpTypeVoid, &[_]u32{ result }), | 124 | .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{ result_id }), |
| 115 | .Bool => try writeInstruction(&self.spv.types_and_globals, .OpTypeBool, &[_]u32{ result }), | 125 | .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{ result_id }), |
| 116 | .Int => { | 126 | .Int => { |
| 117 | const int_info = t.intInfo(self.module.getTarget()); | 127 | const int_info = ty.intInfo(self.module.getTarget()); |
| 118 | const backing_bits = self.backingIntBits(int_info.bits) orelse | 128 | const backing_bits = self.backingIntBits(int_info.bits) orelse |
| 119 | return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement fallback for integer of {} bits", .{ int_info.bits }); | 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{ | 131 | try writeInstruction(code, .OpTypeInt, &[_]u32{ |
| 122 | result, | 132 | result_id, |
| 123 | backing_bits, | 133 | backing_bits, |
| 124 | switch (int_info.signedness) { | 134 | switch (int_info.signedness) { |
| 125 | .unsigned => 0, | 135 | .unsigned => 0, |
| ... | @@ -128,7 +138,34 @@ pub const DeclGen = struct { | ... | @@ -128,7 +138,34 @@ pub const DeclGen = struct { |
| 128 | }); | 138 | }); |
| 129 | }, | 139 | }, |
| 130 | // TODO: Capabilities. | 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 | .Null, | 169 | .Null, |
| 133 | .Undefined, | 170 | .Undefined, |
| 134 | .EnumLiteral, | 171 | .EnumLiteral, |
| ... | @@ -139,23 +176,21 @@ pub const DeclGen = struct { | ... | @@ -139,23 +176,21 @@ pub const DeclGen = struct { |
| 139 | | 176 | |
| 140 | .BoundFn => unreachable, // this type will be deleted from the language. | 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); | 182 | try self.types.put(ty, result_id); |
| 146 | return result; | 183 | return result_id; |
| 147 | } | 184 | } |
| 148 | | 185 | |
| 149 | pub fn gen(self: *DeclGen) !void { | 186 | pub fn gen(self: *DeclGen) !void { |
| 150 | const typed_value = self.decl.typed_value.most_recent.typed_value; | 187 | const tv = 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) }); | | |
| 155 | | 188 | |
| 156 | _ = try self.getOrGenType(typed_value.ty.fnReturnType()); | 189 | if (tv.val.castTag(.function)) |func_payload| { |
| 157 | }, | 190 | std.debug.assert(tv.ty.zigTypeTag() == .Fn); |
| 158 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl with tag {}", .{ tag }), | 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 | }; |