authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-05 13:16:14-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-05 13:16:14-07:00
log5d7f2697deadbcc6f2b370c6eaa69099d6a54ee7
treeb6648cc43d6d13d1665e473813cdf0a210204255
parent6b5d0b371111fd458e7fd0a1500f0d93914b5db1

stage2: add `zig changelist` debug command

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,5 +1,11 @@
1 * AstGen threadlocal1 * implement the iterators that updateZirRefs needs
2 * extern "foo" for vars and for functions2 - 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 * namespace decls table can't reference ZIR memory because it can get modified on updates9 * namespace decls table can't reference ZIR memory because it can get modified on updates
4 - change it for astgen worker to compare old and new ZIR, updating existing10 - change it for astgen worker to compare old and new ZIR, updating existing
5 namespaces & decls, and creating a changelist.11 namespaces & decls, and creating a changelist.
...@@ -55,4 +61,8 @@...@@ -55,4 +61,8 @@
55 natural alignment for fields and do not have any comptime fields. this61 natural alignment for fields and do not have any comptime fields. this
56 will save 16 bytes per struct field in the compilation.62 will save 16 bytes per struct field in the compilation.
5763
64 * AstGen threadlocal
65 * extern "foo" for vars
5866
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,6 +190,7 @@ pub const Decl = struct {
190 /// Index to ZIR `extra` array to the entry in the parent's decl structure190 /// Index to ZIR `extra` array to the entry in the parent's decl structure
191 /// (the part that says "for every decls_len"). The first item at this index is191 /// (the part that says "for every decls_len"). The first item at this index is
192 /// the contents hash, followed by line, name, etc.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 zir_decl_index: Zir.Inst.Index,194 zir_decl_index: Zir.Inst.Index,
194195
195 /// Represents the "shallow" analysis status. For example, for decls that are functions,196 /// Represents the "shallow" analysis status. For example, for decls that are functions,
...@@ -329,6 +330,7 @@ pub const Decl = struct {...@@ -329,6 +330,7 @@ pub const Decl = struct {
329 }330 }
330331
331 pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 {332 pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 {
333 assert(decl.zir_decl_index != 0);
332 const name_index = zir.extra[decl.zir_decl_index + 5];334 const name_index = zir.extra[decl.zir_decl_index + 5];
333 if (name_index <= 1) return null;335 if (name_index <= 1) return null;
334 return zir.nullTerminatedString(name_index);336 return zir.nullTerminatedString(name_index);
...@@ -340,24 +342,28 @@ pub const Decl = struct {...@@ -340,24 +342,28 @@ pub const Decl = struct {
340 }342 }
341343
342 pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash {344 pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash {
345 assert(decl.zir_decl_index != 0);
343 const hash_u32s = zir.extra[decl.zir_decl_index..][0..4];346 const hash_u32s = zir.extra[decl.zir_decl_index..][0..4];
344 const contents_hash = @bitCast(std.zig.SrcHash, hash_u32s.*);347 const contents_hash = @bitCast(std.zig.SrcHash, hash_u32s.*);
345 return contents_hash;348 return contents_hash;
346 }349 }
347350
348 pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index {351 pub fn zirBlockIndex(decl: Decl) Zir.Inst.Index {
352 assert(decl.zir_decl_index != 0);
349 const zir = decl.namespace.file_scope.zir;353 const zir = decl.namespace.file_scope.zir;
350 return zir.extra[decl.zir_decl_index + 6];354 return zir.extra[decl.zir_decl_index + 6];
351 }355 }
352356
353 pub fn zirAlignRef(decl: Decl) Zir.Inst.Ref {357 pub fn zirAlignRef(decl: Decl) Zir.Inst.Ref {
354 if (!decl.has_align) return .none;358 if (!decl.has_align) return .none;
359 assert(decl.zir_decl_index != 0);
355 const zir = decl.namespace.file_scope.zir;360 const zir = decl.namespace.file_scope.zir;
356 return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 6]);361 return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 6]);
357 }362 }
358363
359 pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref {364 pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref {
360 if (!decl.has_linksection) return .none;365 if (!decl.has_linksection) return .none;
366 assert(decl.zir_decl_index != 0);
361 const zir = decl.namespace.file_scope.zir;367 const zir = decl.namespace.file_scope.zir;
362 const extra_index = decl.zir_decl_index + 6 + @boolToInt(decl.has_align);368 const extra_index = decl.zir_decl_index + 6 + @boolToInt(decl.has_align);
363 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);369 return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
...@@ -430,6 +436,15 @@ pub const Decl = struct {...@@ -430,6 +436,15 @@ pub const Decl = struct {
430 return tv.ty.zigTypeTag() == .Fn;436 return tv.ty.zigTypeTag() == .Fn;
431 }437 }
432438
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 pub fn dump(decl: *Decl) void {448 pub fn dump(decl: *Decl) void {
434 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);449 const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src);
435 std.debug.print("{s}:{d}:{d} name={s} status={s}", .{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,7 +2350,8 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
2335 // We do not need to hold any locks at this time because all the Decl and Namespace2350 // We do not need to hold any locks at this time because all the Decl and Namespace
2336 // objects being touched are specific to this File, and the only other concurrent2351 // objects being touched are specific to this File, and the only other concurrent
2337 // tasks are touching other File objects.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 }
23402356
2341 // TODO don't report compile errors until Sema @importFile2357 // 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,6 +2366,177 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
2350 }2366 }
2351}2367}
23522368
2369const 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
2384fn 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
2481pub 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
2353pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {2540pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
2354 const tracy = trace(@src());2541 const tracy = trace(@src());
2355 defer tracy.end();2542 defer tracy.end();
...@@ -2487,7 +2674,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {...@@ -2487,7 +2674,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
2487 new_decl.is_exported = false;2674 new_decl.is_exported = false;
2488 new_decl.has_align = false;2675 new_decl.has_align = false;
2489 new_decl.has_linksection = false;2676 new_decl.has_linksection = false;
2490 new_decl.zir_decl_index = undefined;
2491 new_decl.ty = struct_ty;2677 new_decl.ty = struct_ty;
2492 new_decl.val = struct_val;2678 new_decl.val = struct_val;
2493 new_decl.has_tv = true;2679 new_decl.has_tv = true;
...@@ -3256,7 +3442,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node...@@ -3256,7 +3442,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
3256 .linksection_val = undefined,3442 .linksection_val = undefined,
3257 .analysis = .unreferenced,3443 .analysis = .unreferenced,
3258 .deletion_flag = false,3444 .deletion_flag = false,
3259 .zir_decl_index = undefined,3445 .zir_decl_index = 0,
3260 .link = switch (mod.comp.bin_file.tag) {3446 .link = switch (mod.comp.bin_file.tag) {
3261 .coff => .{ .coff = link.File.Coff.TextBlock.empty },3447 .coff => .{ .coff = link.File.Coff.TextBlock.empty },
3262 .elf => .{ .elf = link.File.Elf.TextBlock.empty },3448 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
src/Zir.zig+109-2
...@@ -3702,6 +3702,8 @@ const Writer = struct {...@@ -3702,6 +3702,8 @@ const Writer = struct {
3702 const has_section = @truncate(u1, cur_bit_bag) != 0;3702 const has_section = @truncate(u1, cur_bit_bag) != 0;
3703 cur_bit_bag >>= 1;3703 cur_bit_bag >>= 1;
37043704
3705 const sub_index = extra_index;
3706
3705 const hash_u32s = self.code.extra[extra_index..][0..4];3707 const hash_u32s = self.code.extra[extra_index..][0..4];
3706 extra_index += 4;3708 extra_index += 4;
3707 const line = self.code.extra[extra_index];3709 const line = self.code.extra[extra_index];
...@@ -3738,8 +3740,8 @@ const Writer = struct {...@@ -3738,8 +3740,8 @@ const Writer = struct {
3738 raw_decl_name;3740 raw_decl_name;
3739 const test_str = if (raw_decl_name.len == 0) "test " else "";3741 const test_str = if (raw_decl_name.len == 0) "test " else "";
3740 const export_str = if (is_exported) "export " else "";3742 const export_str = if (is_exported) "export " else "";
3741 try stream.print("{s}{s}{s}{}", .{3743 try stream.print("[{d}] {s}{s}{s}{}", .{
3742 pub_str, test_str, export_str, std.zig.fmtId(decl_name),3744 sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name),
3743 });3745 });
3744 if (align_inst != .none) {3746 if (align_inst != .none) {
3745 try stream.writeAll(" align(");3747 try stream.writeAll(" align(");
...@@ -4334,3 +4336,108 @@ const Writer = struct {...@@ -4334,3 +4336,108 @@ const Writer = struct {
4334 }4336 }
4335 }4337 }
4336};4338};
4339
4340pub 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
4379pub 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,6 +72,7 @@ const debug_usage = normal_usage ++
72 \\Debug Commands:72 \\Debug Commands:
73 \\73 \\
74 \\ astgen Print ZIR code for a .zig source file74 \\ astgen Print ZIR code for a .zig source file
75 \\ changelist Compute mappings from old ZIR to new ZIR
75 \\76 \\
76;77;
7778
...@@ -231,6 +232,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -231,6 +232,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
231 return io.getStdOut().writeAll(usage);232 return io.getStdOut().writeAll(usage);
232 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) {233 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) {
233 return cmdAstgen(gpa, arena, cmd_args);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 } else {237 } else {
235 std.log.info("{s}", .{usage});238 std.log.info("{s}", .{usage});
236 fatal("unknown command: {s}", .{args[1]});239 fatal("unknown command: {s}", .{args[1]});
...@@ -3618,3 +3621,142 @@ pub fn cmdAstgen(...@@ -3618,3 +3621,142 @@ pub fn cmdAstgen(
36183621
3619 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());3622 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());
3620}3623}
3624
3625/// This is only enabled for debug builds.
3626pub 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,13 +28,13 @@ pub fn addCases(ctx: *TestContext) !void {
2828
29 // Incorrect return type29 // Incorrect return type
30 case.addError(30 case.addError(
31 \\export fn _start() noreturn {31 \\pub export fn _start() noreturn {
32 \\}32 \\}
33 , &[_][]const u8{":2:1: error: expected noreturn, found void"});33 , &[_][]const u8{":2:1: error: expected noreturn, found void"});
3434
35 // Regular old hello world35 // Regular old hello world
36 case.addCompareOutput(36 case.addCompareOutput(
37 \\export fn _start() noreturn {37 \\pub export fn _start() noreturn {
38 \\ print();38 \\ print();
39 \\39 \\
40 \\ exit();40 \\ exit();