Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:24
erlang
0101-Do-strength-reduction-of-is_tagged_tuple-a...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0101-Do-strength-reduction-of-is_tagged_tuple-and-is_list.patch of Package erlang
From d3beb34700543298b12e5420a63c618356fab1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Mon, 28 Jun 2021 21:44:53 +0200 Subject: [PATCH] Do strength reduction of is_tagged_tuple and is_list There is an instruction called `is_tagged_tuple` that is used for matching a tuple whose first element is a literal atom. Here is an example where this instruction will be used: f(X) -> case some_function(X) of {ok,Result} -> Result; Error -> Error end. The following BEAM code will be generated: {allocate,0,1}. {call,1,{f,5}}. {test,is_tagged_tuple,{f,3},[{x,0},2,{atom,ok}]}. {get_tuple_element,{x,0},1,{x,0}}. {deallocate,0}. return. {label,3}. {deallocate,0}. return. Better code can be generated if the compiler's type analysis has figured out that the type of the return value of `some_function/1` is: {'ok', any()} | 'error' If that is the case, since the return value of `some_function/1` is either a tuple or the atom `error`, the `is_tagged_tuple` instruction can be replaced with the less expensive (slightly faster and slightly smaller) `is_tuple` instruction: {allocate,0,1}. {call,1,{f,5}}. {test,is_tuple,{f,3},[{x,0}]}. {get_tuple_element,{x,0},1,{x,0}}. {deallocate,0}. return. {label,3}. {deallocate,0}. return. If the compiler has figured out that the type is: {'ok', any()} | {'error', any(), any()} then `is_tagged_tuple` can be replaced with `test_arity`: {allocate,0,1}. {call,1,{f,5}}. % some_function/1 {test,test_arity,{f,3},[{x,0},2]}. {get_tuple_element,{x,0},1,{x,0}}. {deallocate,0}. return. {label,3}. {deallocate,0}. return. Unfortunately, if the type is: {'ok', any()} | {'error', any()} which is not uncommon, there does not exist any alternative sequence of instructions that would be guaranteed to be less expensive, so we will keep `is_tagged_tuple`. Also replace the `is_list/1` guard BIF with a less expensive instruction when we have knowledge of the argument. --- lib/compiler/src/beam_ssa_codegen.erl | 16 ++++++-- lib/compiler/src/beam_ssa_type.erl | 55 ++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index e38a2265d5..f80d7b5bb1 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1209,9 +1209,19 @@ cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=I, cg_block([#cg_set{op=bs_test_tail,dst=Bool,args=Args0}], {Bool,Fail}, St) -> [Ctx,{integer,Bits}] = beam_args(Args0, St), {[{test,bs_test_tail2,bif_fail(Fail),[Ctx,Bits]}],St}; -cg_block([#cg_set{op=is_tagged_tuple,dst=Bool,args=Args0}], {Bool,Fail}, St) -> - [Src,{integer,Arity},Tag] = beam_args(Args0, St), - {[{test,is_tagged_tuple,ensure_label(Fail, St),[Src,Arity,Tag]}],St}; +cg_block([#cg_set{op=is_tagged_tuple,anno=Anno,dst=Bool,args=Args0}], {Bool,Fail}, St) -> + case Anno of + #{constraints := arity} -> + [Src,{integer,Arity},_Tag] = beam_args(Args0, St), + {[{test,test_arity,ensure_label(Fail, St),[Src,Arity]}],St}; + #{constraints := tuple_arity} -> + [Src,{integer,Arity},_Tag] = beam_args(Args0, St), + {[{test,is_tuple,ensure_label(Fail, St),[Src]}, + {test,test_arity,ensure_label(Fail, St),[Src,Arity]}],St}; + #{} -> + [Src,{integer,Arity},Tag] = beam_args(Args0, St), + {[{test,is_tagged_tuple,ensure_label(Fail, St),[Src,Arity,Tag]}],St} + end; cg_block([#cg_set{op=is_nonempty_list,dst=Bool,args=Args0}], {Bool,Fail}, St) -> Args = beam_args(Args0, St), {[{test,is_nonempty_list,ensure_label(Fail, St),Args}],St}; diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 2fa8f0dcf5..8ab7473139 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -886,6 +886,15 @@ simplify(#b_set{op={bif,'=:='},args=[LHS,RHS]}=I, Ts) -> eval_bif(I, Ts) end end; +simplify(#b_set{op={bif,is_list},args=[Src]}=I, Ts) -> + case raw_type(Src, Ts) of + #t_union{list=#t_cons{}} -> + I#b_set{op=is_nonempty_list,args=[Src]}; + #t_union{list=nil} -> + I#b_set{op={bif,'=:='},args=[Src,#b_literal{val=[]}]}; + _ -> + eval_bif(I, Ts) + end; simplify(#b_set{op={bif,Op},args=Args}=I, Ts) -> Types = normalized_types(Args, Ts), case is_float_op(Op, Types) of @@ -945,7 +954,51 @@ simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) -> end; simplify(#b_set{op=is_tagged_tuple, args=[Src,#b_literal{val=Size},#b_literal{}=Tag]}=I, Ts) -> - simplify_is_record(I, normalized_type(Src, Ts), Size, Tag, Ts); + case raw_type(Src, Ts) of + #t_union{tuple_set=TupleSet}=U -> + %% A union of different types, one of them (probably) + %% a tuple. Dig out the tuple type from the union and + %% find out whether it will match. + TupleOnlyType = #t_union{tuple_set=TupleSet}, + TT = beam_types:normalize(TupleOnlyType), + case simplify_is_record(I, TT, Size, Tag, Ts) of + #b_literal{val=true} -> + %% The tuple part of the union will always match. + %% A simple is_tuple/1 test will be sufficient to + %% distinguish the tuple from the other types in + %% the union. + I#b_set{op={bif,is_tuple},args=[Src]}; + #b_literal{val=false}=False -> + %% Src is never a tuple. + False; + _ -> + %% More than one type of tuple can match. Find out + %% whether the possible tuples can be + %% distinguished by size. + TupleArityType = #t_tuple{size=Size,exact=true}, + TTT = beam_types:meet(TupleArityType, TupleOnlyType), + case simplify_is_record(I, TTT, Size, Tag, Ts) of + #b_literal{val=true} -> + %% The possible tuple types have different sizes. + %% Example: {ok, _} | {error, _, _}. + case beam_types:normalize(U) of + #t_tuple{} -> + %% Src is known to be a tuple, so it will + %% be sufficient to test the arity. + beam_ssa:add_anno(constraints, arity, I); + any -> + %% Src might not be a tuple. Must + %% test for a tuple with a given + %% arity. + beam_ssa:add_anno(constraints, tuple_arity, I) + end; + _ -> + I + end + end; + SimpleType -> + simplify_is_record(I, SimpleType, Size, Tag, Ts) + end; simplify(#b_set{op=put_list,args=[#b_literal{val=H}, #b_literal{val=T}]}, _Ts) -> #b_literal{val=[H|T]}; -- 2.31.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor