C++: Generate IR side effects for smart pointer indirections

When inserting side effect instructions for argument indirections, we now insert side effects for smart pointers as we would for raw pointers. The address operand of the side effect instruction is  the smart pointer object, which is a bit odd. However, I'd like to think through the design of a more principled solution before doing additional work.

A few new tests are added to the existing IR tests. In addition, the IR tests now `#include` some of the shared STL headers. I've disabled IR dumps for functions from those headers, since they only get in the way of the test cases we intended.
This commit is contained in:
Dave Bartolomeo
2021-05-07 16:50:03 -04:00
parent f0a994a570
commit 187e136ecc
10 changed files with 210 additions and 3 deletions

View File

@@ -7,6 +7,7 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.models.interfaces.PointerWrapper
private import semmle.code.cpp.models.interfaces.SideEffect
/**
@@ -39,7 +40,8 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
exists(Type t | t = expr.getUnspecifiedType() |
t instanceof ArrayType or
t instanceof PointerType or
t instanceof ReferenceType
t instanceof ReferenceType or
t instanceof PointerWrapper
) and
(
isWrite = true and

View File

@@ -11365,6 +11365,86 @@ perf-regression.cpp:
# 12| Type = [IntType] int
# 12| Value = [Literal] 0
# 12| ValueCategory = prvalue
smart_ptr.cpp:
# 8| [TopLevelFunction] void unique_ptr_arg(std::unique_ptr<int, std::default_delete<int>>)
# 8| <params>:
# 8| getParameter(0): [Parameter] up
# 8| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
# 10| [TopLevelFunction] void call_unique_ptr_arg(int*)
# 10| <params>:
# 10| getParameter(0): [Parameter] p
# 10| Type = [IntPointerType] int *
# 10| getEntryPoint(): [BlockStmt] { ... }
# 11| getStmt(0): [DeclStmt] declaration
# 11| getDeclarationEntry(0): [VariableDeclarationEntry] definition of up
# 11| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
# 11| getVariable().getInitializer(): [Initializer] initializer for up
# 11| getExpr(): [ConstructorCall] call to unique_ptr
# 11| Type = [VoidType] void
# 11| ValueCategory = prvalue
# 11| getArgument(0): [VariableAccess] p
# 11| Type = [IntPointerType] int *
# 11| ValueCategory = prvalue(load)
# 12| getStmt(1): [ExprStmt] ExprStmt
# 12| getExpr(): [FunctionCall] call to unique_ptr_arg
# 12| Type = [VoidType] void
# 12| ValueCategory = prvalue
# 12| getArgument(0): [FunctionCall] call to move
# 12| Type = [RValueReferenceType] type &&
# 12| ValueCategory = prvalue
# 12| getArgument(0): [VariableAccess] up
# 12| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
# 12| ValueCategory = lvalue
# 12| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
# 12| Type = [LValueReferenceType] unique_ptr<int, default_delete<int>> &
# 12| ValueCategory = prvalue
# 12| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
# 12| Type = [ClassTemplateInstantiation] unique_ptr<int, default_delete<int>>
# 12| ValueCategory = lvalue
# 12| getExpr(): [ReferenceDereferenceExpr] (reference dereference)
# 12| Type = [CTypedefType,NestedTypedefType] type
# 12| ValueCategory = prvalue(load)
# 13| getStmt(2): [ReturnStmt] return ...
# 15| [TopLevelFunction] void shared_ptr_arg(std::shared_ptr<float>)
# 15| <params>:
# 15| getParameter(0): [Parameter] sp
# 15| Type = [ClassTemplateInstantiation] shared_ptr<float>
# 17| [TopLevelFunction] void call_shared_ptr_arg(float*)
# 17| <params>:
# 17| getParameter(0): [Parameter] p
# 17| Type = [PointerType] float *
# 17| getEntryPoint(): [BlockStmt] { ... }
# 18| getStmt(0): [DeclStmt] declaration
# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp
# 18| Type = [ClassTemplateInstantiation] shared_ptr<float>
# 18| getVariable().getInitializer(): [Initializer] initializer for sp
# 18| getExpr(): [ConstructorCall] call to shared_ptr
# 18| Type = [VoidType] void
# 18| ValueCategory = prvalue
# 18| getArgument(0): [VariableAccess] p
# 18| Type = [PointerType] float *
# 18| ValueCategory = prvalue(load)
# 19| getStmt(1): [ExprStmt] ExprStmt
# 19| getExpr(): [FunctionCall] call to shared_ptr_arg
# 19| Type = [VoidType] void
# 19| ValueCategory = prvalue
# 19| getArgument(0): [ConstructorCall] call to shared_ptr
# 19| Type = [VoidType] void
# 19| ValueCategory = prvalue
# 19| getArgument(0): [VariableAccess] sp
# 19| Type = [ClassTemplateInstantiation] shared_ptr<float>
# 19| ValueCategory = lvalue
# 19| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
# 19| Type = [LValueReferenceType] const shared_ptr<float> &
# 19| ValueCategory = prvalue
# 19| getExpr(): [CStyleCast] (const shared_ptr<float>)...
# 19| Conversion = [GlvalueConversion] glvalue conversion
# 19| Type = [SpecifiedType] const shared_ptr<float>
# 19| ValueCategory = lvalue
# 19| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
# 19| Type = [ClassTemplateInstantiation] shared_ptr<float>
# 19| ValueCategory = lvalue
# 20| getStmt(2): [ReturnStmt] return ...
struct_init.cpp:
# 1| [TopLevelFunction] int handler1(void*)
# 1| <params>:

View File

@@ -0,0 +1,11 @@
/**
* @kind graph
*/
private import cpp
private import semmle.code.cpp.PrintAST
private import PrintConfig
private class PrintConfig extends PrintASTConfiguration {
override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
}

View File

@@ -1 +0,0 @@
semmle/code/cpp/PrintAST.ql

View File

@@ -0,0 +1,10 @@
private import cpp
/**
* Holds if the AST or IR for the specified function should be printed in the test output.
*
* This predicate excludes functions defined in standard headers.
*/
predicate shouldDumpFunction(Function func) {
not func.getLocation().getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+")
}

View File

@@ -6,6 +6,7 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -7903,6 +7903,80 @@ perf-regression.cpp:
# 9| v9_6(void) = AliasedUse : ~m?
# 9| v9_7(void) = ExitFunction :
smart_ptr.cpp:
# 10| void call_unique_ptr_arg(int*)
# 10| Block 0
# 10| v10_1(void) = EnterFunction :
# 10| mu10_2(unknown) = AliasedDefinition :
# 10| mu10_3(unknown) = InitializeNonLocal :
# 10| r10_4(glval<int *>) = VariableAddress[p] :
# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4
# 10| r10_6(int *) = Load[p] : &:r10_4, ~m?
# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6
# 11| r11_1(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[up] :
# 11| mu11_2(unique_ptr<int, default_delete<int>>) = Uninitialized[up] : &:r11_1
# 11| r11_3(glval<unknown>) = FunctionAddress[unique_ptr] :
# 11| r11_4(glval<int *>) = VariableAddress[p] :
# 11| r11_5(int *) = Load[p] : &:r11_4, ~m?
# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5
# 11| mu11_7(unknown) = ^CallSideEffect : ~m?
# 11| mu11_8(unique_ptr<int, default_delete<int>>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1
# 12| r12_1(glval<unknown>) = FunctionAddress[unique_ptr_arg] :
# 12| r12_2(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[#temp12:20] :
# 12| r12_3(glval<unknown>) = FunctionAddress[move] :
# 12| r12_4(glval<unique_ptr<int, default_delete<int>>>) = VariableAddress[up] :
# 12| r12_5(unique_ptr<int, default_delete<int>> &) = CopyValue : r12_4
# 12| r12_6(unique_ptr<int, default_delete<int>> &&) = Call[move] : func:r12_3, 0:r12_5
# 12| r12_7(unique_ptr<int, default_delete<int>>) = Load[?] : &:r12_6, ~m?
# 12| mu12_8(unique_ptr<int, default_delete<int>>) = Store[#temp12:20] : &:r12_2, r12_7
# 12| r12_9(unique_ptr<int, default_delete<int>>) = Load[#temp12:20] : &:r12_2, ~m?
# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9
# 12| mu12_11(unknown) = ^CallSideEffect : ~m?
# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m?
# 13| v13_1(void) = NoOp :
# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m?
# 10| v10_9(void) = ReturnVoid :
# 10| v10_10(void) = AliasedUse : ~m?
# 10| v10_11(void) = ExitFunction :
# 17| void call_shared_ptr_arg(float*)
# 17| Block 0
# 17| v17_1(void) = EnterFunction :
# 17| mu17_2(unknown) = AliasedDefinition :
# 17| mu17_3(unknown) = InitializeNonLocal :
# 17| r17_4(glval<float *>) = VariableAddress[p] :
# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4
# 17| r17_6(float *) = Load[p] : &:r17_4, ~m?
# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6
# 18| r18_1(glval<shared_ptr<float>>) = VariableAddress[sp] :
# 18| mu18_2(shared_ptr<float>) = Uninitialized[sp] : &:r18_1
# 18| r18_3(glval<unknown>) = FunctionAddress[shared_ptr] :
# 18| r18_4(glval<float *>) = VariableAddress[p] :
# 18| r18_5(float *) = Load[p] : &:r18_4, ~m?
# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5
# 18| mu18_7(unknown) = ^CallSideEffect : ~m?
# 18| mu18_8(shared_ptr<float>) = ^IndirectMustWriteSideEffect[-1] : &:r18_1
# 19| r19_1(glval<unknown>) = FunctionAddress[shared_ptr_arg] :
# 19| r19_2(glval<shared_ptr<float>>) = VariableAddress[#temp19:20] :
# 19| mu19_3(shared_ptr<float>) = Uninitialized[#temp19:20] : &:r19_2
# 19| r19_4(glval<unknown>) = FunctionAddress[shared_ptr] :
# 19| r19_5(glval<shared_ptr<float>>) = VariableAddress[sp] :
# 19| r19_6(glval<shared_ptr<float>>) = Convert : r19_5
# 19| r19_7(shared_ptr<float> &) = CopyValue : r19_6
# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7
# 19| mu19_9(unknown) = ^CallSideEffect : ~m?
# 19| mu19_10(shared_ptr<float>) = ^IndirectMustWriteSideEffect[-1] : &:r19_2
# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m?
# 19| r19_12(shared_ptr<float>) = Load[#temp19:20] : &:r19_2, ~m?
# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12
# 19| mu19_14(unknown) = ^CallSideEffect : ~m?
# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m?
# 20| v20_1(void) = NoOp :
# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m?
# 17| v17_9(void) = ReturnVoid :
# 17| v17_10(void) = AliasedUse : ~m?
# 17| v17_11(void) = ExitFunction :
struct_init.cpp:
# 16| void let_info_escape(Info*)
# 16| Block 0

View File

@@ -0,0 +1,11 @@
/**
* @kind graph
*/
private import cpp
private import semmle.code.cpp.ir.implementation.raw.PrintIR
private import PrintConfig
private class PrintConfig extends PrintIRConfiguration {
override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
}

View File

@@ -1 +0,0 @@
semmle/code/cpp/ir/implementation/raw/PrintIR.ql

View File

@@ -0,0 +1,20 @@
#include "../../../include/memory.h"
#include "../../../include/utility.h"
using std::move;
using std::shared_ptr;
using std::unique_ptr;
void unique_ptr_arg(unique_ptr<int> up);
void call_unique_ptr_arg(int* p) {
unique_ptr<int> up(p);
unique_ptr_arg(move(up));
}
void shared_ptr_arg(shared_ptr<float> sp);
void call_shared_ptr_arg(float* p) {
shared_ptr<float> sp(p);
shared_ptr_arg(sp);
}