summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/comp.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp/comp.el')
-rw-r--r--lisp/emacs-lisp/comp.el349
1 files changed, 34 insertions, 315 deletions
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 5313bfba996..498aae183a5 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -38,6 +38,7 @@
(require 'rx)
(require 'subr-x)
(require 'warnings)
+(require 'comp-cstr)
(defgroup comp nil
"Emacs Lisp native compiler."
@@ -267,6 +268,16 @@ Useful to hook into pass checkers.")
(comp-hint-cons (function (t) cons)))
"Alist used for type propagation.")
+(defconst comp-known-func-cstr-h
+ (cl-loop
+ with comp-ctxt = (make-comp-cstr-ctxt)
+ with h = (make-hash-table :test #'eq)
+ for (f type-spec) in comp-known-type-specifiers
+ for cstr = (comp-type-spec-to-cstr type-spec)
+ do (puthash f cstr h)
+ finally (cl-return h))
+ "Hash table function -> `comp-constraint'")
+
(defconst comp-symbol-values-optimizable '(most-positive-fixnum
most-negative-fixnum)
"Symbol values we can resolve in the compile-time.")
@@ -326,7 +337,7 @@ Useful to hook into pass checkers.")
(idx (make-hash-table :test #'equal) :type hash-table
:documentation "Obj -> position into the previous field."))
-(cl-defstruct comp-ctxt
+(cl-defstruct (comp-ctxt (:include comp-cstr-ctxt))
"Lisp side of the compiler context."
(output nil :type string
:documentation "Target output file-name for the compilation.")
@@ -356,13 +367,7 @@ This is typically for top-level forms other than defun.")
(d-ephemeral (make-comp-data-container) :type comp-data-container
:documentation "Relocated data not necessary after load.")
(with-late-load nil :type boolean
- :documentation "When non-nil support late load.")
- (union-typesets-mem (make-hash-table :test #'equal) :type hash-table
- :documentation "Serve memoization for
-`comp-union-typesets'.")
- (common-supertype-mem (make-hash-table :test #'equal) :type hash-table
- :documentation "Serve memoization for
-`comp-common-supertype'."))
+ :documentation "When non-nil support late load."))
(cl-defstruct comp-args-base
(min nil :type number
@@ -489,26 +494,8 @@ CFG is mutated by a pass.")
(lambda-list nil :type list
:documentation "Original lambda-list."))
-(cl-defstruct comp-constraint
- "Internal representation of a type/value constraint."
- (typeset '(t) :type list
- :documentation "List of possible types the mvar can assume.
-Each element cannot be a subtype of any other element of this slot.")
- (valset '() :type list
- :documentation "List of possible values the mvar can assume.
-Integer values are handled in the `range' slot.")
- (range '() :type list
- :documentation "Integer interval."))
-
-(cl-defstruct comp-constraint-f
- "Internal constraint representation for a function."
- (args nil :type (or null list)
- :documentation "List of `comp-constraint' for its arguments.")
- (ret nil :type (or comp-constraint comp-constraint-f)
- :documentation "Returned value `comp-constraint'."))
-
(cl-defstruct (comp-mvar (:constructor make--comp-mvar)
- (:include comp-constraint))
+ (:include comp-cstr))
"A meta-variable being a slot in the meta-stack."
(id nil :type (or null number)
:documentation "Unique id when in SSA form.")
@@ -592,108 +579,6 @@ To be used by all entry points."
((null (native-comp-available-p))
(error "Cannot find libgccjit"))))
-(cl-defun comp-type-spec-to-constraint (type-specifier)
- "Destructure TYPE-SPECIFIER.
-Return the corresponding `comp-constraint' or `comp-constraint-f'."
- (let (typeset valset range)
- (cl-labels ((star-or-num (x)
- (or (numberp x) (eq '* x)))
- (destructure-push (x)
- (pcase x
- ('&optional
- (cl-return-from comp-type-spec-to-constraint '&optional))
- ('&rest
- (cl-return-from comp-type-spec-to-constraint '&rest))
- ('null
- (push nil valset))
- ('boolean
- (push t valset)
- (push nil valset))
- ('fixnum
- (push `(,most-negative-fixnum . ,most-positive-fixnum)
- range))
- ('bignum
- (push `(- . ,(1- most-negative-fixnum))
- range)
- (push `(,(1+ most-positive-fixnum) . +)
- range))
- ((pred symbolp)
- (push x typeset))
- (`(member . ,rest)
- (setf valset (append rest valset)))
- ('(integer * *)
- (push '(- . +) range))
- (`(integer ,(and low (pred integerp)) *)
- (push `(,low . +) range))
- (`(integer * ,(and high (pred integerp)))
- (push `(- . ,high) range))
- (`(integer ,(and low (pred integerp))
- ,(and high (pred integerp)))
- (push `(,low . ,high) range))
- (`(float ,(pred star-or-num) ,(pred star-or-num))
- ;; No float range support :/
- (push 'float typeset))
- (`(function ,args ,ret-type-spec)
- (cl-return-from
- comp-type-spec-to-constraint
- (make-comp-constraint-f
- :args (mapcar #'comp-type-spec-to-constraint args)
- :ret (comp-type-spec-to-constraint ret-type-spec))))
- (_ (error "Unsopported type specifier")))))
- (if (or (atom type-specifier)
- (memq (car type-specifier) '(member integer float function)))
- (destructure-push type-specifier)
- (if (eq (car type-specifier) 'or)
- (mapc #'destructure-push (cdr type-specifier))
- (error "Unsopported type specifier")))
- (make-comp-constraint :typeset typeset
- :valset valset
- :range range))))
-
-(defconst comp-known-constraints-h
- (let ((h (make-hash-table :test #'eq)))
- (cl-loop
- for (f type-spec) in comp-known-type-specifiers
- for constr = (comp-type-spec-to-constraint type-spec)
- do (puthash f constr h))
- h)
- "Hash table function -> `comp-constraint'")
-
-(defun comp-constraint-to-type-spec (mvar)
- "Given MVAR return its type specifier."
- (let ((valset (comp-mvar-valset mvar))
- (typeset (comp-mvar-typeset mvar))
- (range (comp-mvar-range mvar)))
-
- (when valset
- (when (memq nil valset)
- (if (memq t valset)
- (progn
- ;; t and nil are values, convert into `boolean'.
- (push 'boolean typeset)
- (setf valset (remove t (remove nil valset))))
- ;; Only nil is a value, convert it into a `null' type specifier.
- (setf valset (remove nil valset))
- (push 'null typeset))))
-
- ;; Form proper integer type specifiers.
- (setf range (cl-loop for (l . h) in range
- for low = (if (integerp l) l '*)
- for high = (if (integerp h) h '*)
- collect `(integer ,low , high))
- valset (cl-remove-duplicates valset))
-
- ;; Form the final type specifier.
- (let ((res (append typeset
- (when valset
- `((member ,@valset)))
- range)))
- (if (> (length res) 1)
- `(or ,@res)
- (if (memq (car-safe res) '(member integer))
- res
- (car res))))))
-
(defun comp-set-op-p (op)
"Assignment predicate for OP."
(when (memq op comp-limple-sets) t))
@@ -2392,143 +2277,6 @@ PRE-LAMBDA and POST-LAMBDA are called in pre or post-order if non-nil."
;; This is also responsible for removing function calls to pure functions if
;; possible.
-(defconst comp--typeof-types (mapcar (lambda (x)
- (append x '(t)))
- cl--typeof-types)
- ;; TODO can we just add t in `cl--typeof-types'?
- "Like `cl--typeof-types' but with t as common supertype.")
-
-(defun comp-supertypes (type)
- "Return a list of pairs (supertype . hierarchy-level) for TYPE."
- (cl-loop
- named outer
- with found = nil
- for l in comp--typeof-types
- do (cl-loop
- for x in l
- for i from (length l) downto 0
- when (eq type x)
- do (setf found t)
- when found
- collect `(,x . ,i) into res
- finally (when found
- (cl-return-from outer res)))))
-
-(defun comp-common-supertype-2 (type1 type2)
- "Return the first common supertype of TYPE1 TYPE2."
- (when-let ((types (cl-intersection
- (comp-supertypes type1)
- (comp-supertypes type2)
- :key #'car)))
- (car (cl-reduce (lambda (x y)
- (if (> (cdr x) (cdr y)) x y))
- types))))
-
-(defun comp-common-supertype (&rest types)
- "Return the first common supertype of TYPES."
- (or (gethash types (comp-ctxt-common-supertype-mem comp-ctxt))
- (puthash types
- (cl-reduce #'comp-common-supertype-2 types)
- (comp-ctxt-common-supertype-mem comp-ctxt))))
-
-(defsubst comp-subtype-p (type1 type2)
- "Return t if TYPE1 is a subtype of TYPE1 or nil otherwise."
- (eq (comp-common-supertype-2 type1 type2) type2))
-
-(defun comp-union-typesets (&rest typesets)
- "Union types present into TYPESETS."
- (or (gethash typesets (comp-ctxt-union-typesets-mem comp-ctxt))
- (puthash typesets
- (cl-loop
- with types = (apply #'append typesets)
- with res = '()
- for lane in comp--typeof-types
- do (cl-loop
- with last = nil
- for x in lane
- when (memq x types)
- do (setf last x)
- finally (when last
- (push last res)))
- finally (cl-return (cl-remove-duplicates res)))
- (comp-ctxt-union-typesets-mem comp-ctxt))))
-
-(defsubst comp-range-1+ (x)
- (if (symbolp x)
- x
- (1+ x)))
-
-(defsubst comp-range-1- (x)
- (if (symbolp x)
- x
- (1- x)))
-
-(defsubst comp-range-< (x y)
- (cond
- ((eq x '+) nil)
- ((eq x '-) t)
- ((eq y '+) t)
- ((eq y '-) nil)
- (t (< x y))))
-
-(defun comp-range-union (&rest ranges)
- "Combine integer intervals RANGES by union operation."
- (cl-loop
- with all-ranges = (apply #'append ranges)
- with lows = (mapcar (lambda (x)
- (cons (comp-range-1- (car x)) 'l))
- all-ranges)
- with highs = (mapcar (lambda (x)
- (cons (cdr x) 'h))
- all-ranges)
- with nest = 0
- with low = nil
- with res = ()
- for (i . x) in (cl-sort (nconc lows highs) #'comp-range-< :key #'car)
- if (eq x 'l)
- do
- (when (zerop nest)
- (setf low i))
- (cl-incf nest)
- else
- do
- (when (= nest 1)
- (push `(,(comp-range-1+ low) . ,i) res))
- (cl-decf nest)
- finally (cl-return (reverse res))))
-
-(defun comp-range-intersection (&rest ranges)
- "Combine integer intervals RANGES by intersecting."
- (cl-loop
- with all-ranges = (apply #'append ranges)
- with n-ranges = (length ranges)
- with lows = (mapcar (lambda (x)
- (cons (car x) 'l))
- all-ranges)
- with highs = (mapcar (lambda (x)
- (cons (cdr x) 'h))
- all-ranges)
- with nest = 0
- with low = nil
- with res = ()
- initially (when (cl-some #'null ranges)
- ;; Intersecting with a null range always results in a
- ;; null range.
- (cl-return '()))
- for (i . x) in (cl-sort (nconc lows highs) #'comp-range-< :key #'car)
- if (eq x 'l)
- do
- (cl-incf nest)
- (when (= nest n-ranges)
- (setf low i))
- else
- do
- (when (= nest n-ranges)
- (push `(,low . ,i)
- res))
- (cl-decf nest)
- finally (cl-return (reverse res))))
-
(defun comp-copy-insn (insn)
"Deep copy INSN."
;; Adapted from `copy-tree'.
@@ -2615,55 +2363,16 @@ Return non-nil if the function is folded successfully."
(value (comp-apply-in-env f (mapcar #'comp-mvar-value args))))
(rewrite-insn-as-setimm insn value)))))))
-(defun comp-phi (lval &rest rvals)
- "Phi function propagating RVALS into LVAL.
-Return LVAL."
- (let* ((rhs-mvars (mapcar #'car rvals))
- (values (mapcar #'comp-mvar-valset rhs-mvars))
- (from-latch (cl-some
- (lambda (x)
- (comp-latch-p
- (gethash (cdr x)
- (comp-func-blocks comp-func))))
- rvals)))
-
- ;; Type propagation.
- (setf (comp-mvar-typeset lval)
- (apply #'comp-union-typesets (mapcar #'comp-mvar-typeset rhs-mvars)))
-
- ;; Value propagation.
- (setf (comp-mvar-valset lval)
- (cl-loop
- for v in (cl-remove-duplicates (apply #'append values)
- :test #'equal)
- ;; We propagate only values those types are not already
- ;; into typeset.
- when (cl-notany (lambda (x)
- (comp-subtype-p (type-of v) x))
- (comp-mvar-typeset lval))
- collect v))
-
- ;; Range propagation
- (setf (comp-mvar-range lval)
- (when (and (not from-latch)
- (cl-notany (lambda (x)
- (comp-subtype-p 'integer x))
- (comp-mvar-typeset lval)))
- ;; TODO memoize?
- (apply #'comp-range-union
- (mapcar #'comp-mvar-range rhs-mvars))))
- lval))
-
(defun comp-fwprop-call (insn lval f args)
"Propagate on a call INSN into LVAL.
F is the function being called with arguments ARGS.
Fold the call in case."
(unless (comp-function-call-maybe-fold insn f args)
- (when-let ((constr (gethash f comp-known-constraints-h)))
- (let ((constr (comp-constraint-f-ret constr)))
- (setf (comp-mvar-range lval) (comp-constraint-range constr)
- (comp-mvar-valset lval) (comp-constraint-valset constr)
- (comp-mvar-typeset lval) (comp-constraint-typeset constr))))))
+ (when-let ((cstr-f (gethash f comp-known-func-cstr-h)))
+ (let ((cstr (comp-cstr-f-ret cstr-f)))
+ (setf (comp-mvar-range lval) (comp-cstr-range cstr)
+ (comp-mvar-valset lval) (comp-cstr-valset cstr)
+ (comp-mvar-typeset lval) (comp-cstr-typeset cstr))))))
(defun comp-fwprop-insn (insn)
"Propagate within INSN."
@@ -2695,7 +2404,17 @@ Fold the call in case."
(`(setimm ,lval ,v)
(setf (comp-mvar-value lval) v))
(`(phi ,lval . ,rest)
- (apply #'comp-phi lval rest))))
+ (let* ((from-latch (cl-some
+ (lambda (x)
+ (comp-latch-p
+ (gethash (cdr x)
+ (comp-func-blocks comp-func))))
+ rest))
+ (prop-fn (if from-latch
+ #'comp-cstr-union-no-range
+ #'comp-cstr-union))
+ (rvals (mapcar #'car rest)))
+ (apply prop-fn lval rvals)))))
(defun comp-fwprop* ()
"Propagate for set* and phi operands.
@@ -2966,8 +2685,8 @@ These are substituted with a normal 'set' op."
"Compute type specifier for `comp-func' FUNC.
Set it into the `ret-type-specifier' slot."
(let* ((comp-func (make-comp-func))
- (res-mvar (apply #'comp-phi
- (make-comp-mvar)
+ (res-mvar (apply #'comp-cstr-union
+ (make-comp-cstr)
(cl-loop
with res = nil
for bb being the hash-value in (comp-func-blocks
@@ -2978,10 +2697,10 @@ Set it into the `ret-type-specifier' slot."
;; mvars and union results.
do (pcase insn
(`(return ,mvar)
- (push `(,mvar . nil) res))))
+ (push mvar res))))
finally (cl-return res)))))
(setf (comp-func-ret-type-specifier func)
- (comp-constraint-to-type-spec res-mvar))))
+ (comp-cstr-to-type-spec res-mvar))))
(defun comp-finalize-container (cont)
"Finalize data container CONT."