-
Notifications
You must be signed in to change notification settings - Fork 0
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
implement single return invariant (diamond shaped procedures) #178
Conversation
37e4c14
to
b19358c
Compare
make the presence of begin and end block invariant in procedure
b19358c
to
58a7f90
Compare
src/main/scala/ir/IR_Dsl.scala
Outdated
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.
You've duplicated this since it was moved
4153965
to
9ca9d90
Compare
src/main/scala/ir/IRCursor.scala
Outdated
// TODO: this method doesn't require aftercall blocks only have 1 incoming jump | ||
def isAfterCall : Boolean = p.incomingJumps.nonEmpty && p.incomingJumps.forall(_.isAfterCall) | ||
|
||
def begin: CFGPosition = p | ||
def end: CFGPosition = p.jump | ||
|
||
extension (p: Command) | ||
def isReturn: Boolean = p.parent.parent.returnBlock.jump eq p |
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.
shouldn't this just be replaced entirely since we can match on Return now
src/test/scala/SystemTests.scala
Outdated
case (false , false, true) => "Test passed" | ||
case (_, _, false) => "Prover error: unknown result: " + boogieResult | ||
case (false, false, true) => "Test passed" | ||
case (_, _, false) => "Prover error: unknown result" |
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 change this, this was useful
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.
This was probably unintentionally removed in a merge
What's the point of mandating empty entry blocks? Doesn't this just make Procedure completely redundant as a node in the CFG? It doesn't really make sense to have both? |
Rather than requiring it has no statements I think its more important to require it has no incoming (intraprocedural) jumps and only one outgoing jump.
|
Looking at removing Procedure from the CFG sounds like a good idea to me |
# Conflicts: # src/main/scala/analysis/SSAForm.scala # src/main/scala/ir/Program.scala # src/main/scala/ir/Statement.scala
val procs = program.procs | ||
assert(liveVarAnalysisResults(procs("main")) == Map(R30 -> TwoElementTop)) | ||
assert(liveVarAnalysisResults(procs("callee1")) == Map(R0 -> TwoElementTop, R30 -> TwoElementTop)) | ||
assert(liveVarAnalysisResults(procs("callee2")) == Map(R1 -> TwoElementTop, R30 -> TwoElementTop)) | ||
// assert(liveVarAnalysisResults(procs("main")) == Map(R30 -> TwoElementTop)) | ||
assert(liveVarAnalysisResults(procs("callee1")) == Map(R0 -> TwoElementTop)) | ||
assert(liveVarAnalysisResults(procs("callee2")) == Map(R1 -> TwoElementTop)) | ||
} |
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.
Do we want to remove R30 from these? It makes sense to still consider it 'live' since it is called in a Return?
We probably need to figure out more broadly how this should relate to non-returning procedures, and how they should be handled in the analyses (which generally do not account for their existence). |
I think to address this we might add a "Halt" statement, and just have an analysis determine for each procedure if all paths reach a halt statement or non-returning call. I don't think it necessarily interacts with this PR, because we don't want a single halt per-procedure in the same way we do returns for backwards analysis. Otherwise, I think this PR is overcomplicated. I like having a fixed entry and return block, but the automatic rewriting of Returns into jumps to the exit block is unneccessary. It would make more sense to just have an analysis find all the returns and rewrite the procedure into a diamond shape, and have an invariant checker make sure this stays the case through subsequent transforms. |
Is the idea that the 'halt' statement would follow a non-returning call? This could just be an 'assume false' then, but it could be made its own special type for ease of matching. I agree that automatically rewriting returns like this is more complicated than necessary, and what you now suggest sounds like a better approach. |
I think that creating a new Return class is a good idea though. It's worth making a simpler PR that just does that part of it. |
Adds return statement and ensures a procedure always has exactly one return statement (which may not always be reachable).
To simplify this we also make the entryBlock and returnBlock of a procedure always defined. The returnBlock must contain a return, and the entryBlock is assumed to have no blocks that jump to it.
internalBlocks
containing the mutable block listProcedure.blocks
returns entryBlock ++ internalBlocks ++ returnBlock.Procedure.hasImplementation
method to define whether the procedure is a stub.addBlock
andremoveBlock
methods on procedure ensure that the only return statement in the procedure is the one in the return block by: