[hls-fuzzer] Add concept of weak dependencies#969
Conversation
In some circumstances, such as in the limit type system, the transfer functions of an AST node might not care about the specific order of how elements are generated, but rather just *if* a sub element has been generated previously. This is e.g. the case in the limit type system where the total number of statements does not care about whether the sub-statementlist or its statement is generated first, only which has been generated first. This PR therefore adds the new concept of a "weak" dependency to transfer functions which are merely used to affect the transfer function signature: A possibly null pointer and an optional AST node are passed to the transfer function iff that sub-element has been generated already.
63479a4 to
4c8855e
Compare
|
I can't think of why a nullptr or nullopt AST node would be passed to the input of the transferFn; conceptually, how does the topological sort work on these weak edges? |
|
The topological sort simply ignores weak edges entirely. That means it might be null or null opt because the given sub element hasn't been generated yet. The advantage is that you can have cyclic dependencies using weak edges. This way the limit type system does not force an order on statement generation, but still counts the number of edges correctly |
So it is not an issue for depth but for the total statement count Maybe if there are no other major use cases for weak deps, we could make the total statements a reference to a global value (it is a global property of the AST instead of a locally derived one) |
The way it is used right now does have strictly more information and that it gives the total number of statements generated so far at all times. There are also other use cases where this would not work. Eg the dynamatic type system also currently forces an order in which binary expressions are generated when it has actually no need to (just to avoid cycles). I would avoid globals when possible, best to always keep things local when possible |
Ok, I see the point Could we introduce the concept of "the last generated AST node + its context?" BTW "last" could also be the parent AST node + its context We can specify that a TransferFn requires such an argument if we need to have some accumulated history (e.g., the number of edges that we need here), but not necessary from a specific node. This looks also useful for the bitwidth thingie |
I can see that being useful yeah. I think what you'd then probably want though is that it shouldn't actually be "the last generated sub-element" but "the last generated node from a set of sub-elements that we care about". Technically speaking, what While something like an accumulated history is doable, I'd probably require much more effort and I am not sure its necessary for our use-cases. |
|
okay i see For the counting statement use case, I guess it is possible that we count the statement before the subelem has been generated? How do we deal with this case? |
|
The increment only happens directly in the transfer functions of the respective statements and is then propagated back up through the output context later. Example trace for the last case:
|
|
Oh I just realized that Are the users supposed to mark weak edges directly or only use the convenient functions like |
|
If those weak edges are only used internally, maybe it is better to put them in the |
Users are meant to use this directly if they need to yes. I don't think |
|
I feel like it is better to limit a bit how the user could use this feature (just for safety). For instance, let's say the user specify a |
I made it such that |
| /// passed instead. This is the big difference to normal dependencies: They do | ||
| /// not force an AST-node to have been generated previously (i.e., do not | ||
| /// participate in the topological sort performed by the generator). This makes | ||
| /// it legal to have cycles involving weak dependencies. |
There was a problem hiding this comment.
The last remark here could be a new paragraph.
Maybe also add:
In this case, the transferFn with weak dep at its input will receive an empty context and nullptr for that dependency, and it must ignore that input and/or fall back to analyzing other output contexts or the input context
There was a problem hiding this comment.
I've reformatted the paragraph but refrained from adding that sentence since I don't want to prescribe what users can or cannot do. The fact that its an optional and pointer that are empty and nullptr already signals that they must ignore it/cannot do anything with these values.
Especially since this is a stark contrast to the const-references that are passed for non-weak dependencies.
In some circumstances, such as in the limit type system, the transfer functions of an AST node might not care about the specific order of how elements are generated, but rather just if a sub element has been generated previously. This is e.g. the case in the limit type system where the total number of statements does not care about whether the sub-statementlist or its statement is generated first, only which has been generated first.
This PR therefore adds the new concept of a "weak" dependency to transfer functions which are merely used to affect the transfer function signature: A possibly null pointer and an optional AST node are passed to the transfer function iff that sub-element has been generated already.