Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add INLINE syntax #1290

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Conversation

digikar99
Copy link
Contributor

The final PR towards adding inlining support in coalton. The earlier ones include:

This PR adds support for (inline) syntax in (i) coalton toplevel (ii) inside define-instance before methods. See the file tests/inliner-tests.lisp for a number of examples.

Particular aspects I can think of reviewing in this PR include:

  • if someone prefers the name inline to inline-p at some places
  • adding more tests / improving them

But yes, let's address any other worries or issues others spot!

Copy link
Collaborator

@macrologist macrologist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nits

@@ -421,8 +424,10 @@ requires direct constructor calls."
(alexandria:when-let*
((name (node-rator-name node))
(code (tc:lookup-code env name :no-error t))
(fun-env-entry (tc:lookup-function env name :no-error t))
(inline-p (when fun-env-entry (tc:function-env-entry-inline-p fun-env-entry)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when not needed here. If fun-env-entry is nil, the the whole when-let* form will be nil and the form bound to inline-p will never be evaluated.

(_ (and (node-abstraction-p code)
(funcall heuristic code)
(or inline-p (funcall heuristic code))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, inline-p will always be non-nil at this point if this code is evaluated at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to rewrite this condition as the following?

                  (fun-env-entry (tc:lookup-function env name :no-error t))
                  (_    (and (node-abstraction-p code)
                             (or (tc:function-env-entry-inline-p fun-env-entry)
                                 (funcall heuristic code))
                             (<= (length call-stack)
                                 max-depth)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that is a good way to rewrite it so that it will work with the when-let*.
The only thing I'm unsure of is if there ever can be a case when there is an entry in the code-environment but not in the function-environment. (I can't tell easily just from looking at the code, since the code entries and function entries are added at very different places in the code.) If so, then in order to allow heuristic inlining of functions with code env entries but not function env entries, it seems like a separate let statement might be necessary, like

                  (_    (and (node-abstraction-p code)
                             (or (alexandria:when-let (fun-env-entry (tc:lookup-function env name :no-error t))
                                   (tc:function-env-entry-inline-p fun-env-entry))
                                 (funcall heuristic code))
                             (<= (length call-stack)
                                 max-depth)

or if not, then your suggestion should work exactly as written, or even without the :no-error t.

Copy link
Contributor Author

@digikar99 digikar99 Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remove :no-error t in my suggestion, I obtain the error "Internal coalton bug: Unknown function TREE:INCREASING-ORDER" while compiling library/ord-map.lisp. So, there indeed seem to be cases with code env entries but not function env entries.

However, I note that your suggestion works even without the :no-error t. My hypothesis is there are fun-env-entries only when code is an abstraction node.

Copy link
Contributor Author

@digikar99 digikar99 Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, in those cases, code is an application node.

                  (fun-env-entry (let ((fun-env-entry (tc:lookup-function env name :no-error t)))
                                   (unless fun-env-entry (print (type-of code)))
                                   fun-env-entry))
                  (_    (and (node-abstraction-p code)
                             (or (tc:function-env-entry-inline-p fun-env-entry)
                                 (funcall heuristic code))

With the above code, I obtain COALTON-IMPL/CODEGEN/AST:NODE-APPLICATION as print statements while compiling library/ord-map.lisp and library/seq.lisp.

I'm inclined to commit your suggestion without :no-error t to better understand internal behavior. :no-error t feels like a hack here and I think it would rather be used as such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Committed in 849af1f

nil)

((coalton:define)
(let ((define (parse-define form source))
monomorphize
monomorphize-form)
monomorphize-form

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: superfluous newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a preference for the newlines under the (coalton:declare) clause below? Should I remove them?

(setf (instance-method-definition-inline-p method) inline-p))
(setq forms (cst:rest forms))
method))))

Copy link
Collaborator

@macrologist macrologist Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


:for inline-p := (and (should-inline-p forms) 
                      (prog1 (parse-inline (cst:first forms) source)
                         (setf forms (cst:rest forms))))
:for method := (parse-instance-method-definition (cst:first forms) (cst:second form) source)
:when inline-p :do (setf (instance-method-definition-inline-p method) inline-p)
:collect :method 
:do (setf forms (cst:rest forms))

nit: this seems easier to follow to me. managing the state of forms is a little confusing to follow, I think something like the above makes it clearer.

In something like the above, error signalling would be moved into parse-inline. The should-inline-p wouldn't need to exist, I just hate writing code in github comment boxes so I replaced the check with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this seems easier to follow to me. managing the state of forms is a little confusing to follow, I think something like the above makes it clearer.

Unless I'm missing something, the suggested code still manually manipulates the state of forms. But I understand, this part of the code is a bit complicated to follow.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's correct. It's just a nit. Changes are not necessary

@shirok
Copy link
Collaborator

shirok commented Oct 9, 2024

This is a general comment, probably for future work. As the optimizer gets more sophisticated, there'll be more context like monomorphize-table and inline-p-table to be passed. At some point we might want to consolidate them into optimizer context, maybe?

Comment on lines +102 to +111
"(inline)
(define (factorial-1 n)
(if (== n 0)
1
(* n (factorial-2 (- n 1)))))
(inline)
(define (factorial-2 n)
(if (== n 0)
1
(* n (factorial-1 (- n 1)))))"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what style guidelines we have for multi-line strings, but removing some indents here might make it nicer to read:

Suggested change
"(inline)
(define (factorial-1 n)
(if (== n 0)
1
(* n (factorial-2 (- n 1)))))
(inline)
(define (factorial-2 n)
(if (== n 0)
1
(* n (factorial-1 (- n 1)))))"))
"(inline)
(define (factorial-1 n)
(if (== n 0)
1
(* n (factorial-2 (- n 1)))))
(inline)
(define (factorial-2 n)
(if (== n 0)
1
(* n (factorial-1 (- n 1)))))"))

Comment on lines +68 to +82
(deftest function-inline-error ()
(signals coalton-impl/parser:parse-error
(check-coalton-types
"(inline myfun)
(define (myfun x) x)")))

(deftest method-inline-error ()
(signals coalton-impl/parser:parse-error
(check-coalton-types
"(define-class (C :a)
(m (:a -> :a -> :a)))
(define-instance (m Double-Float)
(inline m)
(define (m x y)
(lisp Double-Float (x y) (cl:+ x y))))")))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could add another test for duplicate inline attributes to match the tests for monomorphize ( https://github.com/search?q=repo%3Acoalton-lang%2Fcoalton+monomorphize+path%3A%2F%5Etests%5C%2Ftest-files%5C%2F%2F&type=code ) but other than that I think this looks pretty good and complete

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants