authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-03 21:31:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-03 21:31:32-07:00
loga2f4adbd19ffea051a4fa0318bb0154e07660e55
treeee53ffcd4587ab34106470dd8895f8b39d26ad50
parent6aedddf900086543fa667d74ca4d1c266dbfcdf9

zig reduce: add "delete unused globals" transform

While walking the AST looking for reductions, notice if any globals are unreferenced, and if they are, add a transformation for removing the global.

3 files changed, 103 insertions(+), 12 deletions(-)

lib/std/zig/render.zig+7-1
...@@ -22,20 +22,25 @@ pub const Fixups = struct {...@@ -22,20 +22,25 @@ pub const Fixups = struct {
22 /// with a function body of `@trap()` instead, with all parameters22 /// with a function body of `@trap()` instead, with all parameters
23 /// discarded.23 /// discarded.
24 gut_functions: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},24 gut_functions: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
25 /// These global declarations will be omitted.
26 omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
2527
26 pub fn count(f: Fixups) usize {28 pub fn count(f: Fixups) usize {
27 return f.unused_var_decls.count() +29 return f.unused_var_decls.count() +
28 f.gut_functions.count();30 f.gut_functions.count() +
31 f.omit_nodes.count();
29 }32 }
3033
31 pub fn clearRetainingCapacity(f: *Fixups) void {34 pub fn clearRetainingCapacity(f: *Fixups) void {
32 f.unused_var_decls.clearRetainingCapacity();35 f.unused_var_decls.clearRetainingCapacity();
33 f.gut_functions.clearRetainingCapacity();36 f.gut_functions.clearRetainingCapacity();
37 f.omit_nodes.clearRetainingCapacity();
34 }38 }
3539
36 pub fn deinit(f: *Fixups, gpa: Allocator) void {40 pub fn deinit(f: *Fixups, gpa: Allocator) void {
37 f.unused_var_decls.deinit(gpa);41 f.unused_var_decls.deinit(gpa);
38 f.gut_functions.deinit(gpa);42 f.gut_functions.deinit(gpa);
43 f.omit_nodes.deinit(gpa);
39 f.* = undefined;44 f.* = undefined;
40 }45 }
41};46};
...@@ -114,6 +119,7 @@ fn renderMember(...@@ -114,6 +119,7 @@ fn renderMember(
114 const token_tags = tree.tokens.items(.tag);119 const token_tags = tree.tokens.items(.tag);
115 const main_tokens = tree.nodes.items(.main_token);120 const main_tokens = tree.nodes.items(.main_token);
116 const datas = tree.nodes.items(.data);121 const datas = tree.nodes.items(.data);
122 if (r.fixups.omit_nodes.contains(decl)) return;
117 try renderDocComments(r, tree.firstToken(decl));123 try renderDocComments(r, tree.firstToken(decl));
118 switch (tree.nodes.items(.tag)[decl]) {124 switch (tree.nodes.items(.tag)[decl]) {
119 .fn_decl => {125 .fn_decl => {
src/reduce.zig+5-2
...@@ -166,8 +166,8 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -166,8 +166,8 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
166 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);166 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
167167
168 const interestingness = try runCheck(arena, interestingness_argv.items);168 const interestingness = try runCheck(arena, interestingness_argv.items);
169 std.debug.print("{d} random transformations: {s}\n", .{169 std.debug.print("{d} random transformations: {s}. {d} remaining\n", .{
170 subset_size, @tagName(interestingness),170 subset_size, @tagName(interestingness), transformations.items.len - start_index,
171 });171 });
172 switch (interestingness) {172 switch (interestingness) {
173 .interesting => {173 .interesting => {
...@@ -250,6 +250,9 @@ fn transformationsToFixups(...@@ -250,6 +250,9 @@ fn transformationsToFixups(
250 .gut_function => |fn_decl_node| {250 .gut_function => |fn_decl_node| {
251 try fixups.gut_functions.put(gpa, fn_decl_node, {});251 try fixups.gut_functions.put(gpa, fn_decl_node, {});
252 },252 },
253 .delete_node => |decl_node| {
254 try fixups.omit_nodes.put(gpa, decl_node, {});
255 },
253 };256 };
254}257}
255258
src/reduce/Walk.zig+91-9
...@@ -5,11 +5,15 @@ const assert = std.debug.assert;...@@ -5,11 +5,15 @@ const assert = std.debug.assert;
55
6ast: *const Ast,6ast: *const Ast,
7transformations: *std.ArrayList(Transformation),7transformations: *std.ArrayList(Transformation),
8unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
9gpa: std.mem.Allocator,
810
9pub const Transformation = union(enum) {11pub const Transformation = union(enum) {
10 /// Replace the fn decl AST Node with one whose body is only `@trap()` with12 /// Replace the fn decl AST Node with one whose body is only `@trap()` with
11 /// discarded parameters.13 /// discarded parameters.
12 gut_function: Ast.Node.Index,14 gut_function: Ast.Node.Index,
15 /// Omit a global declaration.
16 delete_node: Ast.Node.Index,
13};17};
1418
15pub const Error = error{OutOfMemory};19pub const Error = error{OutOfMemory};
...@@ -21,16 +25,59 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran...@@ -21,16 +25,59 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran
21 var walk: Walk = .{25 var walk: Walk = .{
22 .ast = ast,26 .ast = ast,
23 .transformations = transformations,27 .transformations = transformations,
28 .gpa = transformations.allocator,
29 .unreferenced_globals = .{},
24 };30 };
31 defer walk.unreferenced_globals.deinit(walk.gpa);
32
25 try walkMembers(&walk, walk.ast.rootDecls());33 try walkMembers(&walk, walk.ast.rootDecls());
34
35 const unreferenced_globals = walk.unreferenced_globals.values();
36 try transformations.ensureUnusedCapacity(unreferenced_globals.len);
37 for (unreferenced_globals) |node| {
38 transformations.appendAssumeCapacity(.{ .delete_node = node });
39 }
26}40}
2741
28fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {42fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
43 // First we scan for globals so that we can delete them while walking.
44 try scanDecls(w, members);
45
29 for (members) |member| {46 for (members) |member| {
30 try walkMember(w, member);47 try walkMember(w, member);
31 }48 }
32}49}
3350
51fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void {
52 const ast = w.ast;
53 const gpa = w.gpa;
54 const node_tags = ast.nodes.items(.tag);
55 const main_tokens = ast.nodes.items(.main_token);
56 const token_tags = ast.tokens.items(.tag);
57
58 for (members) |member_node| {
59 const name_token = switch (node_tags[member_node]) {
60 .global_var_decl,
61 .local_var_decl,
62 .simple_var_decl,
63 .aligned_var_decl,
64 => main_tokens[member_node] + 1,
65
66 .fn_proto_simple,
67 .fn_proto_multi,
68 .fn_proto_one,
69 .fn_proto,
70 .fn_decl,
71 => main_tokens[member_node] + 1,
72
73 else => continue,
74 };
75 assert(token_tags[name_token] == .identifier);
76 const name_bytes = ast.tokenSlice(name_token);
77 try w.unreferenced_globals.put(gpa, name_bytes, member_node);
78 }
79}
80
34fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {81fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
35 const ast = w.ast;82 const ast = w.ast;
36 const datas = ast.nodes.items(.data);83 const datas = ast.nodes.items(.data);
...@@ -53,6 +100,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {...@@ -53,6 +100,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
53 },100 },
54101
55 .@"usingnamespace" => {102 .@"usingnamespace" => {
103 try w.transformations.append(.{ .delete_node = decl });
56 const expr = datas[decl].lhs;104 const expr = datas[decl].lhs;
57 try walkExpression(w, expr);105 try walkExpression(w, expr);
58 },106 },
...@@ -61,9 +109,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {...@@ -61,9 +109,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
61 .local_var_decl,109 .local_var_decl,
62 .simple_var_decl,110 .simple_var_decl,
63 .aligned_var_decl,111 .aligned_var_decl,
64 => try walkVarDecl(w, ast.fullVarDecl(decl).?),112 => try walkGlobalVarDecl(w, decl, ast.fullVarDecl(decl).?),
65113
66 .test_decl => {114 .test_decl => {
115 try w.transformations.append(.{ .delete_node = decl });
67 try walkExpression(w, datas[decl].rhs);116 try walkExpression(w, datas[decl].rhs);
68 },117 },
69118
...@@ -72,7 +121,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {...@@ -72,7 +121,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
72 .container_field,121 .container_field,
73 => try walkContainerField(w, ast.fullContainerField(decl).?),122 => try walkContainerField(w, ast.fullContainerField(decl).?),
74123
75 .@"comptime" => try walkExpression(w, decl),124 .@"comptime" => {
125 try w.transformations.append(.{ .delete_node = decl });
126 try walkExpression(w, decl);
127 },
76128
77 .root => unreachable,129 .root => unreachable,
78 else => unreachable,130 else => unreachable,
...@@ -86,7 +138,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -86,7 +138,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
86 const node_tags = ast.nodes.items(.tag);138 const node_tags = ast.nodes.items(.tag);
87 const datas = ast.nodes.items(.data);139 const datas = ast.nodes.items(.data);
88 switch (node_tags[node]) {140 switch (node_tags[node]) {
89 .identifier => {},141 .identifier => try walkIdentifier(w, main_tokens[node]),
90142
91 .number_literal,143 .number_literal,
92 .char_literal,144 .char_literal,
...@@ -463,8 +515,32 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -463,8 +515,32 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
463 }515 }
464}516}
465517
518fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.VarDecl) Error!void {
519 _ = decl_node;
520
521 if (var_decl.ast.type_node != 0) {
522 try walkExpression(w, var_decl.ast.type_node);
523 }
524
525 if (var_decl.ast.align_node != 0) {
526 try walkExpression(w, var_decl.ast.align_node);
527 }
528
529 if (var_decl.ast.addrspace_node != 0) {
530 try walkExpression(w, var_decl.ast.addrspace_node);
531 }
532
533 if (var_decl.ast.section_node != 0) {
534 try walkExpression(w, var_decl.ast.section_node);
535 }
536
537 assert(var_decl.ast.init_node != 0);
538
539 return walkExpression(w, var_decl.ast.init_node);
540}
541
466fn walkVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {542fn walkVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
467 try walkIdentifier(w, var_decl.ast.mut_token + 1); // name543 try walkIdentifierNew(w, var_decl.ast.mut_token + 1); // name
468544
469 if (var_decl.ast.type_node != 0) {545 if (var_decl.ast.type_node != 0) {
470 try walkExpression(w, var_decl.ast.type_node);546 try walkExpression(w, var_decl.ast.type_node);
...@@ -571,9 +647,17 @@ fn walkSlice(...@@ -571,9 +647,17 @@ fn walkSlice(
571 }647 }
572}648}
573649
574fn walkIdentifier(w: *Walk, token_index: Ast.TokenIndex) Error!void {650fn walkIdentifier(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
651 const ast = w.ast;
652 const token_tags = ast.tokens.items(.tag);
653 assert(token_tags[name_ident] == .identifier);
654 const name_bytes = ast.tokenSlice(name_ident);
655 _ = w.unreferenced_globals.swapRemove(name_bytes);
656}
657
658fn walkIdentifierNew(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
575 _ = w;659 _ = w;
576 _ = token_index;660 _ = name_ident;
577}661}
578662
579fn walkContainerDecl(663fn walkContainerDecl(
...@@ -585,9 +669,7 @@ fn walkContainerDecl(...@@ -585,9 +669,7 @@ fn walkContainerDecl(
585 if (container_decl.ast.arg != 0) {669 if (container_decl.ast.arg != 0) {
586 try walkExpression(w, container_decl.ast.arg);670 try walkExpression(w, container_decl.ast.arg);
587 }671 }
588 for (container_decl.ast.members) |member| {672 try walkMembers(w, container_decl.ast.members);
589 try walkMember(w, member);
590 }
591}673}
592674
593fn walkBuiltinCall(675fn walkBuiltinCall(