-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 value kinds on blocks #10716
base: trunk
Are you sure you want to change the base?
Add value kinds on blocks #10716
Conversation
9efe700
to
491fb39
Compare
For instance type t = { foo : t } [@@unboxed] let f { foo } bar = ()
23af545
to
9e66b7b
Compare
7d3bf5d
to
c713b20
Compare
No branch are cut by it that wouldn't be by the fuel limit
dfb181d
to
50979e2
Compare
Note: the scary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code in Typeopt
looks fine to me.
It could of course do more, but at least what it infers seems correct.
On a side note, I am bit surprised that scrape_ty
calls correct_levels
for every expansion. This should not be strictly needed (type definitions in the environment are supposed to only contain types at generic level), but maybe it's better to stay on the safe side.
I'm disheartened by the addition of fuel in yet another place. Fuel in general marks that we gave up on understanding the termination properties of our algorithms, and I suspect that having fuel in more places is a form of technical debt that we are building up. Some remarks on this. Doing without fuel?In #10479 we propose to rewrite Factoring the code?There are many analyses in the compiler that work on the type expressions -- they all require termination control due to cyclic and/or recursive types, and that logic is subtle. We could think of factoring the termination-control as a reusable service, but could we also factor the analysis code? This would also be useful for our work-in-progress on constructor unboxing, where we compute the "shape" of datatypes in a way that seems related to the present analysis (we approximate blocks by their tags, with no information on the leaves except eventually the arity, but we have a more fine-grained approximation of unions, keeping a set of tags). Is there a mother-analysis that could be implemented once, and then partially-erased to derive simpler approximations? Explaining the codeWhat's above are what-ifs for the medium term. In the short term, if we are to have another kind of fuel in the compiler codebase, could you please explain in comments:
Different kinds of fuelThere seems to be two different usage modes of things called "fuel" in the compiler:
(In both cases, tuning of the fuel limit is important for performance, even though the limits are very different.) I think that we should be careful to distinguish those, and keep track of which is which. Ideally we would even use different names. For example we could use I'm not sure that I understand, from quickly looking at the code, which of these two sorts of fuel is being added here. It may be that the fuel is doing both things at once. (The combinatorial-explosion fuel can always be used as termination-control fuel due to its low limit, but the converse is not true.) It might be enlightening to actually distinguish two kinds of fuel in the same process, to clarify the situation. |
The |
Out of curiosity: why support If we were serious about the property that we only care about "head" shapes, this could be made clear in the types ( |
If it is possible for other analyses, I'm all for it. We don't need something much more complex than that for a first version, but if is there is something to expose the shape of all kinds of value, we can use that. As to which fuel/depth value to use as @lthls said, it is a trade-off between cost and benefit. We don't expect many situations where more than a depth 2 would be useful, so it might not be worth doubling the cost for those rare cases. While I did notice some normal examples where 2 was required. So this is very heuristic. If the representation was shared in memory between multiple annotations, it would be ok to build something really deeper. Otherwise, for most situations depth 1 would be sufficient with also an annotation on the getfield, but that would not be always sufficient. The general type traversal is not going to be doable soon, so this is not an option right now. What would you suggest changing on that code for it to be ok ? |
I suggested doing the "Explaining the code" part for now. (Note: I won't be able to do an actual review on the patchset before a few weeks, so please don't count on me for the approval.) |
This PR adds one more case on the
Lambda.value_kind
type: Blocks, and populates it when the type of a variable is obviously a block.This is not used in the compiler, but is useful for flambda2. In particular it helps in some complex unboxing situations.
Since types could be quite large and to avoid problems with recursive types that wouldn't be detected by the crude check, there is a fuel counter to prevent producing deep value_kind. In practice there isn't too much information loss by keeping that limit low as most of the time when accessing deeply nested structure, there would be multiple points with type annotations, allowing to recover the full type where it is needed.
I'm not completely sure that the
Transient_expr
thing is correct or that it is the way it is supposed to be used.