mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge pull request #14867 from MathiasVP/reduce-duplication-from-operators
C++: Reduce duplication from crement operations
This commit is contained in:
4
cpp/ql/lib/change-notes/2023-11-30-as-definition.md
Normal file
4
cpp/ql/lib/change-notes/2023-11-30-as-definition.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added a new predicate `Node.asDefinition` on `DataFlow::Node`s for selecting the dataflow node corresponding to a particular definition.
|
||||
@@ -260,6 +260,71 @@ class Node extends TIRDataFlowNode {
|
||||
*/
|
||||
Expr asDefiningArgument() { result = this.asDefiningArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the definition associated with this node, if any.
|
||||
*
|
||||
* For example, consider the following example
|
||||
* ```cpp
|
||||
* int x = 42; // 1
|
||||
* x = 34; // 2
|
||||
* ++x; // 3
|
||||
* x++; // 4
|
||||
* x += 1; // 5
|
||||
* int y = x += 2; // 6
|
||||
* ```
|
||||
* - For (1) the result is `42`.
|
||||
* - For (2) the result is `x = 34`.
|
||||
* - For (3) the result is `++x`.
|
||||
* - For (4) the result is `x++`.
|
||||
* - For (5) the result is `x += 1`.
|
||||
* - For (6) there are two results:
|
||||
* - For the definition generated by `x += 2` the result is `x += 2`
|
||||
* - For the definition generated by `int y = ...` the result is
|
||||
* also `x += 2`.
|
||||
*
|
||||
* For assignments, `node.asDefinition()` and `node.asExpr()` will both exist
|
||||
* for the same dataflow node. However, for expression such as `x++` that
|
||||
* both write to `x` and read the current value of `x`, `node.asDefinition()`
|
||||
* will give the node corresponding to the value after the increment, and
|
||||
* `node.asExpr()` will give the node corresponding to the value before the
|
||||
* increment. For an example of this, consider the following:
|
||||
*
|
||||
* ```cpp
|
||||
* sink(x++);
|
||||
* ```
|
||||
* in the above program, there will not be flow from a node `n` such that
|
||||
* `n.asDefinition() instanceof IncrementOperation` to the argument of `sink`
|
||||
* since the value passed to `sink` is the value before to the increment.
|
||||
* However, there will be dataflow from a node `n` such that
|
||||
* `n.asExpr() instanceof IncrementOperation` since the result of evaluating
|
||||
* the expression `x++` is passed to `sink`.
|
||||
*/
|
||||
Expr asDefinition() {
|
||||
exists(StoreInstruction store |
|
||||
store = this.asInstruction() and
|
||||
result = asDefinitionImpl(store)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indirect definition at a given indirection corresponding to this
|
||||
* node, if any.
|
||||
*
|
||||
* See the comments on `Node.asDefinition` for examples.
|
||||
*/
|
||||
Expr asIndirectDefinition(int indirectionIndex) {
|
||||
exists(StoreInstruction store |
|
||||
this.(IndirectInstruction).hasInstructionAndIndirectionIndex(store, indirectionIndex) and
|
||||
result = asDefinitionImpl(store)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indirect definition at some indirection corresponding to this
|
||||
* node, if any.
|
||||
*/
|
||||
Expr asIndirectDefinition() { result = this.asIndirectDefinition(_) }
|
||||
|
||||
/**
|
||||
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
|
||||
*
|
||||
@@ -1142,22 +1207,6 @@ private module GetConvertedResultExpression {
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl0(Instruction instr) {
|
||||
// For an expression such as `i += 2` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedAssignOperation tao |
|
||||
result = tao.getExpr() and
|
||||
instr = tao.getInstruction(any(AssignmentStoreTag tag))
|
||||
)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` is contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedCrementOperation tco |
|
||||
result = tco.getExpr() and
|
||||
instr = tco.getInstruction(any(CrementStoreTag tag))
|
||||
)
|
||||
or
|
||||
// IR construction inserts an additional cast to a `size_t` on the extent
|
||||
// of a `new[]` expression. The resulting `ConvertInstruction` doesn't have
|
||||
// a result for `getConvertedResultExpression`. We remap this here so that
|
||||
@@ -1165,7 +1214,7 @@ private module GetConvertedResultExpression {
|
||||
// represents the extent.
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(any(AllocationExtentConvertTag tag))
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag())
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
@@ -1174,6 +1223,39 @@ private module GetConvertedResultExpression {
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
or
|
||||
// Certain expressions generate `CopyValueInstruction`s only when they
|
||||
// are needed. Examples of this include crement operations and compound
|
||||
// assignment operations. For example:
|
||||
// ```cpp
|
||||
// int x = ...
|
||||
// int y = x++;
|
||||
// ```
|
||||
// this generate IR like:
|
||||
// ```
|
||||
// r1(glval<int>) = VariableAddress[x] :
|
||||
// r2(int) = Constant[0] :
|
||||
// m3(int) = Store[x] : &:r1, r2
|
||||
// r4(glval<int>) = VariableAddress[y] :
|
||||
// r5(glval<int>) = VariableAddress[x] :
|
||||
// r6(int) = Load[x] : &:r5, m3
|
||||
// r7(int) = Constant[1] :
|
||||
// r8(int) = Add : r6, r7
|
||||
// m9(int) = Store[x] : &:r5, r8
|
||||
// r11(int) = CopyValue : r6
|
||||
// m12(int) = Store[y] : &:r4, r11
|
||||
// ```
|
||||
// When the `CopyValueInstruction` is not generated there is no instruction
|
||||
// whose `getConvertedResultExpression` maps back to the expression. When
|
||||
// such an instruction doesn't exist it means that the old value is not
|
||||
// needed, and in that case the only value that will propagate forward in
|
||||
// the program is the value that's been updated. So in those cases we just
|
||||
// use the result of `node.asDefinition()` as the result of `node.asExpr()`.
|
||||
exists(TranslatedCoreExpr tco |
|
||||
tco.getInstruction(_) = instr and
|
||||
tco.producesExprResult() and
|
||||
result = asDefinitionImpl0(instr)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl(Instruction instr) {
|
||||
@@ -1182,6 +1264,75 @@ private module GetConvertedResultExpression {
|
||||
not exists(getConvertedResultExpressionImpl0(instr)) and
|
||||
result = instr.getConvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result for `node.asDefinition()` (when `node` is the instruction
|
||||
* node that wraps `store`) in the cases where `store.getAst()` should not be
|
||||
* used to define the result of `node.asDefinition()`.
|
||||
*/
|
||||
private Expr asDefinitionImpl0(StoreInstruction store) {
|
||||
// For an expression such as `i += 2` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedAssignOperation tao |
|
||||
store = tao.getInstruction(AssignmentStoreTag()) and
|
||||
result = tao.getExpr()
|
||||
)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` is contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedCrementOperation tco |
|
||||
store = tco.getInstruction(CrementStoreTag()) and
|
||||
result = tco.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression returned by `store.getAst()` should not be
|
||||
* returned as the result of `node.asDefinition()` when `node` is the
|
||||
* instruction node that wraps `store`.
|
||||
*/
|
||||
private predicate excludeAsDefinitionResult(StoreInstruction store) {
|
||||
// Exclude the store to the temporary generated by a ternary expression.
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
store = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
store = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the result of `StoreInstruction` for
|
||||
* dataflow purposes.
|
||||
*
|
||||
* For example, consider the following example
|
||||
* ```cpp
|
||||
* int x = 42; // 1
|
||||
* x = 34; // 2
|
||||
* ++x; // 3
|
||||
* x++; // 4
|
||||
* x += 1; // 5
|
||||
* int y = x += 2; // 6
|
||||
* ```
|
||||
* For (1) the result is `42`.
|
||||
* For (2) the result is `x = 34`.
|
||||
* For (3) the result is `++x`.
|
||||
* For (4) the result is `x++`.
|
||||
* For (5) the result is `x += 1`.
|
||||
* For (6) there are two results:
|
||||
* - For the `StoreInstruction` generated by `x += 2` the result
|
||||
* is `x += 2`
|
||||
* - For the `StoreInstruction` generated by `int y = ...` the result
|
||||
* is also `x += 2`
|
||||
*/
|
||||
Expr asDefinitionImpl(StoreInstruction store) {
|
||||
not exists(asDefinitionImpl0(store)) and
|
||||
not excludeAsDefinitionResult(store) and
|
||||
result = store.getAst().(Expr).getUnconverted()
|
||||
or
|
||||
result = asDefinitionImpl0(store)
|
||||
}
|
||||
}
|
||||
|
||||
private import GetConvertedResultExpression
|
||||
|
||||
Reference in New Issue
Block a user