| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Wire in
tree-bind case via comp-tree-bind method.
(compiler comp-tree-bind): New method.
(expand-bind-mac-parse-params, expand-bind-mac-params): New
functions.
|
|
|
|
|
|
|
|
| |
* vm.c (vm_make_desc): Initialize vd->funvec to nil so that
when the object is created, it doesn't have a garbage field.
However, there is no risk here that the field will be
traversed by the garbage collector, since immediately after
allocating the objct, we initialize the fields.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Let's have a field in struct vm_desc that keeps track of the
ftab size. The problem is when we do length_vec(vd->funvec) in
vm_desc_mark, we are in the middle of garbage collection.
The object's tag has a reachable bit which blows up the
type check.
* vm.c (struct vm_desc): New member, ftsz.
(vm_make_desc): Store the length of the function table
into ftsz.
(vm_desc_mark): Use the stored length as the loop bound;
do not access vd->funvec.
|
|
|
|
|
|
|
|
|
|
| |
* struct.c (struct_set_initfun, struct_set_postinitfun):
Replace incorrect direct assignments with set macro.
This manifested itself as corruption. I ran into a situation
in which the postinitfun of a struct type was prematurely
reclaimed and the heap object was re-used for something else
wreaking havoc on the postinit call when the struct was
instantiated.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
return and return-from special forms.
(compiler comp-return-from, compiler comp-return): New
methods.
|
|
|
|
|
|
| |
* vm.c (vm_no_block_err): New static function.
(vm_retsr, vm_retrs, vm_retrr): Call vm_no_block_err if
uw_block_return returns.
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-setq): Fix
reversed mov instruction arguments in lexical case.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is something that will be useful in compiling some forms.
At first I added it to the compiler only, but it seems wise to
have it in the interpreter also. (sys:upenv form) causes
form to be treated as if it were not in the immediately
surrounding lexical environment but its parent.
Thus (let ((a 1)) (let ((a 2)) (list a (sys:upenv a))))
yields (2 1). This operator needs no special treatment in the
expander; it is expanded as a function call. This is not 100%
correct in the face of all conceivable use. For instance
given (symacrolet ((a 1)) (let ((a 2)) (sys:upenv a))),
we probably want sys:upenv to skip the inner environment at
expansion time too so that a is replaced by 1. However, it
is not documented for application use, and will never be used
in such a situation in the compiler.
* eval.c (op_upenv): New static function.
(eval_init): Register sys:upenv special operator.
* compiler.tl (compiler compile): Implement compiled version
of sys:upenv.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl: If a non-list sequence like a
string or vector is being accumulated, we must have an append
call, because the accumulation cell is never extended with new
cells. After we replace the cdr of ,accum with a string or
vector atom, (last ,accum) just finds ,accum and so then the
cdr will be clobbered on the next iteration.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (sys:env extend-var*):
New method.
(compiler comp-let): When doing sequential bindings, we must
still add the variable to the environment even if there is no
init-form. Oops! Since we're not doing any gensym renaming
trick in this case but binding the real symbol directly, we
need a binding method that allows duplicates.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We want to diagnose (let (a a)) and (lambda (a a)), as well as
the analogous situations for function bindings. This is
certainly ambiguous: if the code accesses a, which one was
intended? The choice is undocumented and so this is a
likely programming error; nobody will write code this
deliberately, investigate what the implementation actually
does, and then rely on that.
However, multiple bindings of special variables are allowed.
Duplicates are also allowed in sequential binding.
Handling duplicates in let* binding creates the following
problem: (a a) means that a should be initialized from the
previous a. We would like to compile the init form in the
environment in which the second a doesn't exist, yet indicate
the destination location of the new a. Since we are clobbering
one environment as we go by adding bindings to it, this is not
possible. The solution is a rename hack: we introduce the new
a as a gensym, so that it is invisible. We indicate the
gensym's location as the destination register for the
init-form code fragment. Then immediately after compiling the
init-form, we rename the gensym variable.
* share/txr/stdlib/compiler.tl (sys:env extend-var, sys:env
extend-fun): Diagnose duplicates.
(sys:env rename-var): New methods.
* share/txr/stdlib/compiler.tl (compile comp-let): Bind to a
gensym instead of the real symbol when binding sequentially.
Then rename the symbol.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
sys:each-op with help of expand-each.
(expand-each): New function.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle and
and or cases via comp-and-or method.
(compiler comp-and-or): New method. This is based on
comp-progn.
|
|
|
|
|
|
|
| |
* eval.c (do_expand): The first argument of the sys:for-op
special operator isn't "vars" but a sequence of initialization
forms. Name the variable appropriately. The neglected
expansion of these forms is now performed.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle
dohash by rewriting the form via expand-dohash, then compiling
result.
(expand-dohash): New function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
There is the possibility that funcall1 through funcall3 will
pass the wrong number of arguments to vm_execute_closure,
which doesn't do any checks. This is only for the code paths
which don't go through generic_funcall, when the function
has no optional arguments.
* lib.c (funcall1, funcall2, funcall3, funcall4): Check
that the closure doesn't require more fixed parameters
than we're giving it in the variadic case, or that
it doesn't require a different number of fixed parameters
we're giving it in the non-variadic case.
|
|
|
|
|
|
|
| |
* gc.c (mark_obj): When marking functions, tail recurse
on the environment. That's the pointer more likely to be a
gateway to serious depth, rather than the vm description or
interpreted function's source.
|
|
|
|
|
|
|
|
|
|
| |
* vm.c (struct vm_closure): Redeclare display member as array
of 1: old-fashioned version of C struct hack.
(vm_make_closure): Make the necessary adjustments to the
allocation code.
(vm_closure_destroy): Static function removed.
(vm_closure_mark): Wire in cobj_destroy_free_op once again,
since there is just one object to free.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-lambda): To
determine whether the argument is missing, we must
test it for equivalence to the : symbol. What we're
testing here is the wrong thing: the register which
will hold the output of the default initform.
That fragment would even be executed yet at this spot
in the code, never mind being the wrong value to test.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-progn): If the
suggested output register is a variable, progn should not use
it for the discarded values of the leading forms, only for
the last form. Writes to the variable space of the display
are costly because in closures, the underlying vector of
a given level has to be passed to gc_mutated. We use a new
temp register for the discarded forms.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Fix
nonsensical fourth argument expression in
comp-progn call.
|
|
|
|
|
|
|
|
|
|
| |
* eval.c (eval_init): Use the old ldiff function under
compatibility with 190 or lower.
* lib.c (ldiff): Rewritten.
(ldiff_old): New function, copy of previous version of ldiff.
* lib.h (ldiff_old): Declared.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-let): The
returned fragment must specify boreg not oreg, because
the output is either in oreg or bfrag.oreg.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Anyway, this kind of thing should really be (and will be)
taken care of peephole optimization. But for now, it's nice to
get somewhat better code without doing too much work.
* share/txr/stdlib/compiler.tl (compiler comp-setq, compiler
comp-cond, compiler cond-if, compiler cond-let, compiler
comp-lambda, compiler comp-for): Replace occurrences of
(if (nequal reg1 reg2) ^((mov ,reg ,reg2))) with call to
maybe-mov.
(maybe-mov): New function.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle if
case via comp-if method.
(compiler comp-if): New method.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Handle cond
case via comp-cond method.
(compiler comp-cond): New method.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This allows a virtual machine's funvec to refer to functions
that are not yet defined. We resolve all the ones that are
defined when the virtual machine description is constructed.
The others are resolved at vm execution time when accessed.
* vm.c (vm_make_desc): When filling ftab with resolved
function bindings, check that lookup_fun has returned nil; in
that case, initialize the binding location to zero,
instead of blowing up calling cdr_l(nil).
(vm_ftab): New static function. Implements the null check
and late resolution.
(vm_gcall, vm_gapply): Acces ftab through vm_ftab instead
of open-coded expression.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (usr:compile-toplevel): Call
release-deferred-warnings to dump accumulated warnings.
Otherwise when we are working in the listener, the pent-up
warnings get dumped in relation to the next input line.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (sys:env :postinit): The call
to register the environment with the compiler must be outside
of the unless form. Otherwise it never takes place, and so the
compiler doesn't find the maximum number of environment
levels, keeping the value at 2. The executing vm then accesses
out of bounds memory when setting up display frames.
(usr:compile-toplevel): Give the root environment the
compiler. Not strictly necessary since we are constent in
doing this elsewhere, so we are not relying on inheritance
of the compiler from parent environment to child.
* vm.c (vm_make_closure): assert added for the environment
levels of the closure not exceeding the display depth given
in the machine description. This was added during debugging
and going off; I'm keeping it.
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (op-close asm): Fix argument
count check being incorrect in the variadic argument
case, causing the assembler to blow up on a correct
(close ...) instruction. The integer indicating the
number of fixed args is one *less* than the number of
arguments given to the instruction not one *more*.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* vm.c (vm_execute_closure): Remove bogus nargs local
variable, and replace its uses by fixparam which is the
correct one to use for getting the list of trailing arguments
using args_get_rest and for the loop which extracts the fixed
arguments. The "if (variadic)" statement near the end
specifically depends on the variadic destination register not
having been extracted yet from the instruction space,
so the argument count used in the previousl loop cannot
include that register. This bug was causing that statement to
extract a zero, thus register t0, which then blew up in the vm
execution as an attempt to modify t0.
|
|
|
|
|
|
|
| |
* lib.c (func_vm): fix incorrect assignment of int boolean to
one-bit-wide bitfield. The argument isn't zero or one; in fact
the value 256 gets passed down representing true. This gets
truncated to one bit which is zero.
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-block): Don't
use bfrag.oreg as the output register of block.
Firstly, bfrag.oreg might be (t 0) which is read-only.
Worse, bfrag.oreg could just be a variable from which
the block obtains its return value; we mustn't clobber
that location with block's dynamic return value. Not only
mustn't but possibly we cannot: the location could be
out of scope!
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-block): If the
block is anonymous, just refer to (t 0) as the name; don't
intern nil via get-dreg.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler): New slots
fidx-cntr, fidx and ftab.
(compiler get-fidx, compiler get-funvec): New methods.
(compiler comp-call-impl): New method.
(compiler comp-call): Look up the symbol to determine
whether the function lexical or global. Call
comp-call-impl appropriately to generate a
gcall or call.
(usr:compile-toplevel): Obtain the funvec from the compiler
and pass to vm-make-desc.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (usr:disassemble): Return the object
that was disassembled, rather than nil. This is useful
in the listener: we can compile and disassemble something in
one step, then have access to the compiled object via a
listener variable.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-lambda): The
incoming oreg, which indicates where the surrounding context
would like to put the closure, cannot be used for the
output location of the lambda body. Because then when the
closure is called, its return value will overwrite the
location where the closure was placed. We must allocate a
a new temporary register for this, and be sure to free it.
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (sys:env lookup-fun): Fix
swapped args in assoc call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The vm now supports gcall and gapply opcodes which index
numerically (using an immediate integer field in the
instruction word) into a table of pre-resolved global function
bindings.
* share/txr/stdlib/asm.tl (op-gcall, op-gapply): New opcodes.
(disassemble-c-d): Take the function vector as an argument and
dump it too.
(usr:disassemble): Extract function vector from VM description
and pass it to disassemble-c-d.
* share/txr/stdlib/compiler.tl (usr:compile-toplevel): Pass
empty function symbol vector to vm-make-desc; it's now a
required argument.
* vm.c (struct vm_desc): New members funvec and ftab.
(struct vm_ftent): New struct type.
(vm_make_desc): New argument, funvec. Store funvec in the
descriptor. Allocate a table of vm_ftent structures equal in
number of elements to the function table, and populate it with
resolved bindings.
(vm_desc_funvec, vm_desc_destroy): New static functions.
(vm_desc_mark): Mark the captured bindings in vd->ftab.
(vm_gcall, vm_gapply): New static functions.
(vm_execute): Handle GCALL and GAPPLY opcodes.
(vm_desc_ops): Wire vm_desc_destroy in place of
cobj_destroy_free_op.
(vm_init): Add argument to vm-make-desc intrinsic. Register
vm-desc-funvec intrinsic.
* vm.h (vm_make_desc): Declaration updated.
* vmop.h: Regenerated
|
|
|
|
|
|
| |
* vm.c (vm_closure_destroy): New static function.
(vm_closure_ops): Use vm_closure_destroy rather than
generic cobj_destroy_free_op.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
With this change we get better code, as well fewer situations
in which tregs are manually released. There are some
subtleties.
Previously, a compiled fragment of code would dictate the
identity of the location into which it has placed output;
then the higher level compile had to work with that location.
The new protocol is that the caller specifies a suggested
output register to the recursive compile call.
If the recursive compile requires a temporary register
for the output, it uses the suggested one. If it doesn't
require a temporary register, it specifies its own output
(for instance a (v x y) entry in the display).
The caller has to deal with the output not being in the
suggested register.
The binding constructs lambda and let also have to deal with
the possibility that the body specifies an output which is
going out of scope, rather than the suggested register,
and in that case must generate a move instruction to transfer
from there to the suggested register before the (end ...).
* share/txr/stdlib/compiler.tl (sys:env out-of-scope): New
method.
(compiler compile): Restructured handling of atom forms.
Function takes output register argument and passes it down
to the special form compile methods.
(compiler comp-atom): Bugfix: this must handle nil by
returning (t 0) rather than wastefully interning nil into the
data table. (quote nil) triggered this.
We no longer need to allocate a temporary register
in this function; we just use oreg when we need it.
(compiler comp-var): Don't alloc temporary, use oreg.
(compiler comp-setq): We don't have to free the temporary
register from compiling the value expression. When the target
is a lexical variable, we pass its location to the
sub-compile, so the result is placed in the variable directly.
Thus we don't have to generate an additional mov instruction
any more, except if that compile wasn't able to use the
suggested location.
(compiler comp-block): Pass oreg to compile of block bodies.
Then use whatever register that block specifies as the
block output.
(compiler comp-let): Binding of variables is streamlined with
oreg in a manner similar to assignment in comp-setq.
The body is compiled with the suggested oreg as its output.
Because it may put the output in a location that is going
out of scope, we have to do the scope check and insert a mov.
(compiler comp-lambda): Similar changes as in comp-let.
(compiler comp-progn): Just use the incoming oreg as the
destination for every form. Use the actual output register of
the last frag as the output register, unless there were no
frags in which case the nil register is specified as the
output.
(compiler comp-prog1): Allocate a register for the ignored
values of the second and subsequent forms. Use oreg for
compiling the first form.
(compiler comp-quasi): Take oreg param and pass down.
(compiler comp-call): Now allocates a batch of suggested
registers for each of the argument compiles, and passes each
down to its corresponding argument compile. These registers
are freed, and the actual output are used in generating the
call. The output of the call goes into oreg; no need to
allocate.
(compiler comp-for): Mostly the obvious changes here.
(usr:compile-toplevel): Here we need to allocate an output
register for the top-level call into the compiler.
|
|
|
|
|
|
|
|
|
|
|
|
| |
It's better to use mac-param-bind than tree-bind because it
provides diagnostics related to the form being destructured.
* share/txr/stdlib/compiler.tl (compiler compile): Pass the
whole form rather than (cdr form) to a number of special form
handlers.
(compiler comp-seq, compiler comp-block, compiler comp-let,
compiler comp-lambda, compiler comp-for): Destructure
arguments with mac-param-bind.
|
|
|
|
|
|
|
| |
* parser.c (is_balanced_line): Introduce the ST_RGXC state to
which we switch when we encounter a regex character class.
Also introduce ST_RGXE for regex subexpressions. In these
states, do not recognize / as the regex terminator.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): The message
will show (car form) followed by a colon, so the wording
should refer to that object rather than the entire form.
|
|
|
|
|
|
|
|
| |
* eval.c (eval_init): Expose raw expand function as
sys:expand*, since sys:expand squelches warnings.
* share/txr/stdlib/compiler.tl (usr:compile-toplevel): Use
expand* instead of expand.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler compile): Add
sys:quasi case, dispatching new comp-quasi method.
(compiler comp-quasi): New method.
(expand-quasi-mods, expand-quasi-args, expand-quasi): New
functions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* gc.c (mark_obj): Recognize FVM functions and mark their
vm_desc.
* lib.c (equal): Handle equality for FVM. If the environment
pointers are equal, consider the functions equal.
(funcall, funcall1, funcall2, funcall3, funcall4):
Recognize and call FVM functions. However, there is
a lack of robustness here that needs to be addressed:
vm_execute_closure doesn't check whether there are
too many or not enough arguments. Interpreted functions
have a run-time check inside bind_args.
(obj_print_impl): Don't print VM functions as #<intrinsic fun...>
but rather #<vm fun>.
|
|
|
|
|
|
|
|
| |
* eval.c (fmt_tostring, fmt_cat): New static functions.
(do_format_field): Replace code block with call to fmt_cat.
(fmt_simple, fmt_flex): Insert needed call to fmt_tostring.
(subst_vars): Replace blocks of code with calls to
fmt_tostring and fmt_cat.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is the second round of changes in perparation for
compiling the string quasiliterals special form.
Having split format_fields into a lower level do_format_field
and format_field, we now provide two functions which allow
Lisp code to call do_format_field.
The sys:fmt-simple function has fixed arguments for
the field width/alignment, indexing/slicing, separator
and plist. Any of them can be defaulted with nil. This
function will be useful when the quasiliteral specifies
modifiers that are simple literals or constant expressions.
The sys:fmt-flex function takes a variable number of
arguments, after the object and the plist. This function
has to scan through the arguments and classify them
by type: a string is a separator, an integer is a width
and left/right alignment and so on. The compiler will use
sys:flex when format field modifiers are present whose
arguments contain expressions that get evaluated.
* eval.c (fmt_simple, fmt_flex): New static functions.
(eval_init): sys:fmt-simple and sys:fmt-flex registered.
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-lambda): When
we have specials we must generate an extra (end ...), to
terminate the (dframe ...) that we inserted.
|