Skip to content

Commit b8bd285

Browse files
author
Robert Marsh
committed
C++: support functions in HashCons
1 parent a8895f4 commit b8bd285

File tree

4 files changed

+182
-2
lines changed

4 files changed

+182
-2
lines changed

cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,25 @@ private cached newtype HCBase =
8080
mk_ArrayAccess(x,i,_)
8181
}
8282
or
83+
HC_NonmemberFunctionCall(Function fcn, HC_Args args) {
84+
mk_NonmemberFunctionCall(fcn, args, _)
85+
}
86+
or
87+
HC_MemberFunctionCall(Function trg, HC qual, HC_Args args) {
88+
mk_MemberFunctionCall(trg, qual, args, _)
89+
}
90+
or
8391
// Any expression that is not handled by the cases above is
8492
// given a unique number based on the expression itself.
8593
HC_Unanalyzable(Expr e) { not analyzableExpr(e,_) }
8694

95+
private cached newtype HC_Args =
96+
HC_EmptyArgs(Function fcn) {
97+
any()
98+
}
99+
or HC_ArgCons(Function fcn, HC hc, int i, HC_Args list) {
100+
mk_ArgCons(fcn, hc, i, list, _)
101+
}
87102
/**
88103
* HC is the hash-cons of an expression. The relationship between `Expr`
89104
* and `HC` is many-to-one: every `Expr` has exactly one `HC`, but multiple
@@ -120,6 +135,8 @@ class HC extends HCBase {
120135
if this instanceof HC_UnaryOp then result = "UnaryOp" else
121136
if this instanceof HC_ArrayAccess then result = "ArrayAccess" else
122137
if this instanceof HC_Unanalyzable then result = "Unanalyzable" else
138+
if this instanceof HC_NonmemberFunctionCall then result = "NonmemberFunctionCall" else
139+
if this instanceof HC_MemberFunctionCall then result = "MemberFunctionCall" else
123140
result = "error"
124141
}
125142

@@ -329,6 +346,116 @@ private predicate mk_Deref(
329346
p = hashCons(deref.getOperand().getFullyConverted())
330347
}
331348

349+
private predicate analyzableNonmemberFunctionCall(
350+
FunctionCall fc) {
351+
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
352+
strictcount(fc.getTarget()) = 1 and
353+
not fc.getTarget().isMember()
354+
}
355+
356+
private predicate mk_NonmemberFunctionCall(
357+
Function fcn,
358+
HC_Args args,
359+
FunctionCall fc
360+
) {
361+
fc.getTarget() = fcn and
362+
analyzableNonmemberFunctionCall(fc) and
363+
(
364+
exists(HC head, HC_Args tail |
365+
args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and
366+
mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc)
367+
)
368+
or
369+
fc.getNumberOfArguments() = 0 and
370+
args = HC_EmptyArgs(fcn)
371+
)
372+
}
373+
374+
private predicate analyzableMemberFunctionCall(
375+
FunctionCall fc) {
376+
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
377+
strictcount(fc.getTarget()) = 1 and
378+
strictcount(fc.getQualifier()) = 1
379+
}
380+
381+
private predicate analyzableFunctionCall(
382+
FunctionCall fc
383+
) {
384+
analyzableNonmemberFunctionCall(fc)
385+
or
386+
analyzableMemberFunctionCall(fc)
387+
}
388+
389+
private predicate mk_MemberFunctionCall(
390+
Function fcn,
391+
HC qual,
392+
HC_Args args,
393+
FunctionCall fc
394+
) {
395+
fc.getTarget() = fcn and
396+
analyzableMemberFunctionCall(fc) and
397+
hashCons(fc.getQualifier()) = qual and
398+
(
399+
exists(HC head, HC_Args tail |
400+
args = HC_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail) and
401+
mk_ArgCons(fcn, head, fc.getNumberOfArguments() - 1, tail, fc)
402+
)
403+
or
404+
fc.getNumberOfArguments() = 0 and
405+
args = HC_EmptyArgs(fcn)
406+
)
407+
}
408+
409+
/*
410+
private predicate analyzableImplicitThisFunctionCall(FunctionCall fc) {
411+
forall(int i | exists(fc.getArgument(i)) | strictcount(fc.getArgument(i)) = 1) and
412+
strictcount(fc.getTarget()) = 1 and
413+
fc.getQualifier().(ThisExpr).isCompilerGenerated() and
414+
fc.getTarget().isMember()
415+
}
416+
417+
private predicate mk_ImplicitThisFunctionCall(Function fcn, Function targ, HC_Args args, FunctionCall fc) {
418+
analyzableImplicitThisFunctionCall(fc) and
419+
fc.getTarget() = targ and
420+
fc.getEnclosingFunction() = fcn and
421+
analyzableImplicitThisFunctionCall(fc) and
422+
(
423+
exists(HC head, HC_Args tail |
424+
args = HC_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail) and
425+
mk_ArgCons(targ, head, fc.getNumberOfArguments() - 1, tail, fc)
426+
)
427+
or
428+
fc.getNumberOfArguments() = 0 and
429+
args = HC_EmptyArgs(targ)
430+
)
431+
}
432+
433+
private predicate mk_ImplicitThisFunctionCall_with_qualifier(
434+
Function fcn,
435+
Function targ,
436+
HC qual,
437+
HC_Args args,
438+
FunctionCall fc) {
439+
mk_ImplicitThisFunctionCall(fcn, targ, args, fc) and
440+
qual = HC_ThisExpr(fcn)
441+
}
442+
*/
443+
private predicate mk_ArgCons(Function fcn, HC hc, int i, HC_Args list, FunctionCall fc) {
444+
analyzableFunctionCall(fc) and
445+
fc.getTarget() = fcn and
446+
hc = hashCons(fc.getArgument(i).getFullyConverted()) and
447+
(
448+
exists(HC head, HC_Args tail |
449+
list = HC_ArgCons(fcn, head, i - 1, tail) and
450+
mk_ArgCons(fcn, head, i - 1, tail, fc) and
451+
i > 0
452+
)
453+
or
454+
i = 0 and
455+
list = HC_EmptyArgs(fcn)
456+
)
457+
}
458+
332459
/** Gets the hash-cons of expression `e`. */
333460
cached HC hashCons(Expr e) {
334461
exists (int val, Type t
@@ -383,6 +510,17 @@ cached HC hashCons(Expr e) {
383510
exists (HC p
384511
| mk_Deref(p, e) and
385512
result = HC_Deref(p))
513+
or
514+
exists(Function fcn, HC_Args args
515+
| mk_NonmemberFunctionCall(fcn, args, e) and
516+
result = HC_NonmemberFunctionCall(fcn, args)
517+
)
518+
or
519+
exists(Function fcn, HC qual, HC_Args args
520+
| mk_MemberFunctionCall(fcn, qual, args, e) and
521+
result = HC_MemberFunctionCall(fcn, qual, args)
522+
)
523+
386524
or
387525
(not analyzableExpr(e,_) and result = HC_Unanalyzable(e))
388526
}
@@ -405,5 +543,7 @@ predicate analyzableExpr(Expr e, string kind) {
405543
(analyzableUnaryOp(e) and kind = "UnaryOp") or
406544
(analyzableThisExpr(e) and kind = "ThisExpr") or
407545
(analyzableArrayAccess(e) and kind = "ArrayAccess") or
408-
(analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr")
546+
(analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or
547+
(analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or
548+
(analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall")
409549
}

cpp/ql/test/library-tests/valuenumbering/HashCons/HashCons.expected

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

cpp/ql/test/library-tests/valuenumbering/HashCons/Uniqueness.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import cpp
22
import semmle.code.cpp.valuenumbering.HashCons
33

4-
// Every expression should have exactly one GVN.
4+
// Every expression should have exactly one HC.
55
// So this query should have zero results.
66
from Expr e
77
where count(hashCons(e)) != 1

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,31 @@ void test07() {
117117
y = 0.1;
118118
y = 0.1;
119119
}
120+
121+
void test08() {
122+
test07();
123+
test07();
124+
125+
my_strspn("foo", "bar");
126+
my_strspn("foo", "bar");
127+
128+
129+
my_strspn("bar", "foo");
130+
}
131+
132+
class IntHolder {
133+
int myInt;
134+
135+
int getInt() {
136+
return myInt;
137+
}
138+
139+
public:
140+
int getDoubledInt() {
141+
return getInt() + this->getInt();
142+
}
143+
};
144+
145+
int quadrupleInt(IntHolder ih) {
146+
return ih.getDoubledInt() + ih.getDoubledInt();
147+
}

0 commit comments

Comments
 (0)