Skip to content

C++: Global value numbering for function calls - #9892

Open
rdmarsh2 wants to merge 21 commits into
mainfrom
rdmarsh2/cpp/gvn-functions
Open

C++: Global value numbering for function calls#9892
rdmarsh2 wants to merge 21 commits into
mainfrom
rdmarsh2/cpp/gvn-functions

Conversation

@rdmarsh2

@rdmarsh2 rdmarsh2 commented Jul 25, 2022

Copy link
Copy Markdown
Contributor

This PR adds global value numbering for function calls, taking into account side effects. It also eliminates global side effects for function calls when the function does not use or modify non-local state other than what's immediately pointed to by its parameters.

@github-actions github-actions Bot added the C++ label Jul 25, 2022
@rdmarsh2
rdmarsh2 requested a review from MathiasVP July 26, 2022 16:36
Robert Marsh added 2 commits July 29, 2022 10:38
This leverages the existing alias analysis to identify functions which
have no reads or writes of the AllAliasedMemory virtual variable, and
therefore have no global side effects. A recursion over the call graph
identifies functions which have no indirect global side effects, and
calls to those functions have their global side effect instructions
removed.
@github-actions github-actions Bot added the C# label Jul 29, 2022

@MathiasVP MathiasVP left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The changes to .expected files LGTM! I've left a couple of comments regarding how many calls we really want to GVN.

I'm eagerly waiting for the missing SideEffectElimination file. Once that has been checked in we should definitely do a DCA run on this. If there's a sign of an unacceptable slowdown we could consider GVN'ing fewer calls like I've suggested in a couple of comments.

)
}

predicate callValueNumber(CallInstruction call, int index, TCallPartialValueNumber vn) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we maybe exclude CallInstructions that has a void return type? That's a slightly less drastic change, and it'll suffice for our vector size use-case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, I think all of the new definitions can be marked as private, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we can

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually the place to do this is in CallPartialValueNumber

Comment on lines +95 to +108
exists(CallSideEffectInstruction cse |
cse.getPrimaryInstruction() = call and
cse.getSideEffectOperand().getAnyDef() = instr and
argIndex = -2 and
isEffect = false
)
or
exists(CallReadSideEffectInstruction cse |
cse.getPrimaryInstruction() = call and
cse.getSideEffectOperand().getAnyDef() = instr and
argIndex = -2 and
isEffect = false
)
or

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It probably doesn't matter a lot, but shouldn't both of these be isEffect = true?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Alternatively, we could choose to only GVN functions that are side-effect free (which I think would cover std::vector's size function, right?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'd prefer to GVN all functions if we can, and eventually GVN the memory side effects as well (but that's work for another PR)

import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSsa
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SideEffectElimination as Elim

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think you've checked SideEffectElimination.qll in yet. That explains all the consistency errors in ql-for-ql.

}

predicate callPartialValueNumber(CallInstruction call, int index, TCallPartialValueNumber head) {
index = 1 and head = TNilArgument()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would really prefer this to be index = 0. Is there a reason it has to be index = 1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

... Apparently I got my recursion wrong here - this is actually skipping the first item in the rank.

call.getStaticCallTarget() = callee and
call.getEnclosingFunction() = func
|
noTransitiveSideEffectWrite(callee)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm always scared of recursion through forall as it's very easy to get a quadratic blowup. There's a general performance trick to this described in this internal issue: https://github.com/github/codeql-core/issues/416. We might want to apply that here.

@rdmarsh2
rdmarsh2 marked this pull request as ready for review August 1, 2022 17:03
@rdmarsh2
rdmarsh2 requested review from a team as code owners August 1, 2022 17:03
@MathiasVP

Copy link
Copy Markdown
Contributor

Small update on this PR. There's a very large performance regression. I would have guessed that it was due to the recursion through forall, but it looks like callPartialValueNumber is to blame. There may just be too much non-linear recursion.

Comment thread csharp/ql/src/experimental/ir/interfaces/SideEffect.qll Fixed
@MathiasVP
MathiasVP force-pushed the rdmarsh2/cpp/gvn-functions branch from eafdc28 to 09d6ea2 Compare August 26, 2022 08:23
@MathiasVP
MathiasVP force-pushed the rdmarsh2/cpp/gvn-functions branch from 09d6ea2 to 9ddacf5 Compare August 29, 2022 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants