%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2019-2021. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% Purpose: Prepare Core Erlang not generated by v3_core. -module(sys_core_prepare). -export([module/2]). -include("core_parse.hrl"). -spec module(cerl:c_module(), [compile:option()]) -> {'ok',cerl:c_module(),[]}. module(Mod0, _Opts) -> Count = cerl_trees:next_free_variable_name(Mod0), {Mod,_} = cerl_trees:mapfold(fun rewrite_recv/2, Count, Mod0), {ok,Mod,[]}. rewrite_recv(#c_receive{clauses=[],timeout=Timeout0,action=Action}, Count0) -> %% Lower a receive with only an after block to its primitive operations. False = #c_literal{val=false}, True = #c_literal{val=true}, {TimeoutVal,Count1} = new_var(Count0), {LoopName,Count2} = new_func_varname(Count1), LoopFun = #c_var{name={LoopName,0}}, ApplyLoop = #c_apply{op=LoopFun,args=[]}, AfterCs = [#c_clause{pats=[True],guard=True,body=Action}, #c_clause{pats=[False],guard=True, body=ApplyLoop}], {TimeoutBool,Count4} = new_var(Count2), TimeoutCase = #c_case{arg=TimeoutBool,clauses=AfterCs}, TimeoutLet = #c_let{vars=[TimeoutBool], arg=primop(recv_wait_timeout, [TimeoutVal]), body=TimeoutCase}, Fun = #c_fun{vars=[],body=TimeoutLet}, Letrec = #c_letrec{anno=[letrec_goto], defs=[{LoopFun,Fun}], body=ApplyLoop}, OuterLet = #c_let{vars=[TimeoutVal],arg=Timeout0,body=Letrec}, {OuterLet,Count4}; rewrite_recv(#c_receive{clauses=Cs0,timeout=Timeout0,action=Action}, Count0) -> %% Lower receive to its primitive operations. False = #c_literal{val=false}, True = #c_literal{val=true}, {TimeoutVal,Count1} = new_var(Count0), {LoopName,Count2} = new_func_varname(Count1), LoopFun = #c_var{name={LoopName,0}}, ApplyLoop = #c_apply{op=LoopFun,args=[]}, Cs1 = rewrite_cs(Cs0), RecvNext = #c_seq{arg=primop(recv_next), body=ApplyLoop}, RecvNextC = #c_clause{anno=[compiler_generated], pats=[#c_var{name='Other'}],guard=True,body=RecvNext}, Cs = Cs1 ++ [RecvNextC], {Msg,Count3} = new_var(Count2), MsgCase = #c_case{arg=Msg,clauses=Cs}, AfterCs = [#c_clause{pats=[True],guard=True,body=Action}, #c_clause{pats=[False],guard=True, body=ApplyLoop}], {TimeoutBool,Count4} = new_var(Count3), TimeoutCase = #c_case{arg=TimeoutBool,clauses=AfterCs}, TimeoutLet = #c_let{vars=[TimeoutBool], arg=primop(recv_wait_timeout, [TimeoutVal]), body=TimeoutCase}, {PeekSucceeded,Count5} = new_var(Count4), PeekCs = [#c_clause{pats=[True],guard=True, body=MsgCase}, #c_clause{pats=[False],guard=True, body=TimeoutLet}], PeekCase = #c_case{arg=PeekSucceeded,clauses=PeekCs}, PeekLet = #c_let{vars=[PeekSucceeded,Msg], arg=primop(recv_peek_message), body=PeekCase}, Fun = #c_fun{vars=[],body=PeekLet}, Letrec = #c_letrec{anno=[letrec_goto], defs=[{LoopFun,Fun}], body=ApplyLoop}, OuterLet = #c_let{vars=[TimeoutVal],arg=Timeout0,body=Letrec}, {OuterLet,Count5}; rewrite_recv(Tree, Count) -> {Tree,Count}. rewrite_cs([#c_clause{body=B0}=C|Cs]) -> B = #c_seq{arg=primop(remove_message),body=B0}, [C#c_clause{body=B}|rewrite_cs(Cs)]; rewrite_cs([]) -> []. primop(Name) -> primop(Name, []). primop(Name, Args) -> #c_primop{name=#c_literal{val=Name},args=Args}. new_var(Count) -> {#c_var{name=Count},Count+1}. new_func_varname(Count) -> Name = list_to_atom("@pre" ++ integer_to_list(Count)), {Name,Count+1}.