summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--parser.c2
-rw-r--r--share/txr/stdlib/asm.tl4
-rw-r--r--share/txr/stdlib/compiler.tl63
-rw-r--r--txr.183
-rw-r--r--vm.c5
-rw-r--r--vmop.h3
6 files changed, 136 insertions, 24 deletions
diff --git a/parser.c b/parser.c
index 8212fdd7..11557be8 100644
--- a/parser.c
+++ b/parser.c
@@ -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))
diff --git a/txr.1 b/txr.1
index dad42dd7..1aa8f26f 100644
--- a/txr.1
+++ b/txr.1
@@ -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
diff --git a/vm.c b/vm.c
index c0a0d505..26d73304 100644
--- a/vm.c
+++ b/vm.c
@@ -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);
}
diff --git a/vmop.h b/vmop.h
index cb2f41af..bfe3011d 100644
--- a/vmop.h
+++ b/vmop.h
@@ -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;