| ... | @@ -44,34 +44,25 @@ const IdResult = spec.IdResult; | ... | @@ -44,34 +44,25 @@ const IdResult = spec.IdResult; |
| 44 | | 44 | |
| 45 | base: link.File, | 45 | base: link.File, |
| 46 | | 46 | |
| 47 | /// This linker backend does not try to incrementally link output SPIR-V code. | 47 | spv: SpvModule, |
| 48 | /// Instead, it tracks all declarations in this table, and iterates over it | 48 | spv_arena: ArenaAllocator, |
| 49 | /// in the flush function. | 49 | decl_ids: codegen.DeclMap, |
| 50 | decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclGenContext) = .{}, | | |
| 51 | | | |
| 52 | const DeclGenContext = struct { | | |
| 53 | air: Air, | | |
| 54 | air_arena: ArenaAllocator.State, | | |
| 55 | liveness: Liveness, | | |
| 56 | | | |
| 57 | fn deinit(self: *DeclGenContext, gpa: Allocator) void { | | |
| 58 | self.air.deinit(gpa); | | |
| 59 | self.liveness.deinit(gpa); | | |
| 60 | self.air_arena.promote(gpa).deinit(); | | |
| 61 | self.* = undefined; | | |
| 62 | } | | |
| 63 | }; | | |
| 64 | | 50 | |
| 65 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { | 51 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { |
| 66 | const spirv = try gpa.create(SpirV); | 52 | const self = try gpa.create(SpirV); |
| 67 | spirv.* = .{ | 53 | self.* = .{ |
| 68 | .base = .{ | 54 | .base = .{ |
| 69 | .tag = .spirv, | 55 | .tag = .spirv, |
| 70 | .options = options, | 56 | .options = options, |
| 71 | .file = null, | 57 | .file = null, |
| 72 | .allocator = gpa, | 58 | .allocator = gpa, |
| 73 | }, | 59 | }, |
| | 60 | .spv = undefined, |
| | 61 | .spv_arena = ArenaAllocator.init(gpa), |
| | 62 | .decl_ids = codegen.DeclMap.init(self.base.allocator), |
| 74 | }; | 63 | }; |
| | 64 | self.spv = SpvModule.init(gpa, self.spv_arena.allocator()); |
| | 65 | errdefer self.deinit(); |
| 75 | | 66 | |
| 76 | // TODO: Figure out where to put all of these | 67 | // TODO: Figure out where to put all of these |
| 77 | switch (options.target.cpu.arch) { | 68 | switch (options.target.cpu.arch) { |
| ... | @@ -88,7 +79,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { | ... | @@ -88,7 +79,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { |
| 88 | return error.TODOAbiNotSupported; | 79 | return error.TODOAbiNotSupported; |
| 89 | } | 80 | } |
| 90 | | 81 | |
| 91 | return spirv; | 82 | return self; |
| 92 | } | 83 | } |
| 93 | | 84 | |
| 94 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*SpirV { | 85 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*SpirV { |
| ... | @@ -107,44 +98,35 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -107,44 +98,35 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 107 | } | 98 | } |
| 108 | | 99 | |
| 109 | pub fn deinit(self: *SpirV) void { | 100 | pub fn deinit(self: *SpirV) void { |
| 110 | self.decl_table.deinit(self.base.allocator); | 101 | self.spv.deinit(); |
| | 102 | self.spv_arena.deinit(); |
| | 103 | self.decl_ids.deinit(); |
| 111 | } | 104 | } |
| 112 | | 105 | |
| 113 | pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | 106 | pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 114 | if (build_options.skip_non_native) { | 107 | if (build_options.skip_non_native) { |
| 115 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 108 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 116 | } | 109 | } |
| 117 | _ = module; | | |
| 118 | | | |
| 119 | // Keep track of all decls so we can iterate over them on flush(). | | |
| 120 | const result = try self.decl_table.getOrPut(self.base.allocator, func.owner_decl); | | |
| 121 | if (result.found_existing) { | | |
| 122 | result.value_ptr.deinit(self.base.allocator); | | |
| 123 | } | | |
| 124 | | | |
| 125 | var arena = ArenaAllocator.init(self.base.allocator); | | |
| 126 | errdefer arena.deinit(); | | |
| 127 | | 110 | |
| 128 | var new_air = try cloneAir(air, self.base.allocator, arena.allocator()); | 111 | var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_ids); |
| 129 | errdefer new_air.deinit(self.base.allocator); | 112 | defer decl_gen.deinit(); |
| 130 | | | |
| 131 | var new_liveness = try cloneLiveness(liveness, self.base.allocator); | | |
| 132 | errdefer new_liveness.deinit(self.base.allocator); | | |
| 133 | | 113 | |
| 134 | result.value_ptr.* = .{ | 114 | if (try decl_gen.gen(func.owner_decl, air, liveness)) |msg| { |
| 135 | .air = new_air, | 115 | try module.failed_decls.put(module.gpa, func.owner_decl, msg); |
| 136 | .air_arena = arena.state, | 116 | } |
| 137 | .liveness = new_liveness, | | |
| 138 | }; | | |
| 139 | } | 117 | } |
| 140 | | 118 | |
| 141 | pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void { | 119 | pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void { |
| 142 | if (build_options.skip_non_native) { | 120 | if (build_options.skip_non_native) { |
| 143 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 121 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 144 | } | 122 | } |
| 145 | _ = module; | 123 | |
| 146 | // Keep track of all decls so we can iterate over them on flush(). | 124 | var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &self.spv, &self.decl_ids); |
| 147 | _ = try self.decl_table.getOrPut(self.base.allocator, decl_index); | 125 | defer decl_gen.deinit(); |
| | 126 | |
| | 127 | if (try decl_gen.gen(decl_index, undefined, undefined)) |msg| { |
| | 128 | try module.failed_decls.put(module.gpa, decl_index, msg); |
| | 129 | } |
| 148 | } | 130 | } |
| 149 | | 131 | |
| 150 | pub fn updateDeclExports( | 132 | pub fn updateDeclExports( |
| ... | @@ -160,13 +142,8 @@ pub fn updateDeclExports( | ... | @@ -160,13 +142,8 @@ pub fn updateDeclExports( |
| 160 | } | 142 | } |
| 161 | | 143 | |
| 162 | pub fn freeDecl(self: *SpirV, decl_index: Module.Decl.Index) void { | 144 | pub fn freeDecl(self: *SpirV, decl_index: Module.Decl.Index) void { |
| 163 | if (self.decl_table.getIndex(decl_index)) |index| { | 145 | _ = self; |
| 164 | const module = self.base.options.module.?; | 146 | _ = decl_index; |
| 165 | const decl = module.declPtr(decl_index); | | |
| 166 | if (decl.val.tag() == .function) { | | |
| 167 | self.decl_table.values()[index].deinit(self.base.allocator); | | |
| 168 | } | | |
| 169 | } | | |
| 170 | } | 147 | } |
| 171 | | 148 | |
| 172 | pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { | 149 | pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { |
| ... | @@ -189,56 +166,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -189,56 +166,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No |
| 189 | sub_prog_node.activate(); | 166 | sub_prog_node.activate(); |
| 190 | defer sub_prog_node.end(); | 167 | defer sub_prog_node.end(); |
| 191 | | 168 | |
| 192 | const module = self.base.options.module.?; | | |
| 193 | const target = comp.getTarget(); | 169 | const target = comp.getTarget(); |
| | 170 | try writeCapabilities(&self.spv, target); |
| | 171 | try writeMemoryModel(&self.spv, target); |
| 194 | | 172 | |
| 195 | var arena = std.heap.ArenaAllocator.init(self.base.allocator); | 173 | try self.spv.flush(self.base.file.?); |
| 196 | defer arena.deinit(); | | |
| 197 | | | |
| 198 | var spv = SpvModule.init(self.base.allocator, arena.allocator()); | | |
| 199 | defer spv.deinit(); | | |
| 200 | | | |
| 201 | // Allocate an ID for every declaration before generating code, | | |
| 202 | // so that we can access them before processing them. | | |
| 203 | // TODO: We're allocating an ID unconditionally now, are there | | |
| 204 | // declarations which don't generate a result? | | |
| 205 | var ids = std.AutoHashMap(Module.Decl.Index, IdResult).init(self.base.allocator); | | |
| 206 | defer ids.deinit(); | | |
| 207 | try ids.ensureTotalCapacity(@intCast(u32, self.decl_table.count())); | | |
| 208 | | | |
| 209 | for (self.decl_table.keys()) |decl_index| { | | |
| 210 | const decl = module.declPtr(decl_index); | | |
| 211 | if (decl.has_tv) { | | |
| 212 | ids.putAssumeCapacityNoClobber(decl_index, spv.allocId()); | | |
| 213 | } | | |
| 214 | } | | |
| 215 | | | |
| 216 | // Now, actually generate the code for all declarations. | | |
| 217 | var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv, &ids); | | |
| 218 | defer decl_gen.deinit(); | | |
| 219 | | | |
| 220 | var it = self.decl_table.iterator(); | | |
| 221 | while (it.next()) |entry| { | | |
| 222 | const decl_index = entry.key_ptr.*; | | |
| 223 | const decl = module.declPtr(decl_index); | | |
| 224 | if (!decl.has_tv) continue; | | |
| 225 | | | |
| 226 | const air = entry.value_ptr.air; | | |
| 227 | const liveness = entry.value_ptr.liveness; | | |
| 228 | | | |
| 229 | log.debug("generating code for {s}", .{decl.name}); | | |
| 230 | | | |
| 231 | // Note, if `decl` is not a function, air/liveness may be undefined. | | |
| 232 | if (try decl_gen.gen(decl_index, air, liveness)) |msg| { | | |
| 233 | try module.failed_decls.put(module.gpa, decl_index, msg); | | |
| 234 | return; // TODO: Attempt to generate more decls? | | |
| 235 | } | | |
| 236 | } | | |
| 237 | | | |
| 238 | try writeCapabilities(&spv, target); | | |
| 239 | try writeMemoryModel(&spv, target); | | |
| 240 | | | |
| 241 | try spv.flush(self.base.file.?); | | |
| 242 | } | 174 | } |
| 243 | | 175 | |
| 244 | fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { | 176 | fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { |
| ... | @@ -281,45 +213,3 @@ fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { | ... | @@ -281,45 +213,3 @@ fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { |
| 281 | .memory_model = memory_model, | 213 | .memory_model = memory_model, |
| 282 | }); | 214 | }); |
| 283 | } | 215 | } |
| 284 | | | |
| 285 | fn cloneLiveness(l: Liveness, gpa: Allocator) !Liveness { | | |
| 286 | const tomb_bits = try gpa.dupe(usize, l.tomb_bits); | | |
| 287 | errdefer gpa.free(tomb_bits); | | |
| 288 | | | |
| 289 | const extra = try gpa.dupe(u32, l.extra); | | |
| 290 | errdefer gpa.free(extra); | | |
| 291 | | | |
| 292 | return Liveness{ | | |
| 293 | .tomb_bits = tomb_bits, | | |
| 294 | .extra = extra, | | |
| 295 | .special = try l.special.clone(gpa), | | |
| 296 | }; | | |
| 297 | } | | |
| 298 | | | |
| 299 | fn cloneAir(air: Air, gpa: Allocator, air_arena: Allocator) !Air { | | |
| 300 | const values = try gpa.alloc(Value, air.values.len); | | |
| 301 | errdefer gpa.free(values); | | |
| 302 | | | |
| 303 | for (values, 0..) |*value, i| { | | |
| 304 | value.* = try air.values[i].copy(air_arena); | | |
| 305 | } | | |
| 306 | | | |
| 307 | var instructions = try air.instructions.toMultiArrayList().clone(gpa); | | |
| 308 | errdefer instructions.deinit(gpa); | | |
| 309 | | | |
| 310 | const air_tags = instructions.items(.tag); | | |
| 311 | const air_datas = instructions.items(.data); | | |
| 312 | | | |
| 313 | for (air_tags, 0..) |tag, i| { | | |
| 314 | switch (tag) { | | |
| 315 | .alloc, .ret_ptr, .const_ty => air_datas[i].ty = try air_datas[i].ty.copy(air_arena), | | |
| 316 | else => {}, | | |
| 317 | } | | |
| 318 | } | | |
| 319 | | | |
| 320 | return Air{ | | |
| 321 | .instructions = instructions.slice(), | | |
| 322 | .extra = try gpa.dupe(u32, air.extra), | | |
| 323 | .values = values, | | |
| 324 | }; | | |
| 325 | } | | |