diff options
-rw-r--r-- | parser.c | 2 | ||||
-rw-r--r-- | share/txr/stdlib/asm.tl | 4 | ||||
-rw-r--r-- | share/txr/stdlib/compiler.tl | 63 | ||||
-rw-r--r-- | txr.1 | 83 | ||||
-rw-r--r-- | vm.c | 5 | ||||
-rw-r--r-- | vmop.h | 3 |
6 files changed, 136 insertions, 24 deletions
@@ -636,7 +636,7 @@ static val read_file_common(val stream, val error_stream, val compiled) if (compiled && first) { val major = car(form); - if (lt(major, one) || gt(major, three)) + if (lt(major, one) || gt(major, four)) uw_throwf(error_s, lit("cannot load ~s: version number mismatch"), stream, nao); diff --git a/share/txr/stdlib/asm.tl b/share/txr/stdlib/asm.tl index be442950..bf2da421 100644 --- a/share/txr/stdlib/asm.tl +++ b/share/txr/stdlib/asm.tl @@ -691,7 +691,7 @@ (:method dis (me asm name reg) ^(,me.symbol ,(operand-to-sym reg) ,(small-op-to-sym name)))) -(defopcode-derived op-getf getf auto op-getv) +(defopcode-derived op-oldgetf oldgetf auto op-getv) (defopcode-derived op-getl1 getl1 auto op-getv) @@ -779,6 +779,8 @@ (:method dis (me asm src idx) ^(,me.symbol ,(operand-to-sym src) ,idx))) +(defopcode-derived op-getf getf auto op-getlx) + (defun disassemble-cdf (code data funv *stdout*) (let ((asm (new assembler buf code))) (put-line "data:") diff --git a/share/txr/stdlib/compiler.tl b/share/txr/stdlib/compiler.tl index bba9bb7e..90eb2d41 100644 --- a/share/txr/stdlib/compiler.tl +++ b/share/txr/stdlib/compiler.tl @@ -26,6 +26,8 @@ (load "vm-param") +(defvarl assumed-fun) + (defstruct (frag oreg code : fvars ffuns) nil oreg code @@ -916,8 +918,7 @@ (mac-param-bind form (op sym) form (iflet ((fbin env.(lookup-fun sym t))) (new (frag fbin.loc nil nil (list sym))) - (let ((dreg me.(get-dreg sym))) - (new (frag oreg ^((getf ,oreg ,dreg)) nil (list sym))))))) + (new (frag oreg ^((getf ,oreg ,me.(get-sidx sym))) nil (list sym)))))) (defmeth compiler comp-progn (me oreg env args) (let* (ffuns fvars @@ -1169,23 +1170,43 @@ (mac-param-bind form (op arg) form (cond ((bindable arg) - (condlet - (((bind env.(lookup-lisp1 arg t))) - (new (frag bind.loc - nil - (if (typep bind 'vbinding) (list arg)) - (if (typep bind 'fbinding) (list arg))))) - (t (new (frag oreg - ^((getl1 ,oreg ,me.(get-dreg arg))) - (list arg) - (list arg)))))) + (let ((bind env.(lookup-lisp1 arg t))) + (cond + (bind + (new (frag bind.loc + nil + (if (typep bind 'vbinding) (list arg)) + (if (typep bind 'fbinding) (list arg))))) + ((not (boundp arg)) + (pushnew arg assumed-fun) + (new (frag oreg + ^((getf ,oreg ,me.(get-sidx arg))) + nil + (list arg)))) + ((special-var-p arg) + (new (frag oreg + ^((getv ,oreg ,me.(get-dreg arg))) + (list arg) + nil))) + (t (new (frag oreg + ^((getlx ,oreg ,me.(get-sidx arg))) + (list arg) + nil)))))) (t me.(compile oreg env arg))))) (defmeth compiler comp-dwim (me oreg env form) (mac-param-bind form (op obj . args) form - (let ((l1-exprs (cdr form))) + (let* ((l1-exprs (cdr form)) + (fun (car l1-exprs)) + (bind env.(lookup-lisp1 fun nil))) me.(compile oreg env - ^(call ,*(mapcar (op list 'sys:lisp1-value) l1-exprs)))))) + (if (and (symbolp fun) + (not bind) + (not (boundp fun))) + (progn + (pushnew fun assumed-fun) + ^(,fun ,*(mapcar (op list 'sys:lisp1-value) (cdr l1-exprs)))) + ^(call ,*(mapcar (op list 'sys:lisp1-value) l1-exprs))))))) (defmeth compiler comp-prof (me oreg env form) (mac-param-bind form (op . forms) form @@ -1536,6 +1557,15 @@ as.(asm ^(,*(mappend .code (nreverse co.lt-frags)) ,*frag.code (end ,frag.oreg))) (vm-make-desc co.nlev (succ as.max-treg) as.buf co.(get-datavec) co.(get-symvec))))) +(defun compiler-emit-warnings () + (let ((warn-fun [keep-if boundp (zap assumed-fun)])) + (when warn-fun + (usr:catch + (throw 'warning + `uses of @{warn-fun ", "} compiled as functions,\ + \ then defined as vars`) + (continue ()))))) + (defvarl %file-suff-rx% #/[.][^\\\/.]+/) (defvar *emit*) @@ -1544,7 +1574,7 @@ (defvarl %big-endian% (equal (ffi-put 1 (ffi uint32)) #b'00000001')) -(defvarl %tlo-ver% ^(3 0 ,%big-endian%)) +(defvarl %tlo-ver% ^(4 0 ,%big-endian%)) (defun open-compile-streams (in-path out-path) (let* ((rsuff (r$ %file-suff-rx% in-path)) @@ -1588,7 +1618,8 @@ (unwind-protect (progn ,*body) (unless ,rec - (release-deferred-warnings)))))) + (release-deferred-warnings) + (compiler-emit-warnings)))))) (defun usr:compile-file (in-path : out-path) (let ((streams (open-compile-streams in-path out-path)) @@ -62644,17 +62644,19 @@ file name suffix) is translated into an object file (named with a .code .tlo suffix) containing a compiled version of those forms. -The key concept is that loading the compiled +The intent is that loading the compiled .code .tlo file via the .code load function produces the same effect as loading the .code .tl -file (except when special arrangements are deliberately put in place for -different behaviors to occur). The difference is that the compiled file +file. The main difference is that the compiled file contains no Lisp source code; only the machine code instructions for the virtual machine, and some accompanying data such as literals and referenced -symbols. +symbols. Note that the behavior of compiled code can differ from interpreted +code in a number of ways. Differences in behavior can be deliberately induced. +Certain erroneous or dubious situations can also cause compiled code to behave +differently from interpreted code. Compilation not only provides faster execution. Compiled files load much faster. Compiled files can be distributed unaccompanied by the source files, @@ -62853,6 +62855,79 @@ that version. Version 199 produces version 3 files and loads version 2 or 3. +Version 200 produces version 4 files and loads version 2, 3 or 4. + +.SS* Semantic Differences between Compilation and Interpretation + +The +.code compile-only +and +.code eval-only +operators can be used to deliberately produce code which behaves differently +when compiled and interpreted. In addition, unwanted differences in behavior +can also occur. The situations are summarized below. + +Forms evaluated by +.code load-time +are treated differently by the compiler. When a top-level form is compiled, +its embedded +.code load-time +forms are factored out such that the compiled image of the top-level form +will evaluate these forms before other evaluations take place. +The interpreter doesn't perform this factoring; it evaluates a +.code load-time +form when it encounters it for the first time. + +Unbound variables are treated differently by the compiler. A reference +to an unbound variable is treated as a global lexical access. This means that +if a variable access is compiled first and then a +.code defvar +is processed which introduces the variable as a dynamically scoped ("special") +variable, the compiled code will not treat the variable as special; it +will refer to the global binding of the variable, even when a dynamic binding +for that variable exists. The interpreter treats all variable references +that do not have lexical bindings as referring to dynamic variables. +The compiler treats a variable as dynamic if a +.code defvar +has been processed which marked that variable as special. + +Arguments of a +.code dwim +form (or the equivalent bracket notation) which are unbound +symbols are treated differently by the +compiler. The code is compiled under the assumption that all such symbols +refer to global functions. For instance, if neither +.code f +nor +.code x +are defined, then +.code "[f x]" +will be compiled under the assumption that they are functions. If they are +later defined as variables, the compiled code will fail because no function +named +.code x +exists. The interpreter resolves each symbol in a +.code dwim +form at the time the form is being executed. If a symbol is defined +as a variable at that time, it is accessed as a variable. If it defined as a +function, it is accessed as a function. + +The symbolic arguments of a +.code dwim +form that refer to global bindings are also treated differently by the compiler. +For each such symbol, the compiler determines whether it refers to a +function or variable and, further, whether the variable is global lexical or +special. This treatment of the symbol is then cemented in the compiled code; +the compiled code will treat that symbol that way regardless of the +run-time situation. By contrast, the interpreter performs this classification +each time the arguments of a +.code dwim +form are evaluated. The rules are otherwise the same: if the symbol is bound as +a variable, it is treated as a variable. If it is bound as a function, it is +treated as a function. If it has both bindings, it is treated as a variable. +The difference is that this is resolved at compile time for compiled code, +and at evaluation time for interpreted code. + .SS* Compilation Library .coNP Function @ compile-toplevel @@ -991,7 +991,7 @@ NOINLINE static val vm_execute(struct vm *vm) case GETV: vm_getsym(vm, insn, lookup_var, lit("variable")); break; - case GETF: + case OLDGETF: vm_getsym(vm, insn, lookup_fun, lit("function")); break; case GETL1: @@ -1024,6 +1024,9 @@ NOINLINE static val vm_execute(struct vm *vm) case SETLX: vm_settab(vm, insn, lookup_var, lit("variable")); break; + case GETF: + vm_gettab(vm, insn, lookup_fun, lit("function")); + break; default: uw_throwf(error_s, lit("invalid opcode ~s"), num_fast(opcode), nao); } @@ -57,7 +57,7 @@ typedef enum vm_op { CATCH = 28, HANDLE = 29, GETV = 30, - GETF = 31, + OLDGETF = 31, GETL1 = 32, GETVB = 33, GETFB = 34, @@ -68,4 +68,5 @@ typedef enum vm_op { CLOSE = 39, GETLX = 40, SETLX = 41, + GETF = 42, } vm_op_t; |