summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--autoload.c2
-rw-r--r--stdlib/comp-opts.tl9
-rw-r--r--stdlib/compiler.tl14
-rw-r--r--txr.139
4 files changed, 48 insertions, 16 deletions
diff --git a/autoload.c b/autoload.c
index 1f513829..64db4901 100644
--- a/autoload.c
+++ b/autoload.c
@@ -686,7 +686,7 @@ static val compiler_set_entries(val fun)
};
val slname[] = {
lit("shadow-fun"), lit("shadow-var"), lit("shadow-cross"),
- lit("unused"), lit("log-level"), nil
+ lit("unused"), lit("log-level"), lit("opt-tail-calls"), nil
};
autoload_sys_set(al_struct, sys_name, fun);
autoload_set(al_struct, sname, fun);
diff --git a/stdlib/comp-opts.tl b/stdlib/comp-opts.tl
index bc0e7c59..c964f341 100644
--- a/stdlib/comp-opts.tl
+++ b/stdlib/comp-opts.tl
@@ -31,12 +31,11 @@
usr:shadow-cross
usr:unused
usr:constant-throws
- usr:log-level)
+ usr:log-level
+ usr:opt-tail-calls)
-(defsymacro %warning-syms% '(shadow-fun shadow-var shadow-cross
- unused log-level constant-throws))
-
-(defvar usr:*compile-opts* (new compile-opts unused t constant-throws t))
+(defvar usr:*compile-opts* (new compile-opts unused t constant-throws t
+ opt-tail-calls :))
(defmacro when-opt (compile-opt . forms)
(with-gensyms (optval)
diff --git a/stdlib/compiler.tl b/stdlib/compiler.tl
index 37c5df56..6a49551a 100644
--- a/stdlib/compiler.tl
+++ b/stdlib/compiler.tl
@@ -342,7 +342,7 @@
;; 0 - no optimization
;; 1 - constant folding, algebraics.
-;; 2 - block elimination, frame elimination
+;; 2 - block elimination, frame elimination, self tail calls
;; 3 - lambda/combinator lifting
;; 4 - control-flow: jump-threading, dead code
;; 5 - data-flow: dead registers, useless regisers
@@ -1127,6 +1127,7 @@
(*top-level* nil)
(tfn *tail-fun*)
(tpos nil)
+ (tco *compile-opts*.opt-tail-calls)
(pars (new (fun-param-parser par-syntax form)))
(need-frame (or (plusp pars.nfix) pars.rest))
(nenv (if need-frame (new env up env co me) env))
@@ -1134,7 +1135,10 @@
(when (> pars.nfix %max-lambda-fixed-args%)
(compile-warning form "~s arguments in a lambda (max is ~s)"
pars.nfix %max-lambda-fixed-args%))
- (when (and tfn (eq tfn.lambda form))
+ (when (and (caseq tco
+ (: (>= *opt-level* 2))
+ ((t) t))
+ tfn (eq tfn.lambda form))
(set tfn.env nenv
tfn.label (gensym "l")
tpos t))
@@ -2846,11 +2850,11 @@
(defmacro usr:with-compile-opts (:form form . clauses)
(match-case clauses
(() ())
- (((@(as op @(or nil t :warn :error @(integerp))) . @syms) . @rest)
+ (((@(as op @(or nil t :warn :error : @(integerp))) . @syms) . @rest)
(each ((s syms))
- (unless (member s %warning-syms%)
+ (unless (member s (load-time (slots 'compile-opts)))
(compile-error form
- "~s isn't a recognized warning option" s)))
+ "~s isn't a recognized compile option" s)))
^(compiler-let ((*compile-opts* (let ((co (copy *compile-opts*)))
(set ,*(mappend (ret ^(co.,@1 ,op))
syms))
diff --git a/txr.1 b/txr.1
index 3a63ff71..cad61a3b 100644
--- a/txr.1
+++ b/txr.1
@@ -95824,7 +95824,10 @@ are translated into calls to more efficient two-argument internal functions.
.IP 2
Blocks which can be easily confirmed not to be used as exit points are removed.
Variable frames in which no lexically captured variables are bound, and no
-dynamic variables are bound, are eliminated.
+dynamic variables are bound, are eliminated. Self tail-calls are optimized,
+which is also controlled by the
+.code opt-tail-calls
+option.
.IP 3
Lambda expressions and calls to combinator functions such as
.code chain
@@ -96084,12 +96087,13 @@ and not to propagate compiled versions of the macros which produced it.
.mets (defstruct compile-opts ()
.mets \ \ shadow-fun shadow-var shadow-cross unused
.mets \ \ log-level constant-throws)
+.mets \ \ opt-tail-calls)
.syne
.desc
The
.code compile-opts
structure represents compiler options: its slots are variables which affect
-compiler behavior.
+compiler behavior, including diagnostics and code generation.
The compiler expects the special variable
.code *compile-opts*
to hold a
@@ -96098,9 +96102,6 @@ structure. It is recommended to manipulate options using the
.code with-compile-opts
macro.
-Currently, all of the options are diagnostic. In the future, there may be other
-kinds of options.
-
Diagnostic options which are Boolean take on the values
.codn nil ,
.codn t ,
@@ -96124,6 +96125,20 @@ The
.code :error
value indicates that the diagnostic will be an error.
+Boolean code generation options are three-valued, taking on the symbols
+.codn t ,
+.code nil
+and
+.code :
+(colon). The
+.code :
+value indicates that the compiler decides whether the option is on or off,
+in a way specific to that option. The
+.code t
+and
+.code nil
+values specify it directly, overriding any such logic.
+
The slots of
.code compile-opts
are as follows:
@@ -96189,6 +96204,20 @@ by default. Controls whether the compiler issues diagnostics when
it encounters a constant expression, whose evaluation throws
an exception, such as
.codn "(/ 0 0)" .
+.coIP opt-tail-calls
+Code generation option, controlling whether the compiler optimizes tail calls.
+The default behavior, requested by the default value
+.code :
+is to turn on the optimization when the
+.code *opt-level*
+is at least 2. Optimizing tail calls means that when a function calls itself
+by name, and the call occurs in a tail position, the call is translated to an
+assignment of the parameter values from the call arguments, and a backwards
+jump to the start of the function. All argument defaulting works as usual. If
+the tail call requires default values for optional arguments, those expressions
+are evaluated, even if they were previously evaluated on entry into the
+function. Most contexts where the function returns the value of the self
+call are tail positions.
.RE
.coNP Special Variable @ *compile-opts*