#:g1: 無効化するとunreachableを出すassert

Posted 2021-05-17 18:44:42 GMT

コンパイラへの指示で、assertを無効にした時に消えてしまうのではなくて、unreachableに変化すると最適化のヒントになって良いのではないか、というのを目にしたので、Common Lispで似たようなことができないか試してみます。

Common Lispでは、assertは継続エラーを出すという仕様なので最適化で消えたりはしませんが、ブロックから早期脱出するようにすれば、後続のコードが不達になって不達コードの警告を出すコンパイラもあると思います(SBCL等)

(ql:quickload 'policy-cond)

(defun innermost-block (env) (let ((blk (car #+sbcl (sb-c::lexenv-blocks env) #+lispworks (compiler::environment-benv env)))) (values (car blk) (cdr blk)))))

(defmacro assert* (test-form &environment env) (multiple-value-bind (name namep) (innermost-block env) (if (or name namep) `(policy-cond:policy-if (eql 0 cl:safety) (unless ,test-form (return-from ,name nil)) (assert ,test-form)) `(assert ,test-form))))

policy-condを利用してsafety 0の時だけassertが早期脱出のコードに変換されるようにしてみました。

(defun bar (n)
  (declare ((integer 0 2) n))
  (declare (optimize (safety 0)))
  (assert* (= 3 n))
  (list n))

(bar 1) → nil

LispWorksあたりだと、早期脱出のコードに変換される程度ですが、SBCLでは不達コードの警告を出したりはできるようです。
とはいえ、残念ながらコンパイル時に判明している値レベルでしか判定できませんが。

(defun bar (n)
  (assert* (= 3 4))
  (print n))

; processing (DEFUN BAR ...) ; file: /tmp/slimert9DD9 ; in: DEFUN BAR ; (PRINT N) ; ==> ; N ; ; note: deleting unreachable code ; ; compilation unit finished ; printed 1 note

まとめ

SBCLを始めとするPythonコンパイラ系では、型情報の伝搬や、不達コードの検出等の機能はそこそこありますが、一度どの程度のことをやってくれるのかまとめてみたいところです。

関連


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus