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

First steps hooking up to LLVM #2

Closed
wants to merge 9 commits into from
Closed

First steps hooking up to LLVM #2

wants to merge 9 commits into from

Conversation

zenhack
Copy link
Contributor

@zenhack zenhack commented Mar 22, 2022

This pulls in llvm-hs-pure as a dependency, and kicks the tires on it by doing some translation of at least types in our last-leg IR to LLVM.

I had to vendor the package to get this working; see llvm-hs/llvm-hs#389; when that is merged & released on hackage we can drop the submodule.

This is still a little bit WIP, but I fear if I wait until I've hit a clear milestone, the patch will be a large burden to review.

1. The current version isn't on hackage yet, see llvm-hs/llvm-hs#369
2. We needed to patch the version bounds to get this to build with our
   other dependencies; see llvm-hs/llvm-hs#389
This results in a bit more verbosity at the type level, but as I tried
to add a doc comment explaining what this function did, I realized it
was very semantically muddled and justifying it required too much appeal
to compressing call sites; it's more clear this way.
@zenhack
Copy link
Contributor Author

zenhack commented Mar 28, 2022

Fixed the merge conflicts.

@zenhack zenhack requested a review from AlexKnauth March 28, 2022 21:30
@zenhack
Copy link
Contributor Author

zenhack commented Mar 28, 2022

@marcinjangrzybowski, I would welcome your review as well; for some reason the "request review" thing only lets me pick one person.

@AlexKnauth
Copy link
Contributor

I don't have many comments on the code in this PR itself, I mainly have questions about the LastLeg types that this PR is translating from.

I notice that the code in this PR ignores the ftEffectful field of function types, and goes through more layers of field-name accessors than I'd like. But what I'm questioning more is the design of the types and why it's necessary to have the ftEffectful field in the types at all if it's not part of the source type system or the target type system, and why there are so many sum-type style single-field variants of ADTs that point to separate type definitions for their fields. Those would make case pattern matching look weird with nested patterns, which may have been why you choose to use field accessors instead? But I would prefer having variants of ADTs written with their fields in the first place where it makes sense.

For example: FPtrType.

data Type
  = -- | The type of a function pointer:
    TFPtr FPtrType
  | ...other variants...

data FPtrType = FPtrType
  { fptCaptures :: [Type],
    fptFuncType :: FuncType
  }

why wouldn't it be

data Type
  = -- | The type of a function pointer with closure captures:
    TFPtr [Type] FuncType
  | ...other variants...

so that the corresponding translation code could more naturally use pattern matching like

translateType ty = case ty of
  ...other cases...
  Ast.TFPtr captures fTy ->
    ...body...

and avoid the field name accessors?

@zenhack
Copy link
Contributor Author

zenhack commented Mar 29, 2022

For the specific example of FPtrType, it's factored out in part because I wanted to be able to talk about types of function pointers separately; e.g. see getFunDefType. Which I suppose could just return a Type, but then you're losing information which you'd have to recover with pattern matching...

Note that it is still possible to match this as:

Ast.TFPtr (Ast.FPtrType captures fty) -> ...

...I suppose it's a question of style whether that scans better. I don't feel super strongly; if you do I could flip it.


Wrt. the general case of "why introduce intermediate records?" I might be a little trigger happy about this, but I have often enough done things the other way and then been in the middle of building out something that used the type, and found myself wanting to pass the arguments to some ctor to a function as a unit, without having to handle the other cases.


Re: ftEffectful, we actually probably should be checking this and having a void/unit return type if it's true, since in that case the "return" value is actually going to get passed to our continuation on the next transaction (this is really the point of the distinction -- we can't keep the stack contents across transaction boundaries, so we have to CPS code that could commit and unwind the stack). Good catch.

@AlexKnauth
Copy link
Contributor

Re: ftEffectful, we actually probably should be checking this and having a void/unit return type if it's true.

Does that mean that effectful functions can't have non-unit return values, or that functions with non-unit return types must be pure?
Am I misunderstanding this?

See the comment. Thanks to Alex K for spotting this.
Comment on lines +59 to +62
-- If the function has side effects, that means it
-- doesn't return a value at the LLVM level - instead we unwind
-- the stack, commit the transaction, and our continuation gets
-- the value in the next transaction.
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment: does that mean the "effectful" distinction is actually a transaction-boundary distinction?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly. I have some scratch work where I'm cleaning up the LastLeg Ast and will probably make that more clear.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is "effectful" really the right word for this then? Plenty of effects don't need a transaction boundary, and a transaction boundary doesn't happen "after" an effect necessarily, it happens before the next participant's effect.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also I would have expected the transaction boundary distinction to be on the labels, not the function types.

@zenhack
Copy link
Contributor Author

zenhack commented Mar 29, 2022

At the LLVM level, yes, effectful functions don't actually return a value, and right now afaik all of our effectful functions (deposit, withdraw) happen to return unit anyway, but if we found a case where an effectful function could return some other value, we'd have to communicate it to the next transaction some way other than the LLVM-level return value (maybe storing next to the continuation when we checkpoint the state).

Anyway, I made the change.

@zenhack
Copy link
Contributor Author

zenhack commented Apr 6, 2022

Most of this no longer makes sense, since we're dropping the LastLeg IR (#10); all that is still relevant is just adding the llvm dependency/submodule. I don't have a strong opinion about whether we should merge that on its own; we'll probably want it at some point, but it feels a bit weird to me to have the dependency there before we're using it.

@zenhack
Copy link
Contributor Author

zenhack commented Apr 19, 2022

Going to close this, since most of the diff is irrelevant now. We can pull in LLVM as a dependency later when we need it.

@zenhack zenhack closed this Apr 19, 2022
@zenhack
Copy link
Contributor Author

zenhack commented Oct 11, 2022 via email

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.

None yet

2 participants