| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In situations when the compiler evaluates a constant expression in order
to make some code generating decision, we don't just want to be using
safe-const-eval. While that prevents the compiler from blowing up, and
issues a diagnostic, it causes incorrect code to be generated: code
which does not incorporate the unsafe expression. Concrete example:
(if (sqrt -1) (foo) (bar))
if we simply evaluate (sqrt -1) with safe-const-eval, we get a
diagnostic, and the value nil comes out. The compiler will thus
constant-fold this to (bar). Though the diagnostic was emitted,
executing the compiled code does not produce the exception from
(sqrt -1) any more, but just calls bar.
In certain cases where the compiler relies on the evaluation of a
constant expression, we should bypass those cases when the expression is
unsafe.
In cases where the expression will be integrated into the output
code, we can test with constantp. The same is true in some other
mitigating circumstances. For instance if we test with constantp,
and then require safe-const-eval to produce an integer, we are
okay, because a throwing evaluation will not produce an integer.
* stdlib/compiler.tl (safe-constantp): New function.
(compiler (comp-if, comp-ift, lambda-apply-transform)): Use
safe-constantp rather than constantp for determining whether
an expression is suitable for compile-time evaluation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When the compiler evaluates constant expressions, it's
possible that they throw, for instance (/ 1 0).
We now handle it better; the compiler warns about it
and is able to keep working, avoiding constant-folding
the expression.
* stdlib/compiler.tl (eval-cache-entry): New struct type.
(%eval-cache%): New hash table variable.
(compiler (comp-arith-form, comp-fun-form)): Add some missing
rlcp calls to track locations for rewritten arithmetic
expressions, so we usefullly diagnose a (sys:b/ ...) and such.
(compiler (comp-if, comp-ift, comp-arith-form,
comp-apply-call, reduce-constant, lambda-apply-transform)):
Replace instances of eval of constantp expressions with
safe-const-eval, and instances of the result of eval being
quoted with safe-const-reduce.
(orig-form, safe-const-reduce, safe-const-eval,
eval-cache-emit-warnings): New functions.
(compile-top-level, with-compilation-unit): Call
eval-emit-cache-warnings to warn about constant expressions
that threw.
squash! compiler: handle constant expressions that throw.
|
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler comp-arith-neg-form): Instead
of the length check on the form, we can use a tree case to
require three argument.
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler comp-arith-neg-form): Remove
algebraically incorrect transformation.
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler comp-arith-form): There is no
need here to pass the form through reduce-constant, since
we are about to divide up its arguments and individualy reduce
them, much like what that function does.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Commit c8f12ee44d226924b89cdd764b65a5f6a4030b81 tried to fix
an aspect of this problem. I ran into an issue where the try
code produced a D register as its output, and this was
clobbered by the catch code. In fact, the catch code simply
must not clobber the try fragment's output register. No matter
what register that is, it is not safe. A writable T register
could hold a variable.
For instance, this infinitely looping code is miscompiled
such that it terminates:
(let ((x 42))
(while (eql x 42)
(catch
(progn (throw 'foo)
x)
(foo () 0))))
When the exception is caught by the (foo () 0) clause
x is overwritten with that 0 value.
The variable x is assigned to a register like t13,
and since the progn form returns x as it value, it
compiles to a fragment (tfrag) which indicates t13
as its output register.
The catch code wrongly borrows ohis as its own output
register, placing the 0 value into it.
* stdlib/compiler.tl (compiler comp-catch): Get rid of the
coreg local variable, replacing all its uses with oreg.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The compiler is lifting top-level lambdas, such as those
generated by defun, using the load-time mechanism. This has
the undesireable effect of unnecessarily placing the lambdas
into a D register.
* stdlib/compiler.tl (*top-level*): New special variable.
This indicates that the compiler is compiling code that
is outside of any lambda.
(compiler comp-lambda-impl): Bind *top-level* to nil when
compiling lambda, so its interior is no longer at the
top level.
(compiler comp-lambda): Suppress the unnecessary lifting
optimization if the lambda expression is in the top-level,
outside of any other lambda, indicated by *top-level* being
true.
(compile-toplevel): Bind *top-level* to t.
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (compile-toplevel): Recently, I removed
the binding of *load-time* to t from this function. That is
not quite right; we want to positively bind it to nil. A new
top-level compile starts out in non-load-time. Suppose that
some compile-time evaluation recurses into the compiler.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler optimize): After the dataflow-driven
peephole optimization, call elim-dead-code again.
* stdlib/optimize.tl (basic-blocks check-bypass-empty): New method.
(basic-bocks elim-dead-code): After eliminating unreachable blocks
from the list, we use check-bypass-empty to squeeze out any
empty blocks: blocks that have no instructions in their list,
other than the leading label. This helps elim-next-jmp
to find more opportunities to eliminate a wasteful jump, because
sometimes these jumps straddle over empty blocks.
Furthermore, elim-next-jmp can generate more empty blocks itself;
so we check for this situation, delete the blocks and iterate.
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (usr:compile-toplevel): Do not
bind *load-time* to t at the top level. The idea behind this
binding was to treat load-time as a transparent form that does
nothing if it occurs in the top-level since the top-level is
already at load-time. However, this is problematic because it
breaks the expectation that load-time calculations are
factored out of a form and done prior to its evaluation, even
if that form is top-level.
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler comp-fun-form): Reduce
single-argument logior and logand calls to just the argument.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
At optimization level 2 or higher, an issue occurs whereby
code generation exhibits instabilities. The same code is
compiled slightly differently (but not incorrectly) depending
on irrelevant circumstances, due to some different registers
being used.
* stdlib/compiler.tl (compiler eliminate-frame): Do not free
the newly allocated t-registers inside a dohash loop.
We have a separate list of them in order; just hand that off
to free-tregs. The dohash loop is not ordered, because it
traverses a hash, which is keyed by object identities; i.e.
machine addresses assigned by memory allocation.
|
|
|
|
|
|
|
|
|
|
|
| |
The open-compile-streams function was calling trim-right with the
arguments in the wrong order, resulting in an output path equal
to the suffix of the input path.
Regression introduced in 8d8fee2e506806d9c117b17432ef3a5ec0d6f457.
* stdlib/compiler.tl (open-compile-streams): Swap in-path and
suff arguments in trim-right call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* Makefile, alloca.h, args.c, args.h, arith.c, arith.h, buf.c,
buf.h, chksum.c, chksum.h, chksums/crc32.c, chksums/crc32.h,
combi.c, combi.h, debug.c, debug.h, eval.c, eval.h, ffi.c,
ffi.h, filter.c, filter.h, ftw.c, ftw.h, gc.c, gc.h, glob.c,
glob.h, hash.c, hash.h, itypes.c, itypes.h, jmp.S, lib.c,
lib.h, lisplib.c, lisplib.h, match.c, match.h, parser.c,
parser.h, parser.l, parser.y, rand.c, rand.h, regex.c,
regex.h, signal.c, signal.h, socket.c, socket.h,
stdlib/asm.tl, stdlib/awk.tl, stdlib/build.tl,
stdlib/compiler.tl, stdlib/constfun.tl, stdlib/conv.tl,
stdlib/copy-file.tl, stdlib/debugger.tl, stdlib/defset.tl,
stdlib/doloop.tl, stdlib/each-prod.tl, stdlib/error.tl,
stdlib/except.tl, stdlib/ffi.tl, stdlib/getopts.tl,
stdlib/getput.tl, stdlib/hash.tl, stdlib/ifa.tl,
stdlib/keyparams.tl, stdlib/match.tl, stdlib/op.tl,
stdlib/optimize.tl, stdlib/package.tl, stdlib/param.tl,
stdlib/path-test.tl, stdlib/pic.tl, stdlib/place.tl,
stdlib/pmac.tl, stdlib/quips.tl, stdlib/save-exe.tl,
stdlib/socket.tl, stdlib/stream-wrap.tl, stdlib/struct.tl,
stdlib/tagbody.tl, stdlib/termios.tl, stdlib/trace.tl,
stdlib/txr-case.tl, stdlib/type.tl, stdlib/vm-param.tl,
stdlib/with-resources.tl, stdlib/with-stream.tl,
stdlib/yield.tl, stream.c, stream.h, struct.c, struct.h,
strudel.c, strudel.h, sysif.c, sysif.h, syslog.c, syslog.h,
termios.c, termios.h, time.c, time.h, tree.c, tree.h, txr.c,
txr.h, unwind.c, unwind.h, utf8.c, utf8.h, vm.c, vm.h, vmop.h:
License reformatted.
* lex.yy.c.shipped, y.tab.c.shipped, y.tab.h.shipped: Updated.
|
|
|
|
|
| |
* stdlib/compiler.tl (open-compile-streams): If in-path end in
a path separator character, throw an error.
|
|
|
|
|
| |
* compiler.tl (open-compile-streams): Use path-cat instead of
quasiliteral.
|
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (open-compile-streams): Replace usage of
%file-suff-rx% with a call to short-suffix.
Streamline (subjectively) the obtention of ip-nosuff. Replace
calls to ends-with with a casequal on the suffix. Actually make
use of ip-nosuff.
(%file-suff-rx%): Delete (now unused) variable.
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (lambda-apply-transform): When processing
optional argument from apply-list, push an entry into
check-opts, so the fixup code is generated for it.
New test cases pass now.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The compiler test case fails on cases which pass a : value to
an optional argument, which is supposed to trigger defaulting.
* stdlib/compiler.tl (lambda-apply-transform): When processing
an optional argument, if the argument is other than a constant
expression evaluating to the : symbol, add an entry into a
new check-opts list. This is later traversed to add code
before the lambda body to check the optional parmeters for :
values and do the init-form processing. The test cases pass
with this, but it needs to be done in the case when optional
values come from the apply list also; this is not being
tested.
|
|
|
|
|
| |
* stdlib/compiler.tl (lambda-apply-transform): In one case,
the add call is missing to actually emit the rest parameter.
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (lambda-apply-transform): There is a bug
in the case when all required and optional parameters have
been satisfied. In the sub-case when there are no fixed
parameters, we need to handle the run-time situation of there
being a non-empty apply list, but no rest variable.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When we compile an immediately applied lambda like
(apply (lambda (a b c) ...) list), we are not emitting code to
handle the run-time situation when there are too many elements
in the list. This shows up in exception handling, for
instance; the compiled version of this executes silently and
returns 42:
(catch (throw 'foo 5) (foo () 42)))
foo is must have a parameter to capture the 5; interpreted
code enforces this.
* stdlib/compiler.tl (lambda-apply-transform): In the
apply-list case, at the end of binding all the optional
arguments, if the parameter list doesn't have a rest
parameter to take any remaining items, we emit code to check
that there aren't any; that everything has been popped out of
the apply list, leaving it nil. If not, we call the
run-time support function lambda-excess-apply-list.
* stdlib/error.tl (lambda-excess-apply-list): New function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This patch improves the constantp function dramatically. It
now performs a full expansion of its argument, and recognizes
all of the constant foldable functions that the compiler
recognizes.
* eval.c (const_foldable_s): New symbol variable.
(const_foldable_hash): New static variable.
(constantp_noex): Look up function in the hash table of const
foldable functions, including in the case when it appears in a
dwim form as in [+ 2 2] which is (dwim + 2 2). In this case,
recursively check the arguments for constantp_noex.
We get the hash table of foldable functions from the
sys:%const-foldable% variable, which comes from an autoloaded
module.
(constantp): Fully expand the input form, not just m
macroexpand.
(eval_init): Register the const_foldable_s variable.
* lisplib.c (constfun_instantiate, constfun_set_entries): New
static functions.
(lisplib_init): Register auto-loading of constfun module
via new static functions.
* stdlib/compiler.tl; Load the constfun module if
%const-foldable% is not defined.
(%const-foldable-funs%, %const-foldable%): Removed from here.
* stdlib/constfun.tl: New file.
(%const-foldable-funs%, %const-foldable%): Moved here.
* txr.1: Documented changes to constantp.
|
|
This affects run-time also. Txr installations where the
executable is not in directory ending in ${bindir}
will look for stdlib rather than share/txr/stdlib,
relative to the determined installation directory.
* txr.c (sysroot_init): If we detect relative to the short
name, or fall back on the program directory, use stdlib
rather than share/txr/stdlib as the stdlib_path.
* INSTALL: Update some installation notes not to refer to
share/txr/stdlib but stdlib.
* Makefile (STDLIB_SRCS): Refer to stdlib, not
share/txr/stdlib.
(clean): In unconfigured mode, remove the old share/txr/stdlib
entirely. Remove .tlo files from stdlib.
(install): Install lib materials from stdlib.
* txr.1: Updated documentation under Deployment Directory Structure.
* share/txr/stdlib/{asm,awk,build,cadr}.tl:
Renamed to stdlib/{asm,awk,build,cadr}.tl.
* share/txr/stdlib/{compiler,conv,copy-file,debugger}.tl:
Renamed to stdlib/{compiler,conv,copy-file,debugger}.tl.
* share/txr/stdlib/{defset,doc-lookup,doc-syms,doloop}.tl:
Renamed to stdlib/{defset,doc-lookup,doc-syms,doloop}.tl.
* share/txr/stdlib/{each-prod,error,except,ffi}.tl:
Renamed to stdlib/{each-prod,error,except,ffi}.tl.
* share/txr/stdlib/{getopts,getput,hash,ifa}.tl:
Renamed to stdlib/{getopts,getput,hash,ifa}.tl.
* share/txr/stdlib/{keyparams,match,op,optimize}.tl:
Renamed to stdlib/{keyparams,match,op,optimize}.tl.
* share/txr/stdlib/{package,param,path-test,pic}.tl:
Renamed to stdlib/{package,param,path-test,pic}.tl.
* share/txr/stdlib/{place,pmac,quips,save-exe}.tl:
Renamed to stdlib/{place,pmac,quips,save-exe}.tl.
* share/txr/stdlib/{socket,stream-wrap,struct,tagbody}.tl:
Renamed to stdlib/{socket,stream-wrap,struct,tagbody}.tl.
* share/txr/stdlib/{termios,trace,txr-case,type}.tl:
Renamed to stdlib/{termios,trace,txr-case,type}.tl.
* share/txr/stdlib/{ver,vm-param,with-resources,with-stream}.tl:
Renamed to stdlib/{ver,vm-param,with-resources,with-stream}.tl.
* share/txr/stdlib/yield.tl: Renamed to stdlib/yield.tl.
* share/txr/stdlib/{txr-case,ver}.txr:
Renamed to stdlib/{txr-case,ver}.txr.
* gencadr.txr: Update to stdlib/place.tl.
* genman.txr: Update to stdlib/cadr.tl.
|