| ... | @@ -8,6 +8,7 @@ ast: *const Ast, | ... | @@ -8,6 +8,7 @@ ast: *const Ast, |
| 8 | transformations: *std.ArrayList(Transformation), | 8 | transformations: *std.ArrayList(Transformation), |
| 9 | unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), | 9 | unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), |
| 10 | in_scope_names: std.StringArrayHashMapUnmanaged(u32), | 10 | in_scope_names: std.StringArrayHashMapUnmanaged(u32), |
| | 11 | replace_names: std.StringArrayHashMapUnmanaged(u32), |
| 11 | gpa: std.mem.Allocator, | 12 | gpa: std.mem.Allocator, |
| 12 | arena: std.mem.Allocator, | 13 | arena: std.mem.Allocator, |
| 13 | | 14 | |
| ... | @@ -17,6 +18,13 @@ pub const Transformation = union(enum) { | ... | @@ -17,6 +18,13 @@ pub const Transformation = union(enum) { |
| 17 | gut_function: Ast.Node.Index, | 18 | gut_function: Ast.Node.Index, |
| 18 | /// Omit a global declaration. | 19 | /// Omit a global declaration. |
| 19 | delete_node: Ast.Node.Index, | 20 | delete_node: Ast.Node.Index, |
| | 21 | /// Delete a local variable declaration and replace all of its references |
| | 22 | /// with `undefined`. |
| | 23 | delete_var_decl: struct { |
| | 24 | var_decl_node: Ast.Node.Index, |
| | 25 | /// Identifier nodes that reference the variable. |
| | 26 | references: std.ArrayListUnmanaged(Ast.Node.Index), |
| | 27 | }, |
| 20 | /// Replace an expression with `undefined`. | 28 | /// Replace an expression with `undefined`. |
| 21 | replace_with_undef: Ast.Node.Index, | 29 | replace_with_undef: Ast.Node.Index, |
| 22 | /// Replace an `@import` with the imported file contents wrapped in a struct. | 30 | /// Replace an `@import` with the imported file contents wrapped in a struct. |
| ... | @@ -48,10 +56,12 @@ pub fn findTransformations( | ... | @@ -48,10 +56,12 @@ pub fn findTransformations( |
| 48 | .arena = arena, | 56 | .arena = arena, |
| 49 | .unreferenced_globals = .{}, | 57 | .unreferenced_globals = .{}, |
| 50 | .in_scope_names = .{}, | 58 | .in_scope_names = .{}, |
| | 59 | .replace_names = .{}, |
| 51 | }; | 60 | }; |
| 52 | defer { | 61 | defer { |
| 53 | walk.unreferenced_globals.deinit(walk.gpa); | 62 | walk.unreferenced_globals.deinit(walk.gpa); |
| 54 | walk.in_scope_names.deinit(walk.gpa); | 63 | walk.in_scope_names.deinit(walk.gpa); |
| | 64 | walk.replace_names.deinit(walk.gpa); |
| 55 | } | 65 | } |
| 56 | | 66 | |
| 57 | try walkMembers(&walk, walk.ast.rootDecls()); | 67 | try walkMembers(&walk, walk.ast.rootDecls()); |
| ... | @@ -133,6 +143,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { | ... | @@ -133,6 +143,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { |
| 133 | try walkExpression(w, fn_proto); | 143 | try walkExpression(w, fn_proto); |
| 134 | const body_node = datas[decl].rhs; | 144 | const body_node = datas[decl].rhs; |
| 135 | if (!isFnBodyGutted(ast, body_node)) { | 145 | if (!isFnBodyGutted(ast, body_node)) { |
| | 146 | w.replace_names.clearRetainingCapacity(); |
| 136 | try w.transformations.append(.{ .gut_function = decl }); | 147 | try w.transformations.append(.{ .gut_function = decl }); |
| 137 | try walkExpression(w, body_node); | 148 | try walkExpression(w, body_node); |
| 138 | } | 149 | } |
| ... | @@ -187,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { | ... | @@ -187,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 187 | const node_tags = ast.nodes.items(.tag); | 198 | const node_tags = ast.nodes.items(.tag); |
| 188 | const datas = ast.nodes.items(.data); | 199 | const datas = ast.nodes.items(.data); |
| 189 | switch (node_tags[node]) { | 200 | switch (node_tags[node]) { |
| 190 | .identifier => try walkIdentifier(w, main_tokens[node]), | 201 | .identifier => { |
| | 202 | const name_ident = main_tokens[node]; |
| | 203 | assert(token_tags[name_ident] == .identifier); |
| | 204 | const name_bytes = ast.tokenSlice(name_ident); |
| | 205 | _ = w.unreferenced_globals.swapRemove(name_bytes); |
| | 206 | if (w.replace_names.get(name_bytes)) |index| { |
| | 207 | try w.transformations.items[index].delete_var_decl.references.append(w.arena, node); |
| | 208 | } |
| | 209 | }, |
| 191 | | 210 | |
| 192 | .number_literal, | 211 | .number_literal, |
| 193 | .char_literal, | 212 | .char_literal, |
| ... | @@ -646,13 +665,31 @@ fn walkBlock( | ... | @@ -646,13 +665,31 @@ fn walkBlock( |
| 646 | .local_var_decl, | 665 | .local_var_decl, |
| 647 | .simple_var_decl, | 666 | .simple_var_decl, |
| 648 | .aligned_var_decl, | 667 | .aligned_var_decl, |
| 649 | => try walkLocalVarDecl(w, ast.fullVarDecl(stmt).?), | 668 | => { |
| | 669 | const var_decl = ast.fullVarDecl(stmt).?; |
| | 670 | if (var_decl.ast.init_node != 0 and |
| | 671 | isUndefinedIdent(w.ast, var_decl.ast.init_node)) |
| | 672 | { |
| | 673 | try w.transformations.append(.{ .delete_var_decl = .{ |
| | 674 | .var_decl_node = stmt, |
| | 675 | .references = .{}, |
| | 676 | } }); |
| | 677 | const name_tok = var_decl.ast.mut_token + 1; |
| | 678 | const name_bytes = ast.tokenSlice(name_tok); |
| | 679 | try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1)); |
| | 680 | } else { |
| | 681 | try walkLocalVarDecl(w, var_decl); |
| | 682 | } |
| | 683 | }, |
| 650 | | 684 | |
| 651 | else => { | 685 | else => { |
| 652 | // Don't try to remove `_ = foo;` discards; those are handled separately. | | |
| 653 | switch (categorizeStmt(ast, stmt)) { | 686 | switch (categorizeStmt(ast, stmt)) { |
| | 687 | // Don't try to remove `_ = foo;` discards; those are handled separately. |
| 654 | .discard_identifier => {}, | 688 | .discard_identifier => {}, |
| 655 | else => try w.transformations.append(.{ .delete_node = stmt }), | 689 | // definitely try to remove `_ = undefined;` though. |
| | 690 | .discard_undefined, .trap_call, .other => { |
| | 691 | try w.transformations.append(.{ .delete_node = stmt }); |
| | 692 | }, |
| 656 | } | 693 | } |
| 657 | try walkExpression(w, stmt); | 694 | try walkExpression(w, stmt); |
| 658 | }, | 695 | }, |
| ... | @@ -905,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool { | ... | @@ -905,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool { |
| 905 | } | 942 | } |
| 906 | | 943 | |
| 907 | const StmtCategory = enum { | 944 | const StmtCategory = enum { |
| | 945 | discard_undefined, |
| 908 | discard_identifier, | 946 | discard_identifier, |
| 909 | trap_call, | 947 | trap_call, |
| 910 | other, | 948 | other, |
| ... | @@ -930,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory { | ... | @@ -930,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory { |
| 930 | }, | 968 | }, |
| 931 | .assign => { | 969 | .assign => { |
| 932 | const infix = datas[stmt]; | 970 | const infix = datas[stmt]; |
| 933 | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) | 971 | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) { |
| 934 | return .discard_identifier; | 972 | const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]); |
| | 973 | if (std.mem.eql(u8, name_bytes, "undefined")) { |
| | 974 | return .discard_undefined; |
| | 975 | } else { |
| | 976 | return .discard_identifier; |
| | 977 | } |
| | 978 | } |
| 935 | return .other; | 979 | return .other; |
| 936 | }, | 980 | }, |
| 937 | else => return .other, | 981 | else => return .other, |
| ... | @@ -951,26 +995,21 @@ fn categorizeBuiltinCall( | ... | @@ -951,26 +995,21 @@ fn categorizeBuiltinCall( |
| 951 | } | 995 | } |
| 952 | | 996 | |
| 953 | fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool { | 997 | fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 954 | const node_tags = ast.nodes.items(.tag); | 998 | return isMatchingIdent(ast, node, "_"); |
| 955 | const main_tokens = ast.nodes.items(.main_token); | | |
| 956 | switch (node_tags[node]) { | | |
| 957 | .identifier => { | | |
| 958 | const token_index = main_tokens[node]; | | |
| 959 | const name_bytes = ast.tokenSlice(token_index); | | |
| 960 | return std.mem.eql(u8, name_bytes, "_"); | | |
| 961 | }, | | |
| 962 | else => return false, | | |
| 963 | } | | |
| 964 | } | 999 | } |
| 965 | | 1000 | |
| 966 | fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { | 1001 | fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| | 1002 | return isMatchingIdent(ast, node, "undefined"); |
| | 1003 | } |
| | 1004 | |
| | 1005 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { |
| 967 | const node_tags = ast.nodes.items(.tag); | 1006 | const node_tags = ast.nodes.items(.tag); |
| 968 | const main_tokens = ast.nodes.items(.main_token); | 1007 | const main_tokens = ast.nodes.items(.main_token); |
| 969 | switch (node_tags[node]) { | 1008 | switch (node_tags[node]) { |
| 970 | .identifier => { | 1009 | .identifier => { |
| 971 | const token_index = main_tokens[node]; | 1010 | const token_index = main_tokens[node]; |
| 972 | const name_bytes = ast.tokenSlice(token_index); | 1011 | const name_bytes = ast.tokenSlice(token_index); |
| 973 | return std.mem.eql(u8, name_bytes, "undefined"); | 1012 | return std.mem.eql(u8, name_bytes, string); |
| 974 | }, | 1013 | }, |
| 975 | else => return false, | 1014 | else => return false, |
| 976 | } | 1015 | } |