Skip to content
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

C++: Parameterize the semantic range analysis #11728

Merged
merged 18 commits into from Jan 18, 2023

Conversation

rdmarsh2
Copy link
Contributor

@rdmarsh2 rdmarsh2 commented Dec 16, 2022

This PR makes the semantic range analysis into a parameterized module, with parameters for the type of deltas, the Bound type, the language-specific predicates in RangeAnalysisSpecific.qll, and the utility predicates in RangeUtil.qll. ModulusAnalysis and SignAnalysis are also parameterized, but are instantiated by the main range analysis module. The range analysis is not yet parameterized over the remainder of the Semantic module. This change should be behavior-preserving - the parameterized module is in the new RangeAnalysisStage.qll file, with an instantiation in RangeAnalysis.qll

@github-actions github-actions bot added the C++ label Dec 16, 2022
@MathiasVP
Copy link
Contributor

This looks great. Looking forward to seeing this PR pulled out of draft!

@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from 3774db7 to 452d438 Compare Dec 19, 2022
@rdmarsh2 rdmarsh2 marked this pull request as ready for review Dec 19, 2022
@rdmarsh2 rdmarsh2 requested a review from a team as a code owner Dec 19, 2022
@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from d2d62ad to 35ecf55 Compare Dec 19, 2022
Copy link
Contributor

@MathiasVP MathiasVP left a comment

Choose a reason for hiding this comment

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

This looks great! If DCA is happy (make sure to run the memory-corruption query pack) then I think this should be pretty safe to merge

SemReason reason
) {
not Specific::ignoreExprBound(e) and
//not Specific::mayOverflow(e, upper.booleanNot()) and
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm guessing this is a subtle hint to the next PR? 😄

predicate mayOverflow(SemExpr e, boolean positively) {
exists(IR::Instruction i |
i = e and
(
positively = true and
Simple::exprMightOverflowPositively(i.getConvertedResultExpression())
or
positively = false and
Simple::exprMightOverflowNegatively(i.getConvertedResultExpression())
)
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you intent to commit this?

@@ -3,86 +3,105 @@
*/

private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisStage
private import semmle.code.cpp.ir.IR as IR
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis as Simple
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you intent to import SimpleRangeAnalysis here?

private module SignAnalysisInstantiated = SignAnalysis<D, UtilParam>; // TODO: will this cause reevaluation if it's instantiated with the same DeltaSig and UtilParam multiple times?

private import SignAnalysisInstantiated

private module ModulusAnalysisInstantiated = ModulusAnalysis<D, UtilParam>; // TODO: will this cause reevaluation if it's instantiated with the same DeltaSig and UtilParam multiple times?
Copy link
Contributor

Choose a reason for hiding this comment

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

A note for anyone else reading this: We discussed in our 1:1 that we believe this should not cause re-evaluation because modules are applicative. But we should still make sure that we don't get re-evaluation due to different optimisation decisions.

@@ -157,7 +186,7 @@ module RangeStage<RangeSig RangeParam> {
*/
cached
predicate possibleReason(SemGuard guard) {
guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _)
guard = boundFlowCond(_, _, _, _, _) or guard = UtilParam::semEqFlowCond(_, _, _, _, _)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we privately import UtilParam inside the RangeStage module to avoid prefixing with UtilParam? (And similarly for the other module parameters.)

Copy link
Contributor

@jketema jketema Jan 4, 2023

Choose a reason for hiding this comment

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

What was the conclusion here? Is this possible, or not? Or is this not something we want?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's done now - see this commit

Copy link
Contributor

Choose a reason for hiding this comment

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

Not in ModulusAnalysis, where I'd also expect it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this again. I'm somewhat split on this. On the one hand, having the imports makes the code more compact. On the other hand, without the imports it's clearer where things are coming from. So I'm fine with leaving things as-is in the modulus analysis.

@@ -146,7 +146,7 @@ module ModulusAnalysis<DeltaSig D, UtilSig<D> U> {
private predicate phiSelfModulus(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
) {
exists(SemSsaBound phibound, int v, int m |
exists(Bounds::SemSsaBound phibound, int v, int m |
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, it would be awesome if we could just import Bounds to avoid all these prefixes 🙏.

@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from a985ccf to a7e5e55 Compare Jan 3, 2023
@rdmarsh2
Copy link
Contributor Author

rdmarsh2 commented Jan 3, 2023

Rebased away the overflow-related changes. I'll import the module params as well.

Copy link

@github-code-scanning github-code-scanning bot left a comment

Choose a reason for hiding this comment

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

CodeQL found more than 10 potential problems in the proposed changes. Check the Files changed tab for more details.

@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from be5ff77 to 359b428 Compare Jan 3, 2023
class UseSignExpr extends FlowSignExpr {
SemSsaVariable v;
/** A binary expression whose sign is computed from the signs of its operands. */
private class BinarySignExpr extends FlowSignExpr {

Check warning

Code scanning / CodeQL

Suggest using non-extending subtype relationships.

Consider defining this class as non-extending subtype of [SemBinaryExpr](1).
private class UnarySignExpr extends FlowSignExpr {
SemUnaryExpr unary;
/** A unary expression whose sign is computed from the sign of its operand. */
private class UnarySignExpr extends FlowSignExpr {

Check warning

Code scanning / CodeQL

Suggest using non-extending subtype relationships.

Consider defining this class as non-extending subtype of [SemUnaryExpr](1).
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
* the sign of its operand and the source and destination types.
*/
abstract private class CastSignExpr extends FlowSignExpr {

Check warning

Code scanning / CodeQL

Suggest using non-extending subtype relationships.

Consider defining this class as non-extending subtype of [SemUnaryExpr](1).
not Lang::ignoreSsaReadArithmeticExpr(result)
)
or
exists(SemSubExpr sub, float d1, SemConstantIntegerExpr c |
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this suddenly float and not D::Delta?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It needs to be either float or int so that we can do the addition on line 29

Copy link
Contributor

Choose a reason for hiding this comment

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

So when should I expect something from DeltaSig and when should I expect and int or a float? And, what determines the choice between int and float?

The answers to both these questions should probably be documented as part of the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Discussed in our sync: prefer delta, but unwrap when needing to do an arithmetic operation. Prefer unwrapping to float, except in the modulus analysis where we unwrap to int (so we can do the modulus analysis).

The answers to both these questions should probably be documented as part of the code.

I don't think this is necessary, but a qldoc string on DeltaSig is still appreciated.

result = sub and
sub.getLeftOperand() = semSsaRead(v, D::fromFloat(d1)) and
sub.getRightOperand() = c and
delta = D::fromFloat(d1 + c.getIntValue()) and
Copy link
Contributor

Choose a reason for hiding this comment

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

Discussed in sync: I was somewhat confused here, as the previous case is more "delta"-heavy. To match that I would expect the following here:

delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and

(with the declaration of d1 changed accordingly).

Copy link
Contributor

@jketema jketema left a comment

Choose a reason for hiding this comment

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

One final question below, otherwise this looks like a pretty straightforward parameterization.

QLDoc checks are currently failing, so that probably still needs to be resolved.

I'm not sure I'm looking at the correct DCA experiment, but the last one seems to show a quite significant slowdown.

@@ -0,0 +1,25 @@
import cpp
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing from this file is being used elsewhere yet. Do I see that correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, didn't mean to include that here.

Copy link
Contributor

@MathiasVP MathiasVP left a comment

Choose a reason for hiding this comment

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

A couple of comments. Otherwise this LGTM! We should also do another DCA run to make sure that the crazy performance regressions are gone.

rdmarsh2 added 10 commits Jan 12, 2023
This makes the modulus analysis and sign analysis into parameterized
modules which are instantiated in the main range analysis module, and
makes RangeAnalysisSpecific and RangeUtils into parameters to the main
range analysis.
Some classes also need to be moved and made into `instanceof` extensions
because they'd otherwise be extending across parameterized module
boundaries.
@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from c93c804 to 337a747 Compare Jan 12, 2023
@MathiasVP
Copy link
Contributor

@rdmarsh2 I tried to fix the merge conflict so that I could start another DCA run on this, but it's not quite trivial enough for me to solve it. Do you mind taking a look at it?

@@ -3,86 +3,91 @@
*/

private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisStage
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
private import experimental.semmle.code.cpp.semantic.analysis.IntDelta

Check warning

Code scanning / CodeQL

QL-for-QL encountered an internal consistency error

TypeConsistency::noResolve
@@ -3,86 +3,91 @@
*/

private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisStage
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
private import experimental.semmle.code.cpp.semantic.analysis.IntDelta

Check warning

Code scanning / CodeQL

QL-for-QL encountered an internal consistency error

TypeConsistency::noResolve ModConsistency::noResolve
* - `upper = false` : `inp >= b + delta`
*/
pragma[noinline]
private predicate boundedPhiCand(

Check warning

Code scanning / CodeQL

Candidate predicate not marked as `nomagic`

Candidate predicate to [boundedPhi](1) is not marked as nomagic.
if semNegative(x)
then upper = true and delta = D::fromInt(0)
else none()

Check warning

Code scanning / CodeQL

Use of 'if' with a 'none()' branch.

Use a conjunction instead.
if semNegative(x)
then upper = false and delta = D::fromInt(0)
else none()

Check warning

Code scanning / CodeQL

Use of 'if' with a 'none()' branch.

Use a conjunction instead.
Conflicting change to boundedPhiInp copied to RangeAnalysisStage.qll
@rdmarsh2 rdmarsh2 force-pushed the rdmarsh2/parameterize-range-analysis branch from 1969c9a to 601b43a Compare Jan 13, 2023
* - `upper = false` : `inp >= b + delta`
*/
pragma[noinline]
private predicate boundedPhiCand(

Check warning

Code scanning / CodeQL

Missing QLDoc for parameter

The QLDoc has no documentation for fromBackEdge, or origdelta, or reason, but the QLDoc mentions inp
@MathiasVP
Copy link
Contributor

DCA looks good, and the CI failure is unrelated to this PR (it would be fixed by merging in main). But I think it's time to merge this 🎉.

@MathiasVP MathiasVP merged commit e26e83b into main Jan 18, 2023
12 of 13 checks passed
@MathiasVP MathiasVP deleted the rdmarsh2/parameterize-range-analysis branch Jan 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants