summaryrefslogtreecommitdiffstats
path: root/share
Commit message (Collapse)AuthorAgeFilesLines
...
* matcher: new pattern operator @(end)Kaz Kylheku2021-04-202-44/+53
| | | | | | | | | | | | | | | * share/txr/stdlib/doc-syms.tl: New entry for end. * share/txr/stdlib/match.tl (check, check-end, check-sym, loosen, pat-len): New functions, taken from original local functions of sme macro. (sme): Refactored by hoisting local functions out. Some local variable renaming. (end): New pattern macro. * tests/011/patmatch.tl: New test for end. * txr.1: Documented.
* compile-file: fix bad diagnostic.Kaz Kylheku2021-04-201-1/+1
| | | | | | | * share/txr/stdlib/compiler.tl (open-compile-streams): When the output file cannot be opened, the diagnostic message wrongly refers to the input stream object rather than the output file path.
* matcher: first pattern macro, sme.Kaz Kylheku2021-04-192-1/+47
| | | | | | | | | | | | * lisplib.c (match_instantiate): Intern sme symbol. * share/txr/stdlib/doc-syms.tl: Update with sme entry. * share/txr/stdlib/match.tl (sme): New defmatch macro. * tests/011/patmatch.tl: New tests for sme. * txr.1: Documented.
* defmatch: pass form to mac-param-bind.Kaz Kylheku2021-04-191-1/+1
| | | | | | | | * share/txr/stdlib/match.tl (defmatch): Pass *match-form* to mac-param-bind so that the context is available to defmatch macros via the :form parameter. * txr.1: Documented use of :form in defmatch.
* compile/eval: print compiler error on *stderr*.Kaz Kylheku2021-04-191-1/+4
| | | | | | | | | | | | | | | | | | | | | | * share/txr/stdlib/error.tl (compile-error): Print the error message on *stderr*, like we do with warnings. This allows the programming environment to pick up the error message and navigate to that line accordingly. The error message is also output by the unhandled exception logic but with a prefix that prevents parsing by the tooling. To avoid sending double error messages to the interactive user, we only issue the *stderr* message if *load-recursive* is true. * tests/common.tl (macro-time-let): New macro. This lets us bind special variables around the macro-expansion of the body, which is useful when expansion-time logic reacts to values of special variables. * tests/012/ifa.tl: Use macro-time-let to suppress *stderr* around the expansion of the erroneous ifa form. We now needs this because the error situation spits out a message on *stderr*, in addition to throwing.
* new: remove superflous prefix from diagnostic.Kaz Kylheku2021-04-191-2/+1
| | | | | * share/txr/stdlib/struct.tl (new-expander): Don't format prefix into error message; compile-error does that.
* matcher: new @(scan) operator.Kaz Kylheku2021-04-182-1/+23
| | | | | | | | | | | | | | * share/txr/stdlib/match.tl (compile-scan-match): New function. (compile-match): Hook scan operator into compiler. * lisplib.c (match_set_entries): Ensure scan is interned in usr package. * txr.1: Documented. * share/txr/stdlib/doc-syms.tl: Updated with new entry for scan.
* matcher: allow user-defined patterns via defmatchKaz Kylheku2021-04-172-16/+36
| | | | | | | | | | | | | | | | | * lisplib.c (match_set_entries): Register defmatch and *match-symbol* to autoload match.tl. * share/txr/stdlib/doc-syms.tl: Updated with entries for defmatch and *match-macro*. * share/txr/stdlib/match.tl (*match-macro*): New special variable holding hash. (compile-match): Handle macros via *match-macro* hash. (defmatch): New macro. * txr.1: Documented. * tags.tl: Recognize defmatch forms.
* compiler: bugfix: rest parameter in inline lambdaKaz Kylheku2021-04-111-1/+4
| | | | | | | | | | | * share/txr/stdlib/compiler.tl (lambda-apply-transform): Do not take all of the fixed arguments and rest expression to be the trailing list. Rather, skip as many elements from these as the function has fixed parameters. E.g. if there are two fixed parameters as in (lambda (a b . c)) and the call specifies four fixed parameters and a trailing x (1 2 3 4 . x) then the rest argument c must be (list* 3 4 . x) and not (list* 1 2 3 4 . x).
* compiler: bug: symbol not in ffuns in call forms.Kaz Kylheku2021-04-101-6/+9
| | | | | | | | | | | | | | | | | | | | | | | | | This bug causes forms like (call (fun 'foo) ...) not to register foo as a free reference in the function space, leading to inappropriate lambda lifting optimizations. The compiler thinks that a lambda is safe to move because that lambda doesn't reference any surrounding lexical functions, which is incorrect. A failing test case for this is (compile-file "tests/012/man-or-boy.tl") at *opt-level* 3 or higher. A bogus error occurs similar to "function #:g0144 is not defined", due to that function being referenced from a lifted lambda, and not being in its scope. * share/txr/stdlib/compiler.tl (compiler (comp-fun-form, comp-apply-call)): Pass the function symbol as an extra argument to comp-fun-form so that it's added to ffuns. (compiler comp-call-impl): Take new optional argument: a symbol to be added to the ffuns slot of the returned fragment, indicating that a function symbol is referenced.
* Version 256txr-256Kaz Kylheku2021-04-071-1/+1
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Bumped. * txr.vim, tl.vim: Regenerated.
* doc: support doc function on android.Kaz Kylheku2021-04-071-2/+2
| | | | | * share/txr/stdlib/doc-lookup.tl (open-url): Define for android, which has xdg-open in the termux environment.
* awk: bugfix: string rs must not compile as regex.Kaz Kylheku2021-04-071-5/+5
| | | | | | | | * share/txr/stdlib/awk.tl (awk-state loop): When rs contains a string, do not pass it directly to regex-compile, because that function calls regex-parse when the argument is a string. Wrap it it a (compound ...) tree node to get it to be treated as sequence of characters to match.
* qref: bugfix: handle a.(b).?c correctly.Kaz Kylheku2021-04-051-1/+1
| | | | | * share/txr/stdlib/struct.tl (qref): Do not assume that (b) is the name of a slot to be looked up. Use qref to handle it.
* struct: fix lack of hygiene in null-safe qref.Kaz Kylheku2021-04-051-1/+3
| | | | | | | | | The expression a.?b is not being treated hygienically; a is evaluated twice. This is only if the null-safe object is the left most; a.b.?c is hygienic. * share/txr/stdlib/struct.tl (qref): Add the necessary gensym use to fix the broken case.
* compiler: remove optional param from lookup-var.Kaz Kylheku2021-04-051-5/+3
| | | | | | * share/txr/stdlib/compiler.tl (struct env): The mark-used optional parameter of lookup-var is not used anywhere, and so always nil. Let's remove it.
* awk: relax restriction on :name.Kaz Kylheku2021-04-041-4/+0
| | | | | | | | * share/txr/stdlib/awk.tl (sys:awk-expander): Do not impose stricter restrictions on :name than the block mechanism itself. * txr.1: Documentation updated.
* lib: new function for documentation lookup.Kaz Kylheku2021-04-032-0/+2076
| | | | | | | | | | | | | | | | * genman.txr: dump contents of symhash into a doc-syms.tl library file, as a defvarl form. * lisplib.c (doc_instantiate, doc_set_entries): New static functions. (lisplib_init): Register autoload for doc-lookup module to symbols doc and *doc-url*. * share/txr/stdlib/doc-lookup.tl: New file. * share/txr/stdlib/doc-syms.tl: Likewise. * txr.1: Documented.
* compiler: incorrect self-check in spy framework.Kaz Kylheku2021-03-301-2/+2
| | | | | | * share/txr/stdlib/compiler.tl (compiler (pop-closure-spy, pop-access-spy)): The stack underflow checkt must be done by checking top, not the incoming spy argument.
* compiler: cache param-info objects.Kaz Kylheku2021-03-272-13/+16
| | | | | | | | | | | | | | | * share/txr/stdlib/compiler.tl (%param-info%): New global variable. (compiler comp-fun-form): Use get-param-info function to get param-info object. (get-param-info): Retrieve object from cache, using the function as the key. If not found, create the entry. (compiler-emit-warning): Use get-param-info. * share/txr/stdlib/param.tl (struct param-info): Remove symbol slot, replacing it with the function. (param-info :postinit): No need to do symbol-function lookup; the function is given.
* compiler: regressions in source loc propagationKaz Kylheku2021-03-271-25/+27
| | | | | * share/txr/stdlib/compiler.tl (reduce-lisp, reduce-constant): Propagate source location to rewritten forms.
* compile/eval: more standard formatting for diags.Kaz Kylheku2021-03-272-6/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This patch eliminates parentheses from the error messages, as well as a leading ./ being added to relative paths. The word "warning: " is moved into the error message, so that it does not appear before the location. Example, when doing (compile-file "path/to/foo.tl"). Before patch: warning: (./path/to/foo.tl:37): unbound function foo After: path/to/foo.tl:37: warning: unbound function foo Now when I compile out of Vim, it nicely jumps to errors in Lisp code. * eval.c (eval_exception): Drop parentheses from error location, add colon. (eval_warn): Prepend "warning: " to format string. (eval_defr_warn): Drop parentheses from location, and prepend "warning: " to format string. * parser.c (repl-warning): Drop "warning:" prefix. * share/txr/stdlib/compiler.tl (open-compile-streams): Do not do parent substitution for relative paths if the parent path is the empty string "", to avoid inserting ./ onto relative paths in that case. * share/txr/stdlib/error.tl (sys:loc): Drop parentheses and space from location. (compile-error) Separate location with colon and space. (compile-warning, compile-defr-warning): Likewise and add "warning: " prefix. * unwind.c (uw_rthrow): Drop "warning: " prefix. (uw_warningf): Add "warning: " prefix. (uw_dump_deferred_warnings): Drop "warning: " prefix.
* compiler: bugfix: bad expand-quasi-mods call.Kaz Kylheku2021-03-271-1/+1
| | | | | | | | | * share/txr/stdlib/compiler.tl (expand-quasi-args): Here, expand-quasi-mods is being called with the wrong number of arguments. This was likely intended to be a recursive call to expand-quasi-args. Let's convert it to that. Removing this case also works, but it is nicer not to generate the sys:fmt-simple call.
* compiler: check number of arguments.Kaz Kylheku2021-03-272-35/+81
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We implement rudimentary compile-time checking beween function calls and function definitions. * share/txr/stdlib/compiler.tl (dstruct frag): We add one more optional BOA parameter, corresponding to a new slot. This is used when compiling a lambda. A lambda fragment is annotated with the parameter parser object which gives information about its arguments. (struct fbinding): New slot, pars. When processing a sys:fbind or sys:lbind form, we decorate the lexical function bindings with the parameter object pulled from the lambda fragment that is compiled for each function binding. (*unchecked-calls*): New special variable. This is used for checking, at the end of the compilation unit, the arguments of calls to functions that were not defined at the time of the call. (compiler comp-fbind): When processing the lambda expressions, propagate the parameter object from the compiled lambda fragment to the function binding. (compiler comp-fun-form): On entry, look up the function being called and if it is lexical or has a global definition, check the arguments. If it has no definition, push information into the *unchecked-calls* list to do the check later, if possible. Also, there is a behavior change here now: optimizations are now applied here only to functions that don't have a lexical binding. Thus if the application lexically redefines a standard function, and calls it, we won't try to optimize it. (param-check): New function. * share/txr/stdlib/param.tl (param-info): New struct. This presents information about a global function in a similar way to param-parser, using some of the same fields. With this object we can check the call to a lexical function or global function in a uniform way, using the same code.
* compiler: fix: careless constant folding of call.Kaz Kylheku2021-03-271-1/+4
| | | | | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-apply-call): The conditions for constant-folding a call expressions are too weak. The first argument could be a quoted symbol, which is a constant expression, and so we end up wrongly evaluating an expression like (call 'print '3) at compile time. We can constant-fold if the first expression evaluates to a symbol, which names a constant-foldable function, or else if it evaluates to something which is not a bindable symbol.
* Version 255txr-255Kaz Kylheku2021-03-261-1/+1
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Bumped. * txr.vim, tl.vim: Regenerated.
* compiler: new jump skid optimization case.Kaz Kylheku2021-03-251-0/+9
| | | | | | | | | | | | | | ifq tN t0 label0 ifq tN t0 label1 ... ... label0: --> ... if tN label2 label1: label1: ... ... * share/txr/stdlib/optimize.tl (basic-blocks thread-jumps-block): Implement case. There are there are 56 "hits" for this in the standard library.
* ffi: support float type as variadic argument.Kaz Kylheku2021-03-221-6/+12
| | | | | | | | | | | | | | | | | | | The float type promotes to double when passed as a variadic argument. This patch adds internal FFI types which models that promotion. It uses double for its C type, while still performing the range checks for float. Also, the types be-float and le-float are rejected from being variadic arguments. * share/txr/stdlib/ffi.tl (analyze-argtypes): Rewrite function, adding validation and substitution for the variadic part of the argument type list. Map float type to double, and reject be-float and le-float. * txr.1: Documented.
* compiler: improve end-propagating optimization.Kaz Kylheku2021-03-171-7/+13
| | | | | | | | | | | | | | | | | | | | When a jmp instruction is replaced by the end instruction that it jumps to, or possibly by a two-instruction sequence ending in end, there can be more opportunities to optimize. For instance, the second reduction in a sequence like this: mov t3, t5 mov t3, t5 jmp label --> end t3 --> end t5 ... label: end t3 * share/txr/stdlib/optimize.tl (basic-blocks peephole-block): If the end-propagation is done, we change the linkage of the current block to indicate that it has no next blocks. We add it to the rescan list and set the recalc flag so the liveness information is updated.
* compiler: use registers for function parameters.Kaz Kylheku2021-03-171-67/+74
| | | | | | | | | | | | | | | | | | | | | | | | | | | If a function has nothing but parameters that are not captured in lexical closures, they can be converted registers. The function then doesn't need a variable frame for its parameters. This is similar to the eliminate-frame optimization, and borrows the same code and logic. * share/txr/stdlib/compiler.tl (compiler eliminate-frame): We no longer assume that the code coming in starts with a frame instruction we can eliminate using (cdr code) and an end insruction we can eliminate with a trailing pattern. This is because when this function is used for a lambda, this is not the case; a lambda's variable frame is implicit, created by the VM for any lambda with a nonzero frame size, rather than by a frame instruction. (compiler comp-let): In the call to eliminate-frame, we now trim away the first and last instruction, to get rid of the (frame ...) and (end ...). (compiler comp-lambda-impl): Install a closure spy against the variable frame to detect which variables are captured in closures, similarly to in comp-let. Under the right conditions, pass the code through eliminate-frame to turn the variables into registers. The close instruction has to be rewritten, because the frame size is now zero, and the number of t registers has changed.
* compiler: split variable spies into two types.Kaz Kylheku2021-03-161-33/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We have the situation that there are effectively two kinds of spies: let constructs plant spies only in order to learn about what variables are being captured, whereas lambdas plant spies in order to intercept variable accesses (and to inform the spies that are interested in what is captured). Let us split these up into two separate types, with different methods, in different stacks. * share/txr/stdlib/compiler.tl (struct var-spy): Renamed to closure-spy. (var-spy accessed, var-spy assigned): Methods removed: the closure-spy type has only the captured method. (struct capture-var-spy): Renamed to accesss-spy. (capture-var-spy var-spies): Renamed to access-spy closure-spies. (capture-var-spy captured): Method removed; the access spy is doesn't receive captured calls. (struct compiler): Slot var-spies removed, replaced with closure-spies and access-spies. (with-var-spy): Macro removed. (with-spy): New function. (with-closure-spy, with-access-spy): New macros. (compiler push-var-spy, compiler pop-var-spy): Methods removed. (compiler push-closure-spy, compiler pop-closure-spy, compiler push-access-spy, compiler pop-access-spy): New methods. (compiler comp-var, compiler comp-setq, compiler comp-lisp1-setq, compiler comp-lisp1-value): Walk new access-spies list rather than var-spies to report about accesses and assignments. (compiler comp-let): Use with-closure-spy macro rather than with var-spy. The spy object is now a closure-spy type, and the variable is cspy rather than vspy. (compiler comp-lambda-impl): Use with-access-spy instead of with-var-spy. The spy object is now of type access-spy. It refers to the current me.closure-spies from the compiler.
* compiler: trim unused accumulation from var-spy.Kaz Kylheku2021-03-161-8/+2
| | | | | | | | | | | The var-spy structure is only being used for detecting captured variables, not assigned or accessed variables. So the information about accessed and assigned is being wastefully accumulated, never used. * share/txr/stdlib/compiler.tl (struct var-spy): Remove slots acc-vars and set-vars. (var-spy accessed, var-spy assigned): Methods become no-ops.
* compiler: eliminate unused list in optimizer.Kaz Kylheku2021-03-161-12/+9
| | | | | | | * share/txr/stdlib/optimize.tl (basic-blocks elim-dead-code): Ironically, we have a dead variable named unreachable in this function. Let's remove it and the build form which calculates it.
* Version 254txr-254Kaz Kylheku2021-03-101-1/+1
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Bumped. * txr.vim, tl.vim: Regenerated.
* compiler: eliminate unused global symbol accesses.Kaz Kylheku2021-03-101-1/+1
| | | | | | * share/txr/stdlib/optimize.tl (basic-blocks peephole-blocks): Extend dead reg mov pattern to also handle the getlx, getv, getf and getfb instructions.
* compiler: use effect-free criterion for elimination.Kaz Kylheku2021-03-102-1/+25
| | | | | | | | | | | | | | | | | | | | | | | | When the result of a function call is not used, the call can be eliminated if the function has no effects. Effect-free functions are superset of constant-foldable functions. Not all effect-free functions are const-foldable because in some cases, creating an object at run-time is a documented semantics which cannot be constant-folded. For instance (list 1) cannot be constant folded, because it may be relied upon to generate a fresh object each time it is called. However, bringing a new list to life is not an effect. If the value is not used, we can safely eliminate it. The same reasoning applies to (gensym "abc"). It must generate a unique symbol each time, and so cannot be constant-folded. But call to gensym whose value is not used can be eliminated. * share/txr/stdlib/compiler.tl (%effect-free-funs%): List of side-effect-free functions registered in eval.c. (%effect-free%): Hash of effect-free functions, incorporating %const-foldable% also. * share/txr/stdlib/optimize.tl (basic-blocks peephole-block): Refer to %effect-free% rather than %const-foldable%.
* compiler: more const-foldable functions.Kaz Kylheku2021-03-101-6/+7
| | | | | * share/txr/stdlib/compiler.tl (%const-foldable-funs%): Add rest, nilf, tf, join, join-with, empty.
* compiler: eliminate unused closures.Kaz Kylheku2021-03-101-1/+10
| | | | | | | | | | | | | | | | | | | In this patch, we optimize away closures that are not used. Unused closures that have been hoisted to the top level are not affected. We look for close instructions which produce a dead treg, and rewrite these to jmp instructions to the same label. When this happend, we set a flag for a dead code elimination pass to be done again, to actually remove the now unreachable closure code. * share/txr/stdlib/optimize.tl (struct basic-blocks): New slot, reelim, indicating that dead code elimination is to be done again after peephole since the control flow graph has changed. (basic-blocks peephole-block): New pattern for eliminating a dead register, targeting the close instruction. (basic-blocks peephole): After peephole, check whether the reelim flag has been set and do elim-dead-code again.
* compiler: eliminate dead calls.Kaz Kylheku2021-03-102-74/+98
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The main idea in this patch is to identify calls functions whose values are not used and which have no side effects. For this purpose, we borrow the same set of functions that we use as targets for constant folding: those in the compiler's %const-foldable% hash. A call is dead if it is a gcall or gapply instruction which calls one of these functions, and the destination register is dead. To maximize the opportunities for this elimination, whenever we eliminate such an instruction, we mark the block for re-scanning, and we re-calculate the liveness info for that block and then globally. * share/txr/stdlib/compiler.tl (struct compiler): New slots, datavec and symvec. (compiler get-datavec): Cache the calculated datavec in the new datavec slot. (compiler get-symvec): Cache the calculated symvec in the new symvec slot. (compiler optimize): Pass the symvec to the basic-blocks BOA constructor; it is now needed for identifying functions that are referenced by symvec index number in the gcall and gapply instructions. * share/txr/stdlib/optimize.tl (struct basic-blocks): New symvec slot, added to the the BOA parameter list also. New slot recalc, indicating re-calculation of liveness is needed. (basic-blocks cut-block): Use pushnew to put bl onto the rescan list, to prevent duplicates. Also push the new block onto the rescan list. (basic-blocks local-liveness): This method can now be called again to re-calculate local liveness, so we must reset the live slot to nil. (basic-blocks calc-liveness): Take an optional list of blocks to scan for local liveness, defaulting to all of them. (basic-blocks peephole-block): Factor out some register liveness tests to local functions. Add a pattern targetting gcall and gapply instructions, testing for a dead register from a call to a %const-foldable% function. Use pushnew everywhere to add to the rescan list. Set the recalc flag when the liveness-based reductions are applied. (basic-blocks peephole): If there are blocks to be scanned again, then if the recalc flag is set, recalculate local liveness for all the blocks to be re-scanned and re-do global liveness.
* lib: new functions join, join-with.Kaz Kylheku2021-03-091-4/+4
| | | | | | | | | | | | | | | | | | | | | | | | | That old cat-str function is often a pain, requiring the pieces as a list. We have a sys:fmt-join that is undocumented. That functions is now exposed as usr:join, and documented. Also introducing join-with that takes a separator as the leftmost argument. Thus (op join-with "::") gives us a function that joins pieces with :: in between. * eval.c (eval_init): Regiser fmt_join function under join symbol also. Register join-with. * lib.c (join_with): New function. (fmt_join): Now a wrapper for join_with that passes a nil separator. * lib.h (join_with): Declared. * share/txr/stdlib/optimize.tl (basic-blocks join-blocks): Rename the local function join, which now triggers a warning about a standard function being redefined. * txr.1: Redocumented cat-str, and documented join-with and join.
* compiler: optimization control.Kaz Kylheku2021-03-082-107/+138
| | | | | | | | | | | | | | | | | | | | | | | | | | | * lisplib.c (compiler_set_entries): Register *opt-level* symbol for auto-loading. * share/txr/stdlib/compiler.tl (*opt-level*): New special variable. (compiler comp-let): Eliminate frames only at level 3. (compiler comp-lambda-impl): Lift load time at level 3. (compiler comp-arith-form): Constant-folding only at lvl 1. (compiler comp-fun-form): Algebraic substitutions and reductions and constant-folding only at level 1. (compiler comp-apply-call): Constant folding at level 1. (compiler optimize): Optimizations off if level zero. Thread jumps and eliminate dead code at level 2. Flow-analysis based optimizations at level 3. Additional optimizations at level 4. (compile comp-block): Block elimination at level 3. (compile-toplevel): Rebind *opt-level*, giving it value zero if it is previously nil. * share/txr/stdlib/optimize.tl (basic-blocks get-insns): Just retrieve the instructions, letting caller decide whether to call late-peephole or not. * txr.1: Documented *opt-level*.
* Version 253txr-253Kaz Kylheku2021-03-061-1/+1
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Bumped. * txr.vim, tl.vim: Regenerated.
* compiler: streamline load-time hoisting of calls.Kaz Kylheku2021-03-041-17/+23
| | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-fun-form): Rearrange the logic so that we only try the speculative compilation when the three main conditions are right, not before. This drastically reduces the number of times we need to take the compiler snapshot.
* compiler: bug: duplicate code in load-time lifting.Kaz Kylheku2021-03-041-5/+22
| | | | | | | | | | | | | | | | | | | | | | | This issue affects the original code which lifts lambdas to load-time, as well as the new, recently added code for similarly lifting functional combinator expressions. The problem is that the trick works by compiling an expression twice. The result of the first compile is thrown away in the case when we compile it again in the load-time context. But compiling has a side effect: the expression itself may have an embedded load-time-liftable expression, which gets deposited into the load-time fragment list. Thus garbage ends up in the list of load-time fragments. We likely want to save and restore other things, like allocated D regisers. * share/txr/stdlib/compiler.tl (compiler shapshot, compiler restore): New methods. (comp-lambda-impl, comp-fun): Save a snapshot of the compiler state before doing the speculative compilation. If we don't use that compilation, we restore the state from the snapshot.
* compiler: frame depth bug in load-time.Kaz Kylheku2021-03-041-6/+3
| | | | | | | | | | | | | | * share/txr/stdlib/compiler.tl (compile-in-toplevel): This macro must not save and restore the nlev variable of the compiler. The nlev variable is a maximum: it measures the maximum environment depth, establishing the depth of the display needed when executing the code of the resulting VM description. By saving and restoring this variable around the compilation of a load-time form, we cause the load-time form's contribution to the maximum to be ignored. If the load-time form perpetrates nesting that is deeper than other code, it will execute with an under-sized display, causing an assertion crash or corruption.
* compiler: another late-peephole pattern.Kaz Kylheku2021-03-031-0/+21
| | | | | | | | | | | * share/txr/stdlib/optimize.tl (late-peephole): Add a 6-instruction pattern which is seen in pattern-matching code in optimize.tlo and compiler.tlo. This can be tidied up a little bit, reducing the instructions to 5, eliminating redundant comparisons and threading a jump. At this point we are probably working too hard for too little gain. More effort in other areas like common subexpression elimination could produce bigger wins.
* compiler: new late-peephole pass.Kaz Kylheku2021-03-031-1/+16
| | | | | | | | | | | | | | | | | | | | | | | | I noticed a lot of this in compiled code: (if reg label1) label2 (jmp label3) label1 by inverting the test, this can be shortened to (ifq reg (t 0) label2) label1 We must keep label1 in case it is the target of other branches. Also, we only perform this if label2 is unreachable: the (jmp label3) instruction is reached only when the previous if branch is not taken. * share/txr/stdlib/optimize.tl (basic-blocks get-insns): Call new late-peephole method. (basic-blocks late-peephole): New method, incorporating the above pattern.
* compiler: lift uslot and umethod forms too.Kaz Kylheku2021-03-031-1/+1
| | | | | | | | | | | | | | | | The uslot and umethod functions produce functions; and should be lifted to load-time, if possible. For instance, the .foo expression [mapcar .foo list] translates to a (uslot 'foo) function call. This references no variables, and so is liftable to load-time. The umethod function is an extension of uslot that allows partially applied method arguments to be carried. If those arguments are all functional, the umethod call is liftable. * share/txr/stdlib/compiler.tl (%functional-funs%): Include umethod and uslot.
* lib: remove unnecessary load-time forms.Kaz Kylheku2021-03-032-10/+9
| | | | | | | | | | | | | | Because of the previous optimization, some load-time forms that appear in the library are unnecessary. * share/txr/stdlib/optimize.tl (basic-blocks merge-jump-thunks): Remove load-time around functional combinators. * share/txr/stdlib/socket.tl (sys:in6addr-condensed-text): Remove one load-time that is now unnecessary, and one around an op which was unnecessary even at the time it was written, since the lambda is lifted without it.
* compiler: lift functional expressions to load-time.Kaz Kylheku2021-03-031-0/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The idea behind this optimization is that certain expressions that only calculate functions can be hoisted to load time. These expressions meet these criteria: 1. Are not already in a top-level or load-time context. 2. Are function calls to a standard library functional operator like chain andf, orf, juxt, ... 3. Do not access any variables. 3. Do not access any functions other than public (usr package) global functions in the standard library. An example of such an expression might be: [chain cdr [iff symbolp list]] If such an expression is embedded in a function, we don't want the function to re-calculate it every time, which requires time and generates garbage. We can transform it to the equivalent of: (load-time [chain cdr [iff symbolp list]]) to have it calculated once. * share/txr/stdlib/compiler.tl (%functional-funs%, %functional%): New global variables. (compiler comp-fun-form): After compiling the function call, check for the conditions for lifting. If so, compile the form again as a load-time literal. The logic is similar to how lambdas are lifted to load-time, though the conditions are different.