summaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
...
* compiler: switch bugfix: missing index code.Kaz Kylheku2018-04-021-1/+2
| | | | | | | | * share/txr/stdlib/compiler.tl (comp-switch): Emit the code for evaluating the switch index. This has been working by fluke, because when the index is a variable, the assembly code for ifrag is nil; the swtch instruction accesses the variable directly and all is cool.
* compiler: big oreg-related bugfixKaz Kylheku2018-04-011-40/+74
| | | | | | | | | | | | | | | | | | | | | | | | | | There is a smattering of incorrect logic affecting a number of the compiler's special form sub-compilers. Basically the issue is that a compiler routine cannot arbitrarily use the oreg that it is given. If it generates multiple instructions which clobber a destination, only the last clobber may target oreg. The reason is that oreg is not necessarily a fresh temporary that can be used arbitrarily. It can be a variable which is evaluated by the forms that are compiled by the sub-compiler. Prematurely storing a value into oreg can affect the behavior and result value of not-yet executed code. The recent "indirect function calls" fix addressed just one case of this; the problem is more wide-spread. * share/txr/stdlib/compiler.tl (compiler (maybe-alloc-treg, maybe-free-treg)): New methods. (compiler (comp-if, comp-switch, comp-unwind-protect, comp-block, comp-handler-bind, comp-catch, comp-let, comp-progn, comp-and-or, comp-prog1, comp-for, comp-call, comp-tree-case): Do not carelessly use oreg for intermediate computations; allocate one or more temporary registers for that purpose, and either only move the final value into oreg, or else indicate a temporary register as the returned frag's output register.
* compiler: bugfix: wrong lambda frame size.Kaz Kylheku2018-04-011-1/+1
| | | | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-lambda): We must not deduce the frame size from the nenv v-counter, because in the case when no frame is needed (the function has no arguments), nenv is just env. Our need-frame variable indicates whether or not a frame is needed; if not, we must use size zero. This is crucial because the VM otherwise ends up throwing an exception due to the frame level mismatch.
* compiler: bugfix: return value optionalKaz Kylheku2018-04-011-2/+2
| | | | | | * share/txr/stdlib/compiler.tl (comp-return-from, comp-return): The value expression is optional in all the return operators.
* compiler: bug: indirect function calls.Kaz Kylheku2018-04-011-1/+3
| | | | | | | | * 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.
* compiler: bugfix: bad len check for tree-case.Kaz Kylheku2018-04-011-3/+5
| | | | | | | | | * 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.
* compiler: bugfix: incorrect jmp in catch.Kaz Kylheku2018-04-011-3/+1
| | | | | | | | | | * 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.
* compiler: bugfix: scope of init-forms in lambda.Kaz Kylheku2018-03-311-1/+1
| | | | | | * 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.
* expander: bugfix: fixed params visible to optionals.Kaz Kylheku2018-03-311-0/+1
| | | | | | | | | | | * 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.
* doc: codify infinite looping behavior of each.Kaz Kylheku2018-03-301-0/+6
| | | | | | | | | * 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: add missing checks in fun operator.Kaz Kylheku2018-03-301-0/+13
| | | | | | * 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.
* doc: null char in str out stream: incorrect.Kaz Kylheku2018-03-301-4/+8
| | | | | | | * 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.
* string output streams: null pointer deref.Kaz Kylheku2018-03-301-4/+10
| | | | | | | | | | | | | | | 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.
* doc: incorrect text in rlet exampleKaz Kylheku2018-03-301-3/+2
| | | | * txr.1: Example isn't about swapping variables.
* doc: mistake in ldiff example.Kaz Kylheku2018-03-301-1/+1
| | | | * txr.1: A let in the example should be let*.
* compiler: change message for uhandled special op.Kaz Kylheku2018-03-291-1/+1
| | | | | | | * 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.
* continuations: don't fixup pointers if delta is zero.Kaz Kylheku2018-03-291-1/+1
| | | | | | * 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.
* vm: integrate with delimited continuations.Kaz Kylheku2018-03-293-2/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* doc: fix too-wide-for-80-cols syntax synopses.Kaz Kylheku2018-03-291-3/+6
| | | | | * txr.1: defmacro, macrolet and mac-param-bind syntax synopses are too wide under 80 column "man txr": breaking up.
* compiler: improve register use when compiling calls.Kaz Kylheku2018-03-291-6/+11
| | | | | | | | | | | | | | * 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.
* compiler: enforce register balance.Kaz Kylheku2018-03-291-0/+7
| | | | | | | * 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.
* case macros: translate big case forms to hash+switch.Kaz Kylheku2018-03-291-1/+50
| | | | | | | | | | | | | | | | | | | | | | | | | | | 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: elminate reduce_right from expt.Kaz Kylheku2018-03-291-1/+9
| | | | | | * lib.c (rexpt): New static function. (exptv): Create reversed arguments on the stack, then process with nary_op and the rexpt shim.
* args: new args reversing function.Kaz Kylheku2018-03-292-0/+12
| | | | | | * args.c (args_copy_reverse): New function. * args.h (args_copy_reverse): Declared.
* lib: eliminate reduce-left from n-ary math ops.Kaz Kylheku2018-03-292-36/+46
| | | | | | | | | | | | | | | | 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.
* compiler: incorrect null destructuring pattern.Kaz Kylheku2018-03-291-2/+2
| | | | | | | * 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.
* eval: bugs/regressions in handling nil in param expansion.Kaz Kylheku2018-03-291-8/+12
| | | | | | | | | | | | | | | | | | | | 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.
* compiler: special case if + eq combo.Kaz Kylheku2018-03-281-6/+58
| | | | | | | | | | | | | | | | | 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.
* compiler: pass whole form to comp-fun-form.Kaz Kylheku2018-03-281-19/+20
| | | | | | | | | * 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.
* compiler: replace cond implementation.Kaz Kylheku2018-03-281-67/+10
| | | | | | | | | * 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.
* compiler: move some lookup tables out of compiler struct.Kaz Kylheku2018-03-281-5/+7
| | | | | | | | * 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.
* compiler: if: remove spurious instruction.Kaz Kylheku2018-03-281-2/+1
| | | | | | * 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.
* asm: use capital hex for dregs.Kaz Kylheku2018-03-281-1/+1
| | | | | | * share/txr/stdlib/asm.tl (disassemble-c-d): When printing the data table, use capital hexadecimal for the dregs, consistent with the instruction listing.
* compiler: bugfix in let.Kaz Kylheku2018-03-271-1/+2
| | | | | | | | | | | | | | | | | | | | | 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.
* compiler: don't wastefully enter into dreg table.Kaz Kylheku2018-03-271-0/+1
| | | | | | * share/txr/stdlib/compiler.tl (compiler get-dreg): If the input atom is nil, return the t0 register that always holds nil.
* compiler: diagnose exhaustion of dregs.Kaz Kylheku2018-03-271-4/+6
| | | | | * share/txr/stdlib/compiler.tl (compiler get-dreg): Restructure code. Don't increment dreg-cntr past 255.
* compiler: use counter instead of preallocating tregs.Kaz Kylheku2018-03-271-7/+7
| | | | | | | | | | * 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.
* compiler: bugfix: register double free.Kaz Kylheku2018-03-271-1/+2
| | | | | | * 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.
* compiler: implement prof special op.Kaz Kylheku2018-03-271-0/+10
| | | | | | * share/txr/stdlib/compiler.tl (compiler compile): Handle prof via comp-prof method. (comp-prof): New method.
* vm/asm: new prof instruction.Kaz Kylheku2018-03-273-33/+52
| | | | | | | | | | * 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: refactor op_prof to support reuse.Kaz Kylheku2018-03-272-2/+19
| | | | | | | | | | | * 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.
* compiler: implement sys:switch special op.Kaz Kylheku2018-03-271-0/+32
| | | | | | * share/txr/stdlib/compiler.tl (compiler compile): Handle sys:switch via comp-switch method. (comp-switch): New method.
* compiler: bugfix in sys:fbind op.Kaz Kylheku2018-03-271-1/+2
| | | | | | | | | | | | 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.
* compiler: recognize call and apply forms.Kaz Kylheku2018-03-271-11/+23
| | | | | | | | | | * 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.
* regression: fix broken tagbody.Kaz Kylheku2018-03-261-19/+5
| | | | | | | | | | | | | | | | | | | | | | | | | 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.
* compiler: implement sys:catch special op.Kaz Kylheku2018-03-261-0/+46
| | | | | | * share/txr/stdlib/compiler.tl (compiler compile): Handle sys:catch via comp-catch method. (comp-catch): New method.
* vm/asm: new swtch instruction.Kaz Kylheku2018-03-263-18/+80
| | | | | | | | | | | | | * 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.
* compiler: implement handler-bind special op.Kaz Kylheku2018-03-261-0/+14
| | | | | | * share/txr/stdlib/compiler.tl (compiler compile): Handle defmacro via comp-handler-bind method. (comp-handler-bind): New method.
* vm: bugfix: faulty memcpy in closure.Kaz Kylheku2018-03-261-1/+1
| | | | | | * 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.
* compiler/vm: implement sys:abscond-from special form.Kaz Kylheku2018-03-264-14/+30
| | | | | | | | | | | | | | | * 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.