1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-27 07:30:59 -08:00
emacs/test/src/comp-tests.el
Andrea Corallo e96cd4e82c Add initial nativecomp typeset and range propagation support
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.
2020-11-12 00:55:36 +01:00

930 lines
33 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; 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