%-----------------------------------------------------------------------------%
% Copyright (C) 1996-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.
%-----------------------------------------------------------------------------%

% optimize.m - LLDS to LLDS optimizations.

% Main author: zs.

%-----------------------------------------------------------------------------%

:- module optimize.

:- interface.

:- import_module llds.
:- import_module io, list.

:- pred optimize_main(list(c_procedure)::in, global_data::in,
	list(c_procedure)::out, io__state::di, io__state::uo) is det.

:- pred optimize__proc(c_procedure::in, global_data::in,
	c_procedure::out, io__state::di, io__state::uo) is det.

%-----------------------------------------------------------------------------%

:- implementation.

:- import_module jumpopt, labelopt, dupelim, peephole.
:- import_module frameopt, delay_slot, value_number, options.
:- import_module globals, passes_aux, opt_util, opt_debug, vn_debug.
:- import_module continuation_info.

:- import_module bool, int, map, bimap, set, std_util.

optimize_main([], _, []) --> [].
optimize_main([Proc0 | Procs0], GlobalData, [Proc | Procs]) -->
	optimize__proc(Proc0, GlobalData, Proc), !,
	optimize_main(Procs0, GlobalData, Procs).

optimize__proc(CProc0, GlobalData, CProc) -->
	{ CProc0 = c_procedure(Name, Arity, PredProcId, Instrs0) },
	globals__io_lookup_bool_option(debug_opt, DebugOpt),
	opt_debug__msg(DebugOpt, "before optimization"),
	opt_debug__dump_instrs(DebugOpt, Instrs0),
	globals__io_lookup_int_option(optimize_repeat, AllRepeat),
	globals__io_lookup_int_option(optimize_vnrepeat, VnRepeat),
	globals__io_lookup_bool_option(optimize_value_number, ValueNumber),
	{
		global_data_maybe_get_proc_layout(GlobalData, PredProcId,
			ProcLayout)
	->
		ProcLayout = proc_layout_info(_, _, _, _, _, _, _, _,
			LabelMap),
		map__sorted_keys(LabelMap, LayoutLabels),
		set__sorted_list_to_set(LayoutLabels, LayoutLabelSet)
	;
		set__init(LayoutLabelSet)
	},
	( { ValueNumber = yes } ->
		{ NovnRepeat is AllRepeat - VnRepeat },
		optimize__repeat(NovnRepeat, no,  LayoutLabelSet,
			Instrs0, Instrs1),
		optimize__middle(Instrs1, no, LayoutLabelSet, Instrs2),
		optimize__repeat(VnRepeat, yes, LayoutLabelSet,
			Instrs2, Instrs3)
	;
		optimize__repeat(AllRepeat, no,  LayoutLabelSet,
			Instrs0, Instrs1),
		optimize__middle(Instrs1, yes, LayoutLabelSet, Instrs3)
	),
	optimize__last(Instrs3, LayoutLabelSet, Instrs),
	{ CProc = c_procedure(Name, Arity, PredProcId, Instrs) }.

%-----------------------------------------------------------------------------%

:- pred optimize__repeat(int::in, bool::in, set(label)::in,
	list(instruction)::in, list(instruction)::out,
	io__state::di, io__state::uo) is det.

optimize__repeat(Iter0, DoVn, LayoutLabelSet, Instrs0, Instrs) -->
	(
		{ Iter0 > 0 }
	->
		{ Iter1 is Iter0 - 1 },
		( { Iter1 = 0 } ->
			{ Final = yes }
		;
			{ Final = no }
		),
		optimize__repeated(Instrs0, DoVn, Final, LayoutLabelSet,
			Instrs1, Mod),
		( { Mod = yes } ->
			optimize__repeat(Iter1, DoVn, LayoutLabelSet,
				Instrs1, Instrs)
		;
			{ Instrs = Instrs1 }
		)
	;
		{ Instrs = Instrs0 }
	).

	% We short-circuit jump sequences before normal peepholing
	% to create more opportunities for use of the tailcall macro.

:- pred optimize__repeated(list(instruction)::in, bool::in, bool::in,
	set(label)::in, list(instruction)::out, bool::out,
	io__state::di, io__state::uo) is det.

optimize__repeated(Instrs0, DoVn, Final, LayoutLabelSet, Instrs, Mod) -->
	globals__io_lookup_bool_option(very_verbose, VeryVerbose),
	globals__io_lookup_bool_option(debug_opt, DebugOpt),
	{ opt_util__find_first_label(Instrs0, Label) },
	{ opt_util__format_label(Label, LabelStr) },

	globals__io_lookup_bool_option(optimize_value_number, ValueNumber),
	( { ValueNumber = yes, DoVn = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing value number for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		value_number_main(Instrs0, LayoutLabelSet, Instrs1),
		( { Instrs1 = Instrs0 } ->
			[]
		;
			opt_debug__msg(DebugOpt, "after value numbering"),
			opt_debug__dump_instrs(DebugOpt, Instrs1)
		)
	;
		{ Instrs1 = Instrs0 }
	),
	globals__io_lookup_bool_option(optimize_jumps, Jumpopt),
	globals__io_lookup_bool_option(optimize_fulljumps, FullJumpopt),
	globals__io_lookup_bool_option(checked_nondet_tailcalls,
		CheckedNondetTailCalls),
	globals__io_get_trace_level(TraceLevel),
	( { Jumpopt = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing jumps for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ jumpopt_main(Instrs1, LayoutLabelSet, TraceLevel,
			FullJumpopt, Final, CheckedNondetTailCalls,
			Instrs2, Mod1) },
		( { Mod1 = yes } ->
			opt_debug__msg(DebugOpt, "after jump optimization"),
			opt_debug__dump_instrs(DebugOpt, Instrs2)
		;
			[]
		)
	;
		{ Instrs2 = Instrs1 },
		{ Mod1 = no }
	),
	globals__io_lookup_bool_option(optimize_peep, Peephole),
	( { Peephole = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing locally for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		globals__io_get_gc_method(GC_Method),
		{ peephole__optimize(GC_Method, Instrs2, Instrs3, Mod2) },
		( { Mod2 = yes } ->
			opt_debug__msg(DebugOpt, "after peepholing"),
			opt_debug__dump_instrs(DebugOpt, Instrs3)
		;
			[]
		)
	;
		{ Instrs3 = Instrs2 },
		{ Mod2 = no }
	),
	globals__io_lookup_bool_option(optimize_labels, LabelElim),
	( { LabelElim = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing labels for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ labelopt_main(Instrs3, Final, LayoutLabelSet,
			Instrs4, Mod3) },
		( { Mod3 = yes } ->
			opt_debug__msg(DebugOpt, "after label optimization"),
			opt_debug__dump_instrs(DebugOpt, Instrs4)
		;
			[]
		)
	;
		{ Instrs4 = Instrs3 },
		{ Mod3 = no }
	),
	globals__io_lookup_bool_option(optimize_dups, DupElim),
	( { DupElim = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing duplicates for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ dupelim_main(Instrs4, Instrs) },
		( { Instrs = Instrs4 } ->
			[]
		;
			opt_debug__msg(DebugOpt, "after duplicate elimination"),
			opt_debug__dump_instrs(DebugOpt, Instrs)
		)
	;
		{ Instrs = Instrs4 }
	),
	{ Mod1 = no, Mod2 = no, Mod3 = no, Instrs = Instrs0 ->
		Mod = no
	;
		Mod = yes
	},
	globals__io_lookup_bool_option(statistics, Statistics),
	maybe_report_stats(Statistics).

:- pred optimize__middle(list(instruction)::in, bool::in, set(label)::in,
	list(instruction)::out, io__state::di, io__state::uo) is det.

optimize__middle(Instrs0, Final, LayoutLabelSet, Instrs) -->
	globals__io_lookup_bool_option(very_verbose, VeryVerbose),
	globals__io_lookup_bool_option(debug_opt, DebugOpt),
	{ opt_util__find_first_label(Instrs0, Label) },
	{ opt_util__format_label(Label, LabelStr) },

	globals__io_lookup_bool_option(optimize_frames, FrameOpt),
	( { FrameOpt = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing frames for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ frameopt_main(Instrs0, Instrs1, Mod1, Jumps) },
		( { Mod1 = yes } ->
			opt_debug__msg(DebugOpt, "after frame optimization"),
			opt_debug__dump_instrs(DebugOpt, Instrs1)
		;
			[]
		),
		globals__io_lookup_bool_option(optimize_fulljumps, FullJumpopt),
		globals__io_lookup_bool_option(checked_nondet_tailcalls,
			CheckedNondetTailCalls),
		globals__io_get_trace_level(TraceLevel),
		( { Jumps = yes, FullJumpopt = yes } ->
			( { VeryVerbose = yes } ->
				io__write_string("% Optimizing jumps for "),
				io__write_string(LabelStr),
				io__write_string("\n")
			;
				[]
			),
			{ jumpopt_main(Instrs1, LayoutLabelSet, TraceLevel,
				FullJumpopt, Final, CheckedNondetTailCalls,
				Instrs2, Mod2) },
			( { Mod2 = yes } ->
				opt_debug__msg(DebugOpt, "after jump optimization"),
				opt_debug__dump_instrs(DebugOpt, Instrs2)
			;
				[]
			)
		;
			{ Instrs2 = Instrs1 }
		),
		( { Mod1 = yes } ->
			( { VeryVerbose = yes } ->
				io__write_string("% Optimizing labels for "),
				io__write_string(LabelStr),
				io__write_string("\n")
			;
				[]
			),
			{ labelopt_main(Instrs2, Final, LayoutLabelSet,
				Instrs, Mod3) },
			( { Mod3 = yes } ->
				opt_debug__msg(DebugOpt, "after label optimization"),
				opt_debug__dump_instrs(DebugOpt, Instrs)
			;
				[]
			)
		;
			{ Instrs = Instrs2 }
		)
	;
		{ Instrs = Instrs0 }
	).

:- pred optimize__last(list(instruction)::in, set(label)::in,
	list(instruction)::out, io__state::di, io__state::uo) is det.

optimize__last(Instrs0, LayoutLabelSet, Instrs) -->
	globals__io_lookup_bool_option(very_verbose, VeryVerbose),
	globals__io_lookup_bool_option(debug_opt, DebugOpt),
	{ opt_util__find_first_label(Instrs0, Label) },
	{ opt_util__format_label(Label, LabelStr) },

	globals__io_lookup_bool_option(optimize_delay_slot, DelaySlot),
	globals__io_lookup_bool_option(optimize_value_number, ValueNumber),
	( { DelaySlot = yes ; ValueNumber = yes } ->
		% We must get rid of any extra labels added by other passes,
		% since they can confuse both post_value_number and delay_slot.
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing labels for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ labelopt_main(Instrs0, no, LayoutLabelSet, Instrs1, Mod1) },
		( { Mod1 = yes } ->
			opt_debug__msg(DebugOpt, "after label optimization"),
			opt_debug__dump_instrs(DebugOpt, Instrs1)
		;
			[]
		)
	;
		{ Instrs1 = Instrs0 }
	),
	( { DelaySlot = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing delay slot for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ fill_branch_delay_slot(Instrs1, Instrs2) },
		( { Instrs1 = Instrs0 } ->
			opt_debug__msg(DebugOpt, "after delay slot filling"),
			opt_debug__dump_instrs(DebugOpt, Instrs2)
		;
			[]
		)
	;
		{ Instrs2 = Instrs1 }
	),
	( { ValueNumber = yes } ->
		( { VeryVerbose = yes } ->
			io__write_string("% Optimizing post value number for "),
			io__write_string(LabelStr),
			io__write_string("\n")
		;
			[]
		),
		{ value_number__post_main(Instrs2, Instrs) },
		( { Instrs = Instrs2 } ->
			[]
		;
			opt_debug__msg(DebugOpt, "after post value number"),
			opt_debug__dump_instrs(DebugOpt, Instrs)
		)
	;
		{ Instrs = Instrs1 }
	).
