1
1
class NQP::Optimizer {
2
2
has @ ! block_stack ;
3
+ has @ ! local_var_stack ;
3
4
4
5
method optimize ($ ast , * % adverbs ) {
5
6
@ ! block_stack := [$ ast [0 ]];
7
+ @ ! local_var_stack := nqp ::list();
6
8
self . visit_children($ ast );
7
9
$ ast ;
8
10
}
9
11
10
12
method visit_block ($ block ) {
11
13
@ ! block_stack . push ($ block );
14
+ my % * shallow_var_usages := nqp ::hash();
15
+ @ ! local_var_stack . push (% * shallow_var_usages );
16
+
17
+ # Push all the lexically declared variables into our shallow vars hash.
18
+ # Currently we limit ourselves to natives, because they are guaranteed
19
+ # not to have some weird setup that we have to be careful about.
20
+ if nqp ::istype($ block [0 ], QAST ::Stmts) {
21
+ my int $ idx := 0 ;
22
+ # before the $_ come the arguments. we must not turn these into locals,
23
+ # otherwise our signature binding will explode.
24
+ # say("THE BLOCK:::::::::::::::::::::::::::::::::::::::::::::::");
25
+ # say($block.dump);
26
+ # say(":::::::::::::::::::::::::::::::::::::::::::::::THE BLOCK");
27
+ while $ idx < + @ ($ block [0 ]) {
28
+ my $ var := $ block [0 ][$ idx ];
29
+ if nqp ::istype($ var , QAST ::Op) && $ var . op eq ' bind' {
30
+ # variables are initialised on the spot in nqp.
31
+ $ var := $ var [0 ]
32
+ }
33
+ if nqp ::istype($ var , QAST ::Var) && $ var . scope eq ' lexical' && $ var . decl eq ' var' {
34
+ # also make sure we don't turn dynamic vars into locals
35
+ my $ twigil := nqp :: substr ($ var . name , 1 , 1 );
36
+ my $ sigil := nqp :: substr ($ var . name , 0 , 1 );
37
+ if $ sigil eq ' $' && $ twigil ne ' *'
38
+ && $ var . name ne ' $_' && $ var . name ne ' $/' && $ var . name ne ' $!' && $ var . name ne ' $¢' {
39
+ % * shallow_var_usages {$ var . name } := nqp ::list($ var );
40
+ }
41
+ } elsif nqp ::istype($ var , QAST ::Op) && $ var . op eq ' bind' {
42
+ if nqp ::existskey(% * shallow_var_usages , $ var [0 ]. name ) {
43
+ nqp :: push (% * shallow_var_usages {$ var [0 ]. name }, $ var [0 ]);
44
+ }
45
+ }
46
+ $ idx := $ idx + 1 ;
47
+ }
48
+ }
12
49
13
50
self . visit_children($ block );
14
51
52
+ my $ succ ;
53
+ for % * shallow_var_usages {
54
+ my $ name := $ _ . key ;
55
+ my $ newname := $ block . unique (' lex_to_loc_' ~ + @ ! block_stack );
56
+ my @ usages := $ _ . value ;
57
+ say (" found " ~ + @ usages ~ " usages for $ name" );
58
+ for @ usages -> $ var {
59
+ say ($ var . scope ~ " : " ~ $ var . name ~ " <- old" );
60
+ $ var . scope(' local' );
61
+ $ var . name ($ newname );
62
+ # if $var.decl eq 'var' {
63
+ # $var.decl('static');
64
+ # }
65
+ $ succ := 1 ;
66
+ }
67
+ say (" turned $ name into a local ($ newname ), yippie" );
68
+ }
69
+ if $ succ {
70
+ say ($ block . dump);
71
+ }
72
+
15
73
@ ! block_stack . pop ();
74
+ @ ! local_var_stack . pop ();
16
75
17
76
$ block ;
18
77
}
19
78
20
- method find_lex ($ name ) {
79
+ method lexical_depth ($ name ) {
21
80
my int $ i := + @ ! block_stack ;
22
81
while $ i > 0 {
23
82
$ i := $ i - 1 ;
24
- my % sym := @ ! block_stack [$ i ]. symbol($ name );
83
+ my $ block := @ ! block_stack [$ i ];
84
+ my % sym := $ block . symbol($ name );
25
85
if + % sym {
26
- return % sym ;
86
+ return $ i ;
27
87
}
28
88
}
29
- NQPMu;
89
+ -1
90
+ }
91
+
92
+ method find_lex ($ name ) {
93
+ my int $ d := self . lexical_depth($ name );
94
+ if $ d >= 0 {
95
+ my $ block := @ ! block_stack [$ d ];
96
+ my % sym := $ block . symbol($ name );
97
+ return % sym ;
98
+ } else {
99
+ nqp ::hash();
100
+ }
30
101
}
31
102
32
103
method find_sym ($ name ) {
@@ -39,6 +110,34 @@ class NQP::Optimizer {
39
110
}
40
111
}
41
112
113
+ method visit_var ($ var ) {
114
+ if $ var . scope eq ' lexical' {
115
+ my int $ lexdepth := self . lexical_depth($ var . name );
116
+ my int $ vardepth := + @ ! block_stack - 1 - $ lexdepth ;
117
+ if $ lexdepth != -1 {
118
+ if $ vardepth == 0 {
119
+ if nqp ::existskey(% * shallow_var_usages , $ var . name ) {
120
+ say (" found a usage for " ~ $ var . name );
121
+ nqp :: push (% * shallow_var_usages {$ var . name }, $ var );
122
+ }
123
+ } else {
124
+ say (" poisoned " ~ $ var . name );
125
+ say (" my local var stack is " ~ + @ ! local_var_stack ~ " deep, my lexdepth is $ lexdepth" );
126
+ say (nqp ::existskey(@ ! local_var_stack [$ lexdepth -1], $ var . name ));
127
+ try {
128
+ nqp ::deletekey(@ ! local_var_stack [$ lexdepth -1], $ var . name );
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ if nqp ::istype($ var , QAST ::VarWithFallback) {
135
+ self . visit_children($ var );
136
+ }
137
+
138
+ $ var ;
139
+ }
140
+
42
141
method visit_op ($ op ) {
43
142
sub returns_int ($ node ) {
44
143
if nqp ::objprimspec($ node . returns ) == 1 {
@@ -63,6 +162,7 @@ class NQP::Optimizer {
63
162
}
64
163
return 0 ;
65
164
}
165
+
66
166
self . visit_children($ op );
67
167
68
168
my $ typeinfo := nqp :: chars ($ op . op) >= 2
@@ -118,6 +218,8 @@ class NQP::Optimizer {
118
218
$ node [$ i ] := self . visit_op($ visit )
119
219
} elsif nqp ::istype($ visit , QAST ::Block) {
120
220
$ node [$ i ] := self . visit_block($ visit )
221
+ } elsif nqp ::istype($ visit , QAST ::Var) {
222
+ $ node [$ i ] := self . visit_var($ visit );
121
223
} elsif nqp ::istype($ visit , QAST ::Want) {
122
224
self . visit_children($ visit , : skip_selectors)
123
225
} else {
0 commit comments