| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
|
| |
* stdlib/awk.tl (sys:awk-redir): Fix regression from April
2018. The gensym variable introduced must be parallel bound,
since it is referenced by the init expression of the other
variable. This breaks all awk redirection operators.
|
|
|
|
|
|
|
|
| |
* vm.c (vm_call): Specially handle the cases of 0
to 4 arguments, avoiding the general loop and
invocation of generic_funcall. This gets us about
a 1% improvement in recompiling the standard
libarry (touch stdlib/*.tl; make).
|
|
|
|
| |
* stdlib/quips.tl (%quips%): New entry.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The with-compile-opts macro is rewritten such that
it cad occur inside code that is being compiled, and
change compiler options for individual subexpressions.
It continues to work as before in scripted build steps
such as when calls to (compile-file ...) are wrapped
in it. However, for the time being, that now only works
in interpreted code, because with this change, when
a with-compile-opts form is compiled, it no longer
arranges for the binding of *compile-opts* to be visible
to the subforms; the binding affects the compiler's
own environment.
* stdlib/compiler.tl (with-compile-opts): Rewrite.
* txr.1: Documented.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* eval.c (compiler_let_s): New symbol variable.
(op_let): Recognize compiler-let for sequential
binding.
(do_expand): Traverse and diagnose compiler-let
form.
(eval_init): Initialize compiler_let_s and register
the interpreted version of the operator.
* stdlib/compiler.tl (compiler compile): Handle
compiler-let form.
(compiler comp-compiler-let): New method.
(no-dvbind-eval): New function.
* autoload.c (compiler-set-entries): Intern the
compiler-let symbol in the user package.
* txr.1: Documented.
* stdlib/doc-syms.tl: Updated.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Adding a progv operator, similar to the Common Lisp one.
* eval.c (progv_s): New symbol variable.
(op_progv): New static function.
(do_expand): Recognize and traverse the progv form.
(rt_progv): New static function: run-time support
for compiled progv.
(eval_init): Initialize progv_s, and register the the
op_progv operator interpreting function.
* stdlib/compilert (compiler compile): Handle progv
operator ...
(compiler comp-progv): ... via this new method.
* tests/019/progv.tl: New file.
* txr.1: Documented.
* stdlib/doc-syms.tl: Updated.
|
|
|
|
| |
* stdlib/quips.tl (%quips%): New entry.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The getlx and setlx VM instructions are using dynamic lookup
for uncached bindings, due to using the same lookup_fun
search function. They should use lookup_global_fun.
That doesn't have an environment parameter though, so the
type is not right. However, the VM never uses the environment
parameter; it's always passing nil. We will get rid of the
environment parameter in the lookup_fn callback and introduce
a few wrappers.
* eval.c, eval.h (lookup_global_fun, lookup_dynamic_var,
lookup_dynamic_sym_lisp1): New functions.
* vm.c (vm_stab_slowpath, vm_get_binding): lookup_fn argument
loses environment parameter, and so we don't have to pass nil.
(vm_gcall, vm_gapply): Use pass lookup_global_fun to
to vm_stab.
(vm_getsym, vm_getbind, vm_setsym, vm_gettab, vm_settab):
lookup_fn argument loses environment parameter.
(vm_execute): lookup functions replaced with the appropriate
one-argument ones. GETLX and SETLX see a behavior change,
due to using lookup_global_var which doesn't search the
dynamic environment.
|
|
|
|
| |
* stdlib/quips.tl (%quips%): New entry.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We have a problem. If v is a dynamic variable, then
the form
(let (v)
(set (symbol-value 'v) 3))
is not behaving correctly; it's updating the top-level
value of v not the rebound one.
* eval.c (set_symbol_value): New static function.
(eval_init): Register sys:set-symbol-value intrinsic.
The top-vb variable, though no longer referenced by
the symbol-value place, because existing compiled
code depends on it.
* stdlib/place.tl (symbol-value): Rewrite the place
logic to use symbol-value to access the variable,
and set-symbol-value to update it, instead of referencing
sys:top-vb.
(sys:get-vb): This function has to stay, because it
provides run-time support for code compiled with the
buggy version of the place.
* tests/019/symbol-value.tl: New file.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We use lookup_var_l in many places to look up the current
dynamic value of a built-in variable such as *stdout*.
Those places assume that a a valid location is returned
which can be subject to a deref. If the application calls
makunbound to remove such a variable, that deref will
crash due to a null pointer dereference.
Possible repro steps are numerous, possible for many variables.
One example:
(makunbound '*stdout*)
(put-line)
* eval.c (lookukp_var_l): If the binding is not found, do
not return a nulloc, but throw an error exception.
|
|
|
|
|
| |
* txr.1: Add missing "removes" verb to first sentence of
description of makunbound.
|
|
|
|
|
| |
* stdlib/compiler.tl (with-compile-opts): Remove stray
character from "uncrecognized".
|
|
|
|
|
| |
* ffi.c (ffi_varray_null_term_get): An i variable is
initialized and incremented in a for loop, but never used.
|
|
|
|
|
|
|
|
|
|
| |
* gc.c (gc_free_all): Just like we do in the sweep function,
we must mask back the pointer tag that we removed from
the heap object's pointer, before handing the pointer to
the free function. Starting in Android 11, the pointer
tagging is more strict. It was not enforced previously; now
our logic for stripping and restoring the tags is actualy
being tested.
|
|
|
|
|
|
|
|
| |
* configure: don't set __ANDROID_API__, but instead use
--target to specify a target architecture. Some of the
version checking is now done using symbol attributes; the
preprocessor symbol alone doesn't tell the compiler what SDK
version is being targeted.
|
|
|
|
|
|
|
|
|
| |
* sysif.c (sysif_init): The passwd symbol is used by both the
passwd and group structure, so we need to initialize it if
either HAVE_PWUID or HAVE_GRGID is set. Certain Android SDK
levels have getgrgid but not getpwuid, so it's possible to end
up with HAVE_GRGID but not HAVE_PWUID, in which case passwd_s
ends up nil, blowing up the make_struct_type call.
|
|
|
|
|
|
|
|
|
|
|
|
| |
* RELNOTES: Updated.
* configure (txr_ver): Bumped version.
* stdlib/ver.tl (lib-version): Bumped.
* txr.1: Bumped version and date.
* txr.vim, tl.vim: Regenerated.
|
|
|
|
|
| |
* configure: add mergesort to list of clashing
identifiers to handle.
|
|
|
|
|
|
|
| |
* txr.1: an instance of "comprised of" disappears in a wording
improvement under "Dot Position in Function Calls".
Another instance under doloop is replaced by consisting of.
I don't have anything against it, but it bothers some people.
|
|
|
|
|
|
| |
* hash.c (hash_mark): Cache the table, vector and mask
in local variables, so they don't have to be reloaded
into registers when external functions are called.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* gc.c (mark_obj_norec): New function. Just marks
an object reachable without recursing over its
sub-objects.
(gc_mark_norec): New function.
* gc.h (gc_mark_norec): Declared.
* hash.c (do_weak_tables): Cache the table, mask and
vector pointer in a local variable, since these pointers
are not expected to change across function calls, and
can go into registers.
When visiting an entry that should be reachable, we
mark that entry immediately, and also use the new
gc_mark_norec function to mark the chain cons cell
reachable. I.e. we mark the chain backbone cons,
and that cons' car field, that being the entry.
Thus by the time we march through the chain, we
have marked all of it. Thus, the table entries don't
have to be iterated and marked any more. We use
gc_mark_norec to mark the table, and explicitly mark
its two special slots. The upshot of all this is that
we don't have to make an extra pass over the table,
and the chains, to mark things; we combine the marking
with the expunging of weak values.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* struct.tl (defstruct): When generating the lambda that
initializes slots from boa arguments, instead of we use (set
(qref obj slot) val) instead of slotset. The qref macro will
diagnose use of nonexistent slots.Thus warnings are produced
for, say:
(defstruct (point x y) nil)
where x and y have not been defined, using the imperfect
approach of the qref implementation, which is better than
nothing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When a defmacro form is compiled, the entire form is retained
as a literal in the output. This is wasteful and gives away
the source code. In spite of that, errors in using the
macro are incorrectly reported against defmacro, because
that is the first symbol in the form. These issues arise with
what arguments are passed as the first two parameters of the
compiler's expand-bind-mac-params function, and what exactly
it does with them. We make a tweak to that, as well as some
tweaks to all the calls.
* stdlib/compiler.tl (expand-bind-mac-params): There is
a mix-up here in that both the ctx-form and err-form
arguments are ending up in the compiled output. Let's
have only the first agument, ctx-form going into the
compiled output. Thus that is what is inserted into
the sys:bind-mach-check call that is generated.
Secondly, ctx-form should not be passed to the constructor
for mac-param-parser. ctx-form is a to-be-evaluated
expression which might just be a gensym; we cannot use
it at compile time for error reporting. Here we must
use the second argument. Thus the second argument is now
used only for two purposes: copying the source code info
to the output code, and for error reporting in
the mac-param-parser class. This second purpose is minor,
because the code has been passed through the macro expander
before being compiled, which has caught all the errors.
Thus the argument is changed to rlcp-form, reflecting its
principal use.
(comp-tree-bind, comp-tree-case): Calculate a simplified
version of the tree-bind or tree-case form for error reporting
and pass that as argument the ctx-form argument of
expand-bind-mac-params. Just pass form as the second argument.
(comp-mac-param-bind, comp-mac-env-param-bind):
Just pass form as the second argument of
expand-bind-mac-params.
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks late-peephole):
The test whether lab2 is used is bogus, and will
never be true. The correct test is simply whether
the block has two or more rlinks. This makes no
difference in the standard library images. When
the bug appears, the manifestation would be that
a needed label is deleted, resulting in an exception
from the assembler.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
2022-09-13 commit 6e354e1c2d5d64d18f527d52db75e344a9223d95,
subject "compiler: bugfixes in dead code elimination",
introduced a problem. By allowing the closure body blocks to
be included in the links of the previous basic block that ends
in the close instruction, it caused liveness info to flow out
out of close blocks into the close instruction, which is
wrong. Thus registers used inside a closure, which are
entirely private, wrongly appear live outside of the closure,
interfering with optimizations like eliminating dead
registers.
We can't simply roll back the commit because the bug it
fixes will reappear. The fix is to pair the next field
with a prev field, and maintain them; don't rely on
the rlinks to point to the previous block.
* stdlib/optimize.tl (basic-block): New slot, prev.
(back-block join-block): As we delete the next block,
we must update that block's next block's prev link.
(basic-blocks link-graph): Build the prev links.
Fix the bug in handling the close instruction:
do not list the close body code among the links,
only the branch target of the close.
(basic-blocks do-peephole-block): In a few cases in
which we set the bl.next to nil, we also set the
bl.next.prev to nil, if bl.next exists.
(basic-blocks elim-dead-clode): Reset the bl.prev
of every block also.
(basic-block check-bypass-empty): Here, we no longer
depend on rlinks containing the previous block;
the prev gives it to us. So we move that fixup out
of the link, and also fix up the next blocks prev
pointer.
|
|
|
|
|
|
|
| |
* tests/012/sort.tl: The larger input tests are
testing only vectors, thus covering neither
quicksort nor array binary merge. Cases
added.
|
|
|
|
|
|
|
|
| |
* lib.c (quicksort): Avoid calls to keyfun when
it's known to be identity,
(mergesort): Likewise. Also, avoid redundant
accesses to the vector when merging, for that index
which has not moved between iterations.
|
|
|
|
|
|
|
|
| |
* tests/010/sort.tl: File moved to tests/012.
The reason is that the tests 010 run with the
--gc-debug torture tests. That test case runs
way too long under that test because of the
testing of many permutations and whatnot.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* gc.c (prot_array): Add self pointer; arr member
becomes flexible array.
(prot_array_mark): We now check the handle itself for
null, because the whole thing is freed.
(prot_array_free): Function removed.
(prot_array_ops): Wire cobj_destroy_free_op in place
of prot_array_free. This fixes a memory leak because
prot_array_free was not freeing the handle, only
the array.
(gc_prot_array_alloc): Fix to allocate everything
in one swoop and store the self-pointer in the
named member rather than arr[-1]. The self argument
is not required; we drop it. The size argument cannot
be anywhere near INT_PTR_MAX, because such an array
wouldn't fit into virtual memory, so it is always
safe to add a small value to the size.
(prot_array_free): Obtain the self-pointer, and
free the handle, replacing it with a null pointer.
* gc.h (gc_prot_array_alloc): Declaration updated.
* lib.c (ssort_vec): Don't pass self to gc_prot_array_alloc.
* lib.h (container): New macro.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
For array-like objecgts, these objects use an
array-based merge sort, using an auxiliary array
equal in size to the original array.
To provide the auxiliary array, a new kind of very simple
vector-like object is introduced into the gc module: protected
array. This looks like a raw dynamic C array of val type,
returned as a val *. Under the hood, there is a heap object
there, which makes the array traversable by the garbage
collector.
The whole point of this exercise is to make the new mergesort
function safe even if the caller-supplied functions misbehave
in such a way that the auxiliary array holds the only
references to heap objects.
* gc.c (struct prot_array): New struct,
(prot_array_cls): New static variable.
(gc_late_init): Register COBJ class, retaining in
prot_array_cls.
(prot_array_mark, prot_array_free): New static functions.
(prot_array_ops): New static structure.
(prot_array_alloc, prot_array_free): New functions.
* gc.h (prot_array_alloc, prot_array_free): Declared.
* lib.c (mergesort, ssort_vec): New static function.
(snsort, ssort): New functions.
* lib.h (snsort, ssort): Declared.
* tests/010/sort.tl: Cover ssort.
* txr.1: Documented.
* stdlib/doc-syms.tl: Updated.
|
|
|
|
|
|
| |
* lib.c (sort_vec): Take self argument instead of assuming
that we are sort; this can be called by nsort.
(nsort, sort): Pass self to sort_vec.
|
|
|
|
|
|
|
|
| |
* tests/010/sort.tl: Add some test cases of larger list.
The exhaustive permutation tests are good but only go
up to a relatively short size, where the median-of-three
doesn't even kick in. We also cover choosing an alternative
less function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I'm seeing numbers aobut the same performance on a
sorted vector of integers, and 21% faster on vector of N
random integers in the range [0, N).
Also, this original algorithm handles well the case
of an array consisting of a repeated value.
The code we are replacing degrates to quadratic time.
* lib.c (med_of_three, middle_pivot): We don't use
the return value, so don't calculate and return one.
(quicksort): Revise to Hoare: scanning from both ends
of the array, exchanging elements.
* tests/010/sort.tl: New file. We test sort with
lists and vectors from length zero to eight, all
permutations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We don't have a function in the hash table module which can
create a populated hash table in one step without requiring
the caller to create auxiliary lists. This new function fills
that gap, albeit with some limitations.
* hash.c (hash_props): New function.
(hash_init): Register hash-props intrinsic.
* tests/010/hash.tl: New tests.
* txr.1: Documented.
* stdlib/doc-syms.tl: Updated.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* time.c (time_str_local, time_str_utc): New static functions.
(time_fields_local, time_fields_utc, time_struct_local,
time_struct_utc): Time argument
becomes optional, defaulted to current time.
(time_init): Use time_s symbol instead of interning
twice. Register new time-str-local and time-str-utc
intrinsics. Fix registration of functions that take
optional args.
* txr.1: New functions documented; optional arguments
documented; existing documentation revised.
* stdlib/doc-syms.tl: Updated.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Quasiquote patterns not containing unquotes are not
working, because the parser transforms them into
quoted objects. For instance ^#S(time) becomes
the form (quote #S(time)) and not the
form (sys:qquote (sys:struct-lit time)).
The pattern matching compiler doesn't treat quote
specially, only sys:qquote.
* parser.y (unquotes_occur): Function removed.
(vector, hash, struct, tree, json_vals, json_pairs):
Remove use of unquotes_occur. Thus vector, hash,
struct, tree and JSON syntax occurring within a
backquote will be turned into a special literal
whether or not it contains unquotes.
* lib.c (obj_print_impl): Do not print the
form (sys:hash-lit) as #Hnil, but #H().
* stdlib/match.tl (transform-qquote): Add a case
which will handle ^#H(), as if it were ^H(()).
Bugfix in the ^H(() ...) case. The use of @(coll)
means it fails to match the empty syntax when
no key/value pairs are specified, whereas
@(all) respects vacuous truth.
* test/011/patmatch.tl: A few tests.
* y.tab.shipped, y.tab.h.shipped: Updated.
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks local-liveness): Just
store the mask of defined registers into each live-info.
Do not propagate the defined mask from the next instruction
backwards. The way the defined mask is used in calc-liveness,
this makes no difference, and is simpler and faster.
|
|
|
|
|
|
|
|
| |
* stdlib/compiler.tl (compiler comp-call-impl): We can no longer
free the temporary registers as-we-go based on whether the
argument expression frag uses them as the output register
frag. Let's just put them all into the aoregs list to be freed
afterward.
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks rename): When we stop
the renaming due to an end instruction and the src
being a v-reg, we can still do the rename in that end
instruction itself. If the v-reg becomes invalid, that
doesn't happen until after the instruction.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (subst-preserve): Rename list param
to insn for clarity.
(careful-subst-preserve): New function. This is like
subst-preserve, but used only for instructions that
have destination registers. It performs a rewrite
such that those destination positions are avoided.
(basic-blocks rename): When the instruction has src
or dst as a target, don't just stop before that
insn. Do the substitution in the source operands using
careful-subst-preserve.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks do-peephole-block):
Remove the local function only-locally-used-treg.
This is unnecessary because the optimization is valid
even if the treg is used in downstream basic blocks.
It was necessary previously in the old version of
this optimization in which we deleted the first
instruction which sets the treg's value. We are now
depending on it being identified as a dead register.
Also, moving the rule to the end. The reason is
that there are cases when the pattern matches, but
it returns insns. That causes the rewrite macro to
march down to the next instruction, skipping other
patterns. This could be bad, unless the pattern is the
last one tried before the @else fallback.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Instead of the conservative strategy in compiler comp-var of
loading variables into t-registers, and relying on optimization
to remove them, let's just go back to the old way: variables
are just registers. For function calls, we can detect mutated
variables and generate the conservative code.
* stdlib/compiler.tl (frag): New slots vbin and alt-oreg.
When a variable access is compiled, the binding is recorded
in vbin, and the desired output register in alt-oreg.
(simplify-var-spy): New struct type, used for detecting
mutated lexical variables when we compile a function argument
list.
(compiler comp-var): Revert to the old compilation strategy
for lexicals: the code fragment is empty, and the output
register is just the v-reg. However, we record the variable
binding and remember the caller's desired register in the
new frag fields.
(compiler comp-setq): Also revert the strategy here.
Here we get our frag from a recursive compilation, so
we just annotate it.
(compiler comp-call-impl): Use the simplify-var-spy to
obtain a list of the lexical variables that were mutated.
This is used for rewriting the frags, if necessary.
(handle-mutated-var-args): New function. If the mutated-vars
list is non-empty, it rewrites the frag list. Every element
in the frag which is a compiled reference to a lexical
variable which is mutated over the evaluation of the arg list
is substituted with a conservative frag which loads the
variable into a temporary register. That register thus
samples the value of the variable at the correct point in the
left-to-right evaluation, so the function is called with
the correct values.
|
|
|
|
|
|
|
|
|
|
|
|
| |
This change is now possible due to the previous bugfix.
* stdlib/optimize.tl (basic-blocks rename): If
the source register is a v-reg, do not allow
the propagation past an end instruction. This
is a precaution because the end instruction
could be the end of the frame in which the
v-register is valid; we don't want to propagate
it outside of that frame.
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks rename): When
we encounter a close instruction, we must leave
it alone. The registers named in the argument area
of the instruction do not belong to the current
instruction stream or basic block; they belong to
the function body.
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks local-liveness): Handle all
instructions explicitly with no catch-all behavior. Make a
copy of the live-info even for instructions that have no
source or destination operands, so that they don't mistakenly
marked as having defs or refs.
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (live-info): Slot def replaced by def0
and def1.
(basic-blocks local-liveness): The local function def becomes
defs: it can take two defs. These become def0 and def1. In the
catch instruction case, we use both arguments, capture the
resulting live-info and use it to call refs.
(basic-blocks rename): Check whether either def0 or def1 is
the source or destination.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks local-liveness): When
processing a pure def, we don't copy the live-info
unconditionally, which is waseteful since if the destination
register is a t-reg, we will invoke (new live-info) to
make yet another live info. Instead, let's destructively
mutate the incoming live info from the instruction below,
and return a copy that is made before that is done.
In the def-ref case, the local copy is entirely superfluous,
because in all cases we return a new object.
We also eliminate redundant (set [bb.li-hash insn] li)
evaluations.
|
|
|
|
|
|
|
|
|
| |
* stdlib/optimize.tl (basic-blocks local-liveness):
The exception symbol and argument registers in the
catch instruction are clobbers, not references.
We must treat them as defs. Unfortunately, the
instruction has two clobbers but live-info has
only one def slot, which should be fixed.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* optimize.tl (rename): Instead of a mapping operation,
we perform the substitution only until we hit an
instruction that defines either the src or dst register.
(basic-blocks do-peephole-block): Drop the conditions
for doing the rename: that neither register can be
defined somewhere in the rest of the block. This
restriction is too limiting. We have to be careful now;
we cannot delete the first instruction, and must only
set the recalc flag and add to the rescan list if the
substitution did something, to avoid looping.
|