| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (comp-return-from,
comp-return): The value expression is optional in all the
return operators.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (comp-call): We must not use
the output register oreg for compiling the expression which
calculates the function because it could be one of the
arguments. In that case, we clobber an argument before we
have called the function.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (expand-bind-mac-params): When
the strict parameter is the keyword symbol : we are
mis-translating the length check. We are ignoring the presence
of the rest-par, and checking for an exact length.
When rest-par is present, we must check only for a minimum
number of fixed parameters.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (comp-catch): The emitted code
doesn't handle the normal non-exception path. It assumes that
only the frame established with the earlier frame instruction
needs to be terminated; but in fact the catch intruction's
frame has to be ended also. Thus we don't need the frame-end
label; all exit cases jump too the handler-end label in front
of the two end instructions.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (comp-lambda): The init-forms
for optional parameters in a lambda must be compiled in the
environment in which prior arguments are visible.
|
|
|
|
|
|
|
|
|
|
|
| |
* eval.c (expand_params_rec): Add the parameter to the macro-time
environment before processing rest of parameter list. This is
already done for all the symbols of a macro-style
destructuring; just not for a simple parameter. This is
necessary, because the init forms of optional parameters occur
in a lexical environment in which prior parameters are
visible. The test case for this is that (lambda (x : (y x)))
must not produce a warning about unbound x.
|
|
|
|
|
|
|
|
|
| |
* txr.1: the each, append-each and all other variants of
operators exhibit inifite looping if the list of bindings is
empty. In that case, the terminating condition that at least
one list is empty, is never true. The behavior is consistent
under compilation and interpretation, so let's just
document it.
|
|
|
|
|
|
| |
* eval.c (do_expand): When traversing (fun ...)
operator, warn if the function isn't defined or if it is being
applied to a special operator.
|
|
|
|
|
|
|
| |
* txr.1: The described behavior regarding null bytes output
into string output streams is incorrect. In fact they are
effectively dropped by the put-char operation. Let's not
commit to any specific behavior. Also mention pseudo-null.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Originally, string output streams would null out their handle
when the string was extracted. This changed in commit
e44c113ee17c7cf15e8b1891f4d51ec03b16bc24 [Jul 2015], so that
just the string buffer was nulled out. Unfortunately, two
places in the code were not updated, still checking for a null
handle, which is always false, and not defending against the
null handle.
* stream.c (string_out_extracted_error): New static function.
(string_out_put_string, string_out_put_byte): Check the buffer
for null, not the handle, which doesn't go null while the
object is live. Throw an exception rather than returning nil.
|
|
|
|
| |
* txr.1: Example isn't about swapping variables.
|
|
|
|
| |
* txr.1: A let in the example should be let*.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): All
special forms are handled, so "not handled yet" wording is
inappropriate. Going forward, no special form will be added
without compiler support.
|
|
|
|
|
|
| |
* unwind.c (revive_cont): If delta is zero, skip the loop.
This is an important optimization. The delta zero case
can occur frequently; I have observed it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
It doesn't Just Work out of the box. Here is why.
The struct vm state is declared somewhere on the stack,
and then the vm executes various recursive functions.
A block is established by vm_block(). Then if a continuation
is captured up to that block, it will not include that part
of the stack which holds the VM state. When the continuation
is restarted, the struct vm * pointers inside the stack will
be garbage.
The solution: when a continuation is captured to a prompt that
is set up by the VM block instruction (vm_block function),
we put information into the block which says "don't just
capture me plus some slack: please capture the stack all the
way up to this specific address here". That address, of
course, is just past the VM state, ensuring that the VM state
is included in the snapshot.
In short: a delimited continuation terminating in a prompt
set up by the VM just include the entire VM context from the
stack frame where the struct vm on down (i.e. up the stack)
to the capture point.
* unwind.c (uw_push_block): Initialize cont_bottom
member to zero. Interpreted blocks leave this as zero.
Blocks set up by the VM override it.
(revive_cont): Critical: the range check must include
the orig_end endpoint, because when the struct vm is captured
into the continuation it is right at the end of the capture
memory, and the prompt block's cont_bottom member points
exactly to orig_end: one element past the struct vm.
The cont_bottom member must be adjusted by delta
so that continuations captured by the revived VM
inside the continuation get the adjusted cont_bottom at their
current stack location.
(capture_cont): If the block is publishing a non-zer
cont_bottom value, then take that value if it is a higher
address than the lim (including the UW_CONT_FRAME_AFTER
slack).
* unwind.h (struct uw_block): New member, cont_bottom.
By means of this, a block can indicate a higher address
to which a continuation capture can extend.
* vm.c (vm_block): Publish the address one byte past the
virtual machine state as the stack bottom for continuations.
|
|
|
|
|
| |
* txr.1: defmacro, macrolet and mac-param-bind syntax
synopses are too wide under 80 column "man txr": breaking up.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-call-impl):
Instead of allocating N temporary registers for N arguments,
some (or even none) of which may actually be used, we
do this one argument at a time: allocate just one register,
compile the argument expression, and then free the register
immediately if that fragment specifies its own output location
instead of the register. Otherwise keep the register and push
it on a stack. This strategy lowers maximum register use.
Also, since we are pushing the used registers on a stack,
when we call free-tregs, they get liberated in reverse
order of allocation, which keeps things tidy.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler check-treg-leak): New
method.
(usr:compile-toplevel): Free the top-level output register,
then call check-treg-leak to verify all were returned.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Currently, the case macros (caseq, caseql, casequal,
caseq*, caseql* and casequal*) all translate to a cond
statement which tries the cases one by one.
With this change, larger cases are translated to
a lookup through a hash table, which produces an
integer value. The integer value is then used as the
index in an op:switch form for table lookup dispatch.
If the hash lookup fails, then the else-clause is
evaluated.
op:switch is handled efficiently in the interpreter, and
turned into an efficient swtch VM instruction by the new
compiler.
* eval.c (me_case): Add variables and logic to the function
such that while it gathers the materials for the cond-based
translation, it also builds materials for a hash-switch-based
translation. Then, at the end, a decision is made by looking
at how many keys there are and other factors.
Because we don't have hash tables based on the eq function,
but only eql, we must be careful not to turn caseq into
hash lookup, unless we verify that the keys which occur
are fixnum integers, characters or symbols.
|
|
|
|
|
|
| |
* lib.c (rexpt): New static function.
(exptv): Create reversed arguments on the stack, then
process with nary_op and the rexpt shim.
|
|
|
|
|
|
| |
* args.c (args_copy_reverse): New function.
* args.h (args_copy_reverse): Declared.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Using reduce-left is inefficient; it conses up a list.
We can decimate the stacked arguments without consing.
* lib.c (nary_op): Replace reduce_left with iteration.
(nary_simple_op): New function, variant of nary_op
useable by functions that have a mandatory argument
passed separately from the argument list.
(minusv, divv): Replace reduce_left with iteration.
(maxv, minv): Replace reduce_left with nary_simple_op.
(abso_self): New static function.
(gcdv, lcmv): Replace reduce_left with nary_op.
* lib.h (nary_simple_op): Declared.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (expand-bind-mac-params): The
listp function identifies a recursive parameter, not consp,
because nil is an empty pattern, and not a variable name,
in macro-style parameter lists.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Due to a number of changes since December 2016, we have two
problems in the evaluator: (lambda (nil)) is accepted rather
than complaining that nil is not bindable; and
(tree-bind (()) '(3)) silently proceeds rather than
diagnosing that the atom 3 doesn't match the empty pattern ().
* eval.c (expand_opt_params_rec, expand_params_rec): When not
expandin macro-style params, check for a parameter not being
bindable, and also avoid recursion entirely when not doing
macro style.
(bind_macro_params): Don't assume that an atom parameter is
a variable, but rather tha a non-list parameter is
a variable. Otherwise we bind nil rather than treating it
as an empty sub-pattern. Before the December 2016 change
(744340ab), we were checking bindablep here; the idea was to
hoist the detailed checking to expansion time. But then the
pattern versus variable distinction was bungled here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We use the ifq instruction for compiling (if (eq x y) ...)
and (if (neq x y ...) rather than emitting a call to
eq or neq.
* share/txr/stdlib/compiler.tl (%test-funs-pos%,
%test-funs-neg%, %test-funs%, %test-inv%): New global
%variables.
(comp-if): Recognize when test expression is one of the tests
in %test-funs% and transform into (sys:ift ...) syntax.
Also, minor unrelated change here: (if test) optimized away to
nil if test is a constant expression.
(compiler comp-ift): New method.
(compiler comp-fun-form): Recognize sys:ift in operator
position; hand off to comp-ift.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Pass form
to comp-fun-form rather than sym and (cdr form).
(compile comp-fun-form): Take just a form argument.
Internally destructure to sym and args with tree-bind.
This will allow some special cases added in the future
to have access to the original form.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-cond): Replace
pointlessly verbose cond implementation compact implementation
that rewrites cond to combinations of if, progn and smaller
cond. This generates pretty much the same code, and will
automatically benefit from special case translations
applied in if.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler): Remove gcallop and
callop static members.
(%gcall-op%, %call-op%): New global variables.
(compiler comp-fun-form): Use new globals instead of old
static members.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-if): Remove a
trailing maybe-mov from the instruction template that
serves no purpose and can potentially generate an instruction.
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (disassemble-c-d): When printing the
data table, use capital hexadecimal for the dregs, consistent
with the instruction listing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Very similar issue to the sys:fbind issue fixed some commits
ago. When we are setting up parallel bindings, we don't want
to compile initforms in the original env, because then they
get the same frame level as the new env we are compiling. Thus
(let ((x (let (...)))) ...) is mistranslated. Both lets,
have environments which are siblings of the same parent env,
which puts them on the same level. But their lifetimes at
run-time are nested, so they cannot share the same level.
The VM caught this since frame instructions declare their
absolute level and it must be one higher than the current
level. (I had the foresight to predict this might be a source
of problems and put in the checks.)
* share/txr/stdlib/compiler.tl (compiler comp-let): Same trick
as in sys:fbind case: set up an empty environment above the
initforms, so when an initform creates an environment, it is a
granddaughter of env, and thus a niece rather than sister of
of nenv, consequenty one frame level higher.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler get-dreg): If the
input atom is nil, return the t0 register that always
holds nil.
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler get-dreg):
Restructure code. Don't increment dreg-cntr past 255.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler): new slot,
treg-cntr; slot nregs removed; tregs stack initialized to
empty list.
(compiler alloc-treg): Take from stack if possible,
otherwise create new treg using counter, up to 255.
(usr:compile-toplevel): Referenceco.treg-cntr for register
count, rather than removed co.nreg.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-progn): Since
oreg-discard is conditionally allocated, it must be
only be freed if it had been allocated.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
prof via comp-prof method.
(comp-prof): New method.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (prof): New opcode.
(op-prof): New opcode class.
* vm.c (vm_prof_callback, vm_prof): New static functions.
(vm_execute): Handle PROF opcode via vm_prof.
* vmop.h: Regenerated.
|
|
|
|
|
|
|
|
|
|
|
| |
* eval.c (prof_call): New function, contents based on op_prof.
(struct prof_ctx): New struct type.
(op_prof_callback): New static function.
(op_prof): Reduced to call to prof_call, passing context
through to callback which performs the evaluation that is
timed.
* eval.h (prof_call): Declared.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
sys:switch via comp-switch method.
(comp-switch): New method.
|
|
|
|
|
|
|
|
|
|
|
|
| |
When a (flet ...) form is compiled, the function arguments of
the lambda are being bound in the same frame and clashing
against the variables being bound by the construct.
* share/txr/stdlib/compiler.tl (compiler comp-fbind): For the
non-recursive case, insert the dummy empty environment eenv,
and compile the forms in that environment. This raises them
up to the appropriate display level without affecting what
is visible in their scope.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler): New slots, gcallop
and callop.
(compiler comp-fun-form): Restructured to handle apply and
call forms, turning them into better code, exploiting the
call, gcall, apply and gapply instructions.
(compiler comp-call): Take opcode argument so apply calls can
be handled.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
commit 87caead8269055b4791de53be0e03afab01f1dd4, subject
"macros: expand declined form in outer env" broke tagbody.
The tagbody implementation uses a dubious trick of setting up
local macros which return the original form, as a way of
prevening an outer scoped macro from expanding the form. The
above commit specifically changes the behavior in such a way
that this strategy is nullified.
However, the macro fallback feature introduced by the above
commit is exactly what tagbody needs!
* share/txr/stdlib/tagbody.tl (tagbody): Simplify the
treatment of (go label) thanks to new behavior in macro
expander. We no longer need an extra sys:expand pass to
allow inner tagbodies to expand their go forms, and intercept
any unexpanded ones. We have a simple go macrolet which
performs the expansiion when the label is recognized, or else
returns the form to decline expansion. The macro expander
will then fall back by trying the macro in the next outer
scope. Thus, every (go label) is resolved in the tagbody
to which label belongs, or else lands into the top-level
go macro which diagnoses undefined labels.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
sys:catch via comp-catch method.
(comp-catch): New method.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (backpatch-low16, backpatch-high16):
New struct types.
(%backpatch-low16%, %backpatch-high16%): New global variables.
(swtch): New opcode.
(op-swtch): New opcode class.
* vm.c (vm_swtch): New static function.
(vm_execute): Handle SWTCH opcode via vm_swtch.
* vmop.h: Regenerated.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
defmacro via comp-handler-bind method.
(comp-handler-bind): New method.
|
|
|
|
|
|
| |
* vm.c (vm_make_closure): Add missing scale factor to memcpy.
This is the memcpy which relocates display frame contents from
the stack to the frame.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (abscsr): New instruction.
(op-abscsr): New opcode class, derived from op-retsr.
* share/txr/stdlib/compiler.tl: Handle sys:abscond-from via
comp-return-from method.
(compiler comp-return-from): Handle sys:abscond-from by
switching to abscsr opcode instead of ret pseudo-op.
* vm.c (vm_abscsr): New static function.
(vm_execute): Dispatch ABSCSR opcode.
* vmop.h: Regenerated.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Route
block* to same helper method as block.
(compiler comp-block): Handle block* also by compiling the
name form and using the resulting value as the name
operand in the block instruction.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
defsymacro via expand-defsymacro expander.
(expand-defsymacro): New function.
* eval.c (rt_defsymacro): New static function.
(eval_init): register sys:rt-defsymacro intrinsic.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
defmacro via expand-defmacro expander.
(expand-defmacro): New function.
|