diff --git a/src/c/compiler.d b/src/c/compiler.d index 24bd2cb5c..b5a681980 100644 --- a/src/c/compiler.d +++ b/src/c/compiler.d @@ -31,9 +31,6 @@ /********************* EXPORTS *********************/ -#define REGISTER_SPECIALS 1 -#define IGNORE_DECLARATIONS 0 - /* Flags for the compilation routines: */ /* + Push the output of this form */ #define FLAG_PUSH 1 @@ -51,8 +48,6 @@ #define FLAG_LOAD 32 #define FLAG_COMPILE 64 -#define ENV_RECORD_LOCATION(r) CADDDR(r) - #define ECL_SPECIAL_VAR_REF -2 #define ECL_UNDEFINED_VAR_REF -1 @@ -350,9 +345,7 @@ static int c_register_constant(cl_env_ptr env, cl_object c) { int n = c_search_constant(env, c); - return (n < 0)? - asm_constant(env, c) : - n; + return (n < 0) ? asm_constant(env, c) : n; } static void diff --git a/src/c/interpreter.d b/src/c/interpreter.d index 06b17e301..04a318d13 100644 --- a/src/c/interpreter.d +++ b/src/c/interpreter.d @@ -188,7 +188,7 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) VAR is the name of the variable for readability purposes. */ CASE(OP_VAR); { - int lex_env_index; + cl_fixnum lex_env_index; GET_OPARG(lex_env_index, vector); reg0 = ecl_lex_env_get_var(lex_env, lex_env_index); THREAD_NEXT; @@ -258,7 +258,7 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) } /* OP_PUSH - Pushes the object in VALUES(0). + Pushes the object in REG0. */ CASE(OP_PUSH); { ECL_STACK_PUSH(the_env, reg0); @@ -439,8 +439,8 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) THREAD_NEXT; } /* OP_POPREQ - Checks the arguments list. If there are remaining arguments, - REG0 = T and the value is on the stack, otherwise REG0 = NIL. + Checks the arguments list. + If there are remaining arguments, REG0 = ARG, otherwise signal an error. */ CASE(OP_POPREQ); { if (ecl_unlikely(frame_index >= frame->frame.size)) { @@ -450,8 +450,9 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) THREAD_NEXT; } /* OP_POPOPT - Checks the arguments list. If there are remaining arguments, - REG0 = T and the value is on the stack, otherwise REG0 = NIL. + Checks the arguments list. + If there are remaining arguments, REG0 = T and the value is on the stack, + otherwise REG0 = NIL. */ CASE(OP_POPOPT); { if (frame_index >= frame->frame.size) { @@ -463,7 +464,7 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes) THREAD_NEXT; } /* OP_NOMORE - No more arguments. + Asserts that there are no more arguments in the frame. */ CASE(OP_NOMORE); { if (ecl_unlikely(frame_index < frame->frame.size)) diff --git a/src/cmp/cmpbackend-cxx/cmpbackend-cxx.lsp b/src/cmp/cmpbackend-cxx/cmpbackend-cxx.lsp index ff12e25f6..043755393 100644 --- a/src/cmp/cmpbackend-cxx/cmpbackend-cxx.lsp +++ b/src/cmp/cmpbackend-cxx/cmpbackend-cxx.lsp @@ -528,33 +528,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS ") -;;; Code generation - -(defun compiler-pass/generate-cxx (c-pathname h-pathname data-pathname init-name source) - (with-cxx-env () - ;; After this step we still can add new objects, but objects that are - ;; already stored in VV or VVtemp must not change the location. - (optimize-cxx-data *referenced-objects*) - (setq *compiler-phase* 't2) - (with-open-file (*compiler-output1* c-pathname :direction :output - :if-does-not-exist :create - :if-exists :supersede) - (wt-comment-nl "Compiler: ~A ~A" (lisp-implementation-type) (lisp-implementation-version)) - #-ecl-min - (multiple-value-bind (second minute hour day month year) - (get-decoded-time) - (declare (ignore second)) - (wt-comment-nl "Date: ~D/~D/~D ~2,'0D:~2,'0D (yyyy/mm/dd)" year month day hour minute) - (wt-comment-nl "Machine: ~A ~A ~A" (software-type) (software-version) (machine-type))) - (wt-comment-nl "Source: ~A" source) - (with-open-file (*compiler-output2* h-pathname :direction :output - :if-does-not-exist :create - :if-exists :supersede) - (wt-nl1 "#include " *cmpinclude*) - (ctop-write init-name h-pathname data-pathname) - (terpri *compiler-output1*) - (terpri *compiler-output2*))) - (data-c-dump data-pathname))) +;;; Code assembly (defun compiler-pass/assemble-cxx (input-file output-file &key diff --git a/src/cmp/cmpbackend-cxx/cmpc-inliner.lsp b/src/cmp/cmpbackend-cxx/cmpc-inliner.lsp index 8cbb0c6ad..2ddb4285a 100644 --- a/src/cmp/cmpbackend-cxx/cmpc-inliner.lsp +++ b/src/cmp/cmpbackend-cxx/cmpc-inliner.lsp @@ -22,7 +22,7 @@ return-type ;;; Lisp type for the output exact-return-type ;;; Only use this expansion when the output is ;;; declared to have a subtype of RETURN-TYPE - multiple-values ;;; Works with all destinations, including VALUES / RETURN + multiple-values ;;; Works with all destinations, including VALUEZ / LEAVE expansion ;;; C template containing the expansion one-liner ;;; Whether the expansion spans more than one line ) @@ -127,7 +127,7 @@ (defun inline-type-matches (inline-info arg-types return-type) (when (and (not (inline-info-multiple-values inline-info)) - (member *destination* '(VALUES RETURN))) + (member *destination* '(VALUEZ LEAVE))) (return-from inline-type-matches nil)) (let* ((rts nil) (number-max nil)) @@ -215,7 +215,7 @@ (cmpnote "Ignoring form ~S" c-expression)) (wt-nl "value0 = ECL_NIL;") (wt-nl "cl_env_copy->nvalues = 0;") - (return-from produce-inline-loc 'RETURN)) + (return-from produce-inline-loc 'LEAVE)) ;; If the form is a one-liner, we can simply propagate this expression until the ;; place where the value is used. @@ -225,12 +225,12 @@ ,(if (equalp output-rep-type '((VALUES &REST T))) 'VALUES NIL)))) - ;; If the output is a in the VALUES vector, just write down the form and output - ;; the location of the data. + ;; If the output is a in the VALUES vector, just write down the form and + ;; output the location of the data. (when (equalp output-rep-type '((VALUES &REST T))) (wt-c-inline-loc output-rep-type c-expression coerced-arguments side-effects 'VALUES) - (return-from produce-inline-loc 'VALUES)) + (return-from produce-inline-loc 'VALUEZ)) ;; Otherwise we have to set up variables for holding the output. (flet ((make-output-var (type) @@ -245,9 +245,9 @@ (t (loop for v in output-vars for i from 0 - do (let ((*destination* `(VALUE ,i))) (set-loc v))) + do (set-loc `(VALUE ,i) v)) (wt "cl_env_copy->nvalues = " (length output-vars) ";") - 'VALUES)))))) + 'VALUEZ)))))) (defun coerce-locs (inlined-args &optional types args-to-be-saved) ;; INLINED-ARGS is a list of (TYPE LOCATION) produced by the diff --git a/src/cmp/cmpbackend-cxx/cmpc-opt-inl.lsp b/src/cmp/cmpbackend-cxx/cmpc-opt-inl.lsp index 7174e3613..846033d49 100644 --- a/src/cmp/cmpbackend-cxx/cmpc-opt-inl.lsp +++ b/src/cmp/cmpbackend-cxx/cmpc-opt-inl.lsp @@ -42,17 +42,16 @@ (defun save-inline-loc (loc) (let* ((rep-type (loc-representation-type (second loc))) - (temp (make-inline-temp-var (first loc) rep-type)) - (*destination* temp)) - (set-loc loc) + (temp (make-inline-temp-var (first loc) rep-type))) + (set-loc temp loc) temp)) (defun emit-inlined-variable (form rest-forms) (let ((var (c1form-arg 0 form)) (value-type (c1form-primary-type form))) (if (var-changed-in-form-list var rest-forms) - (let* ((temp (make-inline-temp-var value-type (var-rep-type var)))) - (let ((*destination* temp)) (set-loc var)) + (let ((temp (make-inline-temp-var value-type (var-rep-type var)))) + (set-loc temp var) (list value-type temp)) (list value-type var)))) @@ -72,9 +71,8 @@ (fun (find fname *global-funs* :key #'fun-name :test #'same-fname-p)) (loc (call-global-loc fname fun args return-type expected-type)) (type (type-and return-type (loc-type loc))) - (temp (make-inline-temp-var type (loc-representation-type loc))) - (*destination* temp)) - (set-loc loc) + (temp (make-inline-temp-var type (loc-representation-type loc)))) + (set-loc temp loc) (list type temp))) (defun emit-inlined-progn (form forms) diff --git a/src/cmp/cmpbackend-cxx/cmpc-util.lsp b/src/cmp/cmpbackend-cxx/cmpc-util.lsp index b976064bd..f49e7c17d 100644 --- a/src/cmp/cmpbackend-cxx/cmpc-util.lsp +++ b/src/cmp/cmpbackend-cxx/cmpc-util.lsp @@ -9,7 +9,7 @@ (defvar *inline-blocks* 0) (defvar *opened-c-braces* 0) -(defvar *emitted-local-funs* nil) +(defvar *emitted-functions* nil) (defvar *inline-information* nil) ;;; Compiled code uses the following kinds of variables: @@ -38,30 +38,27 @@ ;;; *tail-recursion-info* holds NIL, if tail recursion is impossible. ;;; If possible, *tail-recursion-info* holds -;;; ( c1-lambda-form required-arg .... required-arg ), +;;; ( c1-lambda-form required-arg .... required-arg ), ;;; where each required-arg is a var-object. (defvar *tail-recursion-info* nil) +(defvar *tail-recursion-mark* nil) ;;; --cmpexit.lsp-- ;;; -;;; *last-label* holds the label# of the last used label. ;;; *exit* holds an 'exit', which is -;; ( label# . ref-flag ) or one of RETURNs (i.e. RETURN, RETURN-FIXNUM, -;; RETURN-CHARACTER, RETURN-LONG-FLOAT, RETURN-DOUBLE-FLOAT, RETURN-SINGLE-FLOAT, -;; RETURN-CSFLOAT, RETURN-CDFLOAT, RETURN-CLFLOAT or RETURN-OBJECT). +;; LABEL instance or LEAVE. ;;; *unwind-exit* holds a list consisting of: -;; ( label# . ref-flag ), one of RETURNs, TAIL-RECURSION-MARK, FRAME, -;; JUMP, BDS-BIND (each pushed for a single special binding), or a -;; LCL (which holds the bind stack pointer used to unbind). +;; LABEL instance, LEAVE, FRAME, JUMP, BDS-BIND (each pushed for a single +;; special binding), or a LCL (which holds the bind stack pointer used to +;; unbind). ;;; -(defvar *last-label* 0) + (defvar *exit*) (defvar *unwind-exit*) ;;; C forms to find out (SETF fname) locations (defvar *setf-definitions*) ; holds { name fun-vv name-vv }* (defvar *global-cfuns-array*) ; holds { fun-vv fname-loc fun }* -(defvar *local-funs*) ; holds { fun }* ;;; T/NIL flag to determine whether one may generate lisp constant values as C ;;; structs. @@ -94,17 +91,18 @@ (*max-temp* 0) (*next-cfun* 0) (*last-label* 0) + (*unwind-exit* nil) (*inline-information* (ext:if-let ((r (machine-inline-information *machine*))) (si:copy-hash-table r) (make-inline-information *machine*))) (*setf-definitions* nil) (*global-cfuns-array* nil) - (*local-funs* nil) (*static-constants* nil) (*optimizable-constants* (make-optimizable-constants *machine*)) (*permanent-objects* (make-array 128 :adjustable t :fill-pointer 0)) - (*temporary-objects* (make-array 128 :adjustable t :fill-pointer 0))) + (*temporary-objects* (make-array 128 :adjustable t :fill-pointer 0)) + (*compiler-declared-globals* (make-hash-table))) ,@body)) (defun-cached env-var-name (n) eql @@ -141,7 +139,7 @@ (plusp *env*) (dolist (exit *unwind-exit*) (case exit - (RETURN (return NIL)) + (LEAVE (return NIL)) (BDS-BIND) (t (return T)))))) @@ -149,29 +147,64 @@ (let ((code (incf *next-cfun*))) (format nil prefix code (lisp-to-c-name lisp-name)))) -(defun next-label () - (cons (incf *last-label*) nil)) - -(defun next-label* () - (cons (incf *last-label*) t)) - -(defun labelp (x) - (and (consp x) (integerp (si:cons-car x)))) - -(defun maybe-next-label () - (if (labelp *exit*) - *exit* - (next-label))) - -(defmacro with-exit-label ((label) &body body) - `(let* ((,label (next-label)) - (*unwind-exit* (cons ,label *unwind-exit*))) +(defmacro with-lexical-scope (() &body body) + `(progn + (wt-nl-open-brace) ,@body - (wt-label ,label))) + (wt-nl-close-brace))) -(defmacro with-optional-exit-label ((label) &body body) - `(let* ((,label (maybe-next-label)) - (*unwind-exit* (adjoin ,label *unwind-exit*))) - ,@body - (unless (eq ,label *exit*) - (wt-label ,label)))) + +;;; *LAST-LABEL* holds the label# of the last used label. This is used by the +;;; code generator to avoid duplicated labels in the same scope. + +(defvar *last-label* 0) + +;;; LABEL represents a destination for a possible control transfer. An unique ID +;;; is assigned to ensure that there are no two labels of the same name. DENV +;;; captures the dynamic environment of the label, so when we jump to the label +;;; we may unwind the dynamic state (see the exit manager). USED-P is a flag is +;;; set to T when the code "jumps" to the label. -- jd 2023-11-25 +(defstruct (label (:predicate labelp)) + id + denv + used-p) + +(defun next-label (used-p) + (make-label :id (incf *last-label*) :denv *unwind-exit* :used-p used-p)) + +;;; This macro binds VAR to a label where forms may exit or jump. +;;; LABEL may be supplied to reuse a label when it exists. +(defmacro with-exit-label ((var &optional exit) &body body) + (ext:with-gensyms (reuse label) + `(let* ((,label ,exit) + (,reuse (labelp ,label)) + (,var (if ,reuse ,label (next-label nil))) + (*unwind-exit* (adjoin ,var *unwind-exit*))) + ,@body + (unless ,reuse + (wt-label ,var))))) + +;;; This macro estabilishes a frame to handle dynamic escapes like GO, THROW and +;;; RETURN-FROM to intercept the control and eval UNWIND-PROTECT cleanup forms. +;;; ecl_frs_pop is emited by the exit manager or the caller. -- jd 2023-11-19 +(defmacro with-unwind-frame ((tag) handler-form &body body) + `(with-lexical-scope () + (let ((*unwind-exit* (list* 'FRAME *unwind-exit*))) + (wt-nl "ecl_frs_push(cl_env_copy," ,tag ");") + (wt-nl "if (__ecl_frs_push_result!=0) {") + ,handler-form + ,@(when body + `((wt-nl "} else {") + ,@body)) + (wt-nl "}")))) + +(defmacro with-stack-frame ((var &optional loc) &body body) + (ext:with-gensyms (hlp) + `(with-lexical-scope () + (let* ((,var ,(or loc "_ecl_inner_frame")) + (,hlp "_ecl_inner_frame_aux") + (*unwind-exit* (list* (list 'STACK ,var) *unwind-exit*))) + (wt-nl "struct ecl_stack_frame " ,hlp ";") + (wt-nl *volatile* "cl_object " ,var + "=ecl_stack_frame_open(cl_env_copy,(cl_object)&" ,hlp ",0);") + ,@body)))) diff --git a/src/cmp/cmpbackend-cxx/cmpc-wt.lsp b/src/cmp/cmpbackend-cxx/cmpc-wt.lsp index 85b01f027..6a5cc7fdd 100644 --- a/src/cmp/cmpbackend-cxx/cmpc-wt.lsp +++ b/src/cmp/cmpbackend-cxx/cmpc-wt.lsp @@ -110,13 +110,17 @@ ;;; LABELS AND JUMPS ;;; +(defun wt-nl-go (label) + (wt-nl-indent) + (wt-go label)) + (defun wt-go (label) - (setf (cdr label) t - label (car label)) - (wt "goto L" label ";")) + (setf (label-used-p label) t) + (wt "goto L" (label-id label) ";")) (defun wt-label (label) - (when (cdr label) (wt-nl1 "L" (car label) ":;"))) + (when (label-used-p label) + (wt-nl1 "L" (label-id label) ":;"))) ;;; ;;; C/C++ COMMENTS @@ -127,7 +131,7 @@ (if single-line (progn (fresh-line stream) - (princ "/* " stream)) + (princ "/* " stream)) (format stream "~50T/* ")) (let* ((l (1- (length text)))) (declare (fixnum l)) @@ -144,8 +148,7 @@ (t (princ c stream))))) (princ (schar text l) stream)) - (format stream "~70T*/") - ) + (format stream "~78T*/")) (defun do-wt-comment (message-or-format args single-line-p) (unless (and (symbolp message-or-format) (not (symbol-package message-or-format))) diff --git a/src/cmp/cmpbackend-cxx/cmppass2-call.lsp b/src/cmp/cmpbackend-cxx/cmppass2-call.lsp index e444642c1..dc0238668 100644 --- a/src/cmp/cmpbackend-cxx/cmppass2-call.lsp +++ b/src/cmp/cmpbackend-cxx/cmppass2-call.lsp @@ -82,17 +82,15 @@ (defun tail-recursion-possible () (dolist (ue *unwind-exit* (baboon :format-control "tail-recursion-possible: should never return.")) - (cond ((eq ue 'TAIL-RECURSION-MARK) + (cond ((eq ue *tail-recursion-mark*) (return t)) - ((or (numberp ue) (eq ue 'BDS-BIND) (eq ue 'FRAME)) + ((or (eq ue 'BDS-BIND) (eq ue 'FRAME)) (return nil)) - ((or (consp ue) (eq ue 'JUMP) (eq ue 'IHS-ENV))) + ((or (consp ue) (labelp ue) (eq ue 'IHS-ENV))) (t (baboon :format-control "tail-recursion-possible: unexpected situation."))))) (defun last-call-p () - (member *exit* - '(RETURN RETURN-FIXNUM RETURN-CHARACTER RETURN-SINGLE-FLOAT - RETURN-DOUBLE-FLOAT RETURN-LONG-FLOAT RETURN-OBJECT))) + (eq *exit* 'LEAVE)) (defun c2try-tail-recursive-call (fun args) (when (and *tail-recursion-info* @@ -101,16 +99,12 @@ (tail-recursion-possible) (inline-possible (fun-name fun)) (= (length args) (length (rest *tail-recursion-info*)))) - (let* ((*destination* 'TRASH) - (*exit* (next-label)) - (*unwind-exit* (cons *exit* *unwind-exit*))) - (c2psetq nil ;; We do not provide any C2FORM - (cdr *tail-recursion-info*) args) - (wt-label *exit*)) - (unwind-no-exit 'TAIL-RECURSION-MARK) - (wt-nl "goto TTL;") - (cmpdebug "Tail-recursive call of ~s was replaced by iteration." - (fun-name fun)) + (with-exit-label (*exit*) + (let ((*destination* 'TRASH)) + ;; We do not provide any C2FORM. + (c2psetq nil (cdr *tail-recursion-info*) args))) + (unwind-jump *tail-recursion-mark*) + (cmpdebug "Tail-recursive call of ~s was replaced by iteration." (fun-name fun)) t)) (defun c2call-local (c1form fun args) @@ -137,18 +131,14 @@ (declare (ignore c1form)) (let* ((*temp* *temp*) (loc (maybe-save-value form args))) - (wt-nl-open-brace) - (wt-nl "struct ecl_stack_frame _ecl_inner_frame_aux;") - (wt-nl *volatile* "cl_object _ecl_inner_frame = ecl_stack_frame_open(cl_env_copy,(cl_object)&_ecl_inner_frame_aux,0);") - (let ((*unwind-exit* `((STACK "_ecl_inner_frame") ,@*unwind-exit*))) - (let ((*destination* (if values-p 'values 'return))) + (with-stack-frame (frame) + (let ((*destination* (if values-p 'VALUEZ 'LEAVE))) (dolist (arg args) (c2expr* arg) (if values-p - (wt-nl "ecl_stack_frame_push_values(_ecl_inner_frame);") - (wt-nl "ecl_stack_frame_push(_ecl_inner_frame,value0);")))) - (unwind-exit (call-stack-loc nil loc))) - (wt-nl-close-brace))) + (wt-nl "ecl_stack_frame_push_values(" frame ");") + (wt-nl "ecl_stack_frame_push(" frame ",value0);")))) + (unwind-exit (call-stack-loc nil loc))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; diff --git a/src/cmp/cmpbackend-cxx/cmppass2-cont.lsp b/src/cmp/cmpbackend-cxx/cmppass2-cont.lsp index 2141be826..ecde25397 100644 --- a/src/cmp/cmpbackend-cxx/cmppass2-cont.lsp +++ b/src/cmp/cmpbackend-cxx/cmppass2-cont.lsp @@ -23,33 +23,27 @@ (let* ((blk-var (blk-var blk)) (*env-lvl* *env-lvl*)) (check-vref blk-var) - (wt-nl-open-brace) - (when (eq :object (var-kind blk-var)) - (setf (var-loc blk-var) (next-lcl)) - (wt-nl "cl_object " blk-var ";")) - (when (env-grows (var-ref-ccb blk-var)) - ;; var is referenced from a closure which may escape. - (let ((env-lvl *env-lvl*)) - (wt-nl "cl_object " *volatile* "env" (incf *env-lvl*) " = env" env-lvl ";"))) - (bind "ECL_NEW_FRAME_ID(cl_env_copy)" blk-var) - (wt-nl-open-brace) - (wt-nl "ecl_frs_push(cl_env_copy," blk-var ");") - (wt-nl "if (__ecl_frs_push_result!=0) {") - (let ((*unwind-exit* (cons 'FRAME *unwind-exit*))) - (unwind-exit 'VALUES) - (wt-nl "} else {") - (c2expr body) - (wt "}")) - (wt-nl-close-brace) - (when (var-ref-ccb blk-var) (decf *env*)) - (wt-nl-close-brace)) + (with-lexical-scope () + (when (eq :object (var-kind blk-var)) + (setf (var-loc blk-var) (next-lcl)) + (wt-nl "cl_object " blk-var ";")) + (when (env-grows (var-ref-ccb blk-var)) + ;; var is referenced from a closure which may escape. + (let ((env-lvl *env-lvl*)) + (wt-nl "cl_object " *volatile* "env" (incf *env-lvl*) " = env" env-lvl ";"))) + (bind "ECL_NEW_FRAME_ID(cl_env_copy)" blk-var) + (with-unwind-frame (blk-var) + (unwind-exit 'VALUEZ) + (c2expr body)) + (when (var-ref-ccb blk-var) + (decf *env*)))) (c2expr body))) (defun c2return-from (c1form blk nonlocal val) (declare (ignore c1form)) (if nonlocal (progn - (let ((*destination* 'VALUES)) + (let ((*destination* 'VALUEZ)) (c2expr* val)) (let ((name (get-object (blk-name blk)))) (wt-nl "cl_return_from(" (blk-var blk) "," name ");"))) @@ -66,11 +60,9 @@ (dolist (x body (c2tagbody-body body)) ;; Allocate labels. (when (and (tag-p x) (plusp (tag-ref x))) - (setf (tag-label x) (next-label*)) - (setf (tag-unwind-exit x) *unwind-exit*))) + (setf (tag-jump x) (next-label t)))) ;; some tag used non locally or inside an unwind-protect - (let ((*unwind-exit* (cons 'FRAME *unwind-exit*)) - (*env* *env*) (*env-lvl* *env-lvl*) + (let ((*env* *env*) (*env-lvl* *env-lvl*) (*lex* *lex*) (*lcl* *lcl*) (*inline-blocks* 0) (env-grows (env-grows (var-ref-ccb tag-loc)))) @@ -84,54 +76,37 @@ (maybe-open-inline-block) (wt-nl "cl_object " tag-loc ";")) (bind "ECL_NEW_FRAME_ID(cl_env_copy)" tag-loc) - (wt-nl-open-brace) - (wt-nl "ecl_frs_push(cl_env_copy," tag-loc ");") - (wt-nl "if (__ecl_frs_push_result) {") - ;; Allocate labels. - (dolist (tag body) - (when (and (tag-p tag) (plusp (tag-ref tag))) - (setf (tag-label tag) (next-label)) - (setf (tag-unwind-exit tag) *unwind-exit*) - (wt-nl "if (cl_env_copy->values[0]==ecl_make_fixnum(" (tag-index tag) "))") - (wt-go (tag-label tag)))) - (when (var-ref-ccb tag-loc) - (wt-nl "ecl_internal_error(\"GO found an inexistent tag\");")) - (wt-nl "}") - (wt-nl-close-brace) - (c2tagbody-body body) + (with-unwind-frame (tag-loc) + (progn + ;; Allocate labels. + (dolist (tag body) + (when (and (tag-p tag) (plusp (tag-ref tag))) + (setf (tag-jump tag) (next-label nil)) + (wt-nl "if (cl_env_copy->values[0]==ecl_make_fixnum(" (tag-index tag) "))") + (wt-go (tag-jump tag)))) + (when (var-ref-ccb tag-loc) + (wt-nl "ecl_internal_error(\"GO found an inexistent tag\");"))) + (c2tagbody-body body)) (close-inline-blocks)))) (defun c2tagbody-body (body) ;;; INV: BODY is a list of tags and forms. We have processed the body ;;; so that the last element is always a form producing NIL. - (do ((l body (cdr l))) - ((null l)) - (let* ((this-form (first l))) - (cond ((tag-p this-form) - (wt-label (tag-label this-form))) - ((endp (rest l)) - ;; Last form, it is never a label! - (c2expr this-form)) - (t - (let* ((next-form (second l)) - (*exit* (if (tag-p next-form) - (tag-label next-form) - (next-label))) - (*unwind-exit* (cons *exit* *unwind-exit*)) - (*destination* 'TRASH)) - (c2expr this-form) - (unless (tag-p next-form) - (wt-label *exit*)))))))) + (loop for (this-form next-form . rest) on body do + (cond ((tag-p this-form) + (wt-label (tag-jump this-form))) + ((tag-p next-form) + (with-exit-label (*exit* (tag-jump next-form)) + (let ((*destination* 'TRASH)) + (c2expr this-form)))) + (t + (c2expr this-form))))) (defun c2go (c1form tag nonlocal) (declare (ignore c1form)) (if nonlocal - (let ((var (tag-var tag))) - (wt-nl "cl_go(" var ",ecl_make_fixnum(" (tag-index tag) "));")) - ;; local go - (progn - (unwind-no-exit-until (tag-unwind-exit tag)) - (wt-nl) (wt-go (tag-label tag))))) + (wt-nl "cl_go(" (tag-var tag) ",ecl_make_fixnum(" (tag-index tag) "));") + (unwind-jump (tag-jump tag)))) (defun c2throw (c1form tag val &aux loc) @@ -139,73 +114,53 @@ (case (c1form-name tag) ((VARIABLE LOCATION) (setq loc (c1form-arg 0 tag))) (t (setq loc (make-temp-var)) - (let ((*destination* loc)) (c2expr* tag)))) - (let ((*destination* 'VALUES)) (c2expr* val)) + (let ((*destination* loc)) + (c2expr* tag)))) + (let ((*destination* 'VALUEZ)) + (c2expr* val)) (wt-nl "cl_throw(" loc ");")) (defun c2catch (c1form tag body) (declare (ignore c1form)) (let* ((new-destination (tmp-destination *destination*)) - (code (incf *last-label*))) + (code (gensym "CATCH"))) (let ((*destination* 'VALUE0)) (c2expr* tag)) - (let* ((*destination* new-destination) - (*unwind-exit* (cons 'FRAME *unwind-exit*))) - (wt-nl-open-brace) - (if (member new-destination '(TRASH VALUES)) - (progn - (wt-nl "ecl_frs_push(cl_env_copy," 'VALUE0 ");") - (wt-nl "if (__ecl_frs_push_result==0) {") - (wt-comment "BEGIN CATCH ~A" code) - (with-indentation - (c2expr* body))) - (progn - (wt-nl "ecl_frs_push(cl_env_copy," 'VALUE0 ");") - (wt-nl "if (__ecl_frs_push_result) {") - (wt-comment "BEGIN CATCH ~A" code) - (with-indentation - (with-exit-label (label) - (let ((*exit* label)) - (unwind-exit 'VALUES)))) - (wt-nl "} else {") - (with-indentation - (c2expr* body))))) - (wt-nl "}") - (wt-nl "ecl_frs_pop(cl_env_copy);") - (wt-comment "END CATCH ~A" code) - (wt-nl-close-brace) + (let ((*destination* new-destination)) + (wt-comment "BEGIN CATCH ~A" code) + (with-unwind-frame ('VALUE0) + (unless (member new-destination '(TRASH VALUEZ)) + (with-indentation + (with-exit-label (*exit*) + (unwind-exit 'VALUEZ)))) + (with-indentation + (c2expr* body))) + (wt-nl "ecl_frs_pop(cl_env_copy);") + (wt-comment "END CATCH ~A" code)) (unwind-exit new-destination))) (defun c2unwind-protect (c1form form body) (declare (ignore c1form)) - (let* ((sp (make-lcl-var :rep-type :cl-index)) - (nargs (make-lcl-var :rep-type :cl-index)) - (*unwind-exit* `((STACK ,sp) ,@*unwind-exit*))) - (wt-nl-open-brace) + (with-stack-frame (frame) + ;; Here we compile the form which is protected. When this form is aborted, + ;; it continues with unwinding=TRUE. We call ecl_frs_pop() manually because + ;; we use C2EXPR* in the body. (wt-nl "volatile bool unwinding = FALSE;") - (wt-nl "cl_index " sp "=ECL_STACK_INDEX(cl_env_copy)," nargs ";") (wt-nl "ecl_frame_ptr next_fr;") - ;; Here we compile the form which is protected. When this form - ;; is aborted, it continues at the ecl_frs_pop() with unwinding=TRUE. - (wt-nl "ecl_frs_push(cl_env_copy,ECL_PROTECT_TAG);") - (wt-nl "if (__ecl_frs_push_result) {") - (wt-nl " unwinding = TRUE; next_fr=cl_env_copy->nlj_fr;") - (wt-nl "} else {") - (let ((*unwind-exit* (cons 'FRAME *unwind-exit*)) - (*destination* 'VALUES)) - (c2expr* form)) - (wt-nl "}") + (with-unwind-frame ("ECL_PROTECT_TAG") + (wt-nl " unwinding = TRUE; next_fr=cl_env_copy->nlj_fr;") + (let ((*destination* 'VALUEZ)) + (c2expr* form))) (wt-nl "ecl_frs_pop(cl_env_copy);") ;; Here we save the values of the form which might have been ;; aborted, and execute some cleanup code. This code may also ;; be aborted by some control structure, but is not protected. - (wt-nl nargs "=ecl_stack_push_values(cl_env_copy);") + (wt-nl "ecl_stack_frame_push_values(" frame ");") (let ((*destination* 'TRASH)) (c2expr* body)) - (wt-nl "ecl_stack_pop_values(cl_env_copy," nargs ");") + (wt-nl "ecl_stack_frame_pop_values(" frame ");") ;; Finally, if the protected form was aborted, jump to the ;; next catch point... (wt-nl "if (unwinding) ecl_unwind(cl_env_copy,next_fr);") ;; ... or simply return the values of the protected form. - (unwind-exit 'VALUES) - (wt-nl-close-brace))) + (unwind-exit 'VALUEZ))) diff --git a/src/cmp/cmpbackend-cxx/cmppass2-eval.lsp b/src/cmp/cmpbackend-cxx/cmppass2-eval.lsp index b46cd206f..8bae8f54c 100644 --- a/src/cmp/cmpbackend-cxx/cmppass2-eval.lsp +++ b/src/cmp/cmpbackend-cxx/cmppass2-eval.lsp @@ -2,14 +2,9 @@ ;;;; Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya ;;;; Copyright (c) 1990, Giuseppe Attardi ;;;; Copyright (c) 2010, Juan Jose Garcia-Ripoll -;;;; Copyright (c) 2021, Daniel Kochmański +;;;; Copyright (c) 2023, Daniel Kochmański ;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU Library General Public -;;;; License as published by the Free Software Foundation; either -;;;; version 2 of the License, or (at your option) any later version. -;;;; -;;;; See file '../Copyright' for full details. +;;;; See the file 'LICENSE' for the copyright details. ;;;; (in-package #:compiler) @@ -26,30 +21,26 @@ ;; other expressions will follow this one. We must thus create ;; a possible label so that the compiled forms exit right at ;; the point where the next form will be compiled. - (with-exit-label (label) - (let* ((*exit* label) - (*unwind-exit* (cons *exit* *unwind-exit*)) - ;;(*lex* *lex*) - (*lcl* *lcl*) - (*temp* *temp*)) + (with-exit-label (*exit*) + (let (;;(*lex* *lex*) + (*lcl* *lcl*) + (*temp* *temp*)) (c2expr form)))) (defun c2progn (c1form forms) (declare (ignore c1form)) - ;; c1progn ensures that the length of forms is not less than 1. - (do ((l forms (cdr l)) - (lex *lex*)) - ((endp (cdr l)) - (c2expr (car l))) - (let* ((this-form (first l)) - (name (c1form-name this-form))) - (let ((*destination* 'TRASH)) - (c2expr* (car l))) - (setq *lex* lex) ; recycle lex locations - ;; Since PROGN does not have tags, any transfer of control means - ;; leaving the current PROGN statement. - (when (or (eq name 'GO) (eq name 'RETURN-FROM)) - (return))))) + ;; INV C1PROGN ensures that the length of forms is not less than 1. + (loop with lex = *lex* + for (form next . rest) on forms do + (if (null next) + (c2expr form) + (let ((*destination* 'TRASH)) + (c2expr* form) + ;; recycle lex locations + (setq *lex* lex))) + ;; Since PROGN does not have tags, any transfer of control means leaving + ;; the current PROGN statement. + until (member (c1form-name form) '(CL:GO CL:RETURN-FROM)))) (defun c2if (c1form fmla form1 form2) (declare (ignore c1form)) @@ -58,7 +49,7 @@ (eq (c1form-name form2) 'LOCATION)) ;; Optimize (IF condition true-branch) or a situation in which ;; the false branch can be discarded. - (with-optional-exit-label (false-label) + (with-exit-label (false-label *exit*) (let ((*destination* `(JUMP-FALSE ,false-label))) (c2expr* fmla)) (c2expr form1))) @@ -66,7 +57,7 @@ (eq (c1form-name form1) 'LOCATION)) ;; Optimize (IF condition useless-value false-branch) when ;; the true branch can be discarded. - (with-optional-exit-label (true-label) + (with-exit-label (true-label *exit*) (let ((*destination* `(JUMP-TRUE ,true-label))) (c2expr* fmla)) (c2expr form2))) @@ -77,6 +68,14 @@ (c2expr form1)) (c2expr form2)))) +(defun jump-true-destination-p (dest) + (declare (si::c-local)) + (and (consp dest) (eq (si:cons-car dest) 'JUMP-TRUE))) + +(defun jump-false-destination-p (dest) + (declare (si::c-local)) + (and (consp dest) (eq (si:cons-car dest) 'JUMP-FALSE))) + (defun negate-argument (inlined-arg dest-loc) (declare (si::c-local)) (let* ((loc (second inlined-arg)) @@ -105,19 +104,9 @@ (t (let ((*inline-blocks* 0) (*temp* *temp*)) - (unwind-exit (negate-argument - (emit-inline-form arg nil) - dest)) + (unwind-exit (negate-argument (emit-inline-form arg nil) dest)) (close-inline-blocks)))))) -(defun jump-true-destination-p (dest) - (declare (si::c-local)) - (and (consp dest) (eq (si:cons-car dest) 'JUMP-TRUE))) - -(defun jump-false-destination-p (dest) - (declare (si::c-local)) - (and (consp dest) (eq (si:cons-car dest) 'JUMP-FALSE))) - (defun c2fmla-and (c1form butlast last) (declare (ignore c1form)) (if (jump-false-destination-p *destination*) @@ -148,77 +137,24 @@ (dolist (f butlast) (let ((*destination* 'VALUE0)) (c2expr* f)) - (set-jump-true 'VALUE0 normal-exit)) + (wt-nl "if (" 'VALUE0 "!=ECL_NIL) ") + (wt-open-brace) (unwind-jump normal-exit) (wt-nl-close-brace)) (c2expr last)) (unwind-exit 'VALUE0))))) -(defun set-jump-true (loc label) - (multiple-value-bind (constantp value) - (loc-immediate-value-p loc) - (cond ((not constantp) - (cond ((eq (loc-representation-type loc) :bool) - (wt-nl "if (" loc ") {")) - (t - (wt-nl "if ((") - (wt-coerce-loc :object loc) - (wt ")!=ECL_NIL) {"))) - (cond ((unwind-no-exit label) - (incf *opened-c-braces*) - (wt-nl) (wt-go label) - (wt-nl-close-brace)) - (t - (wt " ") (wt-go label) (wt " }")))) - ((null value)) - (t - (unwind-no-exit label) - (wt-nl) (wt-go label))))) - -(defun set-jump-false (loc label) - (multiple-value-bind (constantp value) - (loc-immediate-value-p loc) - (cond ((not constantp) - (cond ((eq (loc-representation-type loc) :bool) - (wt-nl "if (!(" loc ")) {")) - (t - (wt-nl "if (Null(") - (wt-coerce-loc :object loc) - (wt ")) {"))) - (cond ((unwind-no-exit label) - (incf *opened-c-braces*) - (wt-nl) (wt-go label) - (wt-nl-close-brace)) - (t - (wt " ") (wt-go label) (wt " }")))) - (value) - (t - (unwind-no-exit label) - (wt-nl) (wt-go label))))) - (defun c2mv-prog1 (c1form form body) - (wt-nl-open-brace) - (wt-nl "struct ecl_stack_frame _ecl_inner_frame_aux;") - (wt-nl *volatile* "cl_object _ecl_inner_frame = ecl_stack_frame_open(cl_env_copy,(cl_object)&_ecl_inner_frame_aux,0);") - (let ((*unwind-exit* `((STACK "_ecl_inner_frame") ,@*unwind-exit*))) - (let ((*destination* 'VALUES)) + (declare (ignore c1form)) + (with-stack-frame (frame) + (let ((*destination* 'VALUEZ)) (c2expr* form)) - (wt-nl "ecl_stack_frame_push_values(_ecl_inner_frame);") + (wt-nl "ecl_stack_frame_push_values(" frame ");") (let ((*destination* 'TRASH)) (mapc #'c2expr* body)) - (wt-nl "ecl_stack_frame_pop_values(_ecl_inner_frame);")) - (wt-nl "ecl_stack_frame_close(_ecl_inner_frame);") - (wt-nl-close-brace) - (unwind-exit 'values)) + (wt-nl "ecl_stack_frame_pop_values(" frame ");") + (unwind-exit 'VALUEZ))) (defun c2values (c1form forms) (declare (ignore c1form)) - (when (and (eq *destination* 'RETURN-OBJECT) - (rest forms) - (consp *current-form*) - (eq 'cl:DEFUN (first *current-form*))) - (cmpwarn "Trying to return multiple values. ~ - ~%;But ~a was proclaimed to have single value.~ - ~%;Only first one will be assured." - (second *current-form*))) (cond ;; When the values are not going to be used, then just ;; process each form separately. @@ -227,25 +163,25 @@ ;; We really pass no value, but we need UNWIND-EXIT to trigger all the ;; frame-pop and all other exit forms. (unwind-exit 'VALUE0)) - ;; For (VALUES) we can replace the output with either NIL (if the value - ;; is actually used) and set only NVALUES when the value is the output - ;; of a function. + ;; For (VALUES) we can replace the output with either NIL (if the value is + ;; actually used) and set only NVALUES when the value is the output of a + ;; function. ((endp forms) - (cond ((eq *destination* 'RETURN) + (cond ((eq *destination* 'LEAVE) (wt-nl "value0 = ECL_NIL;") (wt-nl "cl_env_copy->nvalues = 0;") - (unwind-exit 'RETURN)) - ((eq *destination* 'VALUES) + (unwind-exit 'LEAVE)) + ((eq *destination* 'VALUEZ) (wt-nl "cl_env_copy->values[0] = ECL_NIL;") (wt-nl "cl_env_copy->nvalues = 0;") - (unwind-exit 'VALUES)) + (unwind-exit 'VALUEZ)) (t (unwind-exit *vv-nil*)))) ;; For a single form, we must simply ensure that we only take a single ;; value of those that the function may output. ((endp (rest forms)) (let ((form (first forms))) - (if (or (not (member *destination* '(RETURN VALUES))) + (if (or (not (member *destination* '(LEAVE VALUEZ))) (c1form-single-valued-p form)) (c2expr form) (progn @@ -266,5 +202,5 @@ ((null vl)) (declare (fixnum i)) (wt-nl "cl_env_copy->values[" i "] = " (first vl) ";")) - (unwind-exit 'VALUES) + (unwind-exit 'VALUEZ) (close-inline-blocks))))) diff --git a/src/cmp/cmpbackend-cxx/cmppass2-exit.lsp b/src/cmp/cmpbackend-cxx/cmppass2-exit.lsp index 8ef8d7d18..041785fad 100644 --- a/src/cmp/cmpbackend-cxx/cmppass2-exit.lsp +++ b/src/cmp/cmpbackend-cxx/cmppass2-exit.lsp @@ -4,203 +4,198 @@ ;;;; ;;;; Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya. ;;;; Copyright (c) 1990, Giuseppe Attardi. +;;;; Copyright (c) 2023, Daniel Kochmański. ;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU Library General Public -;;;; License as published by the Free Software Foundation; either -;;;; version 2 of the License, or (at your option) any later version. +;;;; See the file 'LICENSE' for the copyright details. ;;;; -;;;; See file '../Copyright' for full details. ;;;; CMPEXIT Exit manager. +;;;; +;;;; The exit manager has two main operators that unwind the dynamic context: +;;;; +;;;; (UNWIND-EXIT value) carries VALUE to *DESTINATION* and unwinds to *EXIT*. +;;;; (UNWIND-JUMP label) unwinds to LABEL. +;;;; (in-package "COMPILER") +(defun unwind-exit (loc) + (flet ((unwind-cond-p () + (and (consp *destination*) + (member (si:cons-car *destination*) '(JUMP-FALSE JUMP-TRUE)))) + (unwind-jump-p () + (labelp *exit*)) + (unwind-exit-p () + (eq *exit* 'LEAVE))) + (cond ((unwind-cond-p) (unwind-cjump loc)) + ((unwind-jump-p) (unwind-label loc)) + ((unwind-exit-p) (unwind-leave loc)) + (t (baboon-exit-invalid *exit*))))) + +(defun unwind-jump (exit) + (multiple-value-bind (frs-bind bds-lcl bds-bind stack-frame ihs-p) + (compute-unwind (label-denv exit)) + (perform-unwind frs-bind bds-lcl bds-bind stack-frame ihs-p) + (wt-nl-go exit))) + +;;; + +(defun baboon-exit-not-found (exit) + (baboon :format-control "The value of exit~%~A~%is not found in *UNWIND-EXIT*~%~A" + :format-arguments (list exit *unwind-exit*))) + +(defun baboon-exit-invalid (exit) + (baboon :format-control "The value of exit~%~A~%is not valid." + :format-arguments (list exit))) + +(defun baboon-unwind-invalid (unwind-exit) + (baboon :format-control "The value~%~A~%is not a tail of *UNWIND-EXIT*~%~A" + :format-arguments (list unwind-exit *unwind-exit*))) + +(defun baboon-unwind-exit (exit) + (baboon :format-control "The value of exit~%~A~%found in *UNWIND-EXIT*~%~A~%is not valid." + :format-arguments (list exit *unwind-exit*))) + +(defun destination-value-matters-p (dest) + (declare (si::c-local)) + (if (atom dest) + (not (eq dest 'TRASH)) + (not (member (car dest) '(JUMP-FALSE JUMP-TRUE))))) + ;;; UNWIND-EXIT TAGS PURPOSE ;;; -;;; number -> unknown purpose -;;; JUMP -> unknown purpose ;;; FRAME -> ecl_frs_push() +;;; (STACK frame) -> ecl_stack_frame_open(env, frame, initial_size) ;;; IHS -> ihs push ;;; IHS-ENV -> ihs push ;;; BDS-BIND -> binding of 1 special variable -;;; (number . {T|NIL}) -> label -;;; (LCL n) -> n local variables -;;; (STACK n) -> n elements pushed in stack -;;; TAIL-RECURSION-MARK -> TTL: label created -;;; RETURN* -> outermost location -;;; -;;; (*) also RETURN-FIXNUM, -CHARACTER, -SINGLE-FLOAT -;;; -DOUBLE-FLOAT, -OBJECT. -;;; -(defun unwind-bds (bds-lcl bds-bind stack-frame ihs-p) - (declare (fixnum bds-bind)) - (let ((some nil)) - (when stack-frame - (setf some t) - (if (stringp stack-frame) - (wt-nl "ecl_stack_frame_close(" stack-frame ");") - (wt-nl "ECL_STACK_SET_INDEX(cl_env_copy," stack-frame ");"))) - (when bds-lcl - (setf some t) - (wt-nl "ecl_bds_unwind(cl_env_copy," bds-lcl ");")) - (cond ((< bds-bind 4) - (dotimes (n bds-bind) - (declare (fixnum n)) - (setf some t) - (wt-nl "ecl_bds_unwind1(cl_env_copy);"))) - (t - (setf some t) - (wt-nl "ecl_bds_unwind_n(cl_env_copy," bds-bind ");"))) - (case ihs-p - (IHS - (setf some t) - (wt-nl "ecl_ihs_pop(cl_env_copy);")) - (IHS-ENV - (setf some t) - (wt-nl "ihs.lex_env = _ecl_debug_env;"))) - some)) +;;; (LCL n) -> binding stack pointer to Nth local variable +;;; LEAVE -> outermost location +;;; #