mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-27 07:30:59 -08:00
This commit add an initial support for a better type propagation and integer range propagation. Each mvar can be now characterized by a set of types, a set of values and an integral range. * lisp/emacs-lisp/comp.el (comp-known-ret-types): Store into typeset and remove fixnum. (comp-known-ret-ranges, comp-type-predicates): New variables. (comp-ctxt): Remove supertype-memoize slot and add union-typesets-mem. (comp-mvar): Remove const-vld, constant, type slots. Add typeset, valset, range slots. (comp-mvar-value-vld-p, comp-mvar-value, comp-mvar-fixnum-p) (comp-mvar-symbol-p, comp-mvar-cons-p) (comp-mvar-type-hint-match-p, comp-func-ret-typeset) (comp-func-ret-range): New functions. (make-comp-mvar, make-comp-ssa-mvar): Update logic. (comp--typeof-types): New variable. (comp-supertypes, comp-common-supertype): Logic update. (comp-subtype-p, comp-union-typesets, comp-range-1+) (comp-range-1-, comp-range-<, comp-range-union) (comp-range-intersection): New functions. (comp-fwprop-prologue, comp-mvar-propagate) (comp-function-foldable-p, comp-function-call-maybe-fold) (comp-fwprop-insn, comp-call-optim-func, comp-finalize-relocs): Logic update. * src/comp.c (emit_mvar_rval, emit_call_with_type_hint) (emit_call2_with_type_hint): Logic update. * lisp/emacs-lisp/cl-preloaded.el (cl--typeof-types): Undo the add of fixnum and bignum as unnecessary. * test/src/comp-tests.el (comp-tests-mentioned-p-1, comp-tests-cond-rw-checker-val) (comp-tests-cond-rw-checker-type, cond-rw-1, cond-rw-2) (cond-rw-3, cond-rw-4, cond-rw-5): Update for new type interface. (range-simple-union, range-simple-intersection): New integer range tests. (union-types): New union type test.
930 lines
33 KiB
EmacsLisp
930 lines
33 KiB
EmacsLisp
;;; comp-tests.el --- unit tests for src/comp.c -*- lexical-binding: t; -*-
|
||
|
||
;; Copyright (C) 2019-2020 Free Software Foundation, Inc.
|
||
|
||
;; Author: Andrea Corallo <akrl@sdf.org>
|
||
|
||
;; This file is part of GNU Emacs.
|
||
|
||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||
;; it under the terms of the GNU General Public License as published by
|
||
;; the Free Software Foundation, either version 3 of the License, or
|
||
;; (at your option) any later version.
|
||
|
||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
;; GNU General Public License for more details.
|
||
|
||
;; You should have received a copy of the GNU General Public License
|
||
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
;;; Commentary:
|
||
|
||
;; Unit tests for src/comp.c.
|
||
|
||
;;; Code:
|
||
|
||
(require 'ert)
|
||
(require 'cl-lib)
|
||
(require 'comp)
|
||
|
||
(defconst comp-test-directory (file-name-directory (or load-file-name
|
||
buffer-file-name)))
|
||
(defconst comp-test-src
|
||
(concat comp-test-directory "comp-test-funcs.el"))
|
||
|
||
(defconst comp-test-dyn-src
|
||
(concat comp-test-directory "comp-test-funcs-dyn.el"))
|
||
|
||
(when (featurep 'nativecomp)
|
||
(message "Compiling tests...")
|
||
(load (native-compile comp-test-src))
|
||
(load (native-compile comp-test-dyn-src)))
|
||
|
||
(defmacro comp-deftest (name args &rest docstring-and-body)
|
||
"Define a test for the native compiler tagging it as :nativecomp."
|
||
(declare (indent defun)
|
||
(doc-string 3))
|
||
`(ert-deftest ,(intern (concat "comp-tests-" (symbol-name name))) ,args
|
||
:tags '(:nativecomp)
|
||
,@docstring-and-body))
|
||
|
||
|
||
|
||
(ert-deftest comp-tests-bootstrap ()
|
||
"Compile the compiler and load it to compile it-self.
|
||
Check that the resulting binaries do not differ."
|
||
:tags '(:expensive-test :nativecomp)
|
||
(let* ((comp-src (concat comp-test-directory
|
||
"../../lisp/emacs-lisp/comp.el"))
|
||
(comp1-src (make-temp-file "stage1-" nil ".el"))
|
||
(comp2-src (make-temp-file "stage2-" nil ".el"))
|
||
;; Can't use debug symbols.
|
||
(comp-debug 0))
|
||
(copy-file comp-src comp1-src t)
|
||
(copy-file comp-src comp2-src t)
|
||
(let ((load-no-native t))
|
||
(load (concat comp-src "c") nil nil t t))
|
||
(should-not (subr-native-elisp-p (symbol-function #'native-compile)))
|
||
(message "Compiling stage1...")
|
||
(let* ((t0 (current-time))
|
||
(comp1-eln (native-compile comp1-src)))
|
||
(message "Done in %d secs" (float-time (time-since t0)))
|
||
(load comp1-eln nil nil t t)
|
||
(should (subr-native-elisp-p (symbol-function 'native-compile)))
|
||
(message "Compiling stage2...")
|
||
(let ((t0 (current-time))
|
||
(comp2-eln (native-compile comp2-src)))
|
||
(message "Done in %d secs" (float-time (time-since t0)))
|
||
(message "Comparing %s %s" comp1-eln comp2-eln)
|
||
(should (= (call-process "cmp" nil nil nil comp1-eln comp2-eln) 0))))))
|
||
|
||
(comp-deftest provide ()
|
||
"Testing top level provide."
|
||
(should (featurep 'comp-test-funcs)))
|
||
|
||
(comp-deftest varref ()
|
||
"Testing varref."
|
||
(should (= (comp-tests-varref-f) 3)))
|
||
|
||
(comp-deftest list ()
|
||
"Testing cons car cdr."
|
||
(should (equal (comp-tests-list-f) '(1 2 3)))
|
||
(should (equal (comp-tests-list2-f 1 2 3) '(1 2 3)))
|
||
(should (= (comp-tests-car-f '(1 . 2)) 1))
|
||
(should (null (comp-tests-car-f nil)))
|
||
(should-error (comp-tests-car-f 3)
|
||
:type 'wrong-type-argument)
|
||
(should (= (comp-tests-cdr-f '(1 . 2)) 2))
|
||
(should (null (comp-tests-cdr-f nil)))
|
||
(should-error (comp-tests-cdr-f 3)
|
||
:type 'wrong-type-argument)
|
||
(should (= (comp-tests-car-safe-f '(1 . 2)) 1))
|
||
(should (null (comp-tests-car-safe-f 'a)))
|
||
(should (= (comp-tests-cdr-safe-f '(1 . 2)) 2))
|
||
(should (null (comp-tests-cdr-safe-f 'a))))
|
||
|
||
(comp-deftest comp-tests-cons-car-cdr ()
|
||
"Testing cons car cdr."
|
||
(should (= (comp-tests-cons-car-f) 1))
|
||
(should (= (comp-tests-cons-cdr-f 3) 3)))
|
||
|
||
(comp-deftest varset ()
|
||
"Testing varset."
|
||
(comp-tests-varset0-f)
|
||
(should (= comp-tests-var1 55))
|
||
|
||
(should (= (comp-tests-varset1-f) 4))
|
||
(should (= comp-tests-var1 66)))
|
||
|
||
(comp-deftest length ()
|
||
"Testing length."
|
||
(should (= (comp-tests-length-f) 3)))
|
||
|
||
(comp-deftest aref-aset ()
|
||
"Testing aref and aset."
|
||
(should (= (comp-tests-aref-aset-f) 100)))
|
||
|
||
(comp-deftest symbol-value ()
|
||
"Testing aref and aset."
|
||
(should (= (comp-tests-symbol-value-f) 3)))
|
||
|
||
(comp-deftest concat ()
|
||
"Testing concatX opcodes."
|
||
(should (string= (comp-tests-concat-f "bar") "abcdabcabfoobar")))
|
||
|
||
(comp-deftest ffuncall ()
|
||
"Test calling conventions."
|
||
|
||
;; (defun comp-tests-ffuncall-caller-f ()
|
||
;; (comp-tests-ffuncall-callee-f 1 2 3))
|
||
|
||
;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
|
||
|
||
;; ;; After it gets compiled
|
||
;; (native-compile #'comp-tests-ffuncall-callee-f)
|
||
;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
|
||
|
||
;; ;; Recompiling the caller once with callee already compiled
|
||
;; (defun comp-tests-ffuncall-caller-f ()
|
||
;; (comp-tests-ffuncall-callee-f 1 2 3))
|
||
;; (should (equal (comp-tests-ffuncall-caller-f) '(1 2 3)))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3 4)
|
||
'(1 2 3 4)))
|
||
(should (equal (comp-tests-ffuncall-callee-optional-f 1 2 3)
|
||
'(1 2 3 nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-optional-f 1 2)
|
||
'(1 2 nil nil)))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-rest-f 1 2)
|
||
'(1 2 nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3)
|
||
'(1 2 (3))))
|
||
(should (equal (comp-tests-ffuncall-callee-rest-f 1 2 3 4)
|
||
'(1 2 (3 4))))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-more8-f 1 2 3 4 5 6 7 8 9 10)
|
||
'(1 2 3 4 5 6 7 8 9 10)))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-more8-rest-f 1 2 3 4 5 6 7 8 9 10 11)
|
||
'(1 2 3 4 5 6 7 8 9 (10 11))))
|
||
|
||
(should (equal (comp-tests-ffuncall-native-f) [nil]))
|
||
|
||
(should (equal (comp-tests-ffuncall-native-rest-f) [1 2 3]))
|
||
|
||
(should (equal (comp-tests-ffuncall-apply-many-f '(1 2 3))
|
||
'(1 2 3)))
|
||
|
||
(should (= (comp-tests-ffuncall-lambda-f 1) 2)))
|
||
|
||
(comp-deftest jump-table ()
|
||
"Testing jump tables"
|
||
(should (eq (comp-tests-jump-table-1-f 'x) 'a))
|
||
(should (eq (comp-tests-jump-table-1-f 'y) 'b))
|
||
(should (eq (comp-tests-jump-table-1-f 'xxx) 'c))
|
||
|
||
;; Jump table not with eq as test
|
||
(should (eq (comp-tests-jump-table-2-f "aaa") 'a))
|
||
(should (eq (comp-tests-jump-table-2-f "bbb") 'b)))
|
||
|
||
(comp-deftest conditionals ()
|
||
"Testing conditionals."
|
||
(should (= (comp-tests-conditionals-1-f t) 1))
|
||
(should (= (comp-tests-conditionals-1-f nil) 2))
|
||
(should (= (comp-tests-conditionals-2-f t) 1340))
|
||
(should (eq (comp-tests-conditionals-2-f nil) nil)))
|
||
|
||
(comp-deftest fixnum ()
|
||
"Testing some fixnum inline operation."
|
||
(should (= (comp-tests-fixnum-1-minus-f 10) 9))
|
||
(should (= (comp-tests-fixnum-1-minus-f most-negative-fixnum)
|
||
(1- most-negative-fixnum)))
|
||
(should-error (comp-tests-fixnum-1-minus-f 'a)
|
||
:type 'wrong-type-argument)
|
||
(should (= (comp-tests-fixnum-1-plus-f 10) 11))
|
||
(should (= (comp-tests-fixnum-1-plus-f most-positive-fixnum)
|
||
(1+ most-positive-fixnum)))
|
||
(should-error (comp-tests-fixnum-1-plus-f 'a)
|
||
:type 'wrong-type-argument)
|
||
(should (= (comp-tests-fixnum-minus-f 10) -10))
|
||
(should (= (comp-tests-fixnum-minus-f most-negative-fixnum)
|
||
(- most-negative-fixnum)))
|
||
(should-error (comp-tests-fixnum-minus-f 'a)
|
||
:type 'wrong-type-argument))
|
||
|
||
(comp-deftest type-hints ()
|
||
"Just test compiler hints are transparent in this case."
|
||
;; FIXME we should really check they are also effective.
|
||
(should (= (comp-tests-hint-fixnum-f 3) 4))
|
||
(should (= (comp-tests-hint-cons-f (cons 1 2)) 1)))
|
||
|
||
(comp-deftest arith-comp ()
|
||
"Testing arithmetic comparisons."
|
||
(should (eq (comp-tests-eqlsign-f 4 3) nil))
|
||
(should (eq (comp-tests-eqlsign-f 3 3) t))
|
||
(should (eq (comp-tests-eqlsign-f 2 3) nil))
|
||
(should (eq (comp-tests-gtr-f 4 3) t))
|
||
(should (eq (comp-tests-gtr-f 3 3) nil))
|
||
(should (eq (comp-tests-gtr-f 2 3) nil))
|
||
(should (eq (comp-tests-lss-f 4 3) nil))
|
||
(should (eq (comp-tests-lss-f 3 3) nil))
|
||
(should (eq (comp-tests-lss-f 2 3) t))
|
||
(should (eq (comp-tests-les-f 4 3) nil))
|
||
(should (eq (comp-tests-les-f 3 3) t))
|
||
(should (eq (comp-tests-les-f 2 3) t))
|
||
(should (eq (comp-tests-geq-f 4 3) t))
|
||
(should (eq (comp-tests-geq-f 3 3) t))
|
||
(should (eq (comp-tests-geq-f 2 3) nil)))
|
||
|
||
(comp-deftest setcarcdr ()
|
||
"Testing setcar setcdr."
|
||
(should (equal (comp-tests-setcar-f '(10 . 10) 3) '(3 . 10)))
|
||
(should (equal (comp-tests-setcdr-f '(10 . 10) 3) '(10 . 3)))
|
||
(should-error (comp-tests-setcar-f 3 10)
|
||
:type 'wrong-type-argument)
|
||
(should-error (comp-tests-setcdr-f 3 10)
|
||
:type 'wrong-type-argument))
|
||
|
||
(comp-deftest bubble-sort ()
|
||
"Run bubble sort."
|
||
(let* ((list1 (mapcar #'random (make-list 1000 most-positive-fixnum)))
|
||
(list2 (copy-sequence list1)))
|
||
(should (equal (comp-bubble-sort-f list1)
|
||
(sort list2 #'<)))))
|
||
|
||
(comp-deftest apply ()
|
||
"Test some inlined list functions."
|
||
(should (eq (comp-tests-consp-f '(1)) t))
|
||
(should (eq (comp-tests-consp-f 1) nil))
|
||
(let ((x (cons 1 2)))
|
||
(should (= (comp-tests-setcar2-f x) 3))
|
||
(should (equal x '(3 . 2)))))
|
||
|
||
(comp-deftest num-inline ()
|
||
"Test some inlined number functions."
|
||
(should (eq (comp-tests-integerp-f 1) t))
|
||
(should (eq (comp-tests-integerp-f '(1)) nil))
|
||
(should (eq (comp-tests-integerp-f 3.5) nil))
|
||
(should (eq (comp-tests-integerp-f (1+ most-negative-fixnum)) t))
|
||
|
||
(should (eq (comp-tests-numberp-f 1) t))
|
||
(should (eq (comp-tests-numberp-f 'a) nil))
|
||
(should (eq (comp-tests-numberp-f 3.5) t)))
|
||
|
||
(comp-deftest stack ()
|
||
"Test some stack operation."
|
||
(should (= (comp-tests-discardn-f 10) 2))
|
||
(should (string= (with-temp-buffer
|
||
(comp-tests-insertn-f "a" "b" "c" "d")
|
||
(buffer-string))
|
||
"abcd")))
|
||
|
||
(comp-deftest non-locals ()
|
||
"Test non locals."
|
||
(should (string= (comp-tests-condition-case-0-f)
|
||
"arith-error Arithmetic error catched"))
|
||
(should (string= (comp-tests-condition-case-1-f)
|
||
"error foo catched"))
|
||
(should (= (comp-tests-catch-f
|
||
(lambda () (throw 'foo 3)))
|
||
3))
|
||
(should (= (catch 'foo
|
||
(comp-tests-throw-f 3)))))
|
||
|
||
(comp-deftest gc ()
|
||
"Try to do some longer computation to let the gc kick in."
|
||
(dotimes (_ 100000)
|
||
(comp-tests-cons-cdr-f 3))
|
||
(should (= (comp-tests-cons-cdr-f 3) 3)))
|
||
|
||
(comp-deftest buffer ()
|
||
(should (string= (comp-tests-buff0-f) "foo")))
|
||
|
||
(comp-deftest lambda-return ()
|
||
(let ((f (comp-tests-lambda-return-f)))
|
||
(should (subr-native-elisp-p f))
|
||
(should (= (funcall f 3) 4))))
|
||
|
||
(comp-deftest recursive ()
|
||
(should (= (comp-tests-fib-f 10) 55)))
|
||
|
||
(comp-deftest macro ()
|
||
"Just check we can define macros"
|
||
(should (macrop (symbol-function 'comp-tests-macro-m))))
|
||
|
||
(comp-deftest string-trim ()
|
||
(should (string= (comp-tests-string-trim-f "dsaf ") "dsaf")))
|
||
|
||
(comp-deftest trampoline-removal ()
|
||
;; This tests that we can can call primitives with no dedicated bytecode.
|
||
;; At speed >= 2 the trampoline will not be used.
|
||
(should (hash-table-p (comp-tests-trampoline-removal-f))))
|
||
|
||
(comp-deftest signal ()
|
||
(should (equal (condition-case err
|
||
(comp-tests-signal-f)
|
||
(t err))
|
||
'(foo . t))))
|
||
|
||
(comp-deftest func-call-removal ()
|
||
;; See `comp-propagate-insn' `comp-function-call-remove'.
|
||
(should (= (comp-tests-func-call-removal-f) 1)))
|
||
|
||
(comp-deftest doc ()
|
||
(should (string= (documentation #'comp-tests-doc-f)
|
||
"A nice docstring"))
|
||
;; Check a preloaded function, we can't use `comp-tests-doc-f' now
|
||
;; as this is loaded manually with no .elc.
|
||
(should (string-match "\\.*.elc\\'" (symbol-file #'error))))
|
||
|
||
(comp-deftest interactive-form ()
|
||
(should (equal (interactive-form #'comp-test-interactive-form0-f)
|
||
'(interactive "D")))
|
||
(should (equal (interactive-form #'comp-test-interactive-form1-f)
|
||
'(interactive '(1 2))))
|
||
(should (equal (interactive-form #'comp-test-interactive-form2-f)
|
||
'(interactive nil)))
|
||
(should (cl-every #'commandp '(comp-test-interactive-form0-f
|
||
comp-test-interactive-form1-f
|
||
comp-test-interactive-form2-f)))
|
||
(should-not (commandp #'comp-tests-doc-f)))
|
||
|
||
(comp-deftest free-fun ()
|
||
"Check we are able to compile a single function."
|
||
(eval '(defun comp-tests-free-fun-f ()
|
||
"Some doc."
|
||
(interactive)
|
||
3)
|
||
t)
|
||
(native-compile #'comp-tests-free-fun-f)
|
||
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-free-fun-f)))
|
||
(should (= (comp-tests-free-fun-f) 3))
|
||
(should (string= (documentation #'comp-tests-free-fun-f)
|
||
"Some doc."))
|
||
(should (commandp #'comp-tests-free-fun-f))
|
||
(should (equal (interactive-form #'comp-tests-free-fun-f)
|
||
'(interactive))))
|
||
|
||
(comp-deftest free-fun-silly-name ()
|
||
"Check we are able to compile a single function."
|
||
(eval '(defun comp-tests/free\fun-f ()) t)
|
||
(native-compile #'comp-tests/free\fun-f)
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests/free\fun-f))))
|
||
|
||
(comp-deftest bug-40187 ()
|
||
"Check function name shadowing.
|
||
https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-03/msg00914.html."
|
||
(should (eq (comp-test-40187-1-f) 'foo))
|
||
(should (eq (comp-test-40187-2-f) 'bar)))
|
||
|
||
(comp-deftest speed--1 ()
|
||
"Check that at speed -1 we do not native compile."
|
||
(should (= (comp-test-speed--1-f) 3))
|
||
(should-not (subr-native-elisp-p (symbol-function #'comp-test-speed--1-f))))
|
||
|
||
(comp-deftest bug-42360 ()
|
||
"<https://lists.gnu.org/archive/html/bug-gnu-emacs/2020-07/msg00418.html>."
|
||
(should (string= (comp-test-42360-f "Nel mezzo del " 18 0 32 "yyy" nil)
|
||
"Nel mezzo del yyy")))
|
||
|
||
(defvar comp-test-primitive-advice)
|
||
(comp-deftest primitive-advice ()
|
||
"Test effectiveness of primitve advicing."
|
||
(let (comp-test-primitive-advice
|
||
(f (lambda (&rest args)
|
||
(setq comp-test-primitive-advice args))))
|
||
(advice-add #'+ :before f)
|
||
(unwind-protect
|
||
(progn
|
||
(should (= (comp-test-primitive-advice-f 3 4) 7))
|
||
(should (equal comp-test-primitive-advice '(3 4))))
|
||
(advice-remove #'+ f))))
|
||
|
||
(defvar comp-test-primitive-redefine-args)
|
||
(comp-deftest primitive-redefine ()
|
||
"Test effectiveness of primitve redefinition."
|
||
(cl-letf ((comp-test-primitive-redefine-args nil)
|
||
((symbol-function #'-)
|
||
(lambda (&rest args)
|
||
(setq comp-test-primitive-redefine-args args)
|
||
'xxx)))
|
||
(should (eq (comp-test-primitive-redefine-f 10 2) 'xxx))
|
||
(should (equal comp-test-primitive-redefine-args '(10 2)))))
|
||
|
||
(comp-deftest compile-forms ()
|
||
"Verify lambda form native compilation."
|
||
(should-error (native-compile '(+ 1 foo)))
|
||
(let ((lexical-binding t)
|
||
(f (native-compile '(lambda (x) (1+ x)))))
|
||
(should (subr-native-elisp-p f))
|
||
(should (= (funcall f 2) 3)))
|
||
(let* ((lexical-binding nil)
|
||
(f (native-compile '(lambda (x) (1+ x)))))
|
||
(should (subr-native-elisp-p f))
|
||
(should (= (funcall f 2) 3))))
|
||
|
||
(comp-deftest comp-test-defsubst ()
|
||
;; Bug#42664, Bug#43280, Bug#44209.
|
||
(should-not (subr-native-elisp-p (symbol-function #'comp-test-defsubst-f))))
|
||
|
||
(comp-deftest primitive-redefine-compile-44221 ()
|
||
"Test the compiler still works while primitives are redefined (bug#44221)."
|
||
(cl-letf (((symbol-function #'delete-region)
|
||
(lambda (_ _))))
|
||
(should (subr-native-elisp-p
|
||
(native-compile
|
||
'(lambda ()
|
||
(delete-region (point-min) (point-max))))))))
|
||
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;
|
||
;; Tromey's tests. ;;
|
||
;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
(comp-deftest consp ()
|
||
(should-not (comp-test-consp 23))
|
||
(should-not (comp-test-consp nil))
|
||
(should (comp-test-consp '(1 . 2))))
|
||
|
||
(comp-deftest listp ()
|
||
(should-not (comp-test-listp 23))
|
||
(should (comp-test-listp nil))
|
||
(should (comp-test-listp '(1 . 2))))
|
||
|
||
(comp-deftest stringp ()
|
||
(should-not (comp-test-stringp 23))
|
||
(should-not (comp-test-stringp nil))
|
||
(should (comp-test-stringp "hi")))
|
||
|
||
(comp-deftest symbolp ()
|
||
(should-not (comp-test-symbolp 23))
|
||
(should-not (comp-test-symbolp "hi"))
|
||
(should (comp-test-symbolp 'whatever)))
|
||
|
||
(comp-deftest integerp ()
|
||
(should (comp-test-integerp 23))
|
||
(should-not (comp-test-integerp 57.5))
|
||
(should-not (comp-test-integerp "hi"))
|
||
(should-not (comp-test-integerp 'whatever)))
|
||
|
||
(comp-deftest numberp ()
|
||
(should (comp-test-numberp 23))
|
||
(should (comp-test-numberp 57.5))
|
||
(should-not (comp-test-numberp "hi"))
|
||
(should-not (comp-test-numberp 'whatever)))
|
||
|
||
(comp-deftest add1 ()
|
||
(should (eq (comp-test-add1 23) 24))
|
||
(should (eq (comp-test-add1 -17) -16))
|
||
(should (eql (comp-test-add1 1.0) 2.0))
|
||
(should-error (comp-test-add1 nil)
|
||
:type 'wrong-type-argument))
|
||
|
||
(comp-deftest sub1 ()
|
||
(should (eq (comp-test-sub1 23) 22))
|
||
(should (eq (comp-test-sub1 -17) -18))
|
||
(should (eql (comp-test-sub1 1.0) 0.0))
|
||
(should-error (comp-test-sub1 nil)
|
||
:type 'wrong-type-argument))
|
||
|
||
(comp-deftest negate ()
|
||
(should (eq (comp-test-negate 23) -23))
|
||
(should (eq (comp-test-negate -17) 17))
|
||
(should (eql (comp-test-negate 1.0) -1.0))
|
||
(should-error (comp-test-negate nil)
|
||
:type 'wrong-type-argument))
|
||
|
||
(comp-deftest not ()
|
||
(should (eq (comp-test-not 23) nil))
|
||
(should (eq (comp-test-not nil) t))
|
||
(should (eq (comp-test-not t) nil)))
|
||
|
||
(comp-deftest bobp-and-eobp ()
|
||
(with-temp-buffer
|
||
(should (comp-test-bobp))
|
||
(should (comp-test-eobp))
|
||
(insert "hi")
|
||
(goto-char (point-min))
|
||
(should (eq (comp-test-point-min) (point-min)))
|
||
(should (eq (comp-test-point) (point-min)))
|
||
(should (comp-test-bobp))
|
||
(should-not (comp-test-eobp))
|
||
(goto-char (point-max))
|
||
(should (eq (comp-test-point-max) (point-max)))
|
||
(should (eq (comp-test-point) (point-max)))
|
||
(should-not (comp-test-bobp))
|
||
(should (comp-test-eobp))))
|
||
|
||
(comp-deftest car-cdr ()
|
||
(let ((pair '(1 . b)))
|
||
(should (eq (comp-test-car pair) 1))
|
||
(should (eq (comp-test-car nil) nil))
|
||
(should-error (comp-test-car 23)
|
||
:type 'wrong-type-argument)
|
||
(should (eq (comp-test-cdr pair) 'b))
|
||
(should (eq (comp-test-cdr nil) nil))
|
||
(should-error (comp-test-cdr 23)
|
||
:type 'wrong-type-argument)))
|
||
|
||
(comp-deftest car-cdr-safe ()
|
||
(let ((pair '(1 . b)))
|
||
(should (eq (comp-test-car-safe pair) 1))
|
||
(should (eq (comp-test-car-safe nil) nil))
|
||
(should (eq (comp-test-car-safe 23) nil))
|
||
(should (eq (comp-test-cdr-safe pair) 'b))
|
||
(should (eq (comp-test-cdr-safe nil) nil))
|
||
(should (eq (comp-test-cdr-safe 23) nil))))
|
||
|
||
(comp-deftest eq ()
|
||
(should (comp-test-eq 'a 'a))
|
||
(should (comp-test-eq 5 5))
|
||
(should-not (comp-test-eq 'a 'b)))
|
||
|
||
(comp-deftest if ()
|
||
(should (eq (comp-test-if 'a 'b) 'a))
|
||
(should (eq (comp-test-if 0 23) 0))
|
||
(should (eq (comp-test-if nil 'b) 'b)))
|
||
|
||
(comp-deftest and ()
|
||
(should (eq (comp-test-and 'a 'b) 'b))
|
||
(should (eq (comp-test-and 0 23) 23))
|
||
(should (eq (comp-test-and nil 'b) nil)))
|
||
|
||
(comp-deftest or ()
|
||
(should (eq (comp-test-or 'a 'b) 'a))
|
||
(should (eq (comp-test-or 0 23) 0))
|
||
(should (eq (comp-test-or nil 'b) 'b)))
|
||
|
||
(comp-deftest save-excursion ()
|
||
(with-temp-buffer
|
||
(comp-test-save-excursion)
|
||
(should (eq (point) (point-min)))
|
||
(should (eq (comp-test-current-buffer) (current-buffer)))))
|
||
|
||
(comp-deftest > ()
|
||
(should (eq (comp-test-> 0 23) nil))
|
||
(should (eq (comp-test-> 23 0) t)))
|
||
|
||
(comp-deftest catch ()
|
||
(should (eq (comp-test-catch 0 1 2 3 4) nil))
|
||
(should (eq (comp-test-catch 20 21 22 23 24 25 26 27 28) 24)))
|
||
|
||
(comp-deftest memq ()
|
||
(should (equal (comp-test-memq 0 '(5 4 3 2 1 0)) '(0)))
|
||
(should (eq (comp-test-memq 72 '(5 4 3 2 1 0)) nil)))
|
||
|
||
(comp-deftest listN ()
|
||
(should (equal (comp-test-listN 57)
|
||
'(57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57))))
|
||
|
||
(comp-deftest concatN ()
|
||
(should (equal (comp-test-concatN "x") "xxxxxx")))
|
||
|
||
(comp-deftest opt-rest ()
|
||
(should (equal (comp-test-opt-rest 1) '(1 nil nil)))
|
||
(should (equal (comp-test-opt-rest 1 2) '(1 2 nil)))
|
||
(should (equal (comp-test-opt-rest 1 2 3) '(1 2 (3))))
|
||
(should (equal (comp-test-opt-rest 1 2 56 57 58)
|
||
'(1 2 (56 57 58)))))
|
||
|
||
(comp-deftest opt ()
|
||
(should (equal (comp-test-opt 23) '(23)))
|
||
(should (equal (comp-test-opt 23 24) '(23 . 24)))
|
||
(should-error (comp-test-opt)
|
||
:type 'wrong-number-of-arguments)
|
||
(should-error (comp-test-opt nil 24 97)
|
||
:type 'wrong-number-of-arguments))
|
||
|
||
(comp-deftest unwind-protect ()
|
||
(comp-test-unwind-protect 'ignore)
|
||
(should (eq comp-test-up-val 999))
|
||
(condition-case nil
|
||
(comp-test-unwind-protect (lambda () (error "HI")))
|
||
(error
|
||
nil))
|
||
(should (eq comp-test-up-val 999)))
|
||
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; Tests for dynamic scope. ;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
(comp-deftest dynamic-ffuncall ()
|
||
"Test calling convention for dynamic binding."
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-dyn-f 1 2)
|
||
'(1 2)))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3 4)
|
||
'(1 2 3 4)))
|
||
(should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2 3)
|
||
'(1 2 3 nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-opt-dyn-f 1 2)
|
||
'(1 2 nil nil)))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2)
|
||
'(1 2 nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3)
|
||
'(1 2 (3))))
|
||
(should (equal (comp-tests-ffuncall-callee-rest-dyn-f 1 2 3 4)
|
||
'(1 2 (3 4))))
|
||
|
||
(should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2)
|
||
'(1 2 nil nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3)
|
||
'(1 2 3 nil)))
|
||
(should (equal (comp-tests-ffuncall-callee-opt-rest-dyn-f 1 2 3 4)
|
||
'(1 2 3 (4)))))
|
||
|
||
(comp-deftest dynamic-arity ()
|
||
"Test func-arity on dynamic scope functions."
|
||
(should (equal '(2 . 2)
|
||
(func-arity #'comp-tests-ffuncall-callee-dyn-f)))
|
||
(should (equal '(2 . 4)
|
||
(func-arity #'comp-tests-ffuncall-callee-opt-dyn-f)))
|
||
(should (equal '(2 . many)
|
||
(func-arity #'comp-tests-ffuncall-callee-rest-dyn-f)))
|
||
(should (equal '(2 . many)
|
||
(func-arity #'comp-tests-ffuncall-callee-opt-rest-dyn-f))))
|
||
|
||
(comp-deftest dynamic-help-arglist ()
|
||
"Test `help-function-arglist' works on lisp/d (bug#42572)."
|
||
(should (equal (help-function-arglist
|
||
(symbol-function #'comp-tests-ffuncall-callee-opt-rest-dyn-f)
|
||
t)
|
||
'(a b &optional c &rest d))))
|
||
|
||
(comp-deftest cl-macro-exp ()
|
||
"Verify CL macro expansion (bug#42088)."
|
||
(should (equal (comp-tests-cl-macro-exp-f) '(a b))))
|
||
|
||
(comp-deftest cl-uninterned-arg-parse-f ()
|
||
"Verify the parsing of a lambda list with uninterned symbols (bug#42120)."
|
||
(should (equal (comp-tests-cl-uninterned-arg-parse-f 1 2)
|
||
'(1 2))))
|
||
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; Middle-end specific tests. ;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
(defun comp-tests-mentioned-p-1 (x insn)
|
||
(cl-loop for y in insn
|
||
when (cond
|
||
((consp y) (comp-tests-mentioned-p x y))
|
||
((and (comp-mvar-p y) (comp-mvar-value-vld-p y))
|
||
(equal (comp-mvar-value y) x))
|
||
(t (equal x y)))
|
||
return t))
|
||
|
||
(defun comp-tests-mentioned-p (x insn)
|
||
"Check if X is actively mentioned in INSN."
|
||
(unless (eq (car-safe insn)
|
||
'comment)
|
||
(comp-tests-mentioned-p-1 x insn)))
|
||
|
||
(defun comp-tests-map-checker (func-name checker)
|
||
"Apply CHECKER to each insn of FUNC-NAME.
|
||
Return a list of results."
|
||
(cl-loop
|
||
with func-c-name = (comp-c-func-name (or func-name 'anonymous-lambda) "F" t)
|
||
with f = (gethash func-c-name (comp-ctxt-funcs-h comp-ctxt))
|
||
for bb being each hash-value of (comp-func-blocks f)
|
||
nconc
|
||
(cl-loop
|
||
for insn in (comp-block-insns bb)
|
||
collect (funcall checker insn))))
|
||
|
||
(defun comp-tests-tco-checker (_)
|
||
"Check that inside `comp-tests-tco-f' we have no recursion."
|
||
(should
|
||
(cl-notany
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
'comp-tests-tco-f
|
||
(lambda (insn)
|
||
(or (comp-tests-mentioned-p 'comp-tests-tco-f insn)
|
||
(comp-tests-mentioned-p (comp-c-func-name 'comp-tests-tco-f "F" t)
|
||
insn)))))))
|
||
|
||
(comp-deftest tco ()
|
||
"Check for tail recursion elimination."
|
||
(let ((comp-speed 3)
|
||
;; Disable ipa-pure otherwise `comp-tests-tco-f' gets
|
||
;; optimized-out.
|
||
(comp-disabled-passes '(comp-ipa-pure))
|
||
(comp-post-pass-hooks '((comp-tco comp-tests-tco-checker)
|
||
(comp-final comp-tests-tco-checker))))
|
||
(eval '(defun comp-tests-tco-f (a b count)
|
||
(if (= count 0)
|
||
b
|
||
(comp-tests-tco-f (+ a b) a (- count 1))))
|
||
t)
|
||
(native-compile #'comp-tests-tco-f)
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-tco-f)))
|
||
(should (= (comp-tests-tco-f 1 0 10) 55))))
|
||
|
||
(defun comp-tests-fw-prop-checker-1 (_)
|
||
"Check that inside `comp-tests-fw-prop-f' `concat' and `length' are folded."
|
||
(should
|
||
(cl-notany
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
'comp-tests-fw-prop-1-f
|
||
(lambda (insn)
|
||
(or (comp-tests-mentioned-p 'concat insn)
|
||
(comp-tests-mentioned-p 'length insn)))))))
|
||
|
||
(comp-deftest fw-prop ()
|
||
"Some tests for forward propagation."
|
||
(let ((comp-speed 2)
|
||
(comp-post-pass-hooks '((comp-final comp-tests-fw-prop-checker-1))))
|
||
(eval '(defun comp-tests-fw-prop-1-f ()
|
||
(let* ((a "xxx")
|
||
(b "yyy")
|
||
(c (concat a b))) ; <= has to optimize
|
||
(length c))) ; <= has to optimize
|
||
t)
|
||
(native-compile #'comp-tests-fw-prop-1-f)
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-fw-prop-1-f)))
|
||
(should (= (comp-tests-fw-prop-1-f) 6))))
|
||
|
||
(defun comp-tests-pure-checker-1 (_)
|
||
"Check that inside `comp-tests-pure-caller-f' `comp-tests-pure-callee-f' is
|
||
folded."
|
||
(should
|
||
(cl-notany
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
'comp-tests-pure-caller-f
|
||
(lambda (insn)
|
||
(or (comp-tests-mentioned-p 'comp-tests-pure-callee-f insn)
|
||
(comp-tests-mentioned-p (comp-c-func-name
|
||
'comp-tests-pure-callee-f "F" t)
|
||
insn)))))))
|
||
|
||
(defun comp-tests-pure-checker-2 (_)
|
||
"Check that `comp-tests-pure-fibn-f' is folded."
|
||
(should
|
||
(cl-notany
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
'comp-tests-pure-fibn-entry-f
|
||
(lambda (insn)
|
||
(or (comp-tests-mentioned-p 'comp-tests-pure-fibn-f insn)
|
||
(comp-tests-mentioned-p (comp-c-func-name 'comp-tests-pure-fibn-f "F" t)
|
||
insn)))))))
|
||
|
||
(comp-deftest pure ()
|
||
"Some tests for pure functions optimization."
|
||
(let ((comp-speed 3)
|
||
(comp-post-pass-hooks '((comp-final comp-tests-pure-checker-1
|
||
comp-tests-pure-checker-2))))
|
||
(load (native-compile (concat comp-test-directory "comp-test-pure.el")))
|
||
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-pure-caller-f)))
|
||
(should (= (comp-tests-pure-caller-f) 4))
|
||
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-pure-fibn-entry-f)))
|
||
(should (= (comp-tests-pure-fibn-entry-f) 6765))))
|
||
|
||
(defvar comp-tests-cond-rw-checked-function nil
|
||
"Function to be checked.")
|
||
(defun comp-tests-cond-rw-checker-val (_)
|
||
"Check we manage to propagate the correct return value."
|
||
(should
|
||
(cl-some
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
comp-tests-cond-rw-checked-function
|
||
(lambda (insn)
|
||
(pcase insn
|
||
(`(return ,mvar)
|
||
(and (comp-mvar-value-vld-p mvar)
|
||
(eql (comp-mvar-value mvar) 123)))))))))
|
||
|
||
(defvar comp-tests-cond-rw-expected-type nil
|
||
"Type to expect in `comp-tests-cond-rw-checker-type'.")
|
||
(defun comp-tests-cond-rw-checker-type (_)
|
||
"Check we manage to propagate the correct return type."
|
||
(should
|
||
(cl-some
|
||
#'identity
|
||
(comp-tests-map-checker
|
||
comp-tests-cond-rw-checked-function
|
||
(lambda (insn)
|
||
(pcase insn
|
||
(`(return ,mvar)
|
||
(equal (comp-mvar-typeset mvar)
|
||
comp-tests-cond-rw-expected-type))))))))
|
||
|
||
(defvar comp-tests-cond-rw-0-var)
|
||
(comp-deftest cond-rw-0 ()
|
||
"Check we do not miscompile some simple functions."
|
||
(let ((lexical-binding t))
|
||
(let ((f (native-compile '(lambda (l)
|
||
(when (eq (car l) 'x)
|
||
(cdr l))))))
|
||
(should (subr-native-elisp-p f))
|
||
(should (eq (funcall f '(x . y)) 'y))
|
||
(should (null (funcall f '(z . y)))))
|
||
|
||
(should
|
||
(subr-native-elisp-p
|
||
(native-compile '(lambda () (if (eq comp-tests-cond-rw-0-var 123) 5 10)))))))
|
||
|
||
(comp-deftest cond-rw-1 ()
|
||
"Test cond-rw pass allow us to propagate type+val under `eq' tests."
|
||
(let ((lexical-binding t)
|
||
(comp-tests-cond-rw-expected-type '(integer))
|
||
(comp-post-pass-hooks '((comp-final comp-tests-cond-rw-checker-type
|
||
comp-tests-cond-rw-checker-val))))
|
||
(subr-native-elisp-p (native-compile '(lambda (x) (if (eq x 123) x t))))
|
||
(subr-native-elisp-p (native-compile '(lambda (x) (if (eq 123 x) x t))))))
|
||
|
||
(comp-deftest cond-rw-2 ()
|
||
"Test cond-rw pass allow us to propagate type+val under `=' tests."
|
||
(let ((lexical-binding t)
|
||
(comp-tests-cond-rw-expected-type '(integer))
|
||
(comp-post-pass-hooks '((comp-final comp-tests-cond-rw-checker-type
|
||
comp-tests-cond-rw-checker-val))))
|
||
(subr-native-elisp-p (native-compile '(lambda (x) (if (= x 123) x t))))))
|
||
|
||
(comp-deftest cond-rw-3 ()
|
||
"Test cond-rw pass allow us to propagate type+val under `eql' tests."
|
||
(let ((lexical-binding t)
|
||
(comp-tests-cond-rw-expected-type '(integer))
|
||
(comp-post-pass-hooks '((comp-final comp-tests-cond-rw-checker-type
|
||
comp-tests-cond-rw-checker-val))))
|
||
(subr-native-elisp-p (native-compile '(lambda (x) (if (eql 123 x) x t))))))
|
||
|
||
(comp-deftest cond-rw-4 ()
|
||
"Test cond-rw pass allow us to propagate type under `=' tests."
|
||
(let ((lexical-binding t)
|
||
(comp-tests-cond-rw-expected-type '(number))
|
||
(comp-post-pass-hooks '((comp-final comp-tests-cond-rw-checker-type))))
|
||
(subr-native-elisp-p (native-compile '(lambda (x y) (if (= x y) x t))))))
|
||
|
||
(comp-deftest cond-rw-5 ()
|
||
"Test cond-rw pass allow us to propagate type under `=' tests."
|
||
(let ((comp-tests-cond-rw-checked-function #'comp-tests-cond-rw-4-f)
|
||
(comp-tests-cond-rw-expected-type '(integer))
|
||
(comp-post-pass-hooks '((comp-final comp-tests-cond-rw-checker-type))))
|
||
(eval '(defun comp-tests-cond-rw-4-f (x y)
|
||
(declare (speed 3))
|
||
(if (= x (comp-hint-fixnum y))
|
||
x
|
||
t))
|
||
t)
|
||
(native-compile #'comp-tests-cond-rw-4-f)
|
||
(should (subr-native-elisp-p (symbol-function #'comp-tests-cond-rw-4-f)))))
|
||
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;; Range propagation tests. ;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
(comp-deftest range-simple-union ()
|
||
(should (equal (comp-range-union '((-1 . 0)) '((3 . 4)))
|
||
'((-1 . 0) (3 . 4))))
|
||
(should (equal (comp-range-union '((-1 . 2)) '((3 . 4)))
|
||
'((-1 . 4))))
|
||
(should (equal (comp-range-union '((-1 . 3)) '((3 . 4)))
|
||
'((-1 . 4))))
|
||
(should (equal (comp-range-union '((-1 . 4)) '((3 . 4)))
|
||
'((-1 . 4))))
|
||
(should (equal (comp-range-union '((-1 . 5)) '((3 . 4)))
|
||
'((-1 . 5))))
|
||
(should (equal (comp-range-union '((-1 . 0)) '())
|
||
'((-1 . 0)))))
|
||
|
||
(comp-deftest range-simple-intersection ()
|
||
(should (equal (comp-range-intersection '((-1 . 0)) '((3 . 4)))
|
||
'()))
|
||
(should (equal (comp-range-intersection '((-1 . 2)) '((3 . 4)))
|
||
'()))
|
||
(should (equal (comp-range-intersection '((-1 . 3)) '((3 . 4)))
|
||
'((3 . 3))))
|
||
(should (equal (comp-range-intersection '((-1 . 4)) '((3 . 4)))
|
||
'((3 . 4))))
|
||
(should (equal (comp-range-intersection '((-1 . 5)) '((3 . 4)))
|
||
'((3 . 4))))
|
||
(should (equal (comp-range-intersection '((-1 . 0)) '())
|
||
'())))
|
||
|
||
(comp-deftest union-types ()
|
||
(let ((comp-ctxt (make-comp-ctxt)))
|
||
(should (equal (comp-union-typesets '(integer) '(number))
|
||
'(number)))
|
||
(should (equal (comp-union-typesets '(integer symbol) '(number))
|
||
'(symbol number)))
|
||
(should (equal (comp-union-typesets '(integer symbol) '(number list))
|
||
'(list symbol number)))
|
||
(should (equal (comp-union-typesets '(integer symbol) '())
|
||
'(symbol integer)))))
|
||
|
||
;;; comp-tests.el ends here
|