Skip to content

Commit ccfcd90

Browse files
authored
Merge pull request #20156 from MathiasVP/value-numbering-for-noop-casts
C++: Value numbering for casts that only modify specifiers
2 parents 56aacb1 + 65b1b7f commit ccfcd90

File tree

7 files changed

+121
-1
lines changed

7 files changed

+121
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and `semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.

cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ newtype TValueNumber =
4343
} or
4444
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
4545

46+
/**
47+
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
48+
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
49+
* and `U` is `const T` this is a conversion from a non-const integer to a
50+
* const integer.
51+
*
52+
* Generally, the value number of a converted value is different from the value
53+
* number of an unconverted value, but conversions which only modify specifiers
54+
* leave the resulting value bitwise identical to the old value.
55+
*/
56+
class TypePreservingConvertInstruction extends ConvertInstruction {
57+
TypePreservingConvertInstruction() {
58+
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
59+
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
60+
}
61+
}
62+
4663
/**
4764
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
4865
* operand.
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
216233
not instr instanceof InheritanceConversionInstruction and
217234
not instr instanceof CopyInstruction and
218235
not instr instanceof FieldAddressInstruction and
236+
not instr instanceof TypePreservingConvertInstruction and
219237
instr.getOpcode() = opcode and
220238
tvalueNumber(instr.getUnary()) = operand
221239
}
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
351369
or
352370
// The value number of a copy is just the value number of its source value.
353371
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
372+
or
373+
// The value number of a type-preserving conversion is just the value
374+
// number of the unconverted value.
375+
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
354376
)
355377
)
356378
}

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ newtype TValueNumber =
4343
} or
4444
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
4545

46+
/**
47+
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
48+
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
49+
* and `U` is `const T` this is a conversion from a non-const integer to a
50+
* const integer.
51+
*
52+
* Generally, the value number of a converted value is different from the value
53+
* number of an unconverted value, but conversions which only modify specifiers
54+
* leave the resulting value bitwise identical to the old value.
55+
*/
56+
class TypePreservingConvertInstruction extends ConvertInstruction {
57+
TypePreservingConvertInstruction() {
58+
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
59+
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
60+
}
61+
}
62+
4663
/**
4764
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
4865
* operand.
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
216233
not instr instanceof InheritanceConversionInstruction and
217234
not instr instanceof CopyInstruction and
218235
not instr instanceof FieldAddressInstruction and
236+
not instr instanceof TypePreservingConvertInstruction and
219237
instr.getOpcode() = opcode and
220238
tvalueNumber(instr.getUnary()) = operand
221239
}
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
351369
or
352370
// The value number of a copy is just the value number of its source value.
353371
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
372+
or
373+
// The value number of a type-preserving conversion is just the value
374+
// number of the unconverted value.
375+
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
354376
)
355377
)
356378
}

cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ newtype TValueNumber =
4343
} or
4444
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
4545

46+
/**
47+
* A `ConvertInstruction` which converts data of type `T` to data of type `U`
48+
* where `T` and `U` only differ in specifiers. For example, if `T` is `int`
49+
* and `U` is `const T` this is a conversion from a non-const integer to a
50+
* const integer.
51+
*
52+
* Generally, the value number of a converted value is different from the value
53+
* number of an unconverted value, but conversions which only modify specifiers
54+
* leave the resulting value bitwise identical to the old value.
55+
*/
56+
class TypePreservingConvertInstruction extends ConvertInstruction {
57+
TypePreservingConvertInstruction() {
58+
pragma[only_bind_out](this.getResultType().getUnspecifiedType()) =
59+
pragma[only_bind_out](this.getUnary().getResultType().getUnspecifiedType())
60+
}
61+
}
62+
4663
/**
4764
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
4865
* operand.
@@ -216,6 +233,7 @@ private predicate unaryValueNumber(
216233
not instr instanceof InheritanceConversionInstruction and
217234
not instr instanceof CopyInstruction and
218235
not instr instanceof FieldAddressInstruction and
236+
not instr instanceof TypePreservingConvertInstruction and
219237
instr.getOpcode() = opcode and
220238
tvalueNumber(instr.getUnary()) = operand
221239
}
@@ -351,6 +369,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
351369
or
352370
// The value number of a copy is just the value number of its source value.
353371
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
372+
or
373+
// The value number of a type-preserving conversion is just the value
374+
// number of the unconverted value.
375+
result = tvalueNumber(instr.(TypePreservingConvertInstruction).getUnary())
354376
)
355377
)
356378
}

cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,3 +1145,43 @@ test.cpp:
11451145
# 152| v152_7(void) = ReturnVoid :
11461146
# 152| v152_8(void) = AliasedUse : ~m156_7
11471147
# 152| v152_9(void) = ExitFunction :
1148+
1149+
# 166| void test_constMemberFunction()
1150+
# 166| Block 0
1151+
# 166| v166_1(void) = EnterFunction :
1152+
# 166| m166_2(unknown) = AliasedDefinition :
1153+
# 166| valnum = unique
1154+
# 166| m166_3(unknown) = InitializeNonLocal :
1155+
# 166| valnum = unique
1156+
# 166| m166_4(unknown) = Chi : total:m166_2, partial:m166_3
1157+
# 166| valnum = unique
1158+
# 167| r167_1(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
1159+
# 167| valnum = r167_1, r168_2, r169_1, r169_2
1160+
# 167| m167_2(StructWithConstMemberFunction) = Uninitialized[s] : &:r167_1
1161+
# 167| valnum = m167_2, m168_4, r168_3
1162+
# 167| m167_3(unknown) = Chi : total:m166_4, partial:m167_2
1163+
# 167| valnum = unique
1164+
# 168| r168_1(glval<StructWithConstMemberFunction>) = VariableAddress[s2] :
1165+
# 168| valnum = unique
1166+
# 168| r168_2(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
1167+
# 168| valnum = r167_1, r168_2, r169_1, r169_2
1168+
# 168| r168_3(StructWithConstMemberFunction) = Load[s] : &:r168_2, m167_2
1169+
# 168| valnum = m167_2, m168_4, r168_3
1170+
# 168| m168_4(StructWithConstMemberFunction) = Store[s2] : &:r168_1, r168_3
1171+
# 168| valnum = m167_2, m168_4, r168_3
1172+
# 169| r169_1(glval<StructWithConstMemberFunction>) = VariableAddress[s] :
1173+
# 169| valnum = r167_1, r168_2, r169_1, r169_2
1174+
# 169| r169_2(glval<StructWithConstMemberFunction>) = Convert : r169_1
1175+
# 169| valnum = r167_1, r168_2, r169_1, r169_2
1176+
# 169| r169_3(glval<unknown>) = FunctionAddress[constMemberFunction] :
1177+
# 169| valnum = unique
1178+
# 169| v169_4(void) = Call[constMemberFunction] : func:r169_3, this:r169_2
1179+
# 169| m169_5(unknown) = ^CallSideEffect : ~m167_3
1180+
# 169| valnum = unique
1181+
# 169| m169_6(unknown) = Chi : total:m167_3, partial:m169_5
1182+
# 169| valnum = unique
1183+
# 169| v169_7(void) = ^IndirectReadSideEffect[-1] : &:r169_2, ~m169_6
1184+
# 170| v170_1(void) = NoOp :
1185+
# 166| v166_5(void) = ReturnVoid :
1186+
# 166| v166_6(void) = AliasedUse : ~m169_6
1187+
# 166| v166_7(void) = ExitFunction :

cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/test.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,15 @@ void test_read_global_different(int n) {
156156
global_a->y = n;
157157

158158
int d = global_a->x;
159+
}
160+
161+
struct StructWithConstMemberFunction {
162+
int x;
163+
void constMemberFunction() const;
164+
};
165+
166+
void test_constMemberFunction() {
167+
StructWithConstMemberFunction s;
168+
StructWithConstMemberFunction s2 = s;
169+
s.constMemberFunction();
159170
}

cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/StrncpyFlippedArgs.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
| test.cpp:49:2:49:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
1414
| test.cpp:62:3:62:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
1515
| test.cpp:65:3:65:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
16-
| test.cpp:70:2:70:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
1716
| test.cpp:81:3:81:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
1817
| test.cpp:84:3:84:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
1918
| test.cpp:105:2:105:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |

0 commit comments

Comments
 (0)