[m-dev.] diff: MLDS back-end: implement tail recursion optimization
Fergus Henderson
fjh at cs.mu.OZ.AU
Thu Nov 11 03:19:52 AEDT 1999
Estimated hours taken: 12
Implement tail recursion optimization in the MLDS back-end.
compiler/ml_tailcall.m:
A new pass, which marks functions that are suitable for
tail call optimization.
compiler/mercury_compile.m:
Invoke the new tail recursion marking pass.
Also invoke the simplify pass before running the MLDS code generator;
this is needed for the same reason as it is with the LLDS code
generator.
compiler/mlds_to_c.m:
Optimize tail recursive calls.
Workspace: /d-drive/home/hg/fjh/mercury
Index: compiler/mercury_compile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mercury_compile.m,v
retrieving revision 1.143
diff -u -d -r1.143 mercury_compile.m
--- compiler/mercury_compile.m 1999/11/08 22:27:49 1.143
+++ compiler/mercury_compile.m 1999/11/10 15:34:23
@@ -45,7 +45,7 @@
:- import_module llds_common, transform_llds, llds_out.
:- import_module continuation_info, stack_layout.
-:- import_module mlds, ml_code_gen, ml_elim_nested, mlds_to_c.
+:- import_module mlds, ml_code_gen, ml_elim_nested, ml_tailcall, mlds_to_c.
% miscellaneous compiler modules
:- import_module prog_data, hlds_module, hlds_pred, hlds_out, llds, rl.
@@ -2223,21 +2223,33 @@
:- pred mercury_compile__mlds_backend(module_info, io__state, io__state).
:- mode mercury_compile__mlds_backend(in, di, uo) is det.
-mercury_compile__mlds_backend(HLDS) -->
+mercury_compile__mlds_backend(HLDS50) -->
globals__io_lookup_bool_option(verbose, Verbose),
globals__io_lookup_bool_option(statistics, Stats),
+ mercury_compile__simplify(HLDS50, no, yes, Verbose, Stats,
+ process_all_nonimported_nonaditi_procs, HLDS53),
+ mercury_compile__maybe_dump_hlds(HLDS53, "53", "simplify2"),
+
+ { HLDS = HLDS53 },
+
maybe_write_string(Verbose, "% Converting HLDS to MLDS...\n"),
ml_code_gen(HLDS, MLDS0),
maybe_write_string(Verbose, "% done.\n"),
maybe_report_stats(Stats),
+ % XXX this pass should be conditional on a compilation option
+
+ maybe_write_string(Verbose, "% Detecting tail calls...\n"),
+ ml_mark_tailcalls(MLDS0, MLDS1),
+
globals__io_lookup_bool_option(gcc_nested_functions, NestedFuncs),
( { NestedFuncs = no } ->
- maybe_write_string(Verbose, "% Flattening nested functions...\n"),
- ml_elim_nested(MLDS0, MLDS)
+ maybe_write_string(Verbose,
+ "% Flattening nested functions...\n"),
+ ml_elim_nested(MLDS1, MLDS)
;
- { MLDS = MLDS0 }
+ { MLDS = MLDS1 }
),
maybe_write_string(Verbose, "% Converting MLDS to C...\n"),
cvs diff: compiler/ml_tailcall.m is a new entry, no comparison available
Index: compiler/mlds_to_c.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mlds_to_c.m,v
retrieving revision 1.11
diff -u -d -r1.11 mlds_to_c.m
--- compiler/mlds_to_c.m 1999/11/10 04:13:10 1.11
+++ compiler/mlds_to_c.m 1999/11/10 15:35:00
@@ -241,7 +241,8 @@
% GNU C __label__ declarations must precede
% ordinary variable declarations.
%
- { list__filter(defn_is_commit_type_var, Defns, LabelDecls, OtherDefns) },
+ { list__filter(defn_is_commit_type_var, Defns, LabelDecls,
+ OtherDefns) },
list__foldl(OutputDefn, LabelDecls),
list__foldl(OutputDefn, OtherDefns)
;
@@ -429,14 +430,44 @@
;
{ MaybeBody = yes(Body) },
io__write_string("\n"),
+
+ mlds_indent(Indent),
+ io__write_string("{\n"),
+
%
- % C requires function bodies to be blocks
+ % We wrap the function body inside a `for(;;)' loop
+ % so that we can use `continue;' inside the function body
+ % to optimize tail recursive calls.
+ % XXX tail recursion optimization should be disable-able
%
- ( { Body = statement(block(_, _), _) } ->
- mlds_output_statement(Indent, Name, Body)
+ mlds_indent(Indent + 1),
+ io__write_string("for(;;)\n"),
+ mlds_indent(Indent + 2),
+ io__write_string("{\n"),
+
+ { FuncInfo = func_info(Name, Signature) },
+ mlds_output_statement(Indent + 3, FuncInfo, Body),
+
+ %
+ % Output a `return' statement to terminate the `for(;;)' loop.
+ % This should only be necessary for functions with no
+ % return values; for functions with return values,
+ % the function body should never just fall through,
+ % instead it must always return via a `return' statement.
+ %
+ { Signature = mlds__func_params(_Parameters, RetTypes) },
+ ( { RetTypes = [] } ->
+ mlds_output_stmt(Indent + 3, FuncInfo, return([]))
;
- mlds_output_stmt(Indent, Name, block([], [Body]))
- )
+ mlds_indent(Indent + 3),
+ io__write_string("/*NOTREACHED*/\n")
+ ),
+
+ mlds_indent(Indent + 2),
+ io__write_string("}\n"), % end the `for(;;)'
+
+ mlds_indent(Indent),
+ io__write_string("}\n") % end the function
).
:- pred mlds_output_func_decl(int, qualified_entity_name, func_params,
@@ -688,64 +719,67 @@
% Code to output statements
%
-:- pred mlds_output_statements(int, qualified_entity_name,
- list(mlds__statement), io__state, io__state).
+:- type func_info
+ ---> func_info(mlds__qualified_entity_name, mlds__func_params).
+
+:- pred mlds_output_statements(int, func_info, list(mlds__statement),
+ io__state, io__state).
:- mode mlds_output_statements(in, in, in, di, uo) is det.
-mlds_output_statements(Indent, FuncName, Statements) -->
- list__foldl(mlds_output_statement(Indent, FuncName), Statements).
+mlds_output_statements(Indent, FuncInfo, Statements) -->
+ list__foldl(mlds_output_statement(Indent, FuncInfo), Statements).
-:- pred mlds_output_statement(int, qualified_entity_name, mlds__statement,
+:- pred mlds_output_statement(int, func_info, mlds__statement,
io__state, io__state).
:- mode mlds_output_statement(in, in, in, di, uo) is det.
-mlds_output_statement(Indent, FuncName, mlds__statement(Statement, Context)) -->
+mlds_output_statement(Indent, FuncInfo, mlds__statement(Statement, Context)) -->
mlds_output_context(Context),
- mlds_output_stmt(Indent, FuncName, Statement).
+ mlds_output_stmt(Indent, FuncInfo, Statement).
-:- pred mlds_output_stmt(int, qualified_entity_name, mlds__stmt,
- io__state, io__state).
+:- pred mlds_output_stmt(int, func_info, mlds__stmt, io__state, io__state).
:- mode mlds_output_stmt(in, in, in, di, uo) is det.
%
% sequence
%
-mlds_output_stmt(Indent, FuncName, block(Defns, Statements)) -->
+mlds_output_stmt(Indent, FuncInfo, block(Defns, Statements)) -->
mlds_indent(Indent),
io__write_string("{\n"),
( { Defns \= [] } ->
+ { FuncInfo = func_info(FuncName, _) },
{ FuncName = qual(ModuleName, _) },
mlds_output_defns(Indent + 1, ModuleName, Defns),
io__write_string("\n")
;
[]
),
- mlds_output_statements(Indent + 1, FuncName, Statements),
+ mlds_output_statements(Indent + 1, FuncInfo, Statements),
mlds_indent(Indent),
io__write_string("}\n").
%
% iteration
%
-mlds_output_stmt(Indent, FuncName, while(Cond, Statement, no)) -->
+mlds_output_stmt(Indent, FuncInfo, while(Cond, Statement, no)) -->
mlds_indent(Indent),
io__write_string("while ("),
mlds_output_rval(Cond),
io__write_string(")\n"),
- mlds_output_statement(Indent + 1, FuncName, Statement).
-mlds_output_stmt(Indent, FuncName, while(Cond, Statement, yes)) -->
+ mlds_output_statement(Indent + 1, FuncInfo, Statement).
+mlds_output_stmt(Indent, FuncInfo, while(Cond, Statement, yes)) -->
mlds_indent(Indent),
- io__write_string("do {\n"),
- mlds_output_statement(Indent + 1, FuncName, Statement),
+ io__write_string("do\n"),
+ mlds_output_statement(Indent + 1, FuncInfo, Statement),
mlds_indent(Indent),
- io__write_string("} while ("),
+ io__write_string("while ("),
mlds_output_rval(Cond),
io__write_string(");\n").
%
% selection (see also computed_goto)
%
-mlds_output_stmt(Indent, FuncName, if_then_else(Cond, Then0, MaybeElse)) -->
+mlds_output_stmt(Indent, FuncInfo, if_then_else(Cond, Then0, MaybeElse)) -->
%
% we need to take care to avoid problems caused by the
% dangling else ambiguity
@@ -762,11 +796,11 @@
io__write_string("if ("),
mlds_output_rval(Cond),
io__write_string(")\n"),
- mlds_output_statement(Indent + 1, FuncName, Then),
+ mlds_output_statement(Indent + 1, FuncInfo, Then),
( { MaybeElse = yes(Else) } ->
mlds_indent(Indent),
io__write_string("else\n"),
- mlds_output_statement(Indent + 1, FuncName, Else)
+ mlds_output_statement(Indent + 1, FuncInfo, Else)
;
[]
).
@@ -774,7 +808,7 @@
%
% transfer of control
%
-mlds_output_stmt(Indent, _FuncName, label(LabelName)) -->
+mlds_output_stmt(Indent, _FuncInfo, label(LabelName)) -->
%
% Note: MLDS allows labels at the end of blocks.
% C doesn't. Hence we need to insert a semi-colon after the colon
@@ -783,12 +817,12 @@
mlds_indent(Indent - 1),
mlds_output_label_name(LabelName),
io__write_string(":;\n").
-mlds_output_stmt(Indent, _FuncName, goto(LabelName)) -->
+mlds_output_stmt(Indent, _FuncInfo, goto(LabelName)) -->
mlds_indent(Indent),
io__write_string("goto "),
mlds_output_label_name(LabelName),
io__write_string(";\n").
-mlds_output_stmt(Indent, _FuncName, computed_goto(Expr, Labels)) -->
+mlds_output_stmt(Indent, _FuncInfo, computed_goto(Expr, Labels)) -->
% XXX for GNU C, we could output potentially more efficient code
% by using an array of labels; this would tell the compiler that
% it didn't need to do any range check.
@@ -815,34 +849,111 @@
%
% function call/return
%
-mlds_output_stmt(Indent, _FuncName, call(_Signature, Func, MaybeObject,
- Args, Results, IsTailCall)) -->
- mlds_indent(Indent),
- ( { IsTailCall = tail_call } ->
- io__write_string("return ")
- ;
- []
- ),
- ( { MaybeObject = yes(Object) } ->
- mlds_output_bracketed_rval(Object),
- io__write_string(".")
- ;
- []
- ),
- ( { Results = [] } ->
- []
- ; { Results = [Lval] } ->
- mlds_output_lval(Lval),
- io__write_string(" = ")
+mlds_output_stmt(Indent, CallerFuncInfo, call(_Signature, FuncRval,
+ MaybeObject, CallArgs, Results, IsTailCall)) -->
+ %
+ % Optimize directly-recursive tail calls
+ % XXX tail recursion optimization should be disable-able
+ %
+ { CallerFuncInfo = func_info(Name, Params) },
+ (
+ %
+ % check if this call can be optimized as a tail call
+ %
+ { IsTailCall = tail_call },
+
+ %
+ % check if the callee adddress is the same as
+ % the caller
+ %
+ { FuncRval = const(code_addr_const(CodeAddr)) },
+ {
+ CodeAddr = proc(QualifiedProcLabel),
+ MaybeSeqNum = no
+ ;
+ CodeAddr = internal(QualifiedProcLabel, SeqNum),
+ MaybeSeqNum = yes(SeqNum)
+ },
+ { QualifiedProcLabel = qual(ModuleName, PredLabel - ProcId) },
+ % check that the module name matches
+ { Name = qual(ModuleName, FuncName) },
+ % check that the PredLabel, ProcId, and MaybeSeqNum match
+ { FuncName = function(PredLabel, ProcId, MaybeSeqNum, _) },
+
+ %
+ % In C++, `this' is a constant, so our usual technique
+ % of assigning the arguments won't work if it is a
+ % member function. Thus we don't do this optimization
+ % if we're optimizing a member function call
+ %
+ { MaybeObject = no }
+ ->
+ mlds_indent(Indent),
+ io__write_string("{\n"),
+ mlds_indent(Indent + 1),
+ io__write_string("/* tail recursive call */\n"),
+ { Params = mlds__func_params(FuncArgs, _RetTypes) },
+ mlds_output_assign_args(Indent + 1, ModuleName, FuncArgs,
+ CallArgs),
+ mlds_indent(Indent + 1),
+ io__write_string("continue; /* go to start of function */\n"),
+ mlds_indent(Indent),
+ io__write_string("}\n")
;
- { error("mld_output_stmt: multiple return values") }
- ),
- mlds_output_bracketed_rval(Func),
- io__write_string("("),
- io__write_list(Args, ", ", mlds_output_rval),
- io__write_string(");\n").
+ %
+ % Optimize general tail calls.
+ % We can't really do much here except to insert `return'
+ % as an extra hint to the C compiler.
+ %
+ % If Results = [], i.e. the function has `void' return type,
+ % then this would result in code that is not legal ANSI C
+ % (although it _is_ legal in GNU C and in C++),
+ % so for that case, we put the return statement after
+ % the call -- see below. We need to enclose it inside
+ % an extra pair of curly braces in case this `call'
+ % is e.g. inside an if-then-else.
+ %
+ mlds_indent(Indent),
+ ( { IsTailCall = tail_call } ->
+ ( { Results \= [] } ->
+ io__write_string("return ")
+ ;
+ io__write_string("{\n"),
+ mlds_indent(Indent + 1)
+ )
+ ;
+ []
+ ),
+ ( { MaybeObject = yes(Object) } ->
+ mlds_output_bracketed_rval(Object),
+ io__write_string(".") % XXX should this be "->"?
+ ;
+ []
+ ),
+ ( { Results = [] } ->
+ []
+ ; { Results = [Lval] } ->
+ mlds_output_lval(Lval),
+ io__write_string(" = ")
+ ;
+ { error("mld_output_stmt: multiple return values") }
+ ),
+ mlds_output_bracketed_rval(FuncRval),
+ io__write_string("("),
+ io__write_list(CallArgs, ", ", mlds_output_rval),
+ io__write_string(");\n"),
-mlds_output_stmt(Indent, _FuncName, return(Results)) -->
+ ( { IsTailCall = tail_call, Results = [] } ->
+ mlds_indent(Indent + 1),
+ io__write_string("return;\n"),
+ mlds_indent(Indent),
+ io__write_string("}\n")
+ ;
+ []
+ )
+ ).
+
+mlds_output_stmt(Indent, _FuncInfo, return(Results)) -->
mlds_indent(Indent),
io__write_string("return"),
( { Results = [] } ->
@@ -858,7 +969,7 @@
%
% commits
%
-mlds_output_stmt(Indent, _FuncName, do_commit(Ref)) -->
+mlds_output_stmt(Indent, _FuncInfo, do_commit(Ref)) -->
mlds_indent(Indent),
globals__io_lookup_bool_option(gcc_local_labels, GCC_LocalLabels),
( { GCC_LocalLabels = yes } ->
@@ -872,7 +983,7 @@
io__write_string(", 1)")
),
io__write_string(";\n").
-mlds_output_stmt(Indent, FuncName, try_commit(Ref, Stmt0, Handler)) -->
+mlds_output_stmt(Indent, FuncInfo, try_commit(Ref, Stmt0, Handler)) -->
globals__io_lookup_bool_option(gcc_local_labels, GCC_LocalLabels),
(
{ GCC_LocalLabels = yes },
@@ -890,7 +1001,7 @@
% not a complicated expression. If not, the
% C compiler will catch it.
- mlds_output_statement(Indent, FuncName, Stmt0),
+ mlds_output_statement(Indent, FuncInfo, Stmt0),
mlds_indent(Indent),
io__write_string("goto "),
@@ -901,7 +1012,7 @@
mlds_output_lval(Ref),
io__write_string(":\n"),
- mlds_output_statement(Indent, FuncName, Handler),
+ mlds_output_statement(Indent, FuncInfo, Handler),
mlds_indent(Indent - 1),
mlds_output_lval(Ref),
@@ -939,15 +1050,46 @@
mlds_output_lval(Ref),
io__write_string(") == 0)\n"),
- mlds_output_statement(Indent + 1, FuncName, Stmt),
+ mlds_output_statement(Indent + 1, FuncInfo, Stmt),
mlds_indent(Indent),
io__write_string("else\n"),
- mlds_output_statement(Indent + 1, FuncName, Handler)
+ mlds_output_statement(Indent + 1, FuncInfo, Handler)
).
+ % Assign the specified list of rvals to the arguments.
+ % This is used as part of tail recursion optimization (see above).
+:- pred mlds_output_assign_args(int, mlds_module_name, mlds__arguments,
+ list(mlds__rval), io__state, io__state).
+:- mode mlds_output_assign_args(in, in, in, in, di, uo) is det.
+mlds_output_assign_args(_, _, [_|_], []) -->
+ { error("mlds_output_assign_args: length mismatch") }.
+mlds_output_assign_args(_, _, [], [_|_]) -->
+ { error("mlds_output_assign_args: length mismatch") }.
+mlds_output_assign_args(_, _, [], []) --> [].
+mlds_output_assign_args(Indent, ModuleName,
+ [Name - _Type | Rest], [Arg | Args]) -->
+ (
+ %
+ % don't bother assigning a variable to itself
+ %
+ { Name = data(var(VarName)) },
+ { QualVarName = qual(ModuleName, VarName) },
+ { Arg = lval(var(QualVarName)) }
+ ->
+ []
+ ;
+ mlds_indent(Indent),
+ mlds_output_fully_qualified_name(qual(ModuleName, Name),
+ mlds_output_name),
+ io__write_string(" = "),
+ mlds_output_rval(Arg),
+ io__write_string(";\n")
+ ),
+ mlds_output_assign_args(Indent, ModuleName, Rest, Args).
+
%
% exception handling
%
@@ -958,7 +1100,7 @@
%
% atomic statements
%
-mlds_output_stmt(Indent, _FuncName, atomic(AtomicStatement)) -->
+mlds_output_stmt(Indent, _FuncInfo, atomic(AtomicStatement)) -->
mlds_output_atomic_stmt(Indent, AtomicStatement).
:- pred mlds_output_label_name(mlds__label, io__state, io__state).
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3 | -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list