zig reduce: add reduction for removing var decls

This commit is contained in:
Andrew Kelley 2023-11-04 20:27:15 -07:00
parent c4dddcbadb
commit a9002156a0
2 changed files with 62 additions and 17 deletions

View File

@ -321,6 +321,12 @@ fn transformationsToFixups(
.delete_node => |decl_node| {
try fixups.omit_nodes.put(gpa, decl_node, {});
},
.delete_var_decl => |delete_var_decl| {
try fixups.omit_nodes.put(gpa, delete_var_decl.var_decl_node, {});
for (delete_var_decl.references.items) |ident_node| {
try fixups.replace_nodes.put(gpa, ident_node, "undefined");
}
},
.replace_with_undef => |node| {
try fixups.replace_nodes.put(gpa, node, "undefined");
},

View File

@ -8,6 +8,7 @@ ast: *const Ast,
transformations: *std.ArrayList(Transformation),
unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
in_scope_names: std.StringArrayHashMapUnmanaged(u32),
replace_names: std.StringArrayHashMapUnmanaged(u32),
gpa: std.mem.Allocator,
arena: std.mem.Allocator,
@ -17,6 +18,13 @@ pub const Transformation = union(enum) {
gut_function: Ast.Node.Index,
/// Omit a global declaration.
delete_node: Ast.Node.Index,
/// Delete a local variable declaration and replace all of its references
/// with `undefined`.
delete_var_decl: struct {
var_decl_node: Ast.Node.Index,
/// Identifier nodes that reference the variable.
references: std.ArrayListUnmanaged(Ast.Node.Index),
},
/// Replace an expression with `undefined`.
replace_with_undef: Ast.Node.Index,
/// Replace an `@import` with the imported file contents wrapped in a struct.
@ -48,10 +56,12 @@ pub fn findTransformations(
.arena = arena,
.unreferenced_globals = .{},
.in_scope_names = .{},
.replace_names = .{},
};
defer {
walk.unreferenced_globals.deinit(walk.gpa);
walk.in_scope_names.deinit(walk.gpa);
walk.replace_names.deinit(walk.gpa);
}
try walkMembers(&walk, walk.ast.rootDecls());
@ -133,6 +143,7 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
try walkExpression(w, fn_proto);
const body_node = datas[decl].rhs;
if (!isFnBodyGutted(ast, body_node)) {
w.replace_names.clearRetainingCapacity();
try w.transformations.append(.{ .gut_function = decl });
try walkExpression(w, body_node);
}
@ -187,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
const node_tags = ast.nodes.items(.tag);
const datas = ast.nodes.items(.data);
switch (node_tags[node]) {
.identifier => try walkIdentifier(w, main_tokens[node]),
.identifier => {
const name_ident = main_tokens[node];
assert(token_tags[name_ident] == .identifier);
const name_bytes = ast.tokenSlice(name_ident);
_ = w.unreferenced_globals.swapRemove(name_bytes);
if (w.replace_names.get(name_bytes)) |index| {
try w.transformations.items[index].delete_var_decl.references.append(w.arena, node);
}
},
.number_literal,
.char_literal,
@ -646,13 +665,31 @@ fn walkBlock(
.local_var_decl,
.simple_var_decl,
.aligned_var_decl,
=> try walkLocalVarDecl(w, ast.fullVarDecl(stmt).?),
=> {
const var_decl = ast.fullVarDecl(stmt).?;
if (var_decl.ast.init_node != 0 and
isUndefinedIdent(w.ast, var_decl.ast.init_node))
{
try w.transformations.append(.{ .delete_var_decl = .{
.var_decl_node = stmt,
.references = .{},
} });
const name_tok = var_decl.ast.mut_token + 1;
const name_bytes = ast.tokenSlice(name_tok);
try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1));
} else {
try walkLocalVarDecl(w, var_decl);
}
},
else => {
// Don't try to remove `_ = foo;` discards; those are handled separately.
switch (categorizeStmt(ast, stmt)) {
// Don't try to remove `_ = foo;` discards; those are handled separately.
.discard_identifier => {},
else => try w.transformations.append(.{ .delete_node = stmt }),
// definitely try to remove `_ = undefined;` though.
.discard_undefined, .trap_call, .other => {
try w.transformations.append(.{ .delete_node = stmt });
},
}
try walkExpression(w, stmt);
},
@ -905,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
}
const StmtCategory = enum {
discard_undefined,
discard_identifier,
trap_call,
other,
@ -930,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
},
.assign => {
const infix = datas[stmt];
if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier)
return .discard_identifier;
if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) {
const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]);
if (std.mem.eql(u8, name_bytes, "undefined")) {
return .discard_undefined;
} else {
return .discard_identifier;
}
}
return .other;
},
else => return .other,
@ -951,26 +995,21 @@ fn categorizeBuiltinCall(
}
fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {
const node_tags = ast.nodes.items(.tag);
const main_tokens = ast.nodes.items(.main_token);
switch (node_tags[node]) {
.identifier => {
const token_index = main_tokens[node];
const name_bytes = ast.tokenSlice(token_index);
return std.mem.eql(u8, name_bytes, "_");
},
else => return false,
}
return isMatchingIdent(ast, node, "_");
}
fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
return isMatchingIdent(ast, node, "undefined");
}
fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool {
const node_tags = ast.nodes.items(.tag);
const main_tokens = ast.nodes.items(.main_token);
switch (node_tags[node]) {
.identifier => {
const token_index = main_tokens[node];
const name_bytes = ast.tokenSlice(token_index);
return std.mem.eql(u8, name_bytes, "undefined");
return std.mem.eql(u8, name_bytes, string);
},
else => return false,
}