| author | |
| committer | |
| log | 5d7f2697deadbcc6f2b370c6eaa69099d6a54ee7 |
| tree | b6648cc43d6d13d1665e473813cdf0a210204255 |
| parent | 6b5d0b371111fd458e7fd0a1500f0d93914b5db1 |
and implement the first pass at mechanism to map old ZIR to new ZIR.5 files changed, 454 insertions(+), 9 deletions(-)
BRANCH_TODO+12-2| ... | ... | @@ -1,5 +1,11 @@ |
| 1 | * AstGen threadlocal | |
| 2 | * extern "foo" for vars and for functions | |
| 1 | * implement the iterators that updateZirRefs needs | |
| 2 | - for iterating over ZIR decls | |
| 3 | - for iterating over ZIR instructions within a decl to find decl instructions | |
| 4 | * implement `zig zirdiff a.zig b.zig` for showing debug output for how a particular | |
| 5 | source transformation will be seen by the change detection algorithm. | |
| 6 | * communicate the changelist back to the driver code and process it in semantic analysis, | |
| 7 | handling deletions and outdatings. | |
| 8 | ||
| 3 | 9 | * namespace decls table can't reference ZIR memory because it can get modified on updates |
| 4 | 10 | - change it for astgen worker to compare old and new ZIR, updating existing |
| 5 | 11 | namespaces & decls, and creating a changelist. |
| ... | ... | @@ -55,4 +61,8 @@ |
| 55 | 61 | natural alignment for fields and do not have any comptime fields. this |
| 56 | 62 | will save 16 bytes per struct field in the compilation. |
| 57 | 63 | |
| 64 | * AstGen threadlocal | |
| 65 | * extern "foo" for vars | |
| 58 | 66 | |
| 67 | * TODO all decls should probably store source hash. Without this, | |
| 68 | we currently unnecessarily mark all anon decls outdated here. |
src/Module.zig+189-3| ... | ... | @@ -190,6 +190,7 @@ pub const Decl = struct { |
| 190 | 190 | /// Index to ZIR `extra` array to the entry in the parent's decl structure |
| 191 | 191 | /// (the part that says "for every decls_len"). The first item at this index is |
| 192 | 192 | /// the contents hash, followed by line, name, etc. |
| 193 | /// For anonymous decls and also the root Decl for a File, this is 0. | |
| 193 | 194 | zir_decl_index: Zir.Inst.Index, |
| 194 | 195 | |
| 195 | 196 | /// Represents the "shallow" analysis status. For example, for decls that are functions, |
| ... | ... | @@ -329,6 +330,7 @@ pub const Decl = struct { |
| 329 | 330 | } |
| 330 | 331 | |
| 331 | 332 | pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 { |
| 333 | assert(decl.zir_decl_index != 0); | |
| 332 | 334 | const name_index = zir.extra[decl.zir_decl_index + 5]; |
| 333 | 335 | if (name_index <= 1) return null; |
| 334 | 336 | return zir.nullTerminatedString(name_index); |
| ... | ... | @@ -340,24 +342,28 @@ pub const Decl = struct { |
| 340 | 342 | } |
| 341 | 343 | |
| 342 | 344 | pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash { |
| 345 | assert(decl.zir_decl_index != 0); | |
| 343 | 346 | const hash_u32s = zir.extra[decl.zir_decl_index..][0..4]; |
| 344 | 347 | const contents_hash = @bitCast(std.zig.SrcHash, hash_u32s.*); |
| 345 | 348 | return contents_hash; |
| 346 | 349 | } |
| 347 | 350 | |
| 348 | 351 | pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index { |
| 352 | assert(decl.zir_decl_index != 0); | |
| 349 | 353 | const zir = decl.namespace.file_scope.zir; |
| 350 | 354 | return zir.extra[decl.zir_decl_index + 6]; |
| 351 | 355 | } |
| 352 | 356 | |
| 353 | 357 | pub fn zirAlignRef(decl: Decl) Zir.Inst.Ref { |
| 354 | 358 | if (!decl.has_align) return .none; |
| 359 | assert(decl.zir_decl_index != 0); | |
| 355 | 360 | const zir = decl.namespace.file_scope.zir; |
| 356 | 361 | return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 6]); |
| 357 | 362 | } |
| 358 | 363 | |
| 359 | 364 | pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref { |
| 360 | 365 | if (!decl.has_linksection) return .none; |
| 366 | assert(decl.zir_decl_index != 0); | |
| 361 | 367 | const zir = decl.namespace.file_scope.zir; |
| 362 | 368 | const extra_index = decl.zir_decl_index + 6 + @boolToInt(decl.has_align); |
| 363 | 369 | return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); |
| ... | ... | @@ -430,6 +436,15 @@ pub const Decl = struct { |
| 430 | 436 | return tv.ty.zigTypeTag() == .Fn; |
| 431 | 437 | } |
| 432 | 438 | |
| 439 | /// If the Decl has a value and it is a struct, return it, | |
| 440 | /// otherwise null. | |
| 441 | pub fn getStruct(decl: Decl) ?*Struct { | |
| 442 | if (!decl.has_tv) return null; | |
| 443 | const ty = (decl.val.castTag(.ty) orelse return null).data; | |
| 444 | const struct_obj = (ty.castTag(.@"struct") orelse return null).data; | |
| 445 | return struct_obj; | |
| 446 | } | |
| 447 | ||
| 433 | 448 | pub fn dump(decl: *Decl) void { |
| 434 | 449 | const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src); |
| 435 | 450 | std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ |
| ... | ... | @@ -2335,7 +2350,8 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2335 | 2350 | // We do not need to hold any locks at this time because all the Decl and Namespace |
| 2336 | 2351 | // objects being touched are specific to this File, and the only other concurrent |
| 2337 | 2352 | // tasks are touching other File objects. |
| 2338 | @panic("TODO implement update references from old ZIR to new ZIR"); | |
| 2353 | const change_list = try updateZirRefs(gpa, file, prev_zir); | |
| 2354 | @panic("TODO do something with change_list"); | |
| 2339 | 2355 | } |
| 2340 | 2356 | |
| 2341 | 2357 | // TODO don't report compile errors until Sema @importFile |
| ... | ... | @@ -2350,6 +2366,177 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2350 | 2366 | } |
| 2351 | 2367 | } |
| 2352 | 2368 | |
| 2369 | const UpdateChangeList = struct { | |
| 2370 | deleted: []*const Decl, | |
| 2371 | outdated: []*const Decl, | |
| 2372 | ||
| 2373 | fn deinit(self: *UpdateChangeList, gpa: *Allocator) void { | |
| 2374 | gpa.free(self.deleted); | |
| 2375 | gpa.free(self.outdated); | |
| 2376 | } | |
| 2377 | }; | |
| 2378 | ||
| 2379 | /// Patch ups: | |
| 2380 | /// * Struct.zir_index | |
| 2381 | /// * Decl.zir_decl_index | |
| 2382 | /// * Decl.name | |
| 2383 | /// * Namespace.decl keys | |
| 2384 | fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !UpdateChangeList { | |
| 2385 | const new_zir = file.zir; | |
| 2386 | ||
| 2387 | // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which | |
| 2388 | // creates a namespace, gets mapped from old to new here. | |
| 2389 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; | |
| 2390 | defer inst_map.deinit(gpa); | |
| 2391 | // Maps from old ZIR to new ZIR, the extra data index for the sub-decl item. | |
| 2392 | // e.g. the thing that Decl.zir_decl_index points to. | |
| 2393 | var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{}; | |
| 2394 | defer extra_map.deinit(gpa); | |
| 2395 | ||
| 2396 | try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map); | |
| 2397 | ||
| 2398 | // Build string table for new ZIR. | |
| 2399 | var string_table: std.StringHashMapUnmanaged(u32) = .{}; | |
| 2400 | defer string_table.deinit(gpa); | |
| 2401 | { | |
| 2402 | var i: usize = 2; | |
| 2403 | while (i < new_zir.string_bytes.len) { | |
| 2404 | const string = new_zir.nullTerminatedString(i); | |
| 2405 | try string_table.put(gpa, string, @intCast(u32, i)); | |
| 2406 | i += string.len + 1; | |
| 2407 | } | |
| 2408 | } | |
| 2409 | ||
| 2410 | // Walk the Decl graph. | |
| 2411 | ||
| 2412 | var decl_stack: std.ArrayListUnmanaged(*Decl) = .{}; | |
| 2413 | defer decl_stack.deinit(gpa); | |
| 2414 | ||
| 2415 | const root_decl = file.namespace.getDecl(); | |
| 2416 | try decl_stack.append(gpa, root_decl); | |
| 2417 | ||
| 2418 | var deleted_decls: std.ArrayListUnmanaged(*Decl) = .{}; | |
| 2419 | defer deleted_decls.deinit(gpa); | |
| 2420 | var outdated_decls: std.ArrayListUnmanaged(*Decl) = .{}; | |
| 2421 | defer outdated_decls.deinit(gpa); | |
| 2422 | ||
| 2423 | while (decl_stack.popOrNull()) |decl| { | |
| 2424 | // Anonymous decls and the root decl have this set to 0. We still need | |
| 2425 | // to walk them but we do not need to modify this value. | |
| 2426 | if (decl.zir_decl_index != 0) { | |
| 2427 | decl.zir_decl_index = extra_map.get(decl.zir_decl_index) orelse { | |
| 2428 | try deleted_decls.append(gpa, decl); | |
| 2429 | continue; | |
| 2430 | }; | |
| 2431 | const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse { | |
| 2432 | try deleted_decls.append(gpa, decl); | |
| 2433 | continue; | |
| 2434 | }; | |
| 2435 | decl.name = new_zir.nullTerminatedString(new_name_index).ptr; | |
| 2436 | ||
| 2437 | const old_hash = decl.contentsHashZir(old_zir); | |
| 2438 | const new_hash = decl.contentsHashZir(new_zir); | |
| 2439 | if (!std.zig.srcHashEql(old_hash, new_hash)) { | |
| 2440 | try outdated_decls.append(gpa, decl); | |
| 2441 | } | |
| 2442 | } else { | |
| 2443 | // TODO all decls should probably store source hash. Without this, | |
| 2444 | // we currently unnecessarily mark all anon decls outdated here. | |
| 2445 | try outdated_decls.append(gpa, decl); | |
| 2446 | } | |
| 2447 | ||
| 2448 | if (!decl.has_tv) continue; | |
| 2449 | ||
| 2450 | if (decl.getStruct()) |struct_obj| { | |
| 2451 | struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse { | |
| 2452 | try deleted_decls.append(gpa, decl); | |
| 2453 | continue; | |
| 2454 | }; | |
| 2455 | } | |
| 2456 | ||
| 2457 | if (decl.val.getTypeNamespace()) |namespace| { | |
| 2458 | for (namespace.decls.items()) |*entry| { | |
| 2459 | const sub_decl = entry.value; | |
| 2460 | if (sub_decl.zir_decl_index != 0) { | |
| 2461 | const new_key_index = string_table.get(entry.key) orelse { | |
| 2462 | try deleted_decls.append(gpa, sub_decl); | |
| 2463 | continue; | |
| 2464 | }; | |
| 2465 | entry.key = new_zir.nullTerminatedString(new_key_index); | |
| 2466 | } | |
| 2467 | try decl_stack.append(gpa, sub_decl); | |
| 2468 | } | |
| 2469 | } | |
| 2470 | } | |
| 2471 | ||
| 2472 | const outdated_slice = outdated_decls.toOwnedSlice(gpa); | |
| 2473 | const deleted_slice = deleted_decls.toOwnedSlice(gpa); | |
| 2474 | ||
| 2475 | return UpdateChangeList{ | |
| 2476 | .outdated = outdated_slice, | |
| 2477 | .deleted = deleted_slice, | |
| 2478 | }; | |
| 2479 | } | |
| 2480 | ||
| 2481 | pub fn mapOldZirToNew( | |
| 2482 | gpa: *Allocator, | |
| 2483 | old_zir: Zir, | |
| 2484 | new_zir: Zir, | |
| 2485 | inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), | |
| 2486 | extra_map: *std.AutoHashMapUnmanaged(u32, u32), | |
| 2487 | ) Allocator.Error!void { | |
| 2488 | // Contain ZIR indexes of declaration instructions. | |
| 2489 | const MatchedZirDecl = struct { | |
| 2490 | old_inst: Zir.Inst.Index, | |
| 2491 | new_inst: Zir.Inst.Index, | |
| 2492 | }; | |
| 2493 | var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{}; | |
| 2494 | defer match_stack.deinit(gpa); | |
| 2495 | ||
| 2496 | const old_main_struct_inst = old_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] - | |
| 2497 | @intCast(u32, Zir.Inst.Ref.typed_value_map.len); | |
| 2498 | const new_main_struct_inst = new_zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] - | |
| 2499 | @intCast(u32, Zir.Inst.Ref.typed_value_map.len); | |
| 2500 | ||
| 2501 | try match_stack.append(gpa, .{ | |
| 2502 | .old_inst = old_main_struct_inst, | |
| 2503 | .new_inst = new_main_struct_inst, | |
| 2504 | }); | |
| 2505 | ||
| 2506 | while (match_stack.popOrNull()) |match_item| { | |
| 2507 | try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); | |
| 2508 | ||
| 2509 | // Maps name to extra index of decl sub item. | |
| 2510 | var decl_map: std.StringHashMapUnmanaged(u32) = .{}; | |
| 2511 | defer decl_map.deinit(gpa); | |
| 2512 | ||
| 2513 | { | |
| 2514 | var old_decl_it = old_zir.declIterator(match_item.old_inst); | |
| 2515 | while (old_decl_it.next()) |old_decl| { | |
| 2516 | try decl_map.put(gpa, old_decl.name, old_decl.sub_index); | |
| 2517 | } | |
| 2518 | } | |
| 2519 | ||
| 2520 | var new_decl_it = new_zir.declIterator(match_item.new_inst); | |
| 2521 | while (new_decl_it.next()) |new_decl| { | |
| 2522 | const old_extra_index = decl_map.get(new_decl.name) orelse continue; | |
| 2523 | const new_extra_index = new_decl.sub_index; | |
| 2524 | try extra_map.put(gpa, old_extra_index, new_extra_index); | |
| 2525 | ||
| 2526 | //var old_it = declInstIterator(old_zir, old_extra_index); | |
| 2527 | //var new_it = declInstIterator(new_zir, new_extra_index); | |
| 2528 | //while (true) { | |
| 2529 | // const old_decl_inst = old_it.next() orelse break; | |
| 2530 | // const new_decl_inst = new_it.next() orelse break; | |
| 2531 | // try match_stack.append(gpa, .{ | |
| 2532 | // .old_inst = old_decl_inst, | |
| 2533 | // .new_inst = new_decl_inst, | |
| 2534 | // }); | |
| 2535 | //} | |
| 2536 | } | |
| 2537 | } | |
| 2538 | } | |
| 2539 | ||
| 2353 | 2540 | pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2354 | 2541 | const tracy = trace(@src()); |
| 2355 | 2542 | defer tracy.end(); |
| ... | ... | @@ -2487,7 +2674,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2487 | 2674 | new_decl.is_exported = false; |
| 2488 | 2675 | new_decl.has_align = false; |
| 2489 | 2676 | new_decl.has_linksection = false; |
| 2490 | new_decl.zir_decl_index = undefined; | |
| 2491 | 2677 | new_decl.ty = struct_ty; |
| 2492 | 2678 | new_decl.val = struct_val; |
| 2493 | 2679 | new_decl.has_tv = true; |
| ... | ... | @@ -3256,7 +3442,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node |
| 3256 | 3442 | .linksection_val = undefined, |
| 3257 | 3443 | .analysis = .unreferenced, |
| 3258 | 3444 | .deletion_flag = false, |
| 3259 | .zir_decl_index = undefined, | |
| 3445 | .zir_decl_index = 0, | |
| 3260 | 3446 | .link = switch (mod.comp.bin_file.tag) { |
| 3261 | 3447 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, |
| 3262 | 3448 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
src/Zir.zig+109-2| ... | ... | @@ -3702,6 +3702,8 @@ const Writer = struct { |
| 3702 | 3702 | const has_section = @truncate(u1, cur_bit_bag) != 0; |
| 3703 | 3703 | cur_bit_bag >>= 1; |
| 3704 | 3704 | |
| 3705 | const sub_index = extra_index; | |
| 3706 | ||
| 3705 | 3707 | const hash_u32s = self.code.extra[extra_index..][0..4]; |
| 3706 | 3708 | extra_index += 4; |
| 3707 | 3709 | const line = self.code.extra[extra_index]; |
| ... | ... | @@ -3738,8 +3740,8 @@ const Writer = struct { |
| 3738 | 3740 | raw_decl_name; |
| 3739 | 3741 | const test_str = if (raw_decl_name.len == 0) "test " else ""; |
| 3740 | 3742 | const export_str = if (is_exported) "export " else ""; |
| 3741 | try stream.print("{s}{s}{s}{}", .{ | |
| 3742 | pub_str, test_str, export_str, std.zig.fmtId(decl_name), | |
| 3743 | try stream.print("[{d}] {s}{s}{s}{}", .{ | |
| 3744 | sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), | |
| 3743 | 3745 | }); |
| 3744 | 3746 | if (align_inst != .none) { |
| 3745 | 3747 | try stream.writeAll(" align("); |
| ... | ... | @@ -4334,3 +4336,108 @@ const Writer = struct { |
| 4334 | 4336 | } |
| 4335 | 4337 | } |
| 4336 | 4338 | }; |
| 4339 | ||
| 4340 | pub const DeclIterator = struct { | |
| 4341 | extra_index: usize, | |
| 4342 | bit_bag_index: usize, | |
| 4343 | cur_bit_bag: u32, | |
| 4344 | decl_i: u32, | |
| 4345 | decls_len: u32, | |
| 4346 | zir: Zir, | |
| 4347 | ||
| 4348 | pub const Item = struct { | |
| 4349 | name: [:0]const u8, | |
| 4350 | sub_index: u32, | |
| 4351 | }; | |
| 4352 | ||
| 4353 | pub fn next(it: *DeclIterator) ?Item { | |
| 4354 | if (it.decl_i >= it.decls_len) return null; | |
| 4355 | ||
| 4356 | if (it.decl_i % 8 == 0) { | |
| 4357 | it.cur_bit_bag = it.zir.extra[it.bit_bag_index]; | |
| 4358 | it.bit_bag_index += 1; | |
| 4359 | } | |
| 4360 | it.decl_i += 1; | |
| 4361 | ||
| 4362 | const flags = @truncate(u4, it.cur_bit_bag); | |
| 4363 | it.cur_bit_bag >>= 4; | |
| 4364 | ||
| 4365 | const sub_index = @intCast(u32, it.extra_index); | |
| 4366 | it.extra_index += 5; // src_hash(4) + line(1) | |
| 4367 | const name = it.zir.nullTerminatedString(it.zir.extra[it.extra_index]); | |
| 4368 | it.extra_index += 2; // name(1) + value(1) | |
| 4369 | it.extra_index += @truncate(u1, flags >> 2); | |
| 4370 | it.extra_index += @truncate(u1, flags >> 3); | |
| 4371 | ||
| 4372 | return Item{ | |
| 4373 | .sub_index = sub_index, | |
| 4374 | .name = name, | |
| 4375 | }; | |
| 4376 | } | |
| 4377 | }; | |
| 4378 | ||
| 4379 | pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { | |
| 4380 | const tags = zir.instructions.items(.tag); | |
| 4381 | const datas = zir.instructions.items(.data); | |
| 4382 | const decl_info: struct { | |
| 4383 | extra_index: usize, | |
| 4384 | decls_len: u32, | |
| 4385 | } = switch (tags[decl_inst]) { | |
| 4386 | .struct_decl, | |
| 4387 | .struct_decl_packed, | |
| 4388 | .struct_decl_extern, | |
| 4389 | => blk: { | |
| 4390 | const inst_data = datas[decl_inst].pl_node; | |
| 4391 | const extra = zir.extraData(Inst.StructDecl, inst_data.payload_index); | |
| 4392 | break :blk .{ | |
| 4393 | .extra_index = extra.end, | |
| 4394 | .decls_len = extra.data.decls_len, | |
| 4395 | }; | |
| 4396 | }, | |
| 4397 | ||
| 4398 | .union_decl, | |
| 4399 | .union_decl_packed, | |
| 4400 | .union_decl_extern, | |
| 4401 | => blk: { | |
| 4402 | const inst_data = datas[decl_inst].pl_node; | |
| 4403 | const extra = zir.extraData(Inst.UnionDecl, inst_data.payload_index); | |
| 4404 | break :blk .{ | |
| 4405 | .extra_index = extra.end, | |
| 4406 | .decls_len = extra.data.decls_len, | |
| 4407 | }; | |
| 4408 | }, | |
| 4409 | ||
| 4410 | .enum_decl, | |
| 4411 | .enum_decl_nonexhaustive, | |
| 4412 | => blk: { | |
| 4413 | const inst_data = datas[decl_inst].pl_node; | |
| 4414 | const extra = zir.extraData(Inst.EnumDecl, inst_data.payload_index); | |
| 4415 | break :blk .{ | |
| 4416 | .extra_index = extra.end, | |
| 4417 | .decls_len = extra.data.decls_len, | |
| 4418 | }; | |
| 4419 | }, | |
| 4420 | ||
| 4421 | .opaque_decl => blk: { | |
| 4422 | const inst_data = datas[decl_inst].pl_node; | |
| 4423 | const extra = zir.extraData(Inst.OpaqueDecl, inst_data.payload_index); | |
| 4424 | break :blk .{ | |
| 4425 | .extra_index = extra.end, | |
| 4426 | .decls_len = extra.data.decls_len, | |
| 4427 | }; | |
| 4428 | }, | |
| 4429 | ||
| 4430 | else => unreachable, | |
| 4431 | }; | |
| 4432 | ||
| 4433 | const bit_bags_count = std.math.divCeil(usize, decl_info.decls_len, 8) catch unreachable; | |
| 4434 | ||
| 4435 | return .{ | |
| 4436 | .zir = zir, | |
| 4437 | .extra_index = decl_info.extra_index + bit_bags_count, | |
| 4438 | .bit_bag_index = decl_info.extra_index, | |
| 4439 | .cur_bit_bag = undefined, | |
| 4440 | .decl_i = 0, | |
| 4441 | .decls_len = decl_info.decls_len, | |
| 4442 | }; | |
| 4443 | } |
src/main.zig+142| ... | ... | @@ -72,6 +72,7 @@ const debug_usage = normal_usage ++ |
| 72 | 72 | \\Debug Commands: |
| 73 | 73 | \\ |
| 74 | 74 | \\ astgen Print ZIR code for a .zig source file |
| 75 | \\ changelist Compute mappings from old ZIR to new ZIR | |
| 75 | 76 | \\ |
| 76 | 77 | ; |
| 77 | 78 | |
| ... | ... | @@ -231,6 +232,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 231 | 232 | return io.getStdOut().writeAll(usage); |
| 232 | 233 | } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) { |
| 233 | 234 | return cmdAstgen(gpa, arena, cmd_args); |
| 235 | } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) { | |
| 236 | return cmdChangelist(gpa, arena, cmd_args); | |
| 234 | 237 | } else { |
| 235 | 238 | std.log.info("{s}", .{usage}); |
| 236 | 239 | fatal("unknown command: {s}", .{args[1]}); |
| ... | ... | @@ -3618,3 +3621,142 @@ pub fn cmdAstgen( |
| 3618 | 3621 | |
| 3619 | 3622 | return Zir.renderAsTextToFile(gpa, &file, io.getStdOut()); |
| 3620 | 3623 | } |
| 3624 | ||
| 3625 | /// This is only enabled for debug builds. | |
| 3626 | pub fn cmdChangelist( | |
| 3627 | gpa: *Allocator, | |
| 3628 | arena: *Allocator, | |
| 3629 | args: []const []const u8, | |
| 3630 | ) !void { | |
| 3631 | const Module = @import("Module.zig"); | |
| 3632 | const AstGen = @import("AstGen.zig"); | |
| 3633 | const Zir = @import("Zir.zig"); | |
| 3634 | ||
| 3635 | const old_source_file = args[0]; | |
| 3636 | const new_source_file = args[1]; | |
| 3637 | ||
| 3638 | var f = try fs.cwd().openFile(old_source_file, .{}); | |
| 3639 | defer f.close(); | |
| 3640 | ||
| 3641 | const stat = try f.stat(); | |
| 3642 | ||
| 3643 | if (stat.size > max_src_size) | |
| 3644 | return error.FileTooBig; | |
| 3645 | ||
| 3646 | var file: Module.Scope.File = .{ | |
| 3647 | .status = .never_loaded, | |
| 3648 | .source_loaded = false, | |
| 3649 | .tree_loaded = false, | |
| 3650 | .zir_loaded = false, | |
| 3651 | .sub_file_path = old_source_file, | |
| 3652 | .source = undefined, | |
| 3653 | .stat_size = stat.size, | |
| 3654 | .stat_inode = stat.inode, | |
| 3655 | .stat_mtime = stat.mtime, | |
| 3656 | .tree = undefined, | |
| 3657 | .zir = undefined, | |
| 3658 | .pkg = undefined, | |
| 3659 | .namespace = undefined, | |
| 3660 | }; | |
| 3661 | ||
| 3662 | const source = try arena.allocSentinel(u8, stat.size, 0); | |
| 3663 | const amt = try f.readAll(source); | |
| 3664 | if (amt != stat.size) | |
| 3665 | return error.UnexpectedEndOfFile; | |
| 3666 | file.source = source; | |
| 3667 | file.source_loaded = true; | |
| 3668 | ||
| 3669 | file.tree = try std.zig.parse(gpa, file.source); | |
| 3670 | file.tree_loaded = true; | |
| 3671 | defer file.tree.deinit(gpa); | |
| 3672 | ||
| 3673 | for (file.tree.errors) |parse_error| { | |
| 3674 | try printErrMsgToFile(gpa, parse_error, file.tree, old_source_file, io.getStdErr(), .auto); | |
| 3675 | } | |
| 3676 | if (file.tree.errors.len != 0) { | |
| 3677 | process.exit(1); | |
| 3678 | } | |
| 3679 | ||
| 3680 | file.zir = try AstGen.generate(gpa, file.tree); | |
| 3681 | file.zir_loaded = true; | |
| 3682 | defer file.zir.deinit(gpa); | |
| 3683 | ||
| 3684 | if (file.zir.hasCompileErrors()) { | |
| 3685 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | |
| 3686 | try Compilation.AllErrors.addZir(arena, &errors, &file); | |
| 3687 | const ttyconf = std.debug.detectTTYConfig(); | |
| 3688 | for (errors.items) |full_err_msg| { | |
| 3689 | full_err_msg.renderToStdErr(ttyconf); | |
| 3690 | } | |
| 3691 | process.exit(1); | |
| 3692 | } | |
| 3693 | ||
| 3694 | var new_f = try fs.cwd().openFile(new_source_file, .{}); | |
| 3695 | defer new_f.close(); | |
| 3696 | ||
| 3697 | const new_stat = try new_f.stat(); | |
| 3698 | ||
| 3699 | if (new_stat.size > max_src_size) | |
| 3700 | return error.FileTooBig; | |
| 3701 | ||
| 3702 | const new_source = try arena.allocSentinel(u8, new_stat.size, 0); | |
| 3703 | const new_amt = try new_f.readAll(new_source); | |
| 3704 | if (new_amt != new_stat.size) | |
| 3705 | return error.UnexpectedEndOfFile; | |
| 3706 | ||
| 3707 | var new_tree = try std.zig.parse(gpa, new_source); | |
| 3708 | defer new_tree.deinit(gpa); | |
| 3709 | ||
| 3710 | for (new_tree.errors) |parse_error| { | |
| 3711 | try printErrMsgToFile(gpa, parse_error, new_tree, new_source_file, io.getStdErr(), .auto); | |
| 3712 | } | |
| 3713 | if (new_tree.errors.len != 0) { | |
| 3714 | process.exit(1); | |
| 3715 | } | |
| 3716 | ||
| 3717 | var old_zir = file.zir; | |
| 3718 | defer old_zir.deinit(gpa); | |
| 3719 | file.zir_loaded = false; | |
| 3720 | file.zir = try AstGen.generate(gpa, new_tree); | |
| 3721 | file.zir_loaded = true; | |
| 3722 | ||
| 3723 | if (file.zir.hasCompileErrors()) { | |
| 3724 | var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); | |
| 3725 | try Compilation.AllErrors.addZir(arena, &errors, &file); | |
| 3726 | const ttyconf = std.debug.detectTTYConfig(); | |
| 3727 | for (errors.items) |full_err_msg| { | |
| 3728 | full_err_msg.renderToStdErr(ttyconf); | |
| 3729 | } | |
| 3730 | process.exit(1); | |
| 3731 | } | |
| 3732 | ||
| 3733 | var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; | |
| 3734 | defer inst_map.deinit(gpa); | |
| 3735 | ||
| 3736 | var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{}; | |
| 3737 | defer extra_map.deinit(gpa); | |
| 3738 | ||
| 3739 | try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map); | |
| 3740 | ||
| 3741 | var bw = io.bufferedWriter(io.getStdOut().writer()); | |
| 3742 | const stdout = bw.writer(); | |
| 3743 | { | |
| 3744 | try stdout.print("Instruction mappings:\n", .{}); | |
| 3745 | var it = inst_map.iterator(); | |
| 3746 | while (it.next()) |entry| { | |
| 3747 | try stdout.print(" %{d} => %{d}\n", .{ | |
| 3748 | entry.key, entry.value, | |
| 3749 | }); | |
| 3750 | } | |
| 3751 | } | |
| 3752 | { | |
| 3753 | try stdout.print("Extra mappings:\n", .{}); | |
| 3754 | var it = extra_map.iterator(); | |
| 3755 | while (it.next()) |entry| { | |
| 3756 | try stdout.print(" {d} => {d}\n", .{ | |
| 3757 | entry.key, entry.value, | |
| 3758 | }); | |
| 3759 | } | |
| 3760 | } | |
| 3761 | try bw.flush(); | |
| 3762 | } |
test/stage2/test.zig+2-2| ... | ... | @@ -28,13 +28,13 @@ pub fn addCases(ctx: *TestContext) !void { |
| 28 | 28 | |
| 29 | 29 | // Incorrect return type |
| 30 | 30 | case.addError( |
| 31 | \\export fn _start() noreturn { | |
| 31 | \\pub export fn _start() noreturn { | |
| 32 | 32 | \\} |
| 33 | 33 | , &[_][]const u8{":2:1: error: expected noreturn, found void"}); |
| 34 | 34 | |
| 35 | 35 | // Regular old hello world |
| 36 | 36 | case.addCompareOutput( |
| 37 | \\export fn _start() noreturn { | |
| 37 | \\pub export fn _start() noreturn { | |
| 38 | 38 | \\ print(); |
| 39 | 39 | \\ |
| 40 | 40 | \\ exit(); |