I got curious about how I might generate better code for JSS.
The JSS reader macro generates a lambda, so the typical pattern one sees is:
(#"matches" 'integer ".*a" ) -> ((LAMBDA (#:|#"matches"-first| &REST #:|#"matches"-rest|) (JSS:INVOKE-RESTARGS "matches" #:|#"matches"-first| #:|#"matches"-rest| NIL)) 'INTEGER ".*a")
(yes I know this will generate an error at runtime)
Right now the compiled code for one such call goes through 3 functions:
(JSTATIC "matches" #<java class java.lang.Integer {3BBD30F}> ".*a") (APPLY #<JSTATIC {41D16045}> "matches" #<java class java.lang.Integer {3BBD30F}> (".*a")) (INVOKE-RESTARGS "matches" INTEGER (".*a") NIL)
Really we only need: (JSTATIC "matches" #<java class java.lang.Integer {3BBD30F}> ".*a") and we know enough at compile time to generate that form.
If only I could figure out where the right hook would be.
The only place I could figure to do this is in precompile-function-call [1]
With a hook in place, i define the hook as [2]
After checking if the function call is one of the JSS ones, the hook transforms
((lambda(a b ) (jss::invoke-restargs-experimental method a b raw?) c d) to (jss::invoke-restargs-experimental method c '(d) raw? t)
jss::invoke-restargs-experimental is macro that does the transformation I want. [3]
The question is: Is there a more elegant way to do this, or a hook already built that I could use instead of redefining precompile-function-call
If not, would it be reasonable to add a hook in the ABCL source so I don't need to patch it to do the optimization.
Thanks, Alan
[1]
(defun precompile-function-call (form) (let ((op (car form))) (when (and (consp op) (eq (%car op) 'LAMBDA)) (return-from precompile-function-call
I added this line
--- (or (jss-fix-precompile op (mapcar #'precompile1 (cdr form))) --- (cons (precompile-lambda op) (mapcar #'precompile1 (cdr form)))))) (when (or (not *in-jvm-compile*) (notinline-p op)) (return-from precompile-function-call (precompile-cons form))) (when (source-transform op) (let ((new-form (expand-source-transform form))) (when (neq new-form form) (return-from precompile-function-call (precompile1 new-form))))) (when *enable-inline-expansion* (let ((expansion (inline-expansion op))) (when expansion (let ((explain *explain*)) (when (and explain (memq :calls explain)) (format t "; inlining call to ~S~%" op))) (return-from precompile-function-call (precompile1 (expand-inline form expansion)))))) (cons op (mapcar #'precompile1 (cdr form)))))
[2]
(defun jss-fix-precompile (op args) "Check if this is one of mine, and do the rewrite, otherwise pass" (ignore-errors (let ((body (cddr op))) (if (and (= (length body) 1) (consp (car body)) (eq (caar body) 'jss::invoke-restargs-experimental)) (precompile-function-call `(jss::invoke-restargs-experimental ,(second (car body)) ,(car args) ,(cdr args) ,(fifth (car body)) t)) nil))))
[3]
(defmacro invoke-restargs-experimental (&whole form method object args &optional (raw? nil) (precompile nil)) "If I'm precompiling then I can do the transformation. If not I revert to the original method" (if precompile (if (and (consp object) (eq (car object) 'quote)) (let ((object (eval object))) (let* ((object-as-class-name (if (symbolp object) (maybe-resolve-class-against-imports object) )) (object-as-class (if object-as-class-name (find-java-class object-as-class-name)))) (cl-user::print-db object object-as-class-name object-as-class) (if raw? `(jstatic-raw ,method ,object-as-class ,@args) `(jstatic ,method ,object-as-class ,@args)))) (if raw? `(if (symbolp ,object) (jstatic-raw ,method (find-java-class ,object) ,@args) (jcall-raw ,method ,object ,@args)) `(if (symbolp ,object) (jstatic ,method (find-java-class ,object) ,@args) (jcall ,method ,object ,@args)))) `(invoke-restargs ,method ,object ,args ,raw?)))