| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
|
|
|
|
| |
* 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.
|
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/compiler.tl (compiler comp-lambda): Frame
size can't be calculated from nfixed, because that doesn't
account for the extra variables in optional parameters.
It can't be calculated from the number of lexical variables
either, because parameters that are special variables consume
vXXYY frame entries without being counted as lexicals.
Rather, the accurate frame size is given to us by the value
of the counter in the newly created environment.
|
|
|
|
|
|
|
|
|
| |
* lisplib.c (compiler_instantiate, compiler_set_entries): New
static functions.
(lisplib_init): Register auto-load for compiler via new
functions.
* share/txr/stdlib/compiler.tl: New file.
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl: block comment with copyright and
BSD license added.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The objective of this work is to isolate the field-formatting
logic so we can target it in the compiler.
Currently, the sys:quasi special operator relies on calling
subst_vars, which calls format_field. Both subst_vars
and format_field perform dynamic evaluation, requiring
an environment. In the compiler, this will be replaced by
macro-generated logic; but we would like to obtain the use of
the lower-level field formatting as a pure function.
* eval.c (do_format_field): New static function. Does the
field formatting previously done in format_field. Also
performs the indexing on the object implied by the numeric
or range modifier; but the range or index is already
computed and comes in as a parameter.
(format_field): Perform the modifier parsing only, requiring
the dynamic evaluations via eval_fun, and then call
do_format_field on the extracted data. The range indexing
on the input sequence is no longer done during the parsing of
the modifiers. That unfortunately changes some behaviors that
are possible but are fortunately obscure and undocumented.
|
|
|
|
|
|
| |
* lib.c (func_vm): The fixparam argument is the total number
of fixed parameters, including optionals. That argument must
be stored in the same-named member of the function structure.
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (op-close asm): Check that the list
of registers has the right number of registers indicated by
the previous operands.
|
|
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (op-ifq, op-ifql): New opcode
types.
* vm.c (vm_ifq, vm_ifql): New static functions.
(vm_execute): Handle IFQ and IFQL opcodes.
* vmop.h (vm_op_t): Regenerated.
|
|
|
|
|
|
|
|
|
|
| |
* eval.c (op_defun): There is a hidden reference to the
sys:defmeth function here, which should have been
updated in commit 0ae617f463290ff4463db2e479604808f940cc76
that renamed the function to define-method!
Caught this by incidental code inspection, browsing
through special forms in the context of working on the
compiler.
|
|
|
|
|
|
|
|
|
|
|
| |
None of the statements which are conditional on eval_initing
are ever executed, because no code is interpreted during eval
init time; all intrisinc functions are C functions defined
using reg_fun.
* eval.c (eval_initing): Global variable removed.
(op_defun, op_defmacro, eval_init): References to eval_initing
and code conditional on it are removed.
|
|
|
|
|
|
|
|
|
|
|
| |
push doesn't unconditionally require a temporary location for
operational correctness; a temporary is used only for
evaluation order. Therefore it is safe to use alet
to eliminate the temporary when (after all expansion) the
item is a trivial symbolic expression.
* share/txr/stdlib/place.tl (push): Use alet to bind the
temp which holds the new item.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (op-block, op-catch): Some operands
that are destinations need to be parsed as "d", so the
assembler diagnoses invalid destinations. Otherwise we don't
catch the problem until VM run time.
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (assembler asm): Allow
instruction 0 to have a label L labeled by checking for the
range 0 <= L < N.
|
|
|
|
|
|
| |
* vm.c (vm_make_desc): We can't call vecref_l on an empty
vector, because it has no index zero. Let's use a null
location in this case.
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (parse-compound-operand): Add the
forgotten increment by two to the level number of the v
operand, since (v 0 n) is in the third display level.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The test case for this is (let* (a (b a))) which raises
suspicion by diagnosing an "unbound variable a" error against
the (b a) var-init pair. The error goes away if we make it
(let* ((a nil) (b a))), a perfectly equivalent form. The
diagnostic is just a symptom; the problem is that in the case
when a doesn't have an initform, the (b a) var-init pair is
being incorrectly expanded in an environment that hasn't been
extended with the a variable.
* eval.c (expand_vars): In the sequential binding situation
(let*), we must extend the environment for each variable in
the no-init-form case exactly as we do in the with-init-form
case.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* lisplib.c (asm_set_entries): Autoload on usr:disassemble.
* share/txr/stdlib/asm.tl (assembler): Drop initializer
from bstr slot. Requires complex initialization for the
case when the buf is supplied by the constructor caller
for the sake of disassembling existing code.
(assembler :postinit): Handle cases when only one of
buf or bstr are set, and when both are not set,
for the greatest flexibility.
(disassemble-c-d, disassemble): New functions.
* vm.c (vm_desc_datavec): New static function.
(vm_init): Registered vm-desc-datavec intrinsic.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (assembler dis-listing): Use
tostringp when converting the opcode and arguments to text, so
package prefixes won't be shown when the current package isn't
sys.
|
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (parse-args): Include the type spec
in the diagnostic for invalid type spec.
(op-setv): Fix use of invalid type spec s, which should
be rs.
|
|
|
|
|
|
|
|
|
|
|
| |
This was caused by the recent work to reduce consing in
generic_funcall.
* lib.c (generic_funcall): Correct test for too many
arguments. Because we don't normalize the argument list to the
exact number of fixed args, but to at least the fixed args,
the excess args can possibly be part of the fill rather than
part of the list.
|
|
|
|
|
| |
* vm.c (vm_init): Register vm_execute_toplevel as
vm-execute-toplevel rather than vm-interpret-toplevel.
|
|
|
|
|
|
| |
* share/txr/stdlib/asm.tl (assembler): All :method definitions
and the :postinit become defmeth forms outside of the
defstruct.
|
|
|
|
|
|
|
|
| |
* share/txr/stdlib/struct.tl (sys:defmeth): Rename
function to sys:define-method. Otherwise it hides
usr:defmethod when the current package is sys.
(defmeth): Refer to sys:define-method rather than
sys:defmeth.
|
|
|
|
|
|
|
|
|
|
|
|
| |
In additon to the encoded notation like t13 and v013f,
we allow forms like (t 19) and (v 1 63) which
mean the same thing. These are a much more convenient
representation for a compiler.
* share/txr/stdlib/asm.tl (assembler parse-operand): Recognize
a compound expression as an operand, and handle via
parse-compound-operand function.
(parse-compound-operand): New function.
|
|
|
|
|
|
|
|
|
|
|
| |
Remove restriction that labels are keywords; a compiler
cannot pollute the keyword space to generate labels.
We allow them to be uninterned symbols also.
* share/txr/stdlib/asm.tl (assembler parse-args, assembler
asm-one): Use is-label instead of keywordp.
(is-label): New function.
(op-label): Use is-label test.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is for allocating a new frame purely on the stack. The
frame will not be captured by lexical closures, and so can
only be used for non-shared variables and additional
compiler-generated temporaries (if registers run out, for
instance).
* share/txr/stdlib/asm.tl (op-sframe, sframe): New opcode
class and opcode.
* vm.c (vm_do_frame): New static function for the common
implementation of frame and sframe.
(vm_frame): Now just a call with vm_do_frame, passing the flag
indicating that closure capture is enabled for this
environment frame.
(vm_sframe): New static function.
* vmop.h: Regenerated.
|
|
|
|
|
|
|
|
|
|
|
|
| |
This broke when I moved asm.tl into the library directory
and set it up for auto-load.
* genvmop.txr: We must not include "asm" any more.
But then there is no auto-load on any other feature of the
assembler other than the class name. The @(mdo)
directive comes in handy; at expansion time we can trigger
auto-load by doing a lookup on the sys:assembler
struct name.
|
|
|
|
|
|
| |
* vm.c (vm_make_closure): When copying captured environment
from stack to heap, use memcpy instead of a loop with
assignments.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Once upon a time when we didn't have continuable warning
exceptions, it was easy to debug internal error throws in TXR:
just set a breakpoint on uw_throw and catch it in the
debugger. This approach became unworkable with uw_throw being
executed numerous times as part of the ordinary program
control flow. With this change, we get that debugging
experience back.
* unwind.c (uw_break_on_error): New static variable.
(uw_throw): If the break-on-error variable is set and we
are processing an exception derived from error, then
call the breakpt function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit is the start of compiler work to make TXR Lisp
execute faster. In six days of part time work, we now have a
register-style virtual machine with 32 instructions, handling
exceptions, unwind-protect, lexical closures, and global
environment access/mutation. We have a complete assembler and
disassembler for this machine. The assembler supports labels
with forward referencing with backpatching, and features
pseudo-ops: for instance the (mov ...) pseudo-instruction
chooses one of three kinds of specific move instruction based
on the operands.
* Makelfile (OBJS): Add vm.o.
* eval.c (lookup_sym_lisp1): Static function becomes external;
the virtual machine needs to use this to support that style
of lookup.
* genvmop.txr: New file. This is the generator for the
"vmop.h" header.
* lib.c (func_vm): New function.
(generic_funcall): Handle the FVM function type via new
vm_execute_closure function. In the variadic case, we want
to avoid the argument copying which we do for the sake of C
functions that get their fixed arguments directly, and then
just the trailing arguments. Thus the code is restructured a
bit in order to switch twice on the function type.
(init): Call vm_init.
* lib.h (functype_t): New enum member FVM.
(struct func): New member in the .f union: vm_desc.
(func_vm): Declared.
* lisplib.c (set_dlt_entries_impl): New static function,
formed from set_dlt_entries.
(set_dlt_entries): Reduced to wrapper for
set_dlt_entries_impl, passing in the user package.
(set_dlt_entries_sys): New static function: like
set_dlt_entries but targetting the sys package.
(asm_instantiate, asm_set_entries): New static functions.
(lisplib_init): Auto-load the sys:assembler class.
* share/txr/stdlib/asm.tl: New file.
* vm.c, vm.h, vmop.h: New files.
|
|
|
|
|
|
|
|
|
| |
* lib.c (obj_init): Give the system package a fallback list
consisting of one element: the user package.
This will make it easier to develop some library features
that have lots of internal symbols that ought to be hidden
in the system package, without having to put sys: on
everything. That code will just switch to the system package.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Removing the restriction that qualified pkg:sym syntax may not
cause interning to take place if pkg has a nonempty fallback
list. This serves no purpose, only hindering the flexibility
of the package system.
* parser.y (sym_helper): When processing a qualified symbol,
if the package exists, just intern it.
* txr.1: Revise all text which touched on the removed rule,
to remove all traces of it from the documentation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
* args.c (args_normalize): Renamed to args_normalize_exact,
because this tries to split the arguments between an exact
array fill quantity and trailing list. Not all places using
this function actually need an exact fill, which causes
unnecessary consing when args->fill is reduced in order to
move items to args->list.
(args_normalize_least): New function. Variant of
args_normalize that can be used by functions which only
require a minimum fill.
(args_normalize_fill): Use args_normalize_least rather than
args_normalize_exact. This reduces consing in generic_funcall,
in handling variadic calls where arrayed arguments have been
supplied for trailing parameters.
* args.h (args_normalize): Renamed to args_normalize_exact.
(args_normalize_least): Declared.
(args_get_list, args_get_rest): Use args_normalize_exact.
(args_clear): Inline function removed. Was used only in one
place in generic_funcall and is no longer.
* eval.c (gather_free_refs): Use args_normalize_least.
(prod_common): Use args_normalize_exact.
* ffi.c (ffi_call_wrap): Use args_normalize_least.
* lib.c (generic_funcall): Use args_normalize_least in switch
statement that handles various callable non-function objects.
When copying args, ensure that there are ARGS_MIN.
A different strategy is used for producing the trailing args
for variadic calls, further reducing consing. Rather than
normalize the args to the fixed number, and then set
args->fill to zero so that args contains just the list, we use
args_cat_zap_from to create a copy of the args in which the
fixed ones are trimmed out. The resulting args is not
renormalized to be purely a list so no consing or list traversal
takes place. If the rebalancing is needed, the called
function will have to do it.
(dwim_set): Streamline the code that handles hashes assigned
via two or three args.
* struct.c (method_args_fun, umethod_args_fun): Use
args_normalize_exact.
|
|
|
|
|
|
|
|
|
|
|
|
| |
* mpi/mpi.c (s_mp_mul_d): The test used for deciding
whether or not the multiplication will carry, and possibly
needs another digit of space, is broken. There are situations
in which a carry occurs (k > 0) in spite of the test being
negative. We code this the way it should have been done in
the first place: resize the object when carry actually occurs.
This still avoids calling s_mp_pad unless absolutely
necessary, as the removed comment says. Also, in the carry
case, we need not try to clamp away leading zeros.
|
|
|
|
|
|
|
|
|
|
| |
* arith.c (c_unum): Add fallthrough comment.
(minus): Add missing break after case that handles char minus
heap object.. This luckily isn't a bug because type(anum)
isn't RNG, and so when it falls through, the next case also
falls through.
* lib.c (car): Add missing fallthrough comment.
|
|
|
|
|
|
|
|
|
| |
* arith.c (bignum_len): Wew function.
(arith_init): Register bignum-len intrinsic.
* arith.h (bignum_len): Declared.
* txr.1: Documented.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If the trace module has been loaded, we can't
define methods. Repro:
1> (trace)
nil
2> (defmeth time foo (me))
** static-slot-home: #<struct-type time> has no static slot named foo
* struct.c (static_slot_ensure): Do the trace_check
after calling static_slot_ens_rec so that the slot exists.
If the slot doesn't exist, an exception occurs when
sys:trace-canonicalize-name calls static-slot-home.
|