1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: beam_bool.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
18
%% Purpose: Optimizes booleans in guards.
24
-import(lists, [reverse/1,foldl/3,mapfoldl/3,sort/1,member/2]).
25
-define(MAXREG, 1024).
28
{next, %Next label number.
29
ll %Live regs at labels.
32
module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
33
%%io:format("~p:\n", [Mod]),
34
{Fs,_} = mapfoldl(fun(Fn, Lbl) -> function(Fn, Lbl) end, 100000000, Fs0),
35
{ok,{Mod,Exp,Attr,Fs,Lc}}.
37
function({function,Name,Arity,CLabel,Is0}, Lbl0) ->
38
%%io:format("~p/~p:\n", [Name,Arity]),
39
{Is,#st{next=Lbl}} = bool_opt(Is0, Lbl0),
40
{{function,Name,Arity,CLabel,Is},Lbl}.
43
%% Optimize boolean expressions that use guard bifs. Rewrite to
44
%% use test instructions if possible.
48
LiveInfo = index_instructions(Asm),
49
bopt(Asm, [], #st{next=Lbl,ll=LiveInfo}).
51
bopt([{block,Bl0}=Block|
54
{block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
55
{label,Succ}|Is]=Is0], Acc0, St) ->
56
case split_block(Bl0, Dst, Fail) of
58
bopt(Is0, [Block|Acc0], St);
60
Acc1 = case PreBlock of
62
_ -> [{block,PreBlock}|Acc0]
64
Acc = [{protected,[Dst],Bl,{Fail,Succ,Live}}|Acc1],
67
bopt([{test,is_eq_exact,{f,Fail},[Reg,{atom,true}]}=I|Is], [{block,_}|_]=Acc0, St0) ->
68
case bopt_block(Reg, Fail, Is, Acc0, St0) of
69
failed -> bopt(Is, [I|Acc0], St0);
70
{Acc,St} -> bopt(Is, Acc, St)
72
bopt([I|Is], Acc, St) ->
73
bopt(Is, [I|Acc], St);
75
{bopt_reverse(Acc, []),St}.
77
bopt_reverse([{protected,[Dst],Block,{Fail,Succ,Live}}|Is], Acc0) ->
78
Acc = [{block,Block},{jump,{f,Succ}},
80
{block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
82
bopt_reverse(Is, Acc);
83
bopt_reverse([I|Is], Acc) ->
84
bopt_reverse(Is, [I|Acc]);
85
bopt_reverse([], Acc) -> Acc.
87
%% bopt_block(Reg, Fail, OldIs, Accumulator, St) -> failed | {NewAcc,St}
88
%% Attempt to optimized a block of guard BIFs followed by a test
90
bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
91
case split_block(Bl0, Reg, Fail) of
93
%% Reason for failure: The block either contained no
94
%% guard BIFs with the failure label Fail, or the final
95
%% instruction in the block did not assign the Reg register.
97
%%io:format("split ~p: ~P\n", [Reg,Bl0,20]),
100
%% The block has been splitted. Bl1 is a non-empty list
101
%% of guard BIF instructions having the failure label Fail.
102
%% BlPre is a (possibly empty list) of instructions preceeding
104
Acc1 = make_block(BlPre, Acc0),
105
{Bl,Acc} = extend_block(Bl1, Fail, Acc1),
106
case catch bopt_block_1(Bl, Fail, St0) of
108
%% Optimization failed for one of the following reasons:
110
%% 1. Not possible to rewrite because a boolean value is
111
%% passed to another guard bif, e.g. 'abs(A > B)'
112
%% (in this case, obviously nonsense code). Rare in
115
%% 2. Not possible to rewrite because we have not seen
116
%% the complete boolan expression (it is spread out
117
%% over several blocks with jumps and labels).
118
%% The 'or' and 'and' instructions need to that fully
119
%% known operands in order to be eliminated.
121
%% 3. Other bug or limitation.
123
%%io:format("~P\n", [_Reason,20]),
126
case is_opt_safe(Bl, NewCode, OldIs, St) of
128
%% The optimization is not safe. (A register
129
%% used by the instructions following the
130
%% optimized code is either not assigned a
131
%% value at all or assigned a different value.)
133
%%io:format("\nNot safe:\n"),
134
%%io:format("~p\n", [Bl]),
135
%%io:format("~p\n", [reverse(NewCode)]),
137
true -> {NewCode++Acc,St}
142
bopt_block_1(Block, Fail, St) ->
143
{Pre0,[{_,Tree}]} = bopt_tree(Block),
144
Pre = update_fail_label(Pre0, Fail, []),
145
bopt_cg(Tree, Fail, make_block(Pre, []), St).
147
%% is_opt_safe(OriginalCode, OptCode, FollowingCode, State) -> true|false
148
%% Comparing the original code to the optimized code, determine
149
%% whether the optimized code is guaranteed to work in the same
150
%% way as the original code.
152
is_opt_safe(Bl, NewCode, OldIs, St) ->
153
%% Here are the conditions that must be true for the
154
%% optimization to be safe.
156
%% 1. Any register that was assigned a value in the original
157
%% code, but is not in the optimized code, must be guaranteed
158
%% to be KILLED in the following code. (NotSet below.)
160
%% 2. Any register that is assigned a value in the optimized
161
%% code must be UNUSED in the following code. (NewDst, Set.)
162
%% (Possible future improvement: Registers that are known
163
%% to be assigned the SAME value in the original and optimized
164
%% code don't need to be unused in the following code.)
166
PrevDst = dst_regs(Bl),
167
NewDst = dst_regs(NewCode),
168
NotSet = ordsets:subtract(PrevDst, NewDst),
170
%% Note: The following line is an optimization. We don't need
171
%% to test whether variables in NotSet for being unused, because
172
%% they will all be tested for being killed (a stronger condition
173
%% than being unused).
175
Set = ordsets:subtract(NewDst, NotSet),
177
all_killed(NotSet, OldIs, St) andalso
178
none_used(Set, OldIs, St).
180
% update_fail_label([{set,_,_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
181
% update_fail_label(Is, Fail, [I|Acc]);
182
update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
183
update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
184
update_fail_label([], _, Acc) -> Acc.
186
make_block([], Acc) -> Acc;
187
make_block(Bl, Acc) -> [{block,Bl}|Acc].
189
extend_block(BlAcc, Fail, [{protected,_,_,_}=Prot|OldAcc]) ->
190
extend_block([Prot|BlAcc], Fail, OldAcc);
191
extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]=OldAcc0) ->
192
case extend_block_1(reverse(Is0), Fail, BlAcc0) of
193
{[],_} -> {BlAcc0,OldAcc0};
194
{BlAcc,[]} -> extend_block(BlAcc, Fail, OldAcc);
195
{BlAcc,Is} -> {BlAcc,[{block,Is}|OldAcc]}
197
extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}.
199
extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
200
extend_block_1(Is, Fail, [I|Acc]);
201
extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
202
case safe_bool_op(Bif, length(As)) of
203
false -> {Acc,reverse(Is0)};
204
true -> extend_block_1(Is, Fail, [I|Acc])
206
extend_block_1([_|_]=Is, _, Acc) -> {Acc,reverse(Is)};
207
extend_block_1([], _, Acc) -> {Acc,[]}.
209
split_block(Is0, Dst, Fail) ->
211
[{'%live',_}|[{set,[Dst],_,_}|_]=Is] ->
212
split_block_1(Is, Fail);
213
[{set,[Dst],_,_}|_]=Is ->
214
split_block_1(Is, Fail);
218
split_block_1(Is, Fail) ->
219
case split_block_2(Is, Fail, []) of
224
% split_block_2([{set,[_],_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
225
% split_block_2(Is, Fail, [I|Acc]);
226
split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
227
split_block_2(Is, Fail, [I|Acc]);
228
split_block_2([{'%live',_}|Is], Fail, Acc) ->
229
split_block_2(Is, Fail, Acc);
230
split_block_2(Is, _, Acc) -> {Acc,reverse(Is)}.
235
dst_regs([{block,Bl}|Is], Acc) ->
236
dst_regs(Bl, dst_regs(Is, Acc));
237
dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
238
dst_regs(Is, [D|Acc]);
239
dst_regs([_|Is], Acc) ->
241
dst_regs([], Acc) -> ordsets:from_list(Acc).
243
all_killed([R|Rs], OldIs, St) ->
244
case is_killed(R, OldIs, St) of
246
true -> all_killed(Rs, OldIs, St)
248
all_killed([], _, _) -> true.
250
none_used([R|Rs], OldIs, St) ->
251
case is_not_used(R, OldIs, St) of
253
true -> none_used(Rs, OldIs, St)
255
none_used([], _, _) -> true.
258
Block = ssa_block(Block0),
259
Reg = free_variables(Block),
260
%%io:format("~p\n", [Block]),
261
%%io:format("~p\n", [Reg]),
262
Res = bopt_tree_1(Block, Reg, []),
263
%%io:format("~p\n", [Res]),
266
bopt_tree_1([{set,[Dst],As0,{bif,'not',_}}|Is], Forest0, Pre) ->
267
{[Arg],Forest1} = bopt_bool_args(As0, Forest0),
268
Forest = gb_trees:enter(Dst, {'not',Arg}, Forest1),
269
bopt_tree_1(Is, Forest, Pre);
270
bopt_tree_1([{set,[Dst],As0,{bif,'and',_}}|Is], Forest0, Pre) ->
271
{As,Forest1} = bopt_bool_args(As0, Forest0),
272
AndList = make_and_list(As),
273
Forest = gb_trees:enter(Dst, {'and',AndList}, Forest1),
274
bopt_tree_1(Is, Forest, Pre);
275
bopt_tree_1([{set,[Dst],[L0,R0],{bif,'or',_}}|Is], Forest0, Pre) ->
276
L = gb_trees:get(L0, Forest0),
277
R = gb_trees:get(R0, Forest0),
278
Forest1 = gb_trees:delete(L0, gb_trees:delete(R0, Forest0)),
279
OrList = make_or_list([L,R]),
280
Forest = gb_trees:enter(Dst, {'or',OrList}, Forest1),
281
bopt_tree_1(Is, Forest, Pre);
282
bopt_tree_1([{protected,[Dst],_,_}=Prot|Is], Forest0, Pre) ->
283
Forest = gb_trees:enter(Dst, Prot, Forest0),
284
bopt_tree_1(Is, Forest, Pre);
285
bopt_tree_1([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
287
case safe_bool_op(N, Ar) of
289
bopt_good_args(As, Forest0),
290
Forest = gb_trees:enter(Dst, any, Forest0),
291
bopt_tree_1(Is, Forest, [Bif|Pre]);
293
bopt_good_args(As, Forest0),
294
Test = bif_to_test(Dst, N, As),
295
Forest = gb_trees:enter(Dst, Test, Forest0),
296
bopt_tree_1(Is, Forest, Pre)
298
bopt_tree_1([], Forest, Pre) ->
299
{Pre,[R || {_,V}=R <- gb_trees:to_list(Forest), V =/= any]}.
301
safe_bool_op(internal_is_record, 3) -> true;
302
safe_bool_op(N, Ar) ->
303
erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar).
305
bopt_bool_args(As, Forest) ->
306
mapfoldl(fun bopt_bool_arg/2, Forest, As).
308
bopt_bool_arg({T,_}=R, Forest) when T == x; T == y ->
309
{gb_trees:get(R, Forest),gb_trees:delete(R, Forest)};
310
bopt_bool_arg(Term, Forest) ->
313
bopt_good_args([A|As], Regs) ->
314
bopt_good_arg(A, Regs),
315
bopt_good_args(As, Regs);
316
bopt_good_args([], _) -> ok.
318
bopt_good_arg({x,_}=X, Regs) ->
319
case gb_trees:get(X, Regs) of
322
%%io:format("not any: ~p: ~p\n", [X,_Other]),
325
bopt_good_arg(_, _) -> ok.
327
bif_to_test(_, N, As) ->
330
bif_to_test(internal_is_record, [_,_,_]=As) ->
331
{test,internal_is_record,fail,As};
332
bif_to_test('=:=', As) -> {test,is_eq_exact,fail,As};
333
bif_to_test('=/=', As) -> {test,is_ne_exact,fail,As};
334
bif_to_test('==', As) -> {test,is_eq,fail,As};
335
bif_to_test('/=', As) -> {test,is_ne,fail,As};
336
bif_to_test('=<', [L,R]) -> {test,is_ge,fail,[R,L]};
337
bif_to_test('>=', As) -> {test,is_ge,fail,As};
338
bif_to_test('>', [L,R]) -> {test,is_lt,fail,[R,L]};
339
bif_to_test('<', As) -> {test,is_lt,fail,As};
340
bif_to_test(Name, [_]=As) ->
341
case erl_internal:new_type_test(Name, 1) of
342
false -> exit({bif_to_test,Name,As,failed});
343
true -> {test,Name,fail,As}
346
make_and_list([{'and',As}|Is]) ->
347
make_and_list(As++Is);
348
make_and_list([I|Is]) ->
349
[I|make_and_list(Is)];
350
make_and_list([]) -> [].
352
make_or_list([{'or',As}|Is]) ->
353
make_or_list(As++Is);
354
make_or_list([I|Is]) ->
355
[I|make_or_list(Is)];
356
make_or_list([]) -> [].
358
%% Code generation for a boolean tree.
360
bopt_cg({'not',Arg}, Fail, Acc, St) ->
361
I = bopt_cg_not(Arg),
362
bopt_cg(I, Fail, Acc, St);
363
bopt_cg({'and',As}, Fail, Acc, St) ->
364
bopt_cg_and(As, Fail, Acc, St);
365
bopt_cg({'or',As}, Fail, Acc, St0) ->
366
{Succ,St} = new_label(St0),
367
bopt_cg_or(As, Succ, Fail, Acc, St);
368
bopt_cg({test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
369
{[{test,is_eq_exact,{f,Fail},[Tmp,RecordTag]},
370
{get_tuple_element,Tuple,0,Tmp}|Acc],St};
371
bopt_cg({inverted_test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
372
{[{test,is_ne_exact,{f,Fail},[Tmp,RecordTag]},
373
{get_tuple_element,Tuple,0,Tmp}|Acc],St};
374
bopt_cg({test,N,fail,As}, Fail, Acc, St) ->
375
Test = {test,N,{f,Fail},As},
377
bopt_cg({inverted_test,N,fail,As}, Fail, Acc, St0) ->
378
{Lbl,St} = new_label(St0),
379
{[{label,Lbl},{jump,{f,Fail}},{test,N,{f,Lbl},As}|Acc],St};
380
bopt_cg({protected,_,Bl0,{_,_,_}}, Fail, Acc, St0) ->
381
{Bl,St} = bopt_block_1(Bl0, Fail, St0),
383
bopt_cg([_|_]=And, Fail, Acc, St) ->
384
bopt_cg_and(And, Fail, Acc, St).
386
bopt_cg_not({'and',As0}) ->
387
As = [bopt_cg_not(A) || A <- As0],
389
bopt_cg_not({'or',As0}) ->
390
As = [bopt_cg_not(A) || A <- As0],
392
bopt_cg_not({test,Test,Fail,As}) ->
393
{inverted_test,Test,Fail,As}.
395
bopt_cg_and([{atom,false}|_], Fail, _, St) ->
396
{[{jump,{f,Fail}}],St};
397
bopt_cg_and([{atom,true}|Is], Fail, Acc, St) ->
398
bopt_cg_and(Is, Fail, Acc, St);
399
bopt_cg_and([I|Is], Fail, Acc0, St0) ->
400
{Acc,St} = bopt_cg(I, Fail, Acc0, St0),
401
bopt_cg_and(Is, Fail, Acc, St);
402
bopt_cg_and([], _, Acc, St) -> {Acc,St}.
404
bopt_cg_or([I], Succ, Fail, Acc0, St0) ->
405
{Acc,St} = bopt_cg(I, Fail, Acc0, St0),
406
{[{label,Succ}|Acc],St};
407
bopt_cg_or([I|Is], Succ, Fail, Acc0, St0) ->
408
{Lbl,St1} = new_label(St0),
409
{Acc,St} = bopt_cg(I, Lbl, Acc0, St1),
410
bopt_cg_or(Is, Succ, Fail, [{label,Lbl},{jump,{f,Succ}}|Acc], St).
412
new_label(#st{next=LabelNum}=St) when is_integer(LabelNum) ->
413
{LabelNum,St#st{next=LabelNum+1}}.
415
free_variables(Is) ->
417
free_vars_1(Is, E, E).
419
free_vars_1([{set,[Dst],As,{bif,_,_}}|Is], F0, N0) ->
420
F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
421
N = gb_sets:union(N0, var_list([Dst])),
422
free_vars_1(Is, F, N);
423
free_vars_1([{protected,_,Pa,_}|Is], F, N) ->
424
free_vars_1(Pa++Is, F, N);
425
free_vars_1([], F, _) ->
426
gb_trees:from_orddict([{K,any} || K <- gb_sets:to_list(F)]).
429
var_list_1(Is, gb_sets:empty()).
431
var_list_1([{x,_}=X|Is], D) ->
432
var_list_1(Is, gb_sets:add(X, D));
433
var_list_1([_|Is], D) ->
435
var_list_1([], D) -> D.
438
%%% Convert a block to Static Single Assignment (SSA) form.
446
Next = ssa_first_free(Is0, 0),
447
{Is,_} = ssa_block_1(Is0, #ssa{live=Next,sub=gb_trees:empty()}, []),
450
ssa_block_1([{protected,[_],Pa0,Pb}|Is], Sub0, Acc) ->
451
{Pa,Sub} = ssa_block_1(Pa0, Sub0, []),
452
Dst = ssa_last_target(Pa),
453
ssa_block_1(Is, Sub, [{protected,[Dst],Pa,Pb}|Acc]);
454
ssa_block_1([{set,[Dst],As,Bif}|Is], Sub0, Acc0) ->
455
Sub1 = ssa_in_use_list(As, Sub0),
456
Sub = ssa_assign(Dst, Sub1),
457
Acc = [{set,[ssa_sub(Dst, Sub)],ssa_sub_list(As, Sub0),Bif}|Acc0],
458
ssa_block_1(Is, Sub, Acc);
459
ssa_block_1([], Sub, Acc) -> {reverse(Acc),Sub}.
461
ssa_in_use_list(As, Sub) ->
462
foldl(fun ssa_in_use/2, Sub, As).
464
ssa_in_use({x,_}=R, #ssa{sub=Sub0}=Ssa) ->
465
case gb_trees:is_defined(R, Sub0) of
468
Sub = gb_trees:insert(R, R, Sub0),
471
ssa_in_use(_, Ssa) -> Ssa.
473
ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
474
case gb_trees:is_defined(R, Sub0) of
476
Sub = gb_trees:insert(R, R, Sub0),
479
{NewReg,Ssa} = ssa_new_reg(Ssa0),
480
Sub1 = gb_trees:update(R, NewReg, Sub0),
481
Sub = gb_trees:insert(NewReg, NewReg, Sub1),
484
ssa_assign(_, Ssa) -> Ssa.
486
ssa_sub_list(List, Sub) ->
487
[ssa_sub(E, Sub) || E <- List].
489
ssa_sub(R0, #ssa{sub=Sub}) ->
490
case gb_trees:lookup(R0, Sub) of
495
ssa_new_reg(#ssa{live=Reg}=Ssa) ->
496
{{x,Reg},Ssa#ssa{live=Reg+1}}.
498
ssa_first_free([{protected,Ds,_,_}|Is], Next0) ->
499
Next = ssa_first_free_list(Ds, Next0),
500
ssa_first_free(Is, Next);
501
ssa_first_free([{set,[Dst],As,_}|Is], Next0) ->
502
Next = ssa_first_free_list([Dst|As], Next0),
503
ssa_first_free(Is, Next);
504
ssa_first_free([], Next) -> Next.
506
ssa_first_free_list(Regs, Next) ->
507
foldl(fun({x,R}, N) when R >= N -> R+1;
508
(_, N) -> N end, Next, Regs).
510
ssa_last_target([{set,[Dst],_,_},{'%live',_}]) -> Dst;
511
ssa_last_target([{set,[Dst],_,_}]) -> Dst;
512
ssa_last_target([_|Is]) -> ssa_last_target(Is).
514
%% index_instructions(FunctionIs) -> GbTree([{Label,Is}])
515
%% Index the instruction sequence so that we can quickly
516
%% look up the instruction following a specific label.
518
index_instructions(Is) ->
521
ii_1([{label,Lbl}|Is0], Acc) ->
522
Is = lists:dropwhile(fun({label,_}) -> true;
523
(_) -> false end, Is0),
524
ii_1(Is0, [{Lbl,Is}|Acc]);
527
ii_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
529
%% is_killed(Register, [Instruction], State) -> true|false
530
%% Determine whether a register is killed in the instruction sequence.
531
%% The state is used to allow us to determine the kill state
534
is_killed(R, Is, St) ->
535
case is_killed_1(R, Is, St) of
537
%%io:format("nk ~p: ~P\n", [R,Is,15]),
542
is_killed_1(R, [{block,Blk}|Is], St) ->
543
case is_killed_1(R, Blk, St) of
545
false -> is_killed_1(R, Is, St)
547
is_killed_1(R, [{test,_,{f,Fail},As}|Is], St) ->
548
case not member(R, As) andalso is_reg_killed_at(R, Fail, St) of
550
true -> is_killed_1(R, Is, St)
552
is_killed_1(R, [{select_val,R,_,_}|_], _) -> false;
553
is_killed_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
554
is_killed_at_all(R, [Fail|Branches], St);
555
is_killed_1(R, [{jump,{f,F}}|_], St) ->
556
is_reg_killed_at(R, F, St);
557
is_killed_1(Reg, Is, _) ->
558
beam_block:is_killed(Reg, Is).
560
is_reg_killed_at(R, Lbl, #st{ll=Ll}=St) ->
561
Is = gb_trees:get(Lbl, Ll),
562
is_killed_1(R, Is, St).
564
is_killed_at_all(R, [{f,Lbl}|T], St) ->
565
case is_reg_killed_at(R, Lbl, St) of
567
true -> is_killed_at_all(R, T, St)
569
is_killed_at_all(R, [_|T], St) ->
570
is_killed_at_all(R, T, St);
571
is_killed_at_all(_, [], _) -> true.
573
%% is_not_used(Register, [Instruction], State) -> true|false
574
%% Determine whether a register is never used in the instruction sequence
575
%% (it could still referenced by an allocate instruction, meaning that
576
%% it MUST be initialized).
577
%% The state is used to allow us to determine the usage state
580
is_not_used(R, Is, St) ->
581
case is_not_used_1(R, Is, St) of
583
%%io:format("used ~p: ~P\n", [R,Is,15]),
588
is_not_used_1(R, [{block,Blk}|Is], St) ->
589
case is_not_used_1(R, Blk, St) of
591
false -> is_not_used_1(R, Is, St)
593
is_not_used_1(R, [{test,_,{f,Fail},As}|Is], St) ->
594
case not member(R, As) andalso is_reg_not_used_at(R, Fail, St) of
596
true -> is_not_used_1(R, Is, St)
598
is_not_used_1(R, [{select_val,R,_,_}|_], _) -> false;
599
is_not_used_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
600
is_used_at_none(R, [Fail|Branches], St);
601
is_not_used_1(R, [{jump,{f,F}}|_], St) ->
602
is_reg_not_used_at(R, F, St);
603
is_not_used_1(Reg, Is, _) ->
604
beam_block:is_not_used(Reg, Is).
606
is_reg_not_used_at(R, Lbl, #st{ll=Ll}=St) ->
607
Is = gb_trees:get(Lbl, Ll),
608
is_not_used_1(R, Is, St).
610
is_used_at_none(R, [{f,Lbl}|T], St) ->
611
case is_reg_not_used_at(R, Lbl, St) of
613
true -> is_used_at_none(R, T, St)
615
is_used_at_none(R, [_|T], St) ->
616
is_used_at_none(R, T, St);
617
is_used_at_none(_, [], _) -> true.