Files
mercury/compiler/vn_flush.m
Fergus Henderson 773bd702cc Some more changes to minimize the complexity of the intermodule dependencies.
Estimated hours taken: 2.5

Some more changes to minimize the complexity of the intermodule dependencies.
In particular, ensure that bytecode.m does not need to import llds.m.

compiler/llds.m:
compiler/builtin_ops.m:
	Move the definitions of the unary_op and binary_op types into
	a new module `builtin_ops'.  These types are used by three of the
	different back-ends (bytecode, llds, and mlds) and therefore deserve
	to be in their own module.

compiler/bytecode.m:
	Define a type `byte_reg_type' and use that instead of llds__reg_type.
	Delete the import of module llds.

compiler/notes/compiler_design.html:
	Document the new module builtin_ops.

compiler/rl_exprn.m:
	Add a comment explaining why we need to import llds (and builtin_ops).

compiler/base_type_layout.m:
compiler/bytecode.m:
compiler/code_util.m:
compiler/dense_switch.m:
compiler/ite_gen.m:
compiler/jumpopt.m:
compiler/llds_out.m:
compiler/lookup_switch.m:
compiler/middle_rec.m:
compiler/opt_debug.m:
compiler/opt_util.m:
compiler/rl_exprn.m:
compiler/string_switch.m:
compiler/tag_switch.m:
compiler/transform_llds.m:
compiler/unify_gen.m:
compiler/value_number.m:
compiler/vn_block.m:
compiler/vn_cost.m:
compiler/vn_flush.m:
compiler/vn_type.m:
compiler/vn_util.m:
compiler/vn_verify.m:
	Add imports of module builtin_ops to lots of modules that
	imported llds.
1999-07-10 07:20:09 +00:00

1183 lines
37 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1995-1999 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
% vn_flush.m - flush the nodes of the vn graph in order.
% Author: zs.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module vn_flush.
:- interface.
:- import_module vn_type, vn_table, vn_temploc.
:- import_module llds, list.
% Flush the given nodes in the given order.
:- pred vn_flush__nodelist(list(vn_node), ctrlmap, vn_tables, templocs,
vn_params, list(instruction), io__state, io__state).
:- mode vn_flush__nodelist(in, in, in, in, in, out, di, uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module bool, map, int, string, require, std_util.
:- import_module builtin_ops, vn_table, vn_util, vn_debug, opt_debug.
vn_flush__nodelist([], _, _, _, _, []) --> [].
vn_flush__nodelist([Node0 | Nodes0], Ctrlmap, VnTables0, Templocs0, Params,
Instrs) -->
( { Node0 = node_origlval(_) } ->
{ Nodes1 = Nodes0 },
{ VnTables1 = VnTables0 },
{ Templocs1 = Templocs0 },
{ Instrs0 = [] }
;
vn_flush__node(Node0, Ctrlmap,
Nodes0, Nodes1, VnTables0, VnTables1,
Templocs0, Templocs1, Params,Instrs0)
),
vn_flush__nodelist(Nodes1, Ctrlmap, VnTables1, Templocs1, Params,
Instrs1),
{ list__append(Instrs0, Instrs1, Instrs) }.
% Flush the given node.
:- pred vn_flush__node(vn_node, ctrlmap, list(vn_node), list(vn_node),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction),
io__state, io__state).
:- mode vn_flush__node(in, in, in, out, in, out, in, out, in, out, di, uo)
is det.
vn_flush__node(Node, Ctrlmap, Nodes0, Nodes, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) -->
vn_debug__flush_start_msg(Node),
(
{ Node = node_shared(Vn) },
vn_flush__shared_node(Vn, Nodes0, Nodes, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs)
;
{ Node = node_lval(Vnlval) },
vn_flush__lval_node(Vnlval, Ctrlmap, Nodes0, Nodes,
VnTables0, VnTables, Templocs0, Templocs, Params,
Instrs)
;
{ Node = node_origlval(_Vnlval) },
{ Nodes = Nodes0 },
{ VnTables = VnTables0 },
{ Templocs = Templocs0 },
{ Instrs = [] }
;
{ Node = node_ctrl(N) },
{ Nodes = Nodes0 },
{ map__lookup(Ctrlmap, N, VnInstr) },
{ vn_flush__ctrl_node(VnInstr, N, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) }
),
% we should look at all the temporary regs here and call reuse_temploc
% for the ones that store values that are not live and are not needed
% any more.
vn_debug__flush_end_msg(Instrs, VnTables).
%-----------------------------------------------------------------------------%
:- pred vn_flush__lval_node(vnlval, ctrlmap, list(vn_node), list(vn_node),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction),
io__state, io__state).
:- mode vn_flush__lval_node(in, in, in, out, in, out, in, out, in, out, di, uo)
is det.
vn_flush__lval_node(Vnlval, Ctrlmap, Nodes0, Nodes,
VnTables0, VnTables, Templocs0, Templocs, Params, Instrs) -->
{ vn_table__lookup_desired_value(Vnlval, DesVn, "vn_flush__lval_node",
VnTables0) },
{ vn_table__lookup_current_value(Vnlval, CurVn, "vn_flush__lval_node",
VnTables0) },
(
% Even if a vnlval already has the right value,
% we must make sure its access path will not be
% needed again. This requires its storage in a
% register or temporary if it is ever used again.
{ CurVn = DesVn },
{ vn_util__vnlval_access_vns(Vnlval, AccessVns) },
{ AccessVns = [_|_] },
{ vn_table__lookup_uses(DesVn, Uses, "vn_flush__lval_node",
VnTables0) },
{ vn_util__real_uses(Uses, RealUses, VnTables0) },
{ RealUses = [_|_] }
->
( { RealUses = [src_liveval(UserVnlval)] } ->
vn_flush__node(node_lval(UserVnlval), Ctrlmap,
Nodes0, Nodes1, VnTables0, VnTables1,
Templocs0, Templocs1, Params, Instrs1),
{ list__delete_all(Nodes1, node_lval(UserVnlval),
Nodes) }
;
% This path should be taken only if some circularities
% are broken arbitrarily. Otherwise, the shared node
% should come before the user lval nodes.
vn_flush__node(node_shared(DesVn), Ctrlmap,
Nodes0, Nodes, VnTables0, VnTables1,
Templocs0, Templocs1, Params, Instrs1)
),
{ vn_flush__ensure_assignment(Vnlval, DesVn, [],
VnTables1, VnTables,
Templocs1, Templocs, Params, Instrs2) },
{ list__append(Instrs1, Instrs2, Instrs) }
;
{ vn_flush__ensure_assignment(Vnlval, DesVn, [],
VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) },
{ Nodes = Nodes0 }
).
%-----------------------------------------------------------------------------%
:- pred vn_flush__shared_node(vn, list(vn_node), list(vn_node),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction),
io__state, io__state).
:- mode vn_flush__shared_node(in, in, out, in, out, in, out, in, out, di, uo)
is det.
vn_flush__shared_node(Vn, Nodes0, Nodes, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) -->
( { vn_table__lookup_uses(Vn, [], "vn_flush__shared_node", VnTables0) } ->
% earlier nodes must have taken care of this vn
{ Nodes = Nodes0 },
{ VnTables = VnTables0 },
{ Templocs = Templocs0 },
{ Instrs = [] }
;
{ vn_flush__choose_loc_for_shared_vn(Vn, Vnlval, VnTables0,
Templocs0, Templocs1) },
( { vn_table__search_desired_value(Vnlval, Vn, VnTables0) } ->
vn_debug__flush_also_msg(Vnlval),
{ list__delete_all(Nodes0, node_lval(Vnlval), Nodes) }
;
{ Nodes = Nodes0 }
),
{ vn_flush__ensure_assignment(Vnlval, Vn, [],
VnTables0, VnTables, Templocs1, Templocs, Params,
Instrs) }
).
%-----------------------------------------------------------------------------%
:- pred vn_flush__ctrl_node(vn_instr, int, vn_tables, vn_tables,
templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__ctrl_node(in, in, in, out, in, out, in, out) is det.
vn_flush__ctrl_node(Vn_instr, N, VnTables0, VnTables, Templocs0, Templocs,
Params, Instrs) :-
(
Vn_instr = vn_livevals(Livevals),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [livevals(Livevals) - ""]
;
Vn_instr = vn_call(ProcAddr, RetAddr, LiveInfo, CodeModel),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [call(ProcAddr, RetAddr, LiveInfo, CodeModel) - ""]
;
Vn_instr = vn_mkframe(NondetFrameInfo, Redoip),
vn_util__rval_to_vn(const(code_addr_const(Redoip)), AddrVn,
VnTables0, VnTables1),
vn_util__lval_to_vnlval(redoip(lval(maxfr)), SlotVnlval,
VnTables1, VnTables2),
vn_table__set_current_value(SlotVnlval, AddrVn,
VnTables2, VnTables),
Templocs = Templocs0,
Instrs = [mkframe(NondetFrameInfo, Redoip) - ""]
;
Vn_instr = vn_label(Label),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [label(Label) - ""]
;
Vn_instr = vn_goto(TargetAddr),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [goto(TargetAddr) - ""]
;
Vn_instr = vn_computed_goto(Vn, Labels),
vn_flush__vn(Vn, [src_ctrl(N)], [], Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, FlushInstrs),
Instr = computed_goto(Rval, Labels) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_if_val(Vn, TargetAddr),
vn_flush__vn(Vn, [src_ctrl(N)], [], Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, FlushInstrs),
Instr = if_val(Rval, TargetAddr) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_mark_hp(Vnlval),
vn_flush__access_path(Vnlval, [src_ctrl(N)], [], Lval,
VnTables0, VnTables1, Templocs0, Templocs, Params,
FlushInstrs),
vn_table__lookup_assigned_vn(vn_origlval(vn_hp), OldhpVn,
"vn_flush__ctrl_node", VnTables1),
vn_table__set_current_value(Vnlval, OldhpVn,
VnTables1, VnTables),
Instr = mark_hp(Lval) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_restore_hp(Vn),
vn_flush__vn(Vn, [src_ctrl(N)], [], Rval, VnTables0, VnTables1,
Templocs0, Templocs, Params, FlushInstrs),
vn_table__set_current_value(vn_hp, Vn, VnTables1, VnTables),
Instr = restore_hp(Rval) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_store_ticket(Vnlval),
vn_flush__access_path(Vnlval, [src_ctrl(N)], [], Lval,
VnTables0, VnTables1, Templocs0, Templocs, Params,
FlushInstrs),
vn_table__lookup_assigned_vn(vn_origlval(Vnlval), OldVn,
"vn_flush__ctrl_node", VnTables1),
vn_table__set_current_value(Vnlval, OldVn, VnTables1, VnTables),
Instr = store_ticket(Lval) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_reset_ticket(Vn, Reason),
vn_flush__vn(Vn, [src_ctrl(N)], [], Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, FlushInstrs),
Instr = reset_ticket(Rval, Reason) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_discard_ticket,
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [discard_ticket - ""]
;
Vn_instr = vn_mark_ticket_stack(Vnlval),
vn_flush__access_path(Vnlval, [src_ctrl(N)], [], Lval,
VnTables0, VnTables1, Templocs0, Templocs, Params,
FlushInstrs),
vn_table__lookup_assigned_vn(vn_origlval(Vnlval), OldVn,
"vn_flush__ctrl_node", VnTables1),
vn_table__set_current_value(Vnlval, OldVn, VnTables1, VnTables),
Instr = mark_ticket_stack(Lval) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_discard_tickets_to(Vn),
vn_flush__vn(Vn, [src_ctrl(N)], [], Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, FlushInstrs),
Instr = discard_tickets_to(Rval) - "",
list__append(FlushInstrs, [Instr], Instrs)
;
Vn_instr = vn_incr_sp(Incr, Msg),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [incr_sp(Incr, Msg) - ""]
;
Vn_instr = vn_decr_sp(Decr),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = [decr_sp(Decr) - ""]
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Choose a location for a shared value number that does not have to go
% anywhere specific right now; we do have to ensure that the chosen
% location is accessible without access vns.
% We prefer to choose a register or stack slot that already has the
% value; failing that, a register or stack slot that would like to
% have the value and whose contents either don't have to be saved
% or can be saved with a single instruction.
% In either case we have to pay attention when we choose non-register
% destinations. It pays to choose a non-register holder of the value
% only if there is at most one other user of the value. It pays to
% choose a non-register user only if there are no other users of the
% value.
:- pred vn_flush__choose_loc_for_shared_vn(vn, vnlval, vn_tables,
templocs, templocs).
% :- mode vn_flush__choose_loc_for_shared_vn(in, out, in, di, uo) is det.
:- mode vn_flush__choose_loc_for_shared_vn(in, out, in, in, out) is det.
vn_flush__choose_loc_for_shared_vn(Vn, Chosen, VnTables, Templocs0, Templocs) :-
(
vn_table__lookup_current_locs(Vn, CurrentLocs,
"vn_flush__choose_loc_for_shared_vn", VnTables),
vn_util__choose_cheapest_loc(CurrentLocs, BestHolder),
vn_util__vnlval_access_vns(BestHolder, []),
(
vn_util__classify_loc_cost(BestHolder, 0)
;
vn_table__lookup_uses(Vn, Uses,
"vn_flush__choose_loc_for_shared_vn", VnTables),
list__delete_first(Uses, src_liveval(BestHolder),
NewUses),
( NewUses = [] ; NewUses = [_] )
)
->
Chosen = BestHolder,
Templocs = Templocs0
;
vn_flush__find_cheap_users(Vn, Users, VnTables),
vn_util__choose_cheapest_loc(Users, BestUser),
vn_util__vnlval_access_vns(BestUser, []),
(
vn_util__classify_loc_cost(BestUser, 0)
;
vn_table__lookup_uses(Vn, Uses,
"vn_flush__choose_loc_for_shared_vn", VnTables),
list__delete_first(Uses, src_liveval(BestUser), [])
)
->
Chosen = BestUser,
Templocs = Templocs0
;
vn_flush__choose_temp(Vn, VnTables, Templocs0, Templocs,
Chosen)
).
:- pred vn_flush__choose_temp(vn, vn_tables, templocs, templocs, vnlval).
:- mode vn_flush__choose_temp(in, in, in, out, out) is det.
vn_flush__choose_temp(Vn, VnTables, Templocs0, Templocs, Chosen) :-
vn_table__lookup_defn(Vn, Vnrval, "vn_flush__choose_temp", VnTables),
vn_type__vnrval_type(Vnrval, Type),
( Type = float ->
vn_temploc__next_tempf(Templocs0, Templocs, Chosen)
;
vn_temploc__next_tempr(Templocs0, Templocs, Chosen)
).
%-----------------------------------------------------------------------------%
% Find a 'user', location that would like to have the given vn.
% The user should be 'cheap', i.e. either it should hold no value
% that will ever be used by anybody else, or the value it holds
% should be assignable to one of its users without needing any
% more assignments. At the moment we insist that this user be
% a register. We could allow stack/frame variables as well,
% but we must not allow fields, or in general any location
% that needs access vns. (When flushing framevars before assigning
% to curfr, we don't want to pick anything that references a framevar
% in any case.)
:- pred vn_flush__find_cheap_users(vn, list(vnlval), vn_tables).
:- mode vn_flush__find_cheap_users(in, out, in) is det.
vn_flush__find_cheap_users(Vn, Vnlvals, VnTables) :-
( vn_table__search_uses(Vn, Uses, VnTables) ->
vn_flush__find_cheap_users_2(Uses, Vnlvals, VnTables)
;
Vnlvals = []
).
:- pred vn_flush__find_cheap_users_2(list(vn_src), list(vnlval), vn_tables).
:- mode vn_flush__find_cheap_users_2(in, out, in) is det.
vn_flush__find_cheap_users_2([], [], _VnTables).
vn_flush__find_cheap_users_2([Src | Srcs], Vnlvals, VnTables) :-
vn_flush__find_cheap_users_2(Srcs, Vnlvals0, VnTables),
(
Src = src_liveval(Live)
% \+ Live = vn_field(_, _, _)
->
( vn_table__search_current_value(Live, Vn, VnTables) ->
vn_table__lookup_uses(Vn, Uses,
"vn_flush__find_cheap_users_2", VnTables),
(
Uses = []
->
% Live's current value is not used.
Vnlvals = [Live | Vnlvals0]
;
list__member(UserSrc, Uses),
(
UserSrc = src_liveval(User),
vn_table__search_current_value(User,
UserVn, VnTables)
->
User = vn_reg(_, _),
vn_table__lookup_uses(UserVn, [],
"vn_flush__find_cheap_users_2",
VnTables)
;
true
)
->
% Live's current value can be saved to User
% without any further action.
Vnlvals = [Live | Vnlvals0]
;
Vnlvals = Vnlvals0
)
;
% Live doesn't have a value we know about.
Vnlvals = [Live | Vnlvals0]
)
;
Vnlvals = Vnlvals0
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred vn_flush__ensure_assignment(vnlval, vn, list(lval),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__ensure_assignment(in, in, in, in, out, in, out, in, out)
is det.
vn_flush__ensure_assignment(Vnlval, Vn, Forbidden, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) :-
(
vn_table__search_current_value(Vnlval, Cur_vn, VnTables0),
Vn = Cur_vn
->
vn_table__del_old_use(Vn, src_liveval(Vnlval),
VnTables0, VnTables1),
vn_util__vnlval_access_vns(Vnlval, SubVns),
vn_table__del_old_uses(SubVns, src_access(Vnlval),
VnTables1, VnTables),
Templocs = Templocs0,
Instrs = []
;
vn_flush__generate_assignment(Vnlval, Vn, Forbidden,
VnTables0, VnTables, Templocs0, Templocs, Params,
_, Instrs)
).
:- pred vn_flush__generate_assignment(vnlval, vn, list(lval),
vn_tables, vn_tables, templocs, templocs, vn_params,
lval, list(instruction)).
:- mode vn_flush__generate_assignment(in, in, in, in, out, in, out, in,
out, out) is det.
vn_flush__generate_assignment(Vnlval, Vn, Forbidden0, VnTables0, VnTables,
Templocs0, Templocs, Params, Lval, Instrs) :-
( Vnlval = vn_hp ->
error("vn_hp should never need to be explicitly flushed")
% It should be done by the first reference to the old value
% of the heap pointer, which should generate an incr_hp.
;
true
),
( vn_table__search_current_value(Vnlval, OldVn0, VnTables0) ->
SaveVn = yes(OldVn0)
;
SaveVn = no
),
% Only lvals on the heap must have their access path flushed,
% but they cannot appear on the temploc list, so of the next
% next two calls, at most one will modify Temploc.
vn_temploc__no_temploc(Vnlval, Templocs0, Templocs1),
vn_flush__access_path(Vnlval, [src_access(Vnlval)], Forbidden0, Lval,
VnTables0, VnTables1, Templocs1, Templocs2, Params,
AccessInstrs),
vn_flush__vn(Vn, [src_liveval(Vnlval)], Forbidden0, Rval,
VnTables1, VnTables2, Templocs2, Templocs3, Params,
FlushInstrs0),
% The 'current' value of Vnlval may be changed by flush_vn if it
% involves a reference to the old value of hp.
(
SaveVn = yes(OldVn),
vn_util__find_lvals_in_rval(Rval, Forbidden1),
list__append(Forbidden0, Forbidden1, Forbidden),
vn_flush__maybe_save_prev_value(Vnlval, OldVn, Vn, Forbidden,
VnTables2, VnTables3, Templocs3, Templocs, Params,
SaveInstrs)
;
SaveVn = no,
VnTables3 = VnTables2,
Templocs = Templocs3,
SaveInstrs = []
),
( vn_table__search_current_value(Vnlval, Vn, VnTables3) ->
% Flush_vn must perform the entire assignment if it involves
% exactly the actions of an incr_hp operation. Since the
% incr_hp in FlushInstrs overwrites Lval, we must perform it
% after Lval's old value has been saved.
vn_flush__get_incr_hp(FlushInstrs0, Instr, FlushInstrs1),
VnTables = VnTables3
;
vn_table__set_current_value(Vnlval, Vn, VnTables3, VnTables),
Instr = assign(Lval, Rval) - "vn flush",
FlushInstrs1 = FlushInstrs0
),
list__condense([AccessInstrs, FlushInstrs1, SaveInstrs, [Instr]],
Instrs).
% Remove the incr_hp instruction from the list and return it
% separately.
:- pred vn_flush__get_incr_hp(list(instruction), instruction, list(instruction)).
% :- mode vn_flush__get_incr_hp(di, out, uo) is det.
:- mode vn_flush__get_incr_hp(in, out, out) is det.
vn_flush__get_incr_hp([], _, _) :-
error("could not find incr_hp").
vn_flush__get_incr_hp([Instr0 | Instrs0], IncrHp, Instrs) :-
( Instr0 = incr_hp(_, _, _, _) - _ ->
IncrHp = Instr0,
Instrs = Instrs0
;
vn_flush__get_incr_hp(Instrs0, IncrHp, Instrs1),
Instrs = [Instr0 | Instrs1]
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- pred vn_flush__vn(vn, list(vn_src), list(lval), rval, vn_tables, vn_tables,
templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__vn(in, in, in, out, in, out, in, out, in, out) is det.
vn_flush__vn(Vn, Srcs, Forbidden, Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) :-
( Srcs = [SrcPrime | _] ->
Src = SrcPrime
;
error("empty source list in flush_vn")
),
vn_util__is_const_expr(Vn, IsConst, VnTables0),
(
IsConst = yes,
vn_flush__vn_value(Vn, Srcs, Forbidden, Rval,
VnTables0, VnTables3, Templocs0, Templocs, Params,
Instrs)
;
IsConst = no,
vn_table__lookup_current_locs(Vn, Locs, "vn_flush__vn",
VnTables0),
vn_table__lookup_uses(Vn, Uses, "vn_flush__vn", VnTables0),
list__delete_all(Uses, Src, NewUses),
( vn_util__choose_cheapest_loc(Locs, Loc) ->
(
Loc = vn_hp
->
% The first reference to the old value of hp.
vn_flush__old_hp(Srcs, Forbidden, Rval,
VnTables0, VnTables3,
Templocs0, Templocs, Params, Instrs)
;
NewUses = [_,_|_],
vn_util__classify_loc_cost(Loc, Cost),
Cost > 0,
\+ Src = src_liveval(_)
->
vn_flush__choose_temp(Vn, VnTables0,
Templocs0, Templocs1, Vnlval),
vn_flush__generate_assignment(Vnlval, Vn,
Forbidden, VnTables0, VnTables3,
Templocs1, Templocs, Params,
Lval, Instrs),
Rval = lval(Lval)
;
vn_flush__access_path(Loc, [src_vn(Vn) | Srcs],
Forbidden, Lval, VnTables0, VnTables3,
Templocs0, Templocs, Params, Instrs),
Rval = lval(Lval)
)
;
% If there are no more uses, it is useless to assign
% the vn to a location. Otherwise it is useful, but if
% Src is a livevals, the assignment will be done by
% the caller.
(
NewUses = [_|_],
\+ Src = src_liveval(_)
->
vn_flush__choose_loc_for_shared_vn(Vn, Vnlval,
VnTables0, Templocs0, Templocs1),
vn_flush__generate_assignment(Vnlval, Vn,
Forbidden, VnTables0, VnTables3,
Templocs1, Templocs, Params,
Lval, Instrs),
Rval = lval(Lval)
;
vn_flush__vn_value(Vn, Srcs, Forbidden, Rval,
VnTables0, VnTables3,
Templocs0, Templocs, Params, Instrs)
)
)
),
vn_table__del_old_use(Vn, Src, VnTables3, VnTables).
%-----------------------------------------------------------------------------%
:- pred vn_flush__vn_value(vn, list(vn_src), list(lval), rval,
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__vn_value(in, in, in, out, in, out, in, out, in, out) is det.
vn_flush__vn_value(Vn, Srcs, Forbidden, Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) :-
vn_table__lookup_defn(Vn, Vnrval, "vn_flush__vn_value", VnTables0),
(
Vnrval = vn_origlval(Vnlval),
( Vnlval = vn_hp ->
error("vn_hp found in flush_vn_value")
% It should have been caught in flush_vn
;
true
),
vn_table__lookup_current_locs(Vn, Locs0, "vn_flush__vn_value",
VnTables0),
(
% For code understandability, and for aesthetics,
% we prefer to take the value from its original home,
% but only if by doing so we incur no cost penalty.
vn_table__lookup_current_value(Vnlval, CurVn,
"vn_flush__vn_value", VnTables0),
Vn = CurVn
->
Locs1 = [Vnlval | Locs0]
;
Locs1 = Locs0
),
( vn_util__choose_cheapest_loc(Locs1, LocPrime) ->
Loc = LocPrime
;
opt_debug__dump_vnlval(Vnlval, V_str),
string__append("cannot find copy of an origlval: ",
V_str, Str),
error(Str)
),
vn_flush__access_path(Loc, [src_vn(Vn) | Srcs], Forbidden, Lval,
VnTables0, VnTables, Templocs0, Templocs, Params,
Instrs),
Rval = lval(Lval)
;
Vnrval = vn_mkword(Tag, SubVn1),
(
vn_table__lookup_defn(SubVn1, SubVnrval,
"vn_flush__vn_value", VnTables0),
SubVnrval = vn_origlval(vn_hp)
->
vn_flush__vn(SubVn1, [src_vn(Vn) | Srcs], Forbidden,
Rval1, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs),
vn_table__lookup_current_locs(Vn, Locs,
"vn_flush__vn_value", VnTables),
( Locs = [Loc0 | _] ->
% see below for an explanation
vn_flush__access_path(Loc0, [], Forbidden, Lval,
VnTables, _, Templocs0, _, Params, _),
Rval = lval(Lval)
;
Rval = mkword(Tag, Rval1)
)
;
vn_flush__vn(SubVn1, [src_vn(Vn) | Srcs], Forbidden,
Rval1, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs),
Rval = mkword(Tag, Rval1)
)
;
Vnrval = vn_const(Const),
Rval = const(Const),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_create(Tag, MaybeRvals, ArgTypes, StatDyn,
Label, Msg),
Rval = create(Tag, MaybeRvals, ArgTypes, StatDyn, Label, Msg),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_unop(Unop, SubVn1),
vn_flush__vn(SubVn1, [src_vn(Vn) | Srcs], Forbidden, Rval1,
VnTables0, VnTables, Templocs0, Templocs, Params,
Instrs),
Rval = unop(Unop, Rval1)
;
Vnrval = vn_binop(Binop, SubVn1, SubVn2),
vn_flush__vn(SubVn1, [src_vn(Vn) | Srcs], Forbidden, Rval1,
VnTables0, VnTables1, Templocs0, Templocs1, Params,
Instrs1),
vn_flush__vn(SubVn2, [src_vn(Vn) | Srcs], Forbidden, Rval2,
VnTables1, VnTables, Templocs1, Templocs, Params,
Instrs2),
Rval = binop(Binop, Rval1, Rval2),
list__append(Instrs1, Instrs2, Instrs)
;
Vnrval = vn_stackvar_addr(N),
Rval = mem_addr(stackvar_ref(N)),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_framevar_addr(N),
Rval = mem_addr(framevar_ref(N)),
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_heap_addr(SubVn1, Tag, Field),
vn_flush__vn(SubVn1, [src_vn(Vn) | Srcs], Forbidden, Rval1,
VnTables0, VnTables, Templocs0, Templocs, Params,
Instrs),
Rval = mem_addr(heap_ref(Rval1, Tag, Field))
).
%-----------------------------------------------------------------------------%
:- pred vn_flush__old_hp(list(vn_src), list(lval), rval, vn_tables, vn_tables,
templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__old_hp(in, in, out, in, out, in, out, in, out) is det.
vn_flush__old_hp(Srcs0, Forbidden0, ReturnRval, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) :-
% First take care of the "assignment to hp" part of incr_hp.
vn_table__lookup_desired_value(vn_hp, NewhpVn, "vn_flush__old_hp",
VnTables0),
vn_flush__hp_incr(NewhpVn, Srcs0, Forbidden0, MaybeRval,
VnTables0, VnTables1, Templocs0, Templocs1, Params, IncrInstrs),
(
MaybeRval = yes(Rval0),
vn_type__bytes_per_word(Params, BytesPerWord),
( Rval0 = const(int_const(I)) ->
I1 is I // BytesPerWord,
Rval = const(int_const(I1))
; Rval0 = binop((*), Rval1, const(int_const(BytesPerWord))) ->
Rval = Rval1
;
Rval = binop((/), Rval0, const(int_const(BytesPerWord)))
)
;
MaybeRval = no,
error("empty expression for hp increment")
),
vn_table__set_current_value(vn_hp, NewhpVn, VnTables1, VnTables2),
% Find out whether we should tag the old hp, and if so, with what.
vn_table__lookup_assigned_vn(vn_origlval(vn_hp), OldhpVn,
"vn_flush__old_hp", VnTables2),
( Srcs0 = [Src0Prime | Srcs1Prime] ->
Src0 = Src0Prime,
Srcs1 = Srcs1Prime
;
error("empty src list in vn_flush__old_hp")
),
vn_table__del_old_use(OldhpVn, Src0, VnTables2, VnTables3),
vn_table__lookup_uses(OldhpVn, OldhpUses, "vn_flush__old_hp",
VnTables3),
(
OldhpUses = [],
Src0 = src_vn(UserVn),
vn_table__lookup_defn(UserVn, UserVnrval, "vn_flush__old_hp",
VnTables3),
UserVnrval = vn_mkword(Tag, OldhpVn)
->
MaybeTag = yes(Tag),
AssignedVn = UserVn,
% Find out where to put the tagged value.
(
Srcs1 = [src_liveval(VnlvalPrime) | _]
->
Vnlval = VnlvalPrime,
% This call is purely to convert Vnlval in Lval.
% Since this flush will already have been done in
% generate_assign, we give it a bogus Srcs input and
% ignore all its other outputs.
vn_flush__access_path(Vnlval, [], Forbidden0, Lval,
VnTables3, _, Templocs1, _, Params, _),
Templocs2 = Templocs1
;
vn_flush__find_cheap_users(UserVn, UserLocs, VnTables3),
vn_util__choose_cheapest_loc(UserLocs, UserLoc),
UserLoc = vn_reg(_, _)
->
Vnlval = UserLoc,
vn_util__no_access_vnlval_to_lval(Vnlval, MaybeLval),
(
MaybeLval = yes(Lval)
;
MaybeLval = no,
error("register needs access path")
),
Templocs2 = Templocs1
;
vn_temploc__next_tempr(Templocs1, Templocs2, Vnlval),
vn_util__no_access_vnlval_to_lval(Vnlval, MaybeLval),
(
MaybeLval = yes(Lval)
;
MaybeLval = no,
error("temploc needs access path")
)
),
ReturnRval = const(int_const(42)) % should not be used
;
MaybeTag = no,
AssignedVn = OldhpVn,
vn_temploc__next_tempr(Templocs1, Templocs2, Vnlval),
vn_util__no_access_vnlval_to_lval(Vnlval, MaybeLval),
(
MaybeLval = yes(Lval)
;
MaybeLval = no,
error("temploc needs access path")
),
ReturnRval = lval(Lval)
),
% Save the old value if necessary.
( vn_table__search_current_value(Vnlval, OldVn, VnTables3) ->
vn_util__find_lvals_in_rval(Rval, Forbidden1),
list__append(Forbidden0, Forbidden1, Forbidden),
vn_flush__maybe_save_prev_value(Vnlval, OldVn, AssignedVn,
Forbidden, VnTables3, VnTables4, Templocs2, Templocs,
Params, SaveInstrs)
;
VnTables4 = VnTables2,
Templocs = Templocs2,
SaveInstrs = []
),
vn_table__set_current_value(Vnlval, AssignedVn, VnTables4, VnTables),
Instr = incr_hp(Lval, MaybeTag, Rval, "origin_lost_in_value_number")
- "",
list__condense([IncrInstrs, SaveInstrs, [Instr]], Instrs).
%-----------------------------------------------------------------------------%
:- pred vn_flush__hp_incr(vn, list(vn_src), list(lval), maybe(rval),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__hp_incr(in, in, in, out, in, out, in, out, in, out) is det.
vn_flush__hp_incr(Vn, Srcs, Forbidden, MaybeRval, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs) :-
(
vn_flush__rec_find_ref_vns(Vn, SubVns, VnTables0),
vn_flush__free_of_old_hp(SubVns, VnTables0)
->
vn_flush__vn(Vn, Srcs, Forbidden, Rval, VnTables0, VnTables,
Templocs0, Templocs, Params, Instrs),
MaybeRval = yes(Rval)
;
vn_table__lookup_defn(Vn, Vnrval, "vn_flush__hp_incr",
VnTables0),
(
Vnrval = vn_origlval(Vnlval),
( Vnlval = vn_hp ->
MaybeRval = no
;
error("non-hp origlval in flush_hp_incr")
),
VnTables2 = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_mkword(_, _),
error("mkword in calculation of new hp")
;
Vnrval = vn_const(Const),
( Const = int_const(_) ->
MaybeRval = yes(const(Const))
;
error("non-int const in flush_hp_incr")
),
VnTables2 = VnTables0,
Templocs = Templocs0,
Instrs = []
;
Vnrval = vn_create(_, _, _, _, _, _),
error("create in calculation of new hp")
;
Vnrval = vn_unop(_, _),
error("unop in calculation of new hp")
;
Vnrval = vn_binop(Op, SubVn1, SubVn2),
vn_flush__hp_incr(SubVn1, [src_vn(Vn) | Srcs],
Forbidden, MaybeRval1, VnTables0, VnTables1,
Templocs0, Templocs1, Params, Instrs1),
vn_flush__hp_incr(SubVn2, [src_vn(Vn) | Srcs],
Forbidden, MaybeRval2, VnTables1, VnTables2,
Templocs1, Templocs, Params, Instrs2),
list__append(Instrs1, Instrs2, Instrs),
(
MaybeRval1 = yes(Rval1),
MaybeRval2 = yes(Rval2),
MaybeRval = yes(binop(Op, Rval1, Rval2))
;
MaybeRval1 = yes(_Rval1),
MaybeRval2 = no,
( Op = (+) ->
MaybeRval = MaybeRval1
;
error("non-+ op on hp")
)
;
MaybeRval1 = no,
MaybeRval2 = yes(_Rval2),
( Op = (+) ->
MaybeRval = MaybeRval2
;
error("non-+ op on hp")
)
;
MaybeRval1 = no,
MaybeRval2 = no,
error("two 'no's in flush_hp_incr")
)
;
Vnrval = vn_stackvar_addr(_),
error("stackvar_addr in calculation of new hp")
;
Vnrval = vn_framevar_addr(_),
error("framevar_addr in calculation of new hp")
;
Vnrval = vn_heap_addr(_, _, _),
error("heap_addr in calculation of new hp")
),
( Srcs = [SrcPrime | _] ->
Src = SrcPrime
;
error("empty source list in flush_vn")
),
vn_table__del_old_use(Vn, Src, VnTables2, VnTables)
).
%-----------------------------------------------------------------------------%
:- pred vn_flush__free_of_old_hp(list(vn), vn_tables).
:- mode vn_flush__free_of_old_hp(in, in) is semidet.
vn_flush__free_of_old_hp([], _VnTables).
vn_flush__free_of_old_hp([Vn | Vns], VnTables) :-
vn_table__lookup_defn(Vn, Vnrval, "vn_flush__free_of_old_hp", VnTables),
\+ Vnrval = vn_origlval(vn_hp),
vn_flush__free_of_old_hp(Vns, VnTables).
:- pred vn_flush__rec_find_ref_vns(vn, list(vn), vn_tables).
:- mode vn_flush__rec_find_ref_vns(in, out, in) is det.
vn_flush__rec_find_ref_vns(Vn, [Vn | DeepVns], VnTables) :-
vn_table__lookup_defn(Vn, Vnrval, "vn_flush__rec_find_ref_vns",
VnTables),
vn_util__find_sub_vns(Vnrval, ImmedVns),
vn_flush__rec_find_ref_vns_list(ImmedVns, DeepVns, VnTables).
:- pred vn_flush__rec_find_ref_vns_list(list(vn), list(vn), vn_tables).
:- mode vn_flush__rec_find_ref_vns_list(in, out, in) is det.
vn_flush__rec_find_ref_vns_list([], [], _VnTables).
vn_flush__rec_find_ref_vns_list([Vn | Vns], SubVns, VnTables) :-
vn_flush__rec_find_ref_vns(Vn, SubVns0, VnTables),
vn_flush__rec_find_ref_vns_list(Vns, SubVns1, VnTables),
list__append(SubVns0, SubVns1, SubVns).
%-----------------------------------------------------------------------------%
:- pred vn_flush__access_path(vnlval, list(vn_src), list(lval), lval,
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__access_path(in, in, in, out, in, out, in, out, in, out)
is det.
vn_flush__access_path(Vnlval, Srcs, Forbidden, Lval, VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs) :-
(
Vnlval = vn_reg(Type, Num),
Lval = reg(Type, Num),
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_temp(Type, Num),
Lval = temp(Type, Num),
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_stackvar(Slot),
Lval = stackvar(Slot),
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_framevar(Slot),
Lval = framevar(Slot),
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_succip,
Lval = succip,
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_maxfr,
Lval = maxfr,
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_curfr,
Lval = curfr,
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_succfr(Vn1),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = succfr(Rval)
;
Vnlval = vn_prevfr(Vn1),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = prevfr(Rval)
;
Vnlval = vn_redofr(Vn1),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = redofr(Rval)
;
Vnlval = vn_redoip(Vn1),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = redoip(Rval)
;
Vnlval = vn_succip(Vn1),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = succip(Rval)
;
Vnlval = vn_hp,
Lval = hp,
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_sp,
Lval = sp,
VnTables = VnTables0,
Templocs = Templocs0,
AccessInstrs = []
;
Vnlval = vn_field(Tag, Vn1, Vn2),
vn_flush__vn(Vn1, [src_access(Vnlval) | Srcs], Forbidden, Rval1,
VnTables0, VnTables1,
Templocs0, Templocs1, Params, AccessInstrs1),
vn_flush__vn(Vn2, [src_access(Vnlval) | Srcs], Forbidden, Rval2,
VnTables1, VnTables,
Templocs1, Templocs, Params, AccessInstrs2),
Lval = field(Tag, Rval1, Rval2),
list__append(AccessInstrs1, AccessInstrs2, AccessInstrs)
;
Vnlval = vn_mem_ref(Vn),
vn_flush__vn(Vn, [src_access(Vnlval) | Srcs], Forbidden, Rval,
VnTables0, VnTables,
Templocs0, Templocs, Params, AccessInstrs),
Lval = mem_ref(Rval)
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% If the vn currently stored in the vnlval is used elsewhere,
% and if it cannot be recreated blind (or at least not cheaply),
% then save the value somewhere else. We prefer the somewhere else
% to be a location where we have to store the value anyway.
% However, we must not choose a location that is used in the expression
% being assigned to the vnlval.
% If we are overwriting a temporary location, it may not have
% a current value entry in the vn tables.
% We cannot look up the old contents of the Vnlval here, because
% it may have been already overwritten in flush_old_hp.
:- pred vn_flush__maybe_save_prev_value(vnlval, vn, vn, list(lval),
vn_tables, vn_tables, templocs, templocs, vn_params, list(instruction)).
:- mode vn_flush__maybe_save_prev_value(in, in, in, in, in, out, in, out,
in, out) is det.
vn_flush__maybe_save_prev_value(Vnlval, OldVn, NewVn, Forbidden,
VnTables0, VnTables, Templocs0, Templocs, Params, Instrs) :-
(
vn_table__set_current_value(Vnlval, NewVn, VnTables0,
VnTablesProbe),
vn_table__search_uses(OldVn, Uses, VnTablesProbe),
vn_util__real_uses(Uses, RealUses, VnTablesProbe),
\+ RealUses = [],
vn_util__is_const_expr(OldVn, no, VnTables0),
vn_table__lookup_current_locs(OldVn, Locs0,
"vn_flush__maybe_save_prev_value", VnTables0),
list__delete_all(Locs0, Vnlval, Locs),
vn_flush__no_good_copies(Locs)
->
(
vn_flush__find_cheap_users(OldVn, ReqLocs, VnTables0),
vn_util__choose_cheapest_loc(ReqLocs, Presumed)
->
vn_util__no_access_vnlval_to_lval(Presumed,
MaybePresumed),
(
MaybePresumed = yes(PresumedLval),
(
list__member(PresumedLval, Forbidden)
->
vn_flush__choose_temp(OldVn,
VnTables0, Templocs0,
Templocs1, Chosen)
;
RealUses = [_,_|_],
\+ Presumed = vn_reg(_, _)
->
vn_flush__choose_temp(OldVn,
VnTables0, Templocs0,
Templocs1, Chosen)
;
Chosen = Presumed,
Templocs1 = Templocs0
)
;
MaybePresumed = no,
% we cannot use Presumed even if it is not
% in Forbidden
vn_flush__choose_temp(OldVn, VnTables0,
Templocs0, Templocs1, Chosen)
)
;
vn_flush__choose_temp(OldVn, VnTables0,
Templocs0, Templocs1, Chosen)
),
vn_flush__ensure_assignment(Chosen, OldVn, Forbidden,
VnTables0, VnTables, Templocs1, Templocs, Params,
Instrs1),
opt_debug__dump_uses_list(RealUses, Debug),
Instrs = [comment(Debug) - "" | Instrs1]
;
VnTables = VnTables0,
Templocs = Templocs0,
Instrs = []
).
:- pred vn_flush__no_good_copies(list(vnlval)).
:- mode vn_flush__no_good_copies(in) is semidet.
vn_flush__no_good_copies([]).
vn_flush__no_good_copies([Vnlval | Vnlvals]) :-
Vnlval = vn_field(_, _, _),
vn_flush__no_good_copies(Vnlvals).