Merge pull request #5854 from dbartol/dbartol/smart-pointers/side-effects

C++: Generate side effect instructions for smart pointer indirections
This commit is contained in:
Jonas Jensen
2021-06-01 16:57:05 +02:00
committed by GitHub
20 changed files with 397 additions and 10 deletions

View File

@@ -416,3 +416,46 @@ predicate addressOperandAllocationAndOffset(
)
)
}
/**
* Predicates used only for printing annotated IR dumps. These should not be used in production
* queries.
*/
module Print {
string getOperandProperty(Operand operand, string key) {
key = "alloc" and
result =
strictconcat(Configuration::Allocation allocation, IntValue bitOffset |
addressOperandAllocationAndOffset(operand, allocation, bitOffset)
|
allocation.toString() + Ints::getBitOffsetString(bitOffset), ", "
)
or
key = "prop" and
result =
strictconcat(Instruction destInstr, IntValue bitOffset, string value |
operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and
if destInstr = operand.getUse()
then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result"
else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId()
|
value, ", "
)
}
string getInstructionProperty(Instruction instr, string key) {
key = "prop" and
result =
strictconcat(IntValue bitOffset, Operand sourceOperand, string value |
operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and
if instr = sourceOperand.getUse()
then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@"
else
value =
sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() +
Ints::getBitOffsetString(bitOffset) + "->@"
|
value, ", "
)
}
}

View File

@@ -0,0 +1,19 @@
/**
* Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`.
*/
private import AliasAnalysisInternal
private import InputIR
private import AliasAnalysisImports
private import AliasAnalysis
private import semmle.code.cpp.ir.internal.IntegerConstant
private class AliasPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) {
result = Print::getOperandProperty(operand, key)
}
override string getInstructionProperty(Instruction instr, string key) {
result = Print::getInstructionProperty(instr, key)
}
}

View File

@@ -1073,7 +1073,10 @@ module SSAConsistency {
locationCount > 1 and
func = operand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction()) and
message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
message =
operand.getUse().toString() + " " + "Operand has " + locationCount.toString() +
" memory accesses in function '$@': " +
strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ")
)
}

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

@@ -416,3 +416,46 @@ predicate addressOperandAllocationAndOffset(
)
)
}
/**
* Predicates used only for printing annotated IR dumps. These should not be used in production
* queries.
*/
module Print {
string getOperandProperty(Operand operand, string key) {
key = "alloc" and
result =
strictconcat(Configuration::Allocation allocation, IntValue bitOffset |
addressOperandAllocationAndOffset(operand, allocation, bitOffset)
|
allocation.toString() + Ints::getBitOffsetString(bitOffset), ", "
)
or
key = "prop" and
result =
strictconcat(Instruction destInstr, IntValue bitOffset, string value |
operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and
if destInstr = operand.getUse()
then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result"
else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId()
|
value, ", "
)
}
string getInstructionProperty(Instruction instr, string key) {
key = "prop" and
result =
strictconcat(IntValue bitOffset, Operand sourceOperand, string value |
operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and
if instr = sourceOperand.getUse()
then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@"
else
value =
sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() +
Ints::getBitOffsetString(bitOffset) + "->@"
|
value, ", "
)
}
}

View File

@@ -0,0 +1,19 @@
/**
* Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`.
*/
private import AliasAnalysisInternal
private import InputIR
private import AliasAnalysisImports
private import AliasAnalysis
private import semmle.code.cpp.ir.internal.IntegerConstant
private class AliasPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) {
result = Print::getOperandProperty(operand, key)
}
override string getInstructionProperty(Instruction instr, string key) {
result = Print::getInstructionProperty(instr, key)
}
}

View File

@@ -1073,7 +1073,10 @@ module SSAConsistency {
locationCount > 1 and
func = operand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction()) and
message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
message =
operand.getUse().toString() + " " + "Operand has " + locationCount.toString() +
" memory accesses in function '$@': " +
strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ")
)
}

View File

@@ -157,11 +157,11 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side
// parameter.
result.isParameter(1)
else result.isParameterDeref(0)
or
// One of the functions that takes ownership of a raw pointer.
param0.getUnspecifiedType() instanceof PointerType and
result.isParameter(0)
)
or
// One of the functions that takes ownership of a raw pointer.
param0.getUnspecifiedType() instanceof PointerType and
result.isParameter(0)
)
}
}

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);
}