-
Notifications
You must be signed in to change notification settings - Fork 30
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
Backwards reaching definitions #726
Backwards reaching definitions #726
Conversation
|
Yes, it should have the capabilities of both implementations and be more efficient. |
Also, why do you use |
@hernanponcedeleon Do you remember on which benchmark |
Because the |
By the way, using the analysis has the side effect, that final register encodings are omitted, if the register is not used by the spec. This could be a small issue fixed. |
I'm not sure I understand. It seems you could just use |
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
I don't really remember which benchmark that was. But I can try this in some of our larger ones. |
Co-authored-by: Hernan Ponce de Leon <[email protected]>
Which part of our pipelines are (positively) affected by this? E.g., does the alias analysis potentially becomes more precise? Do we get smaller mayset in idd? |
I think this statement is not about precision but just about memory consumption. The forward-directed implementation kept information about (last) register writes even if those registers were never going to get used again. |
Compared to Since
|
If the analysis work fine, I don't think we need to keep around the unused options. |
…ackwards-reaching-definitions
Keeping two implementations of the analysis might be good: if we encounter any example where one is too slow, we can try another (similar to what happened with the alias analysis and However, keeping options for each single pass making use of the analysis seems too fine-grained without much clear benefit. I would rather always use forward or always us backwards. |
/** | ||
* Collects all direct usage relationships between {@link RegWriter} and {@link RegReader}. | ||
* <p> | ||
* In contrast to a usual Reaching-Definitions-Analysis, | ||
* this implementation analyzes the program from back to front, | ||
* assigning each program point the set of readers, | ||
* who may still require a register initialization. | ||
* This means that it does not collect definitions for unused registers. | ||
* Especially, it does not collect last writers for all registers. | ||
* <p> | ||
* This analysis is control-flow-sensitive; | ||
* that is, {@link Label} splits the information among the jumps to it | ||
* and {@link CondJump} merges the information. | ||
* <p> | ||
* This analysis supports loops; | ||
* that is, backward jumps cause re-evaluation of the loop body until convergence. | ||
* This results in a squared worst-case time complexity in terms of events being processed. | ||
*/ |
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.
Can you somehow format the doc? It hurst my eyes that widths are so uneven XD
public Writers getWriters(RegReader reader) { | ||
Preconditions.checkNotNull(reader, "reader is null"); | ||
final ReaderInfo result = readerMap.get(reader); | ||
Preconditions.checkArgument(result != null, "reader %s has not been analyzed.", reader); |
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.
probably it should be checkState
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.
Valid, if reader.getFunction()
had been analyzed by this
. I considered more likely, that a user would try to query events of another function or program.
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Show resolved
Hide resolved
} | ||
|
||
/** | ||
* Analyzes an entire set of threads. |
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.
"an entire set of threads." -> "the entire program (after thread creation)" ... you are not passing any threads as parameter
for (Event event : function.getEvents()) { | ||
if (event instanceof RegWriter writer) { | ||
writerMap.put(writer, new Readers()); | ||
} | ||
} | ||
writerMap.put(null, new Readers()); | ||
for (Event event : function.getEvents()) { | ||
if (event instanceof RegReader reader) { | ||
final Set<Register> usedRegisters = new HashSet<>(); | ||
for (Register.Read read : reader.getRegisterReads()) { | ||
usedRegisters.add(read.register()); | ||
} | ||
readerMap.put(reader, new ReaderInfo(usedRegisters)); | ||
} | ||
} | ||
readerMap.put(null, new ReaderInfo(finalRegisters)); |
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.
Why not process everything in the same loop iteration? I don't see any dependency from the second loop tot eh first one.
Also, what are these X.put(null, ...)
representing?
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 null
entry is likely for reads from uninitialized registers.
I agree about the loop: either you merge them or you keep them and iterate over getEvents(RegWriter.class)
resp. getEvents(RegReader.class)
to skip the instanceof
checks.
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
null
entry is likely for reads from uninitialized registers.
Whatever this is used for, it should be documented, I don't want someone reading the code to have to guess
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.
Yep, since there are no dependencies, both versions are valid. I consider my version better in terms of cache optimization, but not by much.
In writerMap
, null
stands for the initial writer(s). In readerMap
, it denotes the final reader(s). Both cases don't have actual Event
instances. In this analysis, they behave as if they had. For readability, I added named null constants.
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Show resolved
Hide resolved
dartagnan/src/main/java/com/dat3m/dartagnan/verification/solving/ModelChecker.java
Outdated
Show resolved
Hide resolved
dartagnan/src/test/java/com/dat3m/dartagnan/miscellaneous/AnalysisTest.java
Outdated
Show resolved
Hide resolved
analysis.run(function, finalRegisters); | ||
} | ||
analysis.postProcess(); | ||
if (exec != null && program.isUnrolled()) { |
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 analysis can be run during processing where ExecutionAnalysis
is not available, but it can also be run afterwards. In the former case it should subsume UseDefAnalysis
and in the latter case Dependency
.
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Show resolved
Hide resolved
for (Event event : function.getEvents()) { | ||
if (event instanceof RegWriter writer) { | ||
writerMap.put(writer, new Readers()); | ||
} | ||
} | ||
writerMap.put(null, new Readers()); | ||
for (Event event : function.getEvents()) { | ||
if (event instanceof RegReader reader) { | ||
final Set<Register> usedRegisters = new HashSet<>(); | ||
for (Register.Read read : reader.getRegisterReads()) { | ||
usedRegisters.add(read.register()); | ||
} | ||
readerMap.put(reader, new ReaderInfo(usedRegisters)); | ||
} | ||
} | ||
readerMap.put(null, new ReaderInfo(finalRegisters)); |
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 null
entry is likely for reads from uninitialized registers.
I agree about the loop: either you merge them or you keep them and iterate over getEvents(RegWriter.class)
resp. getEvents(RegReader.class)
to skip the instanceof
checks.
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Show resolved
Hide resolved
...src/main/java/com/dat3m/dartagnan/program/analysis/BackwardsReachingDefinitionsAnalysis.java
Outdated
Show resolved
Hide resolved
Anything that needs to be done here? I have not checked the algorithmic details, but given that all tests pass it is likely working as intended. |
Unless I missed something, not all comments were fixed. From the top of my head:
|
479e85e
to
e04d77d
Compare
…ackwards-reaching-definitions # Conflicts: # dartagnan/src/main/java/com/dat3m/dartagnan/Dartagnan.java # dartagnan/src/main/java/com/dat3m/dartagnan/configuration/OptionNames.java # dartagnan/src/main/java/com/dat3m/dartagnan/encoding/ProgramEncoder.java # dartagnan/src/main/java/com/dat3m/dartagnan/utils/options/BaseOptions.java # dartagnan/src/main/java/com/dat3m/dartagnan/verification/solving/ModelChecker.java # dartagnan/src/main/java/com/dat3m/dartagnan/verification/solving/RefinementSolver.java # dartagnan/src/test/java/com/dat3m/dartagnan/miscellaneous/AnalysisTest.java # dartagnan/src/test/java/com/dat3m/dartagnan/utils/rules/Providers.java
… into backwards-reaching-definitions
Remove NaiveLoopBoundAnnotation.newInstance() Add NaiveLoopBoundAnnotation.fromConfig(Configuration) Add ReachingDefinitionsAnalysis.configure(Configuration) Set BackwardsReachingDefinitionsAnalysis package-private
I think all my comments have been solved. Unless @ThomasHaas has some more comments, I'll merge |
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.
LGTM
This PR decouples an interface for
Dependency
andUseDefAnalysis
, and adds an alternative implementation for it,BackwardsReachingDefinitionsAnalysis
(BRDA). It takes into account, that readers on uninitialized registers are unlikely, while final writers are frequent, although rarely useful. This means, that the propagated data is smaller in BRDA, than in the existing forward-directed implementations.The more noteworthy feature is, that I wrote it to support loops. This means that it, as well as the dependent alias analysis could potentially be done before unrolling.