Skip to content

Commit 791dcd2

Browse files
committed
rewrote free variables analysis to do a whole scope at once
1 parent 0d763be commit 791dcd2

File tree

10 files changed

+163
-59
lines changed

10 files changed

+163
-59
lines changed

include/shady/ir.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Nodes filter_out_annotation(IrArena*, Nodes, const char* name);
139139

140140
bool is_abstraction (const Node*);
141141
String get_abstraction_name (const Node* abs);
142+
String get_abstraction_name_unsafe(const Node* abs);
142143
const Node* get_abstraction_body (const Node* abs);
143144
Nodes get_abstraction_params(const Node* abs);
144145

src/common/dict.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,11 @@ KeyHash hash_murmur(const void* data, size_t size) {
334334
final ^= out[3];
335335
return final;
336336
}
337+
338+
KeyHash hash_ptr(void** p) {
339+
return hash_murmur(p, sizeof(void*));
340+
}
341+
342+
bool compare_ptrs(void** a, void** b) {
343+
return *a == *b;
344+
}

src/common/dict.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,7 @@ bool insert_dict_and_get_result_impl(struct Dict*, void* key, void* value);
4646

4747
KeyHash hash_murmur(const void* data, size_t size);
4848

49+
KeyHash hash_ptr(void**);
50+
bool compare_ptrs(void**, void**);
51+
4952
#endif

src/shady/analysis/free_variables.c

Lines changed: 93 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,28 @@
88
#include "list.h"
99
#include "dict.h"
1010

11+
#include <stdlib.h>
1112
#include <assert.h>
1213

1314
KeyHash hash_node(Node**);
1415
bool compare_node(Node**, Node**);
1516

1617
typedef struct {
1718
Visitor visitor;
18-
struct Dict* bound_set;
19-
struct Dict* set;
20-
struct List* free_list;
19+
struct Dict* map;
20+
CFNodeVariables* current_scope;
2121
} Context;
2222

2323
static void search_op_for_free_variables(Context* visitor, NodeClass class, String op_name, const Node* node) {
2424
assert(node);
2525
switch (node->tag) {
2626
case Variable_TAG: {
27-
if (find_key_dict(const Node*, visitor->bound_set, node))
28-
return;
29-
if (insert_set_get_result(const Node*, visitor->set, node)) {
30-
append_list(const Node*, visitor->free_list, node);
27+
Nodes params = get_abstraction_params(visitor->current_scope->node->node);
28+
for (size_t i = 0; i < params.count; i++) {
29+
if (params.nodes[i] == node)
30+
return;
3131
}
32+
insert_set_get_result(const Node*, visitor->current_scope->free_set, node);
3233
break;
3334
}
3435
case Function_TAG:
@@ -38,26 +39,36 @@ static void search_op_for_free_variables(Context* visitor, NodeClass class, Stri
3839
}
3940
}
4041

41-
static void visit_domtree(Context* ctx, CFNode* cfnode, int depth) {
42-
const Node* abs = cfnode->node;
42+
static CFNodeVariables* create_node_variables(CFNode* cfnode) {
43+
CFNodeVariables* v = calloc(sizeof(CFNodeVariables), 1);
44+
*v = (CFNodeVariables) {
45+
.node = cfnode,
46+
.free_set = new_set(const Node*, (HashFn) hash_node, (CmpFn) compare_node)
47+
};
48+
return v;
49+
}
4350

44-
bool is_named = abs->tag != Case_TAG;
51+
static CFNodeVariables* visit_domtree(Context* ctx, CFNode* cfnode, int depth, CFNodeVariables* parent) {
52+
Context new_context = *ctx;
53+
ctx = &new_context;
4554

46-
if (is_named) {
47-
for (int i = 0; i < depth; i++)
48-
debugvv_print(" ");
49-
debugvv_print("%s\n", get_abstraction_name(abs));
55+
ctx->current_scope = create_node_variables(cfnode);
56+
if (parent) {
57+
ctx->current_scope->bound_set = clone_dict(parent->bound_set);
58+
} else {
59+
ctx->current_scope->bound_set = new_set(const Node*, (HashFn) hash_node, (CmpFn) compare_node);
5060
}
61+
insert_dict(CFNode*, CFNodeVariables*, ctx->map, cfnode, ctx->current_scope);
62+
const Node* abs = cfnode->node;
63+
64+
bool is_named = abs->tag != Case_TAG;
5165

5266
// Bind parameters
5367
Nodes params = get_abstraction_params(abs);
5468
for (size_t j = 0; j < params.count; j++) {
5569
const Node* param = params.nodes[j];
56-
bool r = insert_set_get_result(const Node*, ctx->bound_set, param);
57-
// assert(r);
58-
// this can happen if you visit the domtree of a CFG starting _inside_ a loop
59-
// we will meet some unbound params but eventually we'll enter their definition after the fact
60-
// those params should still be considered free in this case.
70+
bool r = insert_set_get_result(const Node*, ctx->current_scope->bound_set, param);
71+
assert(r);
6172
}
6273

6374
const Node* body = get_abstraction_body(abs);
@@ -66,35 +77,83 @@ static void visit_domtree(Context* ctx, CFNode* cfnode, int depth) {
6677

6778
for (size_t i = 0; i < entries_count_list(cfnode->dominates); i++) {
6879
CFNode* child = read_list(CFNode*, cfnode->dominates)[i];
69-
visit_domtree(ctx, child, depth + (is_named ? 1 : 0));
80+
CFNodeVariables* child_variables = visit_domtree(ctx, child, depth + (is_named ? 1 : 1), ctx->current_scope);
81+
size_t j = 0;
82+
const Node* free_var;
83+
while (dict_iter(child_variables->free_set, &j, &free_var, NULL)) {
84+
for (size_t k = 0; k < params.count; k++) {
85+
if (params.nodes[k] == free_var)
86+
goto next;
87+
}
88+
insert_set_get_result(const Node*, ctx->current_scope->free_set, free_var);
89+
next:;
90+
}
7091
}
7192

93+
/*String abs_name = get_abstraction_name_unsafe(abs);
94+
for (int i = 0; i < depth; i++)
95+
debugvv_print(" ");
96+
if (abs_name)
97+
debugvv_print("%s: ", abs_name);
98+
else
99+
debugvv_print("%%%d: ", abs->id);
100+
101+
if (true) {
102+
bool prev = false;
103+
size_t i = 0;
104+
const Node* free_var;
105+
while (dict_iter(ctx->current_scope->free_set, &i, &free_var, NULL)) {
106+
if (prev) {
107+
debugvv_print(", ");
108+
}
109+
log_node(DEBUGVV, free_var);
110+
prev = true;
111+
}
112+
}
113+
114+
if (true) {
115+
debugvv_print(". Binds: ");
116+
bool prev = false;
117+
for (size_t i = 0; i < params.count; i++) {
118+
if (prev) {
119+
debugvv_print(", ");
120+
}
121+
log_node(DEBUGVV, params.nodes[i]);
122+
prev = true;
123+
}
124+
}
125+
126+
debugvv_print("\n");*/
127+
72128
// Unbind parameters
73129
for (size_t j = 0; j < params.count; j++) {
74130
const Node* param = params.nodes[j];
75-
bool r = remove_dict(const Node*, ctx->bound_set, param);
131+
bool r = remove_dict(const Node*, ctx->current_scope->bound_set, param);
76132
assert(r);
77133
}
78-
}
79134

80-
struct List* compute_free_variables(const Scope* scope, const Node* at) {
81-
struct Dict* bound_set = new_set(const Node*, (HashFn) hash_node, (CmpFn) compare_node);
82-
struct Dict* set = new_set(const Node*, (HashFn) hash_node, (CmpFn) compare_node);
83-
struct List* free_list = new_list(const Node*);
135+
return ctx->current_scope;
136+
}
84137

138+
struct Dict* compute_scope_variables_map(const Scope* scope) {
85139
Context ctx = {
86140
.visitor = {
87141
.visit_op_fn = (VisitOpFn) search_op_for_free_variables,
88142
},
89-
.bound_set = bound_set,
90-
.set = set,
91-
.free_list = free_list,
143+
.map = new_dict(CFNode*, CFNodeVariables*, (HashFn) hash_ptr, (CmpFn) compare_ptrs),
92144
};
93145

94-
debugv_print("Computing free variables...\n");
95-
visit_domtree(&ctx, scope_lookup(scope, at), 0);
146+
debugv_print("Computing free variables for function '%s' ...\n", get_abstraction_name(scope->entry->node));
147+
visit_domtree(&ctx, scope->entry, 0, NULL);
148+
return ctx.map;
149+
}
96150

97-
destroy_dict(bound_set);
98-
destroy_dict(set);
99-
return free_list;
151+
void destroy_scope_variables_map(struct Dict* map) {
152+
size_t i = 0;
153+
CFNodeVariables* value;
154+
while (dict_iter(map, &i, NULL, &value)) {
155+
destroy_dict(value->bound_set);
156+
destroy_dict(value->free_set);
157+
free((void*) value);
158+
}
100159
}

src/shady/analysis/free_variables.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@
44
#include "shady/ir.h"
55

66
typedef struct Scope_ Scope;
7+
typedef struct CFNode_ CFNode;
78

8-
struct List* compute_free_variables(const Scope* scope, const Node*);
9+
typedef struct {
10+
CFNode* node;
11+
struct Dict* bound_set;
12+
struct Dict* free_set;
13+
} CFNodeVariables;
14+
15+
struct Dict* compute_scope_variables_map(const Scope* scope);
16+
void destroy_scope_variables_map(struct Dict*);
917

1018
#endif

src/shady/analysis/scope.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,9 +550,10 @@ static void dump_cf_node(FILE* output, const CFNode* n) {
550550

551551
label = format_string_arena(bb->arena->arena, "%s%s", label, node_tags[body->tag]);
552552

553-
if (is_basic_block(bb)) {
554-
label = format_string_arena(bb->arena->arena, "%s\n%s", get_abstraction_name(bb), label);
555-
}
553+
String abs_name = get_abstraction_name_unsafe(bb);
554+
if (!abs_name)
555+
abs_name = format_string_interned(bb->arena, "%%%d", bb->id);
556+
label = format_string_arena(bb->arena->arena, "%s\n%s", abs_name, label);
556557

557558
fprintf(output, "bb_%zu [label=\"%s\", color=\"%s\", shape=box];\n", (size_t) n, label, color);
558559

src/shady/analysis/verify.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ static void verify_scoping(Module* mod) {
4646
struct List* scopes = build_scopes(mod);
4747
for (size_t i = 0; i < entries_count_list(scopes); i++) {
4848
Scope* scope = read_list(Scope*, scopes)[i];
49-
struct List* leaking = compute_free_variables(scope, scope->entry->node);
50-
for (size_t j = 0; j < entries_count_list(leaking); j++) {
51-
log_node(ERROR, read_list(const Node*, leaking)[j]);
49+
struct Dict* map = compute_scope_variables_map(scope);
50+
CFNodeVariables* entry_vars = *find_value_dict(CFNode*, CFNodeVariables*, map, scope->entry);
51+
size_t j = 0;
52+
const Node* leaking;
53+
while (dict_iter(entry_vars->free_set, &j, &leaking, NULL)) {
54+
log_node(ERROR, leaking);
5255
error_print("\n");
5356
}
54-
assert(entries_count_list(leaking) == 0);
55-
destroy_list(leaking);
57+
assert(entries_count_dict(entry_vars->free_set) == 0);
58+
destroy_scope_variables_map(map);
5659
destroy_scope(scope);
5760
}
5861
destroy_list(scopes);

src/shady/node.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,16 @@ String get_abstraction_name(const Node* abs) {
263263
}
264264
}
265265

266+
String get_abstraction_name_unsafe(const Node* abs) {
267+
assert(is_abstraction(abs));
268+
switch (abs->tag) {
269+
case Function_TAG: return abs->payload.fun.name;
270+
case BasicBlock_TAG: return abs->payload.basic_block.name;
271+
case Case_TAG: return NULL;
272+
default: assert(false);
273+
}
274+
}
275+
266276
const Node* get_abstraction_body(const Node* abs) {
267277
assert(is_abstraction(abs));
268278
switch (abs->tag) {

src/shady/passes/lcssa.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef struct Context_ {
1717
const Node* current_fn;
1818
Scope* scope;
1919
const UsesMap* scope_uses;
20+
struct Dict* scope_vars;
2021
LoopTree* loop_tree;
2122
struct Dict* lifted_arguments;
2223
} Context;
@@ -55,9 +56,11 @@ void find_liftable_loop_values(Context* ctx, const Node* old, Nodes* nparams, No
5556
*lparams = empty(a);
5657
*nargs = empty(a);
5758

58-
struct List* fvs = compute_free_variables(ctx->scope, old);
59-
for (size_t i = 0; i < entries_count_list(fvs); i++) {
60-
const Node* fv = read_list(const Node*, fvs)[i];
59+
const Node* fv;
60+
size_t i = 0;
61+
CFNode* cf_node = scope_lookup(ctx->scope, old);
62+
CFNodeVariables* node_vars = *find_value_dict(CFNode*, CFNodeVariables*, ctx->scope_vars, cf_node);
63+
while (dict_iter(node_vars->free_set, &i, &fv, NULL)) {
6164
const Node* defining_abs = get_var_binding_abstraction(ctx->scope_uses, fv);
6265
const CFNode* defining_cf_node = scope_lookup(ctx->scope, defining_abs);
6366
assert(defining_cf_node);
@@ -74,7 +77,6 @@ void find_liftable_loop_values(Context* ctx, const Node* old, Nodes* nparams, No
7477
*nargs = append_nodes(a, *nargs, narg);
7578
}
7679
}
77-
destroy_list(fvs);
7880

7981
if (nparams->count > 0)
8082
insert_dict(const Node*, Nodes, ctx->lifted_arguments, old, *nparams);
@@ -152,6 +154,7 @@ const Node* process_node(Context* ctx, const Node* old) {
152154
ctx->current_fn = old;
153155
ctx->scope = new_scope(old);
154156
ctx->scope_uses = create_uses_map(old, (NcDeclaration | NcType));
157+
ctx->scope_vars = compute_scope_variables_map(ctx->scope);
155158
ctx->loop_tree = build_loop_tree(ctx->scope);
156159

157160
Node* new = recreate_decl_header_identity(&ctx->rewriter, old);
@@ -160,6 +163,7 @@ const Node* process_node(Context* ctx, const Node* old) {
160163
destroy_loop_tree(ctx->loop_tree);
161164
destroy_uses_map(ctx->scope_uses);
162165
destroy_scope(ctx->scope);
166+
destroy_scope_variables_map(ctx->scope_vars);
163167
return new;
164168
}
165169
case Jump_TAG: {

src/shady/passes/lift_indirect_targets.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ typedef struct Context_ {
2626
Rewriter rewriter;
2727
Scope* scope;
2828
const UsesMap* scope_uses;
29+
struct Dict* scope_vars;
2930

3031
struct Dict* lifted;
3132
bool disable_lowering;
@@ -78,19 +79,23 @@ static LiftedCont* lambda_lift(Context* ctx, const Node* cont, String given_name
7879
String name = is_basic_block(cont) ? format_string_arena(a->arena, "%s_%s", get_abstraction_name(cont->payload.basic_block.fn), get_abstraction_name(cont)) : unique_name(a, given_name);
7980

8081
// Compute the live stuff we'll need
81-
Scope* scope = new_scope(cont);
82-
struct List* recover_context = compute_free_variables(scope, cont);
83-
size_t recover_context_size = entries_count_list(recover_context);
84-
destroy_scope(scope);
85-
86-
debugv_print("free (spilled) variables at '%s': ", name);
87-
for (size_t i = 0; i < recover_context_size; i++) {
88-
const Node* item = read_list(const Node*, recover_context)[i];
89-
debugv_print(get_value_name_safe(item));
90-
if (i + 1 < recover_context_size)
91-
debugv_print(", ");
82+
CFNode* cf_node = scope_lookup(ctx->scope, cont);
83+
CFNodeVariables* node_vars = *find_value_dict(CFNode*, CFNodeVariables*, ctx->scope_vars, cf_node);
84+
struct List* recover_context = new_list(const Node*);
85+
size_t recover_context_size = entries_count_dict(node_vars->free_set);
86+
87+
{
88+
debugv_print("lambda_lift: free (to-be-spilled) variables at '%s' (count=%d): ", name, entries_count_dict(node_vars->free_set));
89+
size_t i = 0;
90+
const Node* item;
91+
while (dict_iter(node_vars->free_set, &i, &item, NULL)) {
92+
append_list(const Node*, recover_context, item );
93+
debugv_print(get_value_name_safe(item));
94+
if (i + 1 < recover_context_size)
95+
debugv_print(", ");
96+
}
97+
debugv_print("\n");
9298
}
93-
debugv_print("\n");
9499

95100
// Create and register new parameters for the lifted continuation
96101
Nodes new_params = recreate_variables(&ctx->rewriter, oparams);
@@ -166,12 +171,14 @@ static const Node* process_node(Context* ctx, const Node* node) {
166171
Context fn_ctx = *ctx;
167172
fn_ctx.scope = new_scope(node);
168173
fn_ctx.scope_uses = create_uses_map(node, (NcDeclaration | NcType));
174+
fn_ctx.scope_vars = compute_scope_variables_map(fn_ctx.scope);
169175
ctx = &fn_ctx;
170176

171177
Node* new = recreate_decl_header_identity(&ctx->rewriter, node);
172178
recreate_decl_body_identity(&ctx->rewriter, node, new);
173179

174180
destroy_uses_map(ctx->scope_uses);
181+
destroy_scope_variables_map(ctx->scope_vars);
175182
destroy_scope(ctx->scope);
176183
return new;
177184
}

0 commit comments

Comments
 (0)