C++: support functions in HashCons

This commit is contained in:
Robert Marsh
2018-08-24 16:35:00 -07:00
parent a8895f4bed
commit b8bd285d64
4 changed files with 182 additions and 2 deletions

View File

@@ -80,10 +80,25 @@ private cached newtype HCBase =
mk_ArrayAccess(x,i,_)
}
or
HC_NonmemberFunctionCall(Function fcn, HC_Args args) {
mk_NonmemberFunctionCall(fcn, args, _)
}
or
HC_MemberFunctionCall(Function trg, HC qual, HC_Args args) {
mk_MemberFunctionCall(trg, qual, args, _)
}
or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) }
private cached newtype HC_Args =
HC_EmptyArgs(Function fcn) {
any()
}
or HC_ArgCons(Function fcn, HC hc, int i, HC_Args list) {
mk_ArgCons(fcn, hc, i, list, _)
}
/**
* HC is the hash-cons of an expression. The relationship between `Expr`
* and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple
@@ -120,6 +135,8 @@ class HC extends HCBase {
if this instanceof HC_UnaryOp then result = "UnaryOp" else
if this instanceof HC_ArrayAccess then result = "ArrayAccess" else
if this instanceof HC_Unanalyzable then result = "Unanalyzable" else
if this instanceof HC_NonmemberFunctionCall then result = "NonmemberFunctionCall" else
if this instanceof HC_MemberFunctionCall then result = "MemberFunctionCall" else
result = "error"
}
@@ -329,6 +346,116 @@ private predicate mk_Deref(
p = hashCons(deref.getOperand().getFullyConverted())
}
private predicate analyzableNonmemberFunctionCall(
FunctionCall fc) {
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
strictcount(fc.getTarget()) = 1 and
not fc.getTarget().isMember()
}
private predicate mk_NonmemberFunctionCall(
Function fcn,
HC_Args args,
FunctionCall fc
) {
fc.getTarget() = fcn and
analyzableNonmemberFunctionCall(fc) and
(
exists(HC head, HC_Args tail |
args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and
mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc)
)
or
fc.getNumberOfArguments() = 0 and
args = HC_EmptyArgs(fcn)
)
}
private predicate analyzableMemberFunctionCall(
FunctionCall fc) {
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
strictcount(fc.getTarget()) = 1 and
strictcount(fc.getQualifier()) = 1
}
private predicate analyzableFunctionCall(
FunctionCall fc
) {
analyzableNonmemberFunctionCall(fc)
or
analyzableMemberFunctionCall(fc)
}
private predicate mk_MemberFunctionCall(
Function fcn,
HC qual,
HC_Args args,
FunctionCall fc
) {
fc.getTarget() = fcn and
analyzableMemberFunctionCall(fc) and
hashCons(fc.getQualifier()) = qual and
(
exists(HC head, HC_Args tail |
args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and
mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc)
)
or
fc.getNumberOfArguments() = 0 and
args = HC_EmptyArgs(fcn)
)
}
/*
private predicate analyzableImplicitThisFunctionCall(FunctionCall fc) {
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
strictcount(fc.getTarget()) = 1 and
fc.getQualifier().(ThisExpr).isCompilerGenerated() and
fc.getTarget().isMember()
}
private predicate mk_ImplicitThisFunctionCall(Function fcn, Function targ, HC_Args args, FunctionCall fc) {
analyzableImplicitThisFunctionCall(fc) and
fc.getTarget() = targ and
fc.getEnclosingFunction() = fcn and
analyzableImplicitThisFunctionCall(fc) and
(
exists(HC head, HC_Args tail |
args = HC_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail) and
mk_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail, fc)
)
or
fc.getNumberOfArguments() = 0 and
args = HC_EmptyArgs(targ)
)
}
private predicate mk_ImplicitThisFunctionCall_with_qualifier(
Function fcn,
Function targ,
HC qual,
HC_Args args,
FunctionCall fc) {
mk_ImplicitThisFunctionCall(fcn, targ, args, fc) and
qual = HC_ThisExpr(fcn)
}
*/
private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionCall fc) {
analyzableFunctionCall(fc) and
fc.getTarget() = fcn and
hc = hashCons(fc.getArgument(i).getFullyConverted()) and
(
exists(HC head, HC_Args tail |
list = HC_ArgCons(fcn, head, i - 1, tail) and
mk_ArgCons(fcn, head, i - 1, tail, fc) and
i > 0
)
or
i = 0 and
list = HC_EmptyArgs(fcn)
)
}
/** Gets the hash-cons of expression `e`. */
cached HC hashCons(Expr e) {
exists (int val, Type t
@@ -383,6 +510,17 @@ cached HC hashCons(Expr e) {
exists (HC p
| mk_Deref(p, e) and
result = HC_Deref(p))
or
exists(Function fcn, HC_Args args
| mk_NonmemberFunctionCall(fcn, args, e) and
result = HC_NonmemberFunctionCall(fcn, args)
)
or
exists(Function fcn, HC qual, HC_Args args
| mk_MemberFunctionCall(fcn, qual, args, e) and
result = HC_MemberFunctionCall(fcn, qual, args)
)
or
(not analyzableExpr(e,_) and result = HC_Unanalyzable(e))
}
@@ -405,5 +543,7 @@ predicate analyzableExpr(Expr e, string kind) {
(analyzableUnaryOp(e) and kind = "UnaryOp") or
(analyzableThisExpr(e) and kind = "ThisExpr") or
(analyzableArrayAccess(e) and kind = "ArrayAccess") or
(analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr")
(analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or
(analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or
(analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall")
}

View File

@@ -1,3 +1,4 @@
| file://:0:0:0:0 | this | 0:c0-c0 141:c23-c26 |
| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 7:c7-c7 |
| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 |
| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 |
@@ -30,6 +31,8 @@
| test.cpp:56:13:56:16 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:56:13:56:16 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 |
| test.cpp:77:20:77:28 | call to getAValue | 77:c20-c28 80:c9-c17 |
| test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 80:c9-c19 |
| test.cpp:79:7:79:7 | v | 79:c7-c7 80:c5-c5 |
| test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 |
| test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 |
@@ -51,3 +54,12 @@
| test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 |
| test.cpp:117:7:117:9 | 0.10000000000000001 | 117:c7-c9 118:c7-c9 |
| test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 |
| test.cpp:122:3:122:8 | call to test07 | 122:c3-c8 123:c3-c8 |
| test.cpp:125:3:125:11 | call to my_strspn | 125:c3-c11 126:c3-c11 |
| test.cpp:125:13:125:17 | array to pointer conversion | 125:c13-c17 126:c13-c17 129:c20-c24 |
| test.cpp:125:13:125:17 | foo | 125:c13-c17 126:c13-c17 129:c20-c24 |
| test.cpp:125:20:125:24 | array to pointer conversion | 125:c20-c24 126:c20-c24 129:c13-c17 |
| test.cpp:125:20:125:24 | bar | 125:c20-c24 126:c20-c24 129:c13-c17 |
| test.cpp:141:12:141:17 | call to getInt | 141:c12-c17 141:c29-c34 |
| test.cpp:146:10:146:11 | ih | 146:c10-c11 146:c31-c32 |
| test.cpp:146:13:146:25 | call to getDoubledInt | 146:c13-c25 146:c34-c46 |

View File

@@ -1,7 +1,7 @@
import cpp
import semmle.code.cpp.valuenumbering.HashCons
// Every expression should have exactly one GVN.
// Every expression should have exactly one HC.
// So this query should have zero results.
from Expr e
where count(hashCons(e)) != 1

View File

@@ -117,3 +117,31 @@ void test07() {
y = 0.1;
y = 0.1;
}
void test08() {
test07();
test07();
my_strspn("foo", "bar");
my_strspn("foo", "bar");
my_strspn("bar", "foo");
}
class IntHolder {
int myInt;
int getInt() {
return myInt;
}
public:
int getDoubledInt() {
return getInt() + this->getInt();
}
};
int quadrupleInt(IntHolder ih) {
return ih.getDoubledInt() + ih.getDoubledInt();
}