| ... | ... | @@ -53,20 +53,141 @@ const Block = struct { |
| 53 | 53 | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block); |
| 54 | 54 | |
| 55 | 55 | /// Maps Zig decl indices to SPIR-V linking information. |
| 56 | | pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index); |
| 56 | pub const DeclLinkMap = std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index); |
| 57 | 57 | |
| 58 | 58 | /// Maps anon decl indices to SPIR-V linking information. |
| 59 | | pub const AnonDeclLinkMap = std.AutoHashMap(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index); |
| 59 | pub const AnonDeclLinkMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index); |
| 60 | |
| 61 | /// This structure holds information that is relevant to the entire compilation, |
| 62 | /// in contrast to `DeclGen`, which only holds relevant information about a |
| 63 | /// single decl. |
| 64 | pub const Object = struct { |
| 65 | /// A general-purpose allocator that can be used for any allocation for this Object. |
| 66 | gpa: Allocator, |
| 67 | |
| 68 | /// the SPIR-V module that represents the final binary. |
| 69 | spv: SpvModule, |
| 70 | |
| 71 | /// The Zig module that this object file is generated for. |
| 72 | /// A map of Zig decl indices to SPIR-V decl indices. |
| 73 | decl_link: DeclLinkMap = .{}, |
| 74 | |
| 75 | /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices. |
| 76 | anon_decl_link: AnonDeclLinkMap = .{}, |
| 77 | |
| 78 | /// A map that maps AIR intern pool indices to SPIR-V cache references (which |
| 79 | /// is basically the same thing except for SPIR-V). |
| 80 | /// This map is typically only used for structures that are deemed heavy enough |
| 81 | /// that it is worth to store them here. The SPIR-V module also interns types, |
| 82 | /// and so the main purpose of this map is to avoid recomputation and to |
| 83 | /// cache extra information about the type rather than to aid in validity |
| 84 | /// of the SPIR-V module. |
| 85 | type_map: TypeMap = .{}, |
| 86 | |
| 87 | pub fn init(gpa: Allocator) Object { |
| 88 | return .{ |
| 89 | .gpa = gpa, |
| 90 | .spv = SpvModule.init(gpa), |
| 91 | }; |
| 92 | } |
| 93 | |
| 94 | pub fn deinit(self: *Object) void { |
| 95 | self.spv.deinit(); |
| 96 | self.decl_link.deinit(self.gpa); |
| 97 | self.anon_decl_link.deinit(self.gpa); |
| 98 | self.type_map.deinit(self.gpa); |
| 99 | } |
| 100 | |
| 101 | fn genDecl( |
| 102 | self: *Object, |
| 103 | mod: *Module, |
| 104 | decl_index: Decl.Index, |
| 105 | air: Air, |
| 106 | liveness: Liveness, |
| 107 | ) !void { |
| 108 | var decl_gen = DeclGen{ |
| 109 | .gpa = self.gpa, |
| 110 | .object = self, |
| 111 | .module = mod, |
| 112 | .spv = &self.spv, |
| 113 | .decl_index = decl_index, |
| 114 | .air = air, |
| 115 | .liveness = liveness, |
| 116 | .type_map = &self.type_map, |
| 117 | .current_block_label_id = undefined, |
| 118 | }; |
| 119 | defer decl_gen.deinit(); |
| 120 | |
| 121 | decl_gen.genDecl() catch |err| switch (err) { |
| 122 | error.CodegenFail => { |
| 123 | try mod.failed_decls.put(mod.gpa, decl_index, decl_gen.error_msg.?); |
| 124 | }, |
| 125 | else => |other| { |
| 126 | // There might be an error that happened *after* self.error_msg |
| 127 | // was already allocated, so be sure to free it. |
| 128 | if (decl_gen.error_msg) |error_msg| { |
| 129 | error_msg.deinit(mod.gpa); |
| 130 | } |
| 131 | |
| 132 | return other; |
| 133 | }, |
| 134 | }; |
| 135 | } |
| 136 | |
| 137 | pub fn updateFunc( |
| 138 | self: *Object, |
| 139 | mod: *Module, |
| 140 | func_index: InternPool.Index, |
| 141 | air: Air, |
| 142 | liveness: Liveness, |
| 143 | ) !void { |
| 144 | const decl_index = mod.funcInfo(func_index).owner_decl; |
| 145 | // TODO: Separate types for generating decls and functions? |
| 146 | try self.genDecl(mod, decl_index, air, liveness); |
| 147 | } |
| 148 | |
| 149 | pub fn updateDecl( |
| 150 | self: *Object, |
| 151 | mod: *Module, |
| 152 | decl_index: Decl.Index, |
| 153 | ) !void { |
| 154 | try self.genDecl(mod, decl_index, undefined, undefined); |
| 155 | } |
| 156 | |
| 157 | /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. |
| 158 | /// Note: Function does not actually generate the decl, it just allocates an index. |
| 159 | pub fn resolveDecl(self: *Object, mod: *Module, decl_index: Decl.Index) !SpvModule.Decl.Index { |
| 160 | const decl = mod.declPtr(decl_index); |
| 161 | try mod.markDeclAlive(decl); |
| 162 | |
| 163 | const entry = try self.decl_link.getOrPut(self.gpa, decl_index); |
| 164 | if (!entry.found_existing) { |
| 165 | // TODO: Extern fn? |
| 166 | const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod)) |
| 167 | .func |
| 168 | else |
| 169 | .global; |
| 170 | |
| 171 | entry.value_ptr.* = try self.spv.allocDecl(kind); |
| 172 | } |
| 173 | |
| 174 | return entry.value_ptr.*; |
| 175 | } |
| 176 | }; |
| 60 | 177 | |
| 61 | 178 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. |
| 62 | | pub const DeclGen = struct { |
| 179 | const DeclGen = struct { |
| 63 | 180 | /// A general-purpose allocator that can be used for any allocations for this DeclGen. |
| 64 | 181 | gpa: Allocator, |
| 65 | 182 | |
| 183 | /// The object that this decl is generated into. |
| 184 | object: *Object, |
| 185 | |
| 66 | 186 | /// The Zig module that we are generating decls for. |
| 67 | 187 | module: *Module, |
| 68 | 188 | |
| 69 | 189 | /// The SPIR-V module that instructions should be emitted into. |
| 190 | /// This is the same as `self.object.spv`, repeated here for brevity. |
| 70 | 191 | spv: *SpvModule, |
| 71 | 192 | |
| 72 | 193 | /// The decl we are currently generating code for. |
| ... | ... | @@ -80,30 +201,19 @@ pub const DeclGen = struct { |
| 80 | 201 | /// Note: If the declaration is not a function, this value will be undefined! |
| 81 | 202 | liveness: Liveness, |
| 82 | 203 | |
| 83 | | /// Maps Zig Decl indices to SPIR-V decl indices. |
| 84 | | decl_link: *DeclLinkMap, |
| 85 | | |
| 86 | | /// Maps Zig anon decl indices to SPIR-V decl indices. |
| 87 | | anon_decl_link: *AnonDeclLinkMap, |
| 88 | | |
| 89 | 204 | /// An array of function argument result-ids. Each index corresponds with the |
| 90 | 205 | /// function argument of the same index. |
| 91 | 206 | args: std.ArrayListUnmanaged(IdRef) = .{}, |
| 92 | 207 | |
| 93 | 208 | /// A counter to keep track of how many `arg` instructions we've seen yet. |
| 94 | | next_arg_index: u32, |
| 209 | next_arg_index: u32 = 0, |
| 95 | 210 | |
| 96 | 211 | /// A map keeping track of which instruction generated which result-id. |
| 97 | 212 | inst_results: InstMap = .{}, |
| 98 | 213 | |
| 99 | | /// A map that maps AIR intern pool indices to SPIR-V cache references (which |
| 100 | | /// is basically the same thing except for SPIR-V). |
| 101 | | /// This map is typically only used for structures that are deemed heavy enough |
| 102 | | /// that it is worth to store them here. The SPIR-V module also interns types, |
| 103 | | /// and so the main purpose of this map is to avoid recomputation and to |
| 104 | | /// cache extra information about the type rather than to aid in validity |
| 105 | | /// of the SPIR-V module. |
| 106 | | type_map: TypeMap = .{}, |
| 214 | /// A map that maps AIR intern pool indices to SPIR-V cache references. |
| 215 | /// See Object.type_map |
| 216 | type_map: *TypeMap, |
| 107 | 217 | |
| 108 | 218 | /// We need to keep track of result ids for block labels, as well as the 'incoming' |
| 109 | 219 | /// blocks for a block. |
| ... | ... | @@ -121,7 +231,7 @@ pub const DeclGen = struct { |
| 121 | 231 | |
| 122 | 232 | /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message. |
| 123 | 233 | /// Memory is owned by `module.gpa`. |
| 124 | | error_msg: ?*Module.ErrorMsg, |
| 234 | error_msg: ?*Module.ErrorMsg = null, |
| 125 | 235 | |
| 126 | 236 | /// Possible errors the `genDecl` function may return. |
| 127 | 237 | const Error = error{ CodegenFail, OutOfMemory }; |
| ... | ... | @@ -181,67 +291,10 @@ pub const DeclGen = struct { |
| 181 | 291 | indirect, |
| 182 | 292 | }; |
| 183 | 293 | |
| 184 | | /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, |
| 185 | | /// only set when `gen` is called. |
| 186 | | pub fn init( |
| 187 | | allocator: Allocator, |
| 188 | | module: *Module, |
| 189 | | spv: *SpvModule, |
| 190 | | decl_link: *DeclLinkMap, |
| 191 | | anon_decl_link: *AnonDeclLinkMap, |
| 192 | | ) DeclGen { |
| 193 | | return .{ |
| 194 | | .gpa = allocator, |
| 195 | | .module = module, |
| 196 | | .spv = spv, |
| 197 | | .decl_index = undefined, |
| 198 | | .air = undefined, |
| 199 | | .liveness = undefined, |
| 200 | | .decl_link = decl_link, |
| 201 | | .anon_decl_link = anon_decl_link, |
| 202 | | .next_arg_index = undefined, |
| 203 | | .current_block_label_id = undefined, |
| 204 | | .error_msg = undefined, |
| 205 | | }; |
| 206 | | } |
| 207 | | |
| 208 | | /// Generate the code for `decl`. If a reportable error occurred during code generation, |
| 209 | | /// a message is returned by this function. Callee owns the memory. If this function |
| 210 | | /// returns such a reportable error, it is valid to be called again for a different decl. |
| 211 | | pub fn gen(self: *DeclGen, decl_index: Decl.Index, air: Air, liveness: Liveness) !?*Module.ErrorMsg { |
| 212 | | // Reset internal resources, we don't want to re-allocate these. |
| 213 | | self.decl_index = decl_index; |
| 214 | | self.air = air; |
| 215 | | self.liveness = liveness; |
| 216 | | self.args.items.len = 0; |
| 217 | | self.next_arg_index = 0; |
| 218 | | self.inst_results.clearRetainingCapacity(); |
| 219 | | self.blocks.clearRetainingCapacity(); |
| 220 | | self.current_block_label_id = undefined; |
| 221 | | self.func.reset(); |
| 222 | | self.base_line_stack.items.len = 0; |
| 223 | | self.error_msg = null; |
| 224 | | |
| 225 | | self.genDecl() catch |err| switch (err) { |
| 226 | | error.CodegenFail => return self.error_msg, |
| 227 | | else => |others| { |
| 228 | | // There might be an error that happened *after* self.error_msg |
| 229 | | // was already allocated, so be sure to free it. |
| 230 | | if (self.error_msg) |error_msg| { |
| 231 | | error_msg.deinit(self.module.gpa); |
| 232 | | } |
| 233 | | return others; |
| 234 | | }, |
| 235 | | }; |
| 236 | | |
| 237 | | return null; |
| 238 | | } |
| 239 | | |
| 240 | 294 | /// Free resources owned by the DeclGen. |
| 241 | 295 | pub fn deinit(self: *DeclGen) void { |
| 242 | 296 | self.args.deinit(self.gpa); |
| 243 | 297 | self.inst_results.deinit(self.gpa); |
| 244 | | self.type_map.deinit(self.gpa); |
| 245 | 298 | self.blocks.deinit(self.gpa); |
| 246 | 299 | self.func.deinit(self.gpa); |
| 247 | 300 | self.base_line_stack.deinit(self.gpa); |
| ... | ... | @@ -277,7 +330,7 @@ pub const DeclGen = struct { |
| 277 | 330 | .func => |func| func.owner_decl, |
| 278 | 331 | else => unreachable, |
| 279 | 332 | }; |
| 280 | | const spv_decl_index = try self.resolveDecl(fn_decl_index); |
| 333 | const spv_decl_index = try self.object.resolveDecl(mod, fn_decl_index); |
| 281 | 334 | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); |
| 282 | 335 | return self.spv.declPtr(spv_decl_index).result_id; |
| 283 | 336 | } |
| ... | ... | @@ -288,31 +341,10 @@ pub const DeclGen = struct { |
| 288 | 341 | return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. |
| 289 | 342 | } |
| 290 | 343 | |
| 291 | | /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. |
| 292 | | /// Note: Function does not actually generate the decl. |
| 293 | | fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !SpvModule.Decl.Index { |
| 294 | | const mod = self.module; |
| 295 | | const decl = mod.declPtr(decl_index); |
| 296 | | try mod.markDeclAlive(decl); |
| 297 | | |
| 298 | | const entry = try self.decl_link.getOrPut(decl_index); |
| 299 | | if (!entry.found_existing) { |
| 300 | | // TODO: Extern fn? |
| 301 | | const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod)) |
| 302 | | .func |
| 303 | | else |
| 304 | | .global; |
| 305 | | |
| 306 | | entry.value_ptr.* = try self.spv.allocDecl(kind); |
| 307 | | } |
| 308 | | |
| 309 | | return entry.value_ptr.*; |
| 310 | | } |
| 311 | | |
| 312 | 344 | fn resolveAnonDecl(self: *DeclGen, val: InternPool.Index, storage_class: StorageClass) !IdRef { |
| 313 | 345 | // TODO: This cannot be a function at this point, but it should probably be handled anyway. |
| 314 | 346 | const spv_decl_index = blk: { |
| 315 | | const entry = try self.anon_decl_link.getOrPut(.{ val, storage_class }); |
| 347 | const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, storage_class }); |
| 316 | 348 | if (entry.found_existing) { |
| 317 | 349 | try self.func.decl_deps.put(self.spv.gpa, entry.value_ptr.*, {}); |
| 318 | 350 | return self.spv.declPtr(entry.value_ptr.*).result_id; |
| ... | ... | @@ -988,7 +1020,7 @@ pub const DeclGen = struct { |
| 988 | 1020 | return self.spv.constUndef(ty_ref); |
| 989 | 1021 | } |
| 990 | 1022 | |
| 991 | | const spv_decl_index = try self.resolveDecl(decl_index); |
| 1023 | const spv_decl_index = try self.object.resolveDecl(mod, decl_index); |
| 992 | 1024 | |
| 993 | 1025 | const decl_id = self.spv.declPtr(spv_decl_index).result_id; |
| 994 | 1026 | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); |
| ... | ... | @@ -1624,7 +1656,7 @@ pub const DeclGen = struct { |
| 1624 | 1656 | const mod = self.module; |
| 1625 | 1657 | const ip = &mod.intern_pool; |
| 1626 | 1658 | const decl = mod.declPtr(self.decl_index); |
| 1627 | | const spv_decl_index = try self.resolveDecl(self.decl_index); |
| 1659 | const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index); |
| 1628 | 1660 | |
| 1629 | 1661 | const decl_id = self.spv.declPtr(spv_decl_index).result_id; |
| 1630 | 1662 | |