%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% ============================================================================
%% Filename : hipe_icode_ssa_const_prop.erl
%% Authors : Daniel Luna, Erik Andersson
%% Purpose : Perform sparse conditional constant propagation on Icode.
%% Notes : Works on the control-flow graph.
%%
%% History : * 2003-03-05: Created.
%% * 2003-08-11: Passed simple testsuite.
%% * 2003-10-01: Passed compiler testsuite.
%% ============================================================================
%%
%% Exports: propagate/1.
%%
%% ============================================================================
%%
%% TODO:
%%
%% Take care of failures in call and replace operation with appropriate
%% failure.
%%
%% Handle ifs with non-binary operators
%%
%% We want multisets for easier (and faster) creation of env->ssa_edges
%%
%% Maybe do things with begin_handler, begin_try if possible
%%
%% Propagation of constant arguments when some of the arguments are bottom
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(hipe_icode_ssa_const_prop).
-export([propagate/1]).
-include("../main/hipe.hrl").
-include("hipe_icode.hrl").
-include("../flow/cfg.hrl").
-include("hipe_icode_primops.hrl").
-define(CONST_PROP_MSG(Str,L), ok).
%%-define(CONST_PROP_MSG(Str,L), io:format(Str,L)).
%%-define(DEBUG, 1).
%%-----------------------------------------------------------------------------
%% Include stuff shared between SCCP on Icode and RTL.
%% NOTE: Needs to appear after DEBUG is possibly defined.
%%-----------------------------------------------------------------------------
-define(CODE, hipe_icode).
-define(CFG, hipe_icode_cfg).
-include("../ssa/hipe_ssa_const_prop.inc").
%%-----------------------------------------------------------------------------
visit_expression(Instruction, Environment) ->
EvaluatedArguments = [lookup_lattice_value(Argument, Environment)
|| Argument <- hipe_icode:args(Instruction)],
case Instruction of
#icode_move{} ->
visit_move (Instruction, EvaluatedArguments, Environment);
#icode_if{} ->
visit_if (Instruction, EvaluatedArguments, Environment);
#icode_goto{} ->
visit_goto (Instruction, EvaluatedArguments, Environment);
#icode_type{} ->
visit_type (Instruction, EvaluatedArguments, Environment);
#icode_call{} ->
visit_call (Instruction, EvaluatedArguments, Environment);
#icode_switch_val{} ->
visit_switch_val (Instruction, EvaluatedArguments, Environment);
#icode_switch_tuple_arity{} ->
visit_switch_tuple_arity(Instruction, EvaluatedArguments, Environment);
#icode_begin_handler{} ->
visit_begin_handler (Instruction, EvaluatedArguments, Environment);
#icode_begin_try{} ->
visit_begin_try (Instruction, EvaluatedArguments, Environment);
#icode_fail{} ->
visit_fail (Instruction, EvaluatedArguments, Environment);
_ ->
%% label, end_try, comment, return,
{[], [], Environment}
end.
%%-----------------------------------------------------------------------------
visit_begin_try(Instruction, [], Environment) ->
Label = hipe_icode:begin_try_label(Instruction),
Successor = hipe_icode:begin_try_successor(Instruction),
{[Label, Successor], [], Environment}.
%%-----------------------------------------------------------------------------
visit_begin_handler(Instruction, _Arguments, Environment) ->
Destinations = hipe_icode:begin_handler_dstlist(Instruction),
{Environment1, SSAWork} =
lists:foldl(fun (Dst, {Env0,Work0}) ->
{Env, Work} = update_lattice_value({Dst, bottom}, Env0),
{Env, Work ++ Work0}
end,
{Environment, []},
Destinations),
{[], SSAWork, Environment1}.
%%-----------------------------------------------------------------------------
visit_switch_val(Instruction, [Argument], Environment) ->
Cases = hipe_icode:switch_val_cases(Instruction),
FailLabel = hipe_icode:switch_val_fail_label(Instruction),
case Argument of
bottom ->
FlowWork = [Label || {_Value, Label} <- Cases],
FlowWork1 = [FailLabel | FlowWork],
{FlowWork1, [], Environment};
_ ->
Target = get_switch_target(Cases, Argument, FailLabel),
{[Target], [], Environment}
end.
%%-----------------------------------------------------------------------------
visit_switch_tuple_arity(Instruction, [Argument], Environment) ->
Cases = hipe_icode:switch_tuple_arity_cases(Instruction),
FailLabel = hipe_icode:switch_tuple_arity_fail_label(Instruction),
case Argument of
bottom ->
FlowWork = [Label || {_Value, Label} <- Cases],
FlowWork1 = [FailLabel | FlowWork],
{FlowWork1, [], Environment};
Constant ->
UnTagged = hipe_icode:const_value(Constant),
case is_tuple(UnTagged) of
true ->
Target = get_switch_target(Cases, tuple_size(UnTagged), FailLabel),
{[Target], [], Environment};
false ->
{[FailLabel], [], Environment}
end
end.
%%-----------------------------------------------------------------------------
get_switch_target([], _Argument, FailLabel) ->
FailLabel;
get_switch_target([{CaseValue, Target} | CaseList], Argument, FailLabel) ->
case CaseValue =:= Argument of
true ->
Target;
false ->
get_switch_target(CaseList, Argument, FailLabel)
end.
%%-----------------------------------------------------------------------------
visit_move(Instruction, [SourceValue], Environment) ->
Destination = hipe_icode:move_dst(Instruction),
{Environment1, SSAWork} = update_lattice_value({Destination, SourceValue},
Environment),
{[], SSAWork, Environment1}.
%%-----------------------------------------------------------------------------
visit_if(Instruction, Arguments, Environment) ->
FlowWork =
case evaluate_if(hipe_icode:if_op(Instruction), Arguments) of
true ->
TrueLabel = hipe_icode:if_true_label(Instruction),
[TrueLabel];
false ->
FalseLabel = hipe_icode:if_false_label(Instruction),
[FalseLabel];
bottom ->
TrueLabel = hipe_icode:if_true_label(Instruction),
FalseLabel = hipe_icode:if_false_label(Instruction),
[TrueLabel, FalseLabel]
end,
{FlowWork, [], Environment}.
%%-----------------------------------------------------------------------------
visit_goto(Instruction, _Arguments, Environment) ->
GotoLabel = hipe_icode:goto_label(Instruction),
FlowWork = [GotoLabel],
{FlowWork, [], Environment}.
%%-----------------------------------------------------------------------------
visit_fail(Instruction, _Arguments, Environment) ->
FlowWork = hipe_icode:successors(Instruction),
{FlowWork, [], Environment}.
%%-----------------------------------------------------------------------------
visit_type(Instruction, Values, Environment) ->
FlowWork =
case evaluate_type(hipe_icode:type_test(Instruction), Values) of
true ->
TrueLabel = hipe_icode:type_true_label(Instruction),
[TrueLabel];
false ->
FalseLabel = hipe_icode:type_false_label(Instruction),
[FalseLabel];
bottom ->
TrueLabel = hipe_icode:type_true_label(Instruction),
FalseLabel = hipe_icode:type_false_label(Instruction),
[TrueLabel, FalseLabel]
end,
{FlowWork, [], Environment}.
%%-----------------------------------------------------------------------------
visit_call(Ins, Args, Environment) ->
Dsts = hipe_icode:call_dstlist(Ins),
Fun = hipe_icode:call_fun(Ins),
Fail = call_fail_labels(Ins),
Cont = call_continuation_labels(Ins),
visit_call(Dsts, Args, Fun, Cont, Fail, Environment).
visit_call(Dst, Args, Fun, Cont, Fail, Environment) ->
{FlowWork, {Environment1, SSAWork}} =
case lists:any(fun(X) -> (X =:= bottom) end, Args) of
true ->
{Fail ++ Cont, update_lattice_value({Dst, bottom}, Environment)};
false ->
ConstArgs = [hipe_icode:const_value(Argument) || Argument <- Args],
try evaluate_call_or_enter(ConstArgs, Fun) of
bottom ->
{Fail ++ Cont, update_lattice_value({Dst, bottom}, Environment)};
Constant ->
{Cont, update_lattice_value({Dst, Constant}, Environment)}
catch
_:_ ->
{Fail, update_lattice_value({Dst, bottom}, Environment)}
end
end,
{FlowWork, SSAWork, Environment1}.
%%-----------------------------------------------------------------------------
call_fail_labels(I) ->
case hipe_icode:call_fail_label(I) of
[] -> [];
Label -> [Label]
end.
call_continuation_labels(I) ->
case hipe_icode:call_continuation(I) of
[] -> [];
Label -> [Label]
end.
%%-----------------------------------------------------------------------------
%% Unary calls
evaluate_call_or_enter([Argument], Fun) ->
case Fun of
mktuple ->
hipe_icode:mk_const(list_to_tuple([Argument]));
unsafe_untag_float ->
hipe_icode:mk_const(float(Argument));
conv_to_float ->
hipe_icode:mk_const(float(Argument));
fnegate ->
hipe_icode:mk_const(0.0 - Argument);
'bnot' ->
hipe_icode:mk_const(Argument);
#unsafe_element{index=N} ->
hipe_icode:mk_const(element(N, Argument));
{erlang, hd, 1} ->
hipe_icode:mk_const(hd(Argument));
{erlang, tl, 1} ->
hipe_icode:mk_const(tl(Argument));
{erlang, atom_to_list, 1} ->
hipe_icode:mk_const(atom_to_list(Argument));
{erlang, list_to_atom, 1} ->
hipe_icode:mk_const(list_to_atom(Argument));
{erlang, tuple_to_list, 1} ->
hipe_icode:mk_const(tuple_to_list(Argument));
{erlang, list_to_tuple, 1} ->
hipe_icode:mk_const(list_to_tuple(Argument));
{erlang, length, 1} ->
hipe_icode:mk_const(length(Argument));
{erlang, size, 1} ->
hipe_icode:mk_const(size(Argument));
{erlang, bit_size, 1} ->
hipe_icode:mk_const(bit_size(Argument));
{erlang, byte_size, 1} ->
hipe_icode:mk_const(byte_size(Argument));
{erlang, tuple_size, 1} ->
hipe_icode:mk_const(tuple_size(Argument));
{erlang, abs, 1} ->
hipe_icode:mk_const(abs(Argument));
{erlang, round, 1} ->
hipe_icode:mk_const(round(Argument));
{erlang, trunc, 1} ->
hipe_icode:mk_const(trunc(Argument));
_ ->
bottom
end;
%% Binary calls
evaluate_call_or_enter([Argument1,Argument2], Fun) ->
case Fun of
'+' ->
hipe_icode:mk_const(Argument1 + Argument2);
'-' ->
hipe_icode:mk_const(Argument1 - Argument2);
'*' ->
hipe_icode:mk_const(Argument1 * Argument2);
'/' ->
hipe_icode:mk_const(Argument1 / Argument2);
'band' ->
hipe_icode:mk_const(Argument1 band Argument2);
'bor' ->
hipe_icode:mk_const(Argument1 bor Argument2);
'bsl' ->
hipe_icode:mk_const(Argument1 bsl Argument2);
'bsr' ->
hipe_icode:mk_const(Argument1 bsr Argument2);
'bxor' ->
hipe_icode:mk_const(Argument1 bxor Argument2);
fp_add ->
hipe_icode:mk_const(float(Argument1 + Argument2));
fp_sub ->
hipe_icode:mk_const(float(Argument1 - Argument2));
fp_mul ->
hipe_icode:mk_const(float(Argument1 * Argument2));
fp_div ->
hipe_icode:mk_const(Argument1 / Argument2);
cons ->
hipe_icode:mk_const([Argument1 | Argument2]);
mktuple ->
hipe_icode:mk_const(list_to_tuple([Argument1,Argument2]));
#unsafe_update_element{index=N} ->
hipe_icode:mk_const(setelement(N, Argument1, Argument2));
{erlang, '++', 2} ->
hipe_icode:mk_const(Argument1 ++ Argument2);
{erlang, '--', 2} ->
hipe_icode:mk_const(Argument1 -- Argument2);
{erlang, 'div', 2} ->
hipe_icode:mk_const(Argument1 div Argument2);
{erlang, 'rem', 2} ->
hipe_icode:mk_const(Argument1 rem Argument2);
{erlang, append_element, 2} ->
hipe_icode:mk_const(erlang:append_element(Argument1, Argument2));
{erlang, element, 2} ->
hipe_icode:mk_const(element(Argument1, Argument2));
_Other ->
%% io:format("In ~w(~w,~w)~n", [_Other,Argument1,Argument2]),
bottom
end;
%% The rest of the calls
evaluate_call_or_enter(Arguments, Fun) ->
case Fun of
mktuple ->
hipe_icode:mk_const(list_to_tuple(Arguments));
{erlang, setelement, 3} ->
[Argument1, Argument2, Argument3] = Arguments,
hipe_icode:mk_const(setelement(Argument1, Argument2, Argument3));
_ ->
bottom
end.
%%-----------------------------------------------------------------------------
evaluate_if(Conditional, [Argument1, Argument2]) ->
case ((Argument1 =:= bottom) or (Argument2 =:= bottom)) of
true -> bottom;
false -> evaluate_if_const(Conditional, Argument1, Argument2)
end;
evaluate_if(_Conditional, _Arguments) ->
bottom.
%%-----------------------------------------------------------------------------
evaluate_if_const(Conditional, Argument1, Argument2) ->
case Conditional of
'=:=' -> Argument1 =:= Argument2;
'==' -> Argument1 == Argument2;
'=/=' -> Argument1 =/= Argument2;
'/=' -> Argument1 /= Argument2;
'<' -> Argument1 < Argument2;
'>=' -> Argument1 >= Argument2;
'=<' -> Argument1 =< Argument2;
'>' -> Argument1 > Argument2;
_ -> bottom
end.
%%-----------------------------------------------------------------------------
evaluate_type(Type, Vals) ->
case [X || X <- Vals, X =:= bottom] of
[] -> evaluate_type_const(Type, Vals);
_ -> bottom
end.
%%-----------------------------------------------------------------------------
evaluate_type_const(Type, [Arg|Left]) ->
Test =
case {Type, hipe_icode:const_value(Arg)} of
{nil, [] } -> true;
{nil, _ } -> false;
{cons, [_|_]} -> true;
{cons, _ } -> false;
{{tuple, N}, T} when tuple_size(T) =:= N -> true;
{atom, A} when is_atom(A) -> true;
{{atom, A}, A} when is_atom(A) -> true;
{{record, A, S}, R} when tuple_size(R) =:= S,
element(1, R) =:= A -> true;
{{record, _, _}, _} -> false;
_ -> bottom
end,
case Test of
bottom -> bottom;
false -> false;
true -> evaluate_type_const(Type, Left)
end;
evaluate_type_const(_Type, []) ->
true.
%%-----------------------------------------------------------------------------
%% Icode-specific code below
%%-----------------------------------------------------------------------------
update_instruction(Instruction, Environment) ->
case Instruction of
#icode_call{} ->
update_call(Instruction, Environment);
#icode_enter{} ->
update_enter(Instruction, Environment);
#icode_if{} ->
update_if(Instruction, Environment);
#icode_move{} ->
update_move(Instruction, Environment);
#icode_phi{} ->
update_phi(Instruction, Environment);
#icode_switch_val{} ->
update_switch_val(Instruction, Environment);
#icode_type{} ->
update_type(Instruction, Environment);
#icode_switch_tuple_arity{} ->
update_switch_tuple_arity(Instruction, Environment);
_ ->
%% goto, comment, label, return, begin_handler, end_try,
%% begin_try, fail
%% We could but don't handle: catch?, fail?
[Instruction]
end.
%%-----------------------------------------------------------------------------
update_call(Instruction, Environment) ->
DestList = hipe_icode:call_dstlist(Instruction),
case DestList of
[Destination] ->
case lookup_lattice_value(Destination, Environment) of
bottom ->
NewArguments = update_arguments(
hipe_icode:call_args(Instruction),
Environment),
[hipe_icode:call_args_update(Instruction, NewArguments)];
X ->
NewInstructions =
case is_call_to_fp_op(Instruction) of
true ->
TmpIns =
hipe_icode:call_fun_update(Instruction, unsafe_untag_float),
[hipe_icode:call_args_update(TmpIns, [X])];
false ->
case hipe_icode:call_continuation(Instruction) of
[] ->
[hipe_icode:mk_move(Destination, X)];
ContinuationLabel ->
[hipe_icode:mk_move(Destination, X),
hipe_icode:mk_goto(ContinuationLabel)]
end
end,
?CONST_PROP_MSG("call: ~w ---> ~w\n",
[Instruction, NewInstructions]),
NewInstructions
end;
%% %% [] -> %% No destination; we don't touch this
%% [] ->
%% NewArguments = update_arguments(hipe_icode:call_args(Instruction),
%% Environment),
%% [hipe_icode:call_args_update(Instruction, NewArguments)];
%% List-> %% Means register allocation; not implemented at this point
_ ->
[Instruction]
end.
%%-----------------------------------------------------------------------------
is_call_to_fp_op(Instruction) ->
case hipe_icode:call_fun(Instruction) of
fp_add -> true;
fp_sub -> true;
fp_mul -> true;
fp_div -> true;
fnegate -> true;
conv_to_float -> true;
unsafe_untag_float -> true;
_ -> false
end.
%%-----------------------------------------------------------------------------
update_enter(Instruction, Environment) ->
Args = hipe_icode:enter_args(Instruction),
EvalArgs = [lookup_lattice_value(X, Environment) || X <- Args],
Fun = hipe_icode:enter_fun(Instruction),
case lists:any(fun(X) -> (X =:= bottom) end, EvalArgs) of
true ->
update_enter_arguments(Instruction, Environment);
false ->
ConstVals = [hipe_icode:const_value(X) || X <- EvalArgs],
try evaluate_call_or_enter(ConstVals, Fun) of
bottom ->
update_enter_arguments(Instruction, Environment);
Const ->
Dst = hipe_icode:mk_new_var(),
[hipe_icode:mk_move(Dst, Const),
hipe_icode:mk_return([Dst])]
catch
_:_ ->
update_enter_arguments(Instruction, Environment)
end
end.
update_enter_arguments(Instruction, Env) ->
NewArguments = update_arguments(hipe_icode:enter_args(Instruction), Env),
[hipe_icode:enter_args_update(Instruction, NewArguments)].
%%-----------------------------------------------------------------------------
update_if(Instruction, Environment) ->
Args = hipe_icode:if_args(Instruction),
EvaluatedArguments = [lookup_lattice_value(Argument, Environment)
|| Argument <- Args],
Op = hipe_icode:if_op(Instruction),
case evaluate_if(Op, EvaluatedArguments) of
true ->
TrueLabel = hipe_icode:if_true_label(Instruction),
?CONST_PROP_MSG("ifT: ~w ---> goto ~w\n", [Instruction, TrueLabel]),
[hipe_icode:mk_goto(TrueLabel)];
false ->
FalseLabel = hipe_icode:if_false_label(Instruction),
?CONST_PROP_MSG("ifF: ~w ---> goto ~w\n", [Instruction, FalseLabel]),
[hipe_icode:mk_goto(FalseLabel)];
bottom ->
%% Convert the if-test to a type test if possible.
Op = hipe_icode:if_op(Instruction),
case Op =:= '=:=' orelse Op =:= '=/=' of
false -> [Instruction];
true ->
[Arg1, Arg2] = Args,
case EvaluatedArguments of
[bottom, bottom] ->
[Instruction];
[bottom, X] ->
conv_if_to_type(Instruction, hipe_icode:const_value(X), Arg1);
[X, bottom] ->
conv_if_to_type(Instruction, hipe_icode:const_value(X), Arg2)
end
end
end.
conv_if_to_type(I, Const, Arg) when is_atom(Const);
is_integer(Const);
Const =:= [] ->
Test =
if is_atom(Const) -> {atom, Const};
is_integer(Const) -> {integer, Const};
true -> nil
end,
{T, F} =
case hipe_icode:if_op(I) of
'=:=' -> {hipe_icode:if_true_label(I),hipe_icode:if_false_label(I)};
'=/=' -> {hipe_icode:if_false_label(I),hipe_icode:if_true_label(I)}
end,
NewI = hipe_icode:mk_type([Arg], Test, T, F),
?CONST_PROP_MSG("if: ~w ---> type ~w\n", [I, NewI]),
[NewI];
conv_if_to_type(I, _, _) ->
[I].
%%-----------------------------------------------------------------------------
update_move(Instruction, Environment) ->
Destination = hipe_icode:move_dst(Instruction),
case lookup_lattice_value(Destination, Environment) of
bottom ->
[Instruction];
X ->
case hipe_icode:move_src(Instruction) of
X ->
[Instruction];
_ ->
?CONST_PROP_MSG("move: ~w ---> ~w\n", [Instruction, X]),
[hipe_icode:move_src_update(Instruction, X)]
end
%% == [hipe_icode:mk_move(Destination, X)]
end.
%%-----------------------------------------------------------------------------
update_phi(Instruction, Environment) ->
Destination = hipe_icode:phi_dst(Instruction),
case lookup_lattice_value(Destination, Environment) of
bottom ->
[Instruction];
X ->
?CONST_PROP_MSG("phi: ~w ---> ~w\n", [Instruction, X]),
[hipe_icode:mk_move(Destination, X)]
end.
%%-----------------------------------------------------------------------------
update_type(Instruction, Environment) ->
EvaluatedArguments = [lookup_lattice_value(Argument, Environment) ||
Argument <- hipe_icode:type_args(Instruction)],
case evaluate_type(hipe_icode:type_test(Instruction), EvaluatedArguments) of
true ->
TrueLabel = hipe_icode:type_true_label(Instruction),
?CONST_PROP_MSG("typeT: ~w ---> goto ~w\n", [Instruction, TrueLabel]),
[hipe_icode:mk_goto(TrueLabel)];
false ->
FalseLabel = hipe_icode:type_false_label(Instruction),
?CONST_PROP_MSG("typeF: ~w ---> goto ~w\n", [Instruction, FalseLabel]),
[hipe_icode:mk_goto(FalseLabel)];
bottom ->
[Instruction]
end.
%%-----------------------------------------------------------------------------
update_switch_val(Instruction, Environment) ->
Argument = hipe_icode:switch_val_term(Instruction),
Value = lookup_lattice_value(Argument, Environment),
case Value of
bottom ->
[Instruction];
_ ->
Cases = hipe_icode:switch_val_cases(Instruction),
FailLabel = hipe_icode:switch_val_fail_label(Instruction),
Target = get_switch_target(Cases, Value, FailLabel),
?CONST_PROP_MSG("sv: ~w ---> goto ~w\n", [Instruction, Target]),
[hipe_icode:mk_goto(Target)]
end.
%%-----------------------------------------------------------------------------
update_switch_tuple_arity(Instruction, Environment) ->
Argument = hipe_icode:switch_tuple_arity_term(Instruction),
Value = lookup_lattice_value(Argument, Environment),
case Value of
bottom ->
[Instruction];
Constant ->
UnTagged = hipe_icode:const_value(Constant),
case is_tuple(UnTagged) of
true ->
Cases = hipe_icode:switch_tuple_arity_cases(Instruction),
FailLabel = hipe_icode:switch_tuple_arity_fail_label(Instruction),
Target = get_switch_target(Cases, tuple_size(UnTagged), FailLabel),
?CONST_PROP_MSG("sta: ~w ---> goto ~w\n", [Instruction, Target]),
[hipe_icode:mk_goto(Target)];
false ->
[Instruction]
%% TODO: Can the above be replaced with below??? Perhaps
%% together with some sort of "generate failure".
%% [hipe_icode:mk_goto(FailLabel)]
end
end.
%%-----------------------------------------------------------------------------
lookup_lattice_value(X, Environment) ->
LatticeValues = env__lattice_values(Environment),
case hipe_icode:is_const(X) of
true ->
X;
false ->
case gb_trees:lookup(X, LatticeValues) of
none ->
?WARNING_MSG("Earlier compiler steps generated erroneous "
"code for X = ~w. We are ignoring this.\n",[X]),
bottom;
{value, top} ->
?EXIT({"lookup_lattice_value, top", X});
{value, Y} ->
Y
end
end.
%%-----------------------------------------------------------------------------
update_arguments(ArgumentList, Environment) ->
[case lookup_lattice_value(X, Environment) of
bottom ->
X;
Constant ->
Constant
end || X <- ArgumentList].
%%----------------------------- End of file -----------------------------------