| ... | ... | @@ -27,6 +27,9 @@ decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{}, |
| 27 | 27 | /// While in progress, a separate buffer is used, and then when finished, the |
| 28 | 28 | /// buffer is copied into this one. |
| 29 | 29 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, |
| 30 | /// Tracks all the anonymous decls that are used by all the decls so they can |
| 31 | /// be rendered during flush(). |
| 32 | anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, DeclBlock) = .{}, |
| 30 | 33 | |
| 31 | 34 | /// Optimization, `updateDecl` reuses this buffer rather than creating a new |
| 32 | 35 | /// one with every call. |
| ... | ... | @@ -34,9 +37,6 @@ fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 34 | 37 | /// Optimization, `updateDecl` reuses this buffer rather than creating a new |
| 35 | 38 | /// one with every call. |
| 36 | 39 | code_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 37 | | /// Optimization, `updateDecl` reuses this table rather than creating a new one |
| 38 | | /// with every call. |
| 39 | | scratch_anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, |
| 40 | 40 | /// Optimization, `flush` reuses this buffer rather than creating a new |
| 41 | 41 | /// one with every call. |
| 42 | 42 | lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -45,7 +45,7 @@ lazy_fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 45 | 45 | lazy_code_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 46 | 46 | |
| 47 | 47 | /// A reference into `string_bytes`. |
| 48 | | const String = struct { |
| 48 | const String = extern struct { |
| 49 | 49 | start: u32, |
| 50 | 50 | len: u32, |
| 51 | 51 | |
| ... | ... | @@ -54,8 +54,9 @@ const String = struct { |
| 54 | 54 | .len = 0, |
| 55 | 55 | }; |
| 56 | 56 | }; |
| 57 | |
| 57 | 58 | /// Per-declaration data. |
| 58 | | const DeclBlock = struct { |
| 59 | pub const DeclBlock = struct { |
| 59 | 60 | code: String = String.empty, |
| 60 | 61 | fwd_decl: String = String.empty, |
| 61 | 62 | /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate |
| ... | ... | @@ -120,10 +121,14 @@ pub fn deinit(self: *C) void { |
| 120 | 121 | } |
| 121 | 122 | self.decl_table.deinit(gpa); |
| 122 | 123 | |
| 124 | for (self.anon_decls.values()) |*db| { |
| 125 | db.deinit(gpa); |
| 126 | } |
| 127 | self.anon_decls.deinit(gpa); |
| 128 | |
| 123 | 129 | self.string_bytes.deinit(gpa); |
| 124 | 130 | self.fwd_decl_buf.deinit(gpa); |
| 125 | 131 | self.code_buf.deinit(gpa); |
| 126 | | self.scratch_anon_decl_deps.deinit(gpa); |
| 127 | 132 | } |
| 128 | 133 | |
| 129 | 134 | pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void { |
| ... | ... | @@ -158,57 +163,110 @@ pub fn updateFunc( |
| 158 | 163 | lazy_fns.clearRetainingCapacity(); |
| 159 | 164 | fwd_decl.clearRetainingCapacity(); |
| 160 | 165 | code.clearRetainingCapacity(); |
| 161 | | self.scratch_anon_decl_deps.clearRetainingCapacity(); |
| 162 | 166 | |
| 163 | | { |
| 164 | | var function: codegen.Function = .{ |
| 165 | | .value_map = codegen.CValueMap.init(gpa), |
| 166 | | .air = air, |
| 167 | | .liveness = liveness, |
| 168 | | .func_index = func_index, |
| 169 | | .object = .{ |
| 170 | | .dg = .{ |
| 171 | | .gpa = gpa, |
| 172 | | .module = module, |
| 173 | | .error_msg = null, |
| 174 | | .decl_index = decl_index.toOptional(), |
| 175 | | .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked, |
| 176 | | .fwd_decl = fwd_decl.toManaged(gpa), |
| 177 | | .ctypes = ctypes.*, |
| 178 | | .anon_decl_deps = self.scratch_anon_decl_deps, |
| 179 | | }, |
| 180 | | .code = code.toManaged(gpa), |
| 181 | | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 167 | var function: codegen.Function = .{ |
| 168 | .value_map = codegen.CValueMap.init(gpa), |
| 169 | .air = air, |
| 170 | .liveness = liveness, |
| 171 | .func_index = func_index, |
| 172 | .object = .{ |
| 173 | .dg = .{ |
| 174 | .gpa = gpa, |
| 175 | .module = module, |
| 176 | .error_msg = null, |
| 177 | .decl_index = decl_index.toOptional(), |
| 178 | .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked, |
| 179 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 180 | .ctypes = ctypes.*, |
| 181 | .anon_decl_deps = self.anon_decls, |
| 182 | 182 | }, |
| 183 | | .lazy_fns = lazy_fns.*, |
| 184 | | }; |
| 183 | .code = code.toManaged(gpa), |
| 184 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 185 | }, |
| 186 | .lazy_fns = lazy_fns.*, |
| 187 | }; |
| 185 | 188 | |
| 186 | | function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; |
| 187 | | defer { |
| 188 | | self.scratch_anon_decl_deps = function.object.dg.anon_decl_deps; |
| 189 | | fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); |
| 190 | | code.* = function.object.code.moveToUnmanaged(); |
| 191 | | function.deinit(); |
| 192 | | } |
| 189 | function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; |
| 190 | defer { |
| 191 | self.anon_decls = function.object.dg.anon_decl_deps; |
| 192 | fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); |
| 193 | code.* = function.object.code.moveToUnmanaged(); |
| 194 | function.deinit(); |
| 195 | } |
| 193 | 196 | |
| 194 | | codegen.genFunc(&function) catch |err| switch (err) { |
| 195 | | error.AnalysisFail => { |
| 196 | | try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?); |
| 197 | | return; |
| 198 | | }, |
| 199 | | else => |e| return e, |
| 200 | | }; |
| 197 | codegen.genFunc(&function) catch |err| switch (err) { |
| 198 | error.AnalysisFail => { |
| 199 | try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?); |
| 200 | return; |
| 201 | }, |
| 202 | else => |e| return e, |
| 203 | }; |
| 204 | |
| 205 | ctypes.* = function.object.dg.ctypes.move(); |
| 206 | lazy_fns.* = function.lazy_fns.move(); |
| 201 | 207 | |
| 202 | | ctypes.* = function.object.dg.ctypes.move(); |
| 203 | | lazy_fns.* = function.lazy_fns.move(); |
| 208 | // Free excess allocated memory for this Decl. |
| 209 | ctypes.shrinkAndFree(gpa, ctypes.count()); |
| 210 | lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); |
| 211 | |
| 212 | gop.value_ptr.code = try self.addString(function.object.code.items); |
| 213 | gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items); |
| 214 | } |
| 215 | |
| 216 | fn updateAnonDecl(self: *C, module: *Module, i: usize) !void { |
| 217 | const gpa = self.base.allocator; |
| 218 | const anon_decl = self.anon_decls.keys()[i]; |
| 204 | 219 | |
| 205 | | // Free excess allocated memory for this Decl. |
| 206 | | ctypes.shrinkAndFree(gpa, ctypes.count()); |
| 207 | | lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); |
| 220 | const fwd_decl = &self.fwd_decl_buf; |
| 221 | const code = &self.code_buf; |
| 222 | fwd_decl.clearRetainingCapacity(); |
| 223 | code.clearRetainingCapacity(); |
| 208 | 224 | |
| 209 | | gop.value_ptr.code = try self.addString(function.object.code.items); |
| 210 | | gop.value_ptr.fwd_decl = try self.addString(function.object.dg.fwd_decl.items); |
| 225 | var object: codegen.Object = .{ |
| 226 | .dg = .{ |
| 227 | .gpa = gpa, |
| 228 | .module = module, |
| 229 | .error_msg = null, |
| 230 | .decl_index = .none, |
| 231 | .is_naked_fn = false, |
| 232 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 233 | .ctypes = .{}, |
| 234 | .anon_decl_deps = self.anon_decls, |
| 235 | }, |
| 236 | .code = code.toManaged(gpa), |
| 237 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 238 | }; |
| 239 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 240 | |
| 241 | defer { |
| 242 | self.anon_decls = object.dg.anon_decl_deps; |
| 243 | object.dg.ctypes.deinit(object.dg.gpa); |
| 244 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 245 | code.* = object.code.moveToUnmanaged(); |
| 211 | 246 | } |
| 247 | |
| 248 | const tv: @import("../TypedValue.zig") = .{ |
| 249 | .ty = module.intern_pool.typeOf(anon_decl).toType(), |
| 250 | .val = anon_decl.toValue(), |
| 251 | }; |
| 252 | const c_value: codegen.CValue = .{ .constant = anon_decl }; |
| 253 | codegen.genDeclValue(&object, tv, false, c_value, .none, .none) catch |err| switch (err) { |
| 254 | error.AnalysisFail => { |
| 255 | @panic("TODO: C backend AnalysisFail on anonymous decl"); |
| 256 | //try module.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); |
| 257 | //return; |
| 258 | }, |
| 259 | else => |e| return e, |
| 260 | }; |
| 261 | |
| 262 | // Free excess allocated memory for this Decl. |
| 263 | object.dg.ctypes.shrinkAndFree(gpa, object.dg.ctypes.count()); |
| 264 | |
| 265 | object.dg.anon_decl_deps.values()[i] = .{ |
| 266 | .code = try self.addString(object.code.items), |
| 267 | .fwd_decl = try self.addString(object.dg.fwd_decl.items), |
| 268 | .ctypes = object.dg.ctypes.move(), |
| 269 | }; |
| 212 | 270 | } |
| 213 | 271 | |
| 214 | 272 | pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | ... | @@ -227,7 +285,6 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi |
| 227 | 285 | ctypes.clearRetainingCapacity(gpa); |
| 228 | 286 | fwd_decl.clearRetainingCapacity(); |
| 229 | 287 | code.clearRetainingCapacity(); |
| 230 | | self.scratch_anon_decl_deps.clearRetainingCapacity(); |
| 231 | 288 | |
| 232 | 289 | var object: codegen.Object = .{ |
| 233 | 290 | .dg = .{ |
| ... | ... | @@ -238,14 +295,14 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi |
| 238 | 295 | .is_naked_fn = false, |
| 239 | 296 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 240 | 297 | .ctypes = ctypes.*, |
| 241 | | .anon_decl_deps = self.scratch_anon_decl_deps, |
| 298 | .anon_decl_deps = self.anon_decls, |
| 242 | 299 | }, |
| 243 | 300 | .code = code.toManaged(gpa), |
| 244 | 301 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 245 | 302 | }; |
| 246 | 303 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 247 | 304 | defer { |
| 248 | | self.scratch_anon_decl_deps = object.dg.anon_decl_deps; |
| 305 | self.anon_decls = object.dg.anon_decl_deps; |
| 249 | 306 | object.dg.ctypes.deinit(object.dg.gpa); |
| 250 | 307 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 251 | 308 | code.* = object.code.moveToUnmanaged(); |
| ... | ... | @@ -303,6 +360,13 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo |
| 303 | 360 | const gpa = self.base.allocator; |
| 304 | 361 | const module = self.base.options.module.?; |
| 305 | 362 | |
| 363 | { |
| 364 | var i: usize = 0; |
| 365 | while (i < self.anon_decls.count()) : (i += 1) { |
| 366 | try updateAnonDecl(self, module, i); |
| 367 | } |
| 368 | } |
| 369 | |
| 306 | 370 | // This code path happens exclusively with -ofmt=c. The flush logic for |
| 307 | 371 | // emit-h is in `flushEmitH` below. |
| 308 | 372 | |
| ... | ... | @@ -345,10 +409,15 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo |
| 345 | 409 | for (module.decl_exports.values()) |exports| for (exports.items) |@"export"| |
| 346 | 410 | try export_names.put(gpa, @"export".opts.name, {}); |
| 347 | 411 | |
| 348 | | const decl_keys = self.decl_table.keys(); |
| 349 | | for (decl_keys) |decl_index| { |
| 412 | for (self.anon_decls.values()) |*decl_block| { |
| 413 | try self.flushDeclBlock(&f, decl_block, export_names, .none); |
| 414 | } |
| 415 | |
| 416 | for (self.decl_table.keys(), self.decl_table.values()) |decl_index, *decl_block| { |
| 350 | 417 | assert(module.declPtr(decl_index).has_tv); |
| 351 | | try self.flushDecl(&f, decl_index, export_names); |
| 418 | const decl = module.declPtr(decl_index); |
| 419 | const extern_symbol_name = if (decl.isExtern(module)) decl.name.toOptional() else .none; |
| 420 | try self.flushDeclBlock(&f, decl_block, export_names, extern_symbol_name); |
| 352 | 421 | } |
| 353 | 422 | } |
| 354 | 423 | |
| ... | ... | @@ -358,8 +427,12 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo |
| 358 | 427 | assert(f.ctypes.count() == 0); |
| 359 | 428 | try self.flushCTypes(&f, .none, f.lazy_ctypes); |
| 360 | 429 | |
| 361 | | for (self.decl_table.keys(), self.decl_table.values()) |decl_index, db| { |
| 362 | | try self.flushCTypes(&f, decl_index.toOptional(), db.ctypes); |
| 430 | for (self.anon_decls.values()) |decl_block| { |
| 431 | try self.flushCTypes(&f, .none, decl_block.ctypes); |
| 432 | } |
| 433 | |
| 434 | for (self.decl_table.keys(), self.decl_table.values()) |decl_index, decl_block| { |
| 435 | try self.flushCTypes(&f, decl_index.toOptional(), decl_block.ctypes); |
| 363 | 436 | } |
| 364 | 437 | } |
| 365 | 438 | |
| ... | ... | @@ -377,10 +450,12 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo |
| 377 | 450 | f.file_size += lazy_fwd_decl_len; |
| 378 | 451 | |
| 379 | 452 | // Now the code. |
| 453 | const anon_decl_values = self.anon_decls.values(); |
| 380 | 454 | const decl_values = self.decl_table.values(); |
| 381 | | try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len); |
| 455 | try f.all_buffers.ensureUnusedCapacity(gpa, 1 + anon_decl_values.len + decl_values.len); |
| 382 | 456 | f.appendBufAssumeCapacity(self.lazy_code_buf.items); |
| 383 | | for (decl_values) |decl| f.appendBufAssumeCapacity(self.getString(decl.code)); |
| 457 | for (anon_decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code)); |
| 458 | for (decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code)); |
| 384 | 459 | |
| 385 | 460 | const file = self.base.file.?; |
| 386 | 461 | try file.setEndPos(f.file_size); |
| ... | ... | @@ -526,16 +601,14 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { |
| 526 | 601 | .is_naked_fn = false, |
| 527 | 602 | .fwd_decl = fwd_decl.toManaged(gpa), |
| 528 | 603 | .ctypes = ctypes.*, |
| 529 | | .anon_decl_deps = .{}, |
| 604 | .anon_decl_deps = self.anon_decls, |
| 530 | 605 | }, |
| 531 | 606 | .code = code.toManaged(gpa), |
| 532 | 607 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 533 | 608 | }; |
| 534 | 609 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 535 | 610 | defer { |
| 536 | | // If this assert trips just handle the anon_decl_deps the same as |
| 537 | | // `updateFunc()` does. |
| 538 | | assert(object.dg.anon_decl_deps.count() == 0); |
| 611 | self.anon_decls = object.dg.anon_decl_deps; |
| 539 | 612 | object.dg.ctypes.deinit(gpa); |
| 540 | 613 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 541 | 614 | code.* = object.code.moveToUnmanaged(); |
| ... | ... | @@ -604,22 +677,22 @@ fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError |
| 604 | 677 | } |
| 605 | 678 | } |
| 606 | 679 | |
| 607 | | fn flushDecl( |
| 680 | fn flushDeclBlock( |
| 608 | 681 | self: *C, |
| 609 | 682 | f: *Flush, |
| 610 | | decl_index: Module.Decl.Index, |
| 683 | decl_block: *DeclBlock, |
| 611 | 684 | export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), |
| 685 | extern_symbol_name: InternPool.OptionalNullTerminatedString, |
| 612 | 686 | ) FlushDeclError!void { |
| 613 | 687 | const gpa = self.base.allocator; |
| 614 | | const mod = self.base.options.module.?; |
| 615 | | const decl = mod.declPtr(decl_index); |
| 616 | | |
| 617 | | const decl_block = self.decl_table.getPtr(decl_index).?; |
| 618 | | |
| 619 | 688 | try self.flushLazyFns(f, decl_block.lazy_fns); |
| 620 | 689 | try f.all_buffers.ensureUnusedCapacity(gpa, 1); |
| 621 | | if (!(decl.isExtern(mod) and export_names.contains(decl.name))) |
| 690 | fwd_decl: { |
| 691 | if (extern_symbol_name.unwrap()) |name| { |
| 692 | if (export_names.contains(name)) break :fwd_decl; |
| 693 | } |
| 622 | 694 | f.appendBufAssumeCapacity(self.getString(decl_block.fwd_decl)); |
| 695 | } |
| 623 | 696 | } |
| 624 | 697 | |
| 625 | 698 | pub fn flushEmitH(module: *Module) !void { |