diff options
author | Björn Gustavsson <[email protected]> | 2017-12-20 06:54:14 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2018-01-11 11:53:44 +0100 |
commit | d26326c38dd5e79b78703d26667c1b79e3fb6e9a (patch) | |
tree | a806bfa38849cfde42956de3ff24d6c7e48802ee | |
parent | ad72a944c03095c3505cb151c9a93d243fb698b6 (diff) | |
download | otp-d26326c38dd5e79b78703d26667c1b79e3fb6e9a.tar.gz otp-d26326c38dd5e79b78703d26667c1b79e3fb6e9a.tar.bz2 otp-d26326c38dd5e79b78703d26667c1b79e3fb6e9a.zip |
Refactor '%live' and '%def' annotations
The annotations in the optimizing passes currently looks like this:
{'%live',NumRegistersUsed,RegistersUsedBitmap}
{'%def',RegistersDefinedBitmap}
(NumRegistersUsed is no longer used.)
When I attempted to extend some optimizations, I found that I had to
add additional clauses to tolerate/handle both types of
annotations. That problem would only get worse if any more annotations
are added in the future.
To simplify annotation handling, this commit wraps both types of
annotations in a {'%anno',_} tuple:
{'%anno',{used,RegistersUsedBitmap}}
{'%anno',{def,RegistersDefinedBitmap}}
The '%live' annotation has been renamed to 'used' to make it somewhat
clearer what it means, and the unused NumRegistersUsed part of the
old annotation has been removed.
Alternatives considered: My first attempt was to wrap the annotation
in a 'set' tuple so that there would only be 'set' tuples in a block.
For example:
{set,[],[],{anno,{live,RegistersUsedBitmap}}}
It was not as convenient as expected. Annotations often need to be
handled specially from other instructions in a block. When they are
wrapped in a 'set' tuple, they can very easily be handled incorrectly
or passed on to the next pass. That causes subtle errors or worse
code, and it can be difficult to debug.
Therefore, my conclusion is that annotations should be distinct from
other instructions, to make it obvious when one have missed to handle
an annotation.
-rw-r--r-- | lib/compiler/src/beam_a.erl | 3 | ||||
-rw-r--r-- | lib/compiler/src/beam_block.erl | 10 | ||||
-rw-r--r-- | lib/compiler/src/beam_record.erl | 2 | ||||
-rw-r--r-- | lib/compiler/src/beam_type.erl | 13 | ||||
-rw-r--r-- | lib/compiler/src/beam_utils.erl | 54 |
5 files changed, 47 insertions, 35 deletions
diff --git a/lib/compiler/src/beam_a.erl b/lib/compiler/src/beam_a.erl index 6f09dc4be4..7df2edd714 100644 --- a/lib/compiler/src/beam_a.erl +++ b/lib/compiler/src/beam_a.erl @@ -58,7 +58,8 @@ rename_instrs([{call_only,A,F}|Is]) -> rename_instrs([{call_ext_only,A,F}|Is]) -> [{call_ext,A,F},return|rename_instrs(Is)]; rename_instrs([{'%live',_}|Is]) -> - %% When compiling from old .S files. + %% Ignore old type of live annotation. Only happens when compiling + %% from very old .S files. rename_instrs(Is); rename_instrs([I|Is]) -> [rename_instr(I)|rename_instrs(Is)]; diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 570fc05ae5..c8fea2069a 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -41,7 +41,7 @@ function({function,Name,Arity,CLabel,Is0}) -> Is4 = move_allocates(Is3), Is5 = beam_utils:live_opt(Is4), Is6 = opt_blocks(Is5), - Is7 = beam_utils:delete_live_annos(Is6), + Is7 = beam_utils:delete_annos(Is6), Is = opt_allocs(Is7), %% Done. @@ -137,7 +137,7 @@ embed_lines([], Acc) -> Acc. opt_blocks([{block,Bl0}|Is]) -> %% The live annotation at the beginning is not useful. - [{'%live',_,_}|Bl] = Bl0, + [{'%anno',_}|Bl] = Bl0, [{block,opt_block(Bl)}|opt_blocks(Is)]; opt_blocks([I|Is]) -> [I|opt_blocks(Is)]; @@ -196,7 +196,7 @@ move_allocates([I|Is]) -> [I|move_allocates(Is)]; move_allocates([]) -> []. -move_allocates_1([{'%def',_}|Is], Acc) -> +move_allocates_1([{'%anno',_}|Is], Acc) -> move_allocates_1(Is, Acc); move_allocates_1([I|Is], [{set,[],[],{alloc,Live0,Info}}|Acc]=Acc0) -> case alloc_may_pass(I) of @@ -243,7 +243,7 @@ opt([{set,_,_,{line,_}}=Line1, opt([{set,Ds0,Ss,Op}|Is0]) -> {Ds,Is} = opt_moves(Ds0, Is0), [{set,Ds,Ss,Op}|opt(Is)]; -opt([{'%live',_,_}=I|Is]) -> +opt([{'%anno',_}=I|Is]) -> [I|opt(Is)]; opt([]) -> []. @@ -525,7 +525,7 @@ x_live([], Regs) -> Regs. %% Given a reversed instruction stream, determine the %% the registers that are defined. -defined_regs([{'%def',Def}|_], Regs) -> +defined_regs([{'%anno',{def,Def}}|_], Regs) -> Def bor Regs; defined_regs([{set,Ds,_,{alloc,Live,_}}|_], Regs) -> x_live(Ds, Regs bor ((1 bsl Live) - 1)); diff --git a/lib/compiler/src/beam_record.erl b/lib/compiler/src/beam_record.erl index db1053e48c..58a6de6775 100644 --- a/lib/compiler/src/beam_record.erl +++ b/lib/compiler/src/beam_record.erl @@ -71,7 +71,7 @@ rewrite([{test,test_arity,Fail,[Src,N]}=TA, I = {test,is_tagged_tuple,Fail,[Src,N,Atom]}, rewrite(Is, Idx, Def, [I|Acc]) end; -rewrite([{block,[{'%def',Def}|Bl]}|Is], Idx, _Def, Acc) -> +rewrite([{block,[{'%anno',{def,Def}}|Bl]}|Is], Idx, _Def, Acc) -> rewrite(Is, Idx, Def, [{block,Bl}|Acc]); rewrite([{label,L}=I|Is], Idx0, Def, Acc) -> Idx = beam_utils:index_label(L, Acc, Idx0), diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 14fe22fd27..3b6bf49961 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -40,7 +40,7 @@ function({function,Name,Arity,CLabel,Asm0}) -> Asm1 = beam_utils:live_opt(Asm0), Asm2 = opt(Asm1, [], tdb_new()), Asm3 = beam_utils:live_opt(Asm2), - Asm = beam_utils:delete_live_annos(Asm3), + Asm = beam_utils:delete_annos(Asm3), {function,Name,Arity,CLabel,Asm} catch Class:Error:Stack -> @@ -300,7 +300,7 @@ clearerror([], OrigIs) -> [{set,[],[],fclearerror}|OrigIs]. %% Combine two blocks and eliminate any move instructions that assign %% to registers that are killed later in the block. %% -merge_blocks(B1, [{'%live',_,_}|B2]) -> +merge_blocks(B1, [{'%anno',_}|B2]) -> merge_blocks_1(B1++[{set,[],[],stop_here}|B2]). merge_blocks_1([{set,[],_,stop_here}|Is]) -> Is; @@ -355,7 +355,7 @@ flt_need_heap_2({set,_,_,{alloc,_,_}}, H, Fl) -> {flt_alloc(H, Fl),0,0}; flt_need_heap_2({set,_,_,{set_tuple_element,_}}, H, Fl) -> {flt_alloc(H, Fl),0,0}; -flt_need_heap_2({'%live',_,_}, H, Fl) -> +flt_need_heap_2({'%anno',_}, H, Fl) -> {flt_alloc(H, Fl),0,0}; %% All other instructions are "neutral". We just pass them. flt_need_heap_2(_, H, Fl) -> @@ -382,7 +382,7 @@ build_alloc(Words, Floats) -> {alloc,[{words,Words},{floats,Floats}]}. %% is not continous at an allocation function (e.g. if {x,0} and {x,2} %% are live, but not {x,1}). -flt_liveness([{'%live',_Live,Regs}=LiveInstr|Is]) -> +flt_liveness([{'%anno',{used,Regs}}=LiveInstr|Is]) -> flt_liveness_1(Is, Regs, [LiveInstr]). flt_liveness_1([{set,Ds,Ss,{alloc,Live0,Alloc}}|Is], Regs0, Acc) -> @@ -394,7 +394,7 @@ flt_liveness_1([{set,Ds,Ss,{alloc,Live0,Alloc}}|Is], Regs0, Acc) -> flt_liveness_1([{set,Ds,_,_}=I|Is], Regs0, Acc) -> Regs = x_live(Ds, Regs0), flt_liveness_1(Is, Regs, [I|Acc]); -flt_liveness_1([{'%live',_,_}], _Regs, Acc) -> +flt_liveness_1([{'%anno',_}], _Regs, Acc) -> reverse(Acc). init_regs(Live) -> @@ -418,7 +418,8 @@ x_live([], Regs) -> Regs. %% Update the type database to account for executing an instruction. %% %% First the cases for instructions inside basic blocks. -update({'%live',_,_}, Ts) -> Ts; +update({'%anno',_}, Ts) -> + Ts; update({set,[D],[S],move}, Ts) -> tdb_copy(S, D, Ts); update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) -> diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index a2795e625f..b9ce3befaf 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -25,7 +25,7 @@ is_not_used/3,usage/3, empty_label_index/0,index_label/3,index_labels/1,replace_labels/4, code_at/2,bif_to_test/3,is_pure_test/1, - live_opt/1,delete_live_annos/1,combine_heap_needs/2, + live_opt/1,delete_annos/1,combine_heap_needs/2, anno_defs/1, split_even/1 ]). @@ -95,7 +95,7 @@ is_killed_block({x,X}, [{set,_,_,{alloc,Live,_}}|_]) -> X >= Live; is_killed_block(R, [{set,Ds,Ss,_Op}|Is]) -> not member(R, Ss) andalso (member(R, Ds) orelse is_killed_block(R, Is)); -is_killed_block(R, [{'%live',_,Regs}|Is]) -> +is_killed_block(R, [{'%anno',{used,Regs}}|Is]) -> case R of {x,X} when (Regs bsr X) band 1 =:= 0 -> true; _ -> is_killed_block(R, Is) @@ -270,7 +270,7 @@ is_pure_test({test,Op,_,Ops}) -> %% Go through the instruction sequence in reverse execution %% order, keep track of liveness and remove 'move' instructions %% whose destination is a register that will not be used. -%% Also insert {'%live',Live,Regs} annotations at the beginning +%% Also insert {used,Regs} annotations at the beginning %% and end of each block. -spec live_opt([instruction()]) -> [instruction()]. @@ -285,22 +285,22 @@ live_opt(Is0) -> Bef ++ [Fi|live_opt(reverse(Is), 0, D, [])]. -%% delete_live_annos([Instruction]) -> [Instruction]. -%% Delete all live annotations. +%% delete_annos([Instruction]) -> [Instruction]. +%% Delete all annotations. --spec delete_live_annos([instruction()]) -> [instruction()]. +-spec delete_annos([instruction()]) -> [instruction()]. -delete_live_annos([{block,Bl0}|Is]) -> - case delete_live_annos(Bl0) of - [] -> delete_live_annos(Is); - [_|_]=Bl -> [{block,Bl}|delete_live_annos(Is)] +delete_annos([{block,Bl0}|Is]) -> + case delete_annos(Bl0) of + [] -> delete_annos(Is); + [_|_]=Bl -> [{block,Bl}|delete_annos(Is)] end; -delete_live_annos([{'%live',_,_}|Is]) -> - delete_live_annos(Is); -delete_live_annos([I|Is]) -> - [I|delete_live_annos(Is)]; -delete_live_annos([]) -> []. - +delete_annos([{'%anno',_}|Is]) -> + delete_annos(Is); +delete_annos([I|Is]) -> + [I|delete_annos(Is)]; +delete_annos([]) -> []. + %% combine_heap_needs(HeapNeed1, HeapNeed2) -> HeapNeed %% Combine the heap need for two allocation instructions. @@ -316,7 +316,7 @@ combine_heap_needs(H1, H2) -> %% anno_defs(Instructions) -> Instructions' -%% Add {'%def',RegisterBitmap} annotations to the beginning of +%% Add {def,RegisterBitmap} annotations to the beginning of %% each block. Iff bit X is set in the the bitmap, it means %% that {x,X} is defined when the block is entered. @@ -658,7 +658,7 @@ check_liveness_ret(_, _, St) -> {killed,St}. %% alloc_used - Used only in an allocate instruction %% used - Reg is explicitly used by an instruction %% -%% '%live' annotations are not allowed. +%% Annotations are not allowed. %% %% (Unknown instructions will cause an exception.) @@ -855,9 +855,9 @@ live_opt([{test,bs_start_match2,Fail,Live,[Src,_],_}=I|Is], _, D, Acc) -> %% Other instructions. live_opt([{block,Bl0}|Is], Regs0, D, Acc) -> - Live0 = {'%live',live_regs(Regs0),Regs0}, + Live0 = make_anno({used,Regs0}), {Bl,Regs} = live_opt_block(reverse(Bl0), Regs0, D, [Live0]), - Live = {'%live',live_regs(Regs),Regs}, + Live = make_anno({used,Regs}), live_opt(Is, Regs, D, [{block,[Live|Bl]}|Acc]); live_opt([build_stacktrace=I|Is], _, D, Acc) -> live_opt(Is, live_call(1), D, [I|Acc]); @@ -971,7 +971,7 @@ live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) -> _ -> live_opt_block(Is, Regs, D, [I|Acc]) end; -live_opt_block([{'%live',_,_}|Is], Regs, D, Acc) -> +live_opt_block([{'%anno',_}|Is], Regs, D, Acc) -> live_opt_block(Is, Regs, D, Acc); live_opt_block([], Regs, _, Acc) -> {Acc,Regs}. @@ -1046,7 +1046,7 @@ defs([{bif,_,{f,Fail},_Src,Dst}=I|Is], Regs0, D) -> [I|defs(Is, Regs, update_regs(Fail, Regs0, D))]; defs([{block,Block0}|Is], Regs0, D0) -> {Block,Regs,D} = defs_list(Block0, Regs0, D0), - [{block,[{'%def',Regs0}|Block]}|defs(Is, Regs, D)]; + [{block,[make_anno({def,Regs0})|Block]}|defs(Is, Regs, D)]; defs([{bs_init,{f,L},_,_,_,Dst}=I|Is], Regs0, D) -> Regs = def_regs([Dst], Regs0), [I|defs(Is, Regs, update_regs(L, Regs, D))]; @@ -1236,3 +1236,13 @@ update_regs(L, Regs0, D) -> all_defined(Live, Regs) -> All = (1 bsl Live) - 1, Regs band All =:= All. + +%%% +%%% Utilities. +%%% + +%% make_anno(Anno) -> WrappedAnno. +%% Wrap an annotation term. + +make_anno(Anno) -> + {'%anno',Anno}. |