C++: HashCons for new, new[], sizeof, alignof

This commit is contained in:
Robert Marsh
2018-08-28 13:28:11 -07:00
parent fede8d63d4
commit 5549b6fcab
3 changed files with 383 additions and 6 deletions

View File

@@ -2,9 +2,9 @@
* Provides an implementation of Hash consing.
* See https://en.wikipedia.org/wiki/Hash_consing
*
* The predicate `hashCons` converts an expression into a `HC`, which is an
* The predicate `hashCons` converts an expression into a `HashCons`, which is an
* abstract type presenting the hash-cons of the expression. If two
* expressions have the same `HC` then they are structurally equal.
* expressions have the same `HashCons` then they are structurally equal.
*
* Important note: this library ignores the possibility that the value of
* an expression might change between one occurrence and the next. For
@@ -92,18 +92,51 @@ private cached newtype HCBase =
mk_MemberFunctionCall(trg, qual, args, _)
}
or
HC_NewExpr(Type t, HC_Alloc alloc, HC_Init init) {
mk_NewExpr(t, alloc, init, _, _)
} or
HC_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init) {
mk_NewArrayExpr(t, alloc, init, _, _)
}
or
HC_SizeofType(Type t) {mk_SizeofType(t, _)}
or
HC_SizeofExpr(HashCons child) {mk_SizeofExpr(child, _)}
or
HC_AlignofType(Type t) {mk_AlignofType(t, _)}
or
HC_AlignofExpr(HashCons child) {mk_AlignofExpr(child, _)}
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,_) }
/** Used to implement hash-consing of `new` placement argument lists */
private newtype HC_Alloc =
HC_EmptyAllocArgs(Function fcn) {
exists(NewOrNewArrayExpr n |
n.getAllocator() = fcn
)
}
or HC_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned) {
mk_AllocArgCons(fcn, hc, i, list, aligned, _)
}
or
HC_NoAlloc()
private newtype HC_Init =
HC_NoInit()
or
HC_HasInit(HashCons hc) {mk_HasInit(hc, _)}
/** Used to implement hash-consing of argument lists */
private cached newtype HC_Args =
private newtype HC_Args =
HC_EmptyArgs(Function fcn) {
any()
}
or HC_ArgCons(Function fcn, HashCons hc, int i, HC_Args list) {
mk_ArgCons(fcn, hc, i, list, _)
}
/**
* HashCons 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
@@ -127,8 +160,10 @@ class HashCons extends HCBase {
/** Gets the kind of the HC. This can be useful for debugging. */
string getKind() {
if this instanceof HC_IntLiteral then result = "IntLiteral" else
if this instanceof HC_EnumConstantAccess then result = "EnumConstantAccess" else
if this instanceof HC_FloatLiteral then result = "FloatLiteral" else
if this instanceof HC_StringLiteral then result = "StringLiteral" else
if this instanceof HC_Nullptr then result = "Nullptr" else
if this instanceof HC_Variable then result = "Variable" else
if this instanceof HC_FieldAccess then result = "FieldAccess" else
if this instanceof HC_Deref then result = "Deref" else
@@ -140,6 +175,12 @@ class HashCons extends HCBase {
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
if this instanceof HC_NewExpr then result = "NewExpr" else
if this instanceof HC_NewArrayExpr then result = "NewArrayExpr" else
if this instanceof HC_SizeofType then result = "SizeofTypeOperator" else
if this instanceof HC_SizeofExpr then result = "SizeofExprOperator" else
if this instanceof HC_AlignofType then result = "AlignofTypeOperator" else
if this instanceof HC_AlignofExpr then result = "AlignofExprOperator" else
result = "error"
}
@@ -446,6 +487,195 @@ private predicate mk_ArgCons(Function fcn, HashCons hc, int i, HC_Args list, Fun
)
}
/**
* Holds if `fc` is a call to `fcn`, `fc`'s first `i` arguments have hash-cons
* `list`, and `fc`'s argument at index `i` has hash-cons `hc`.
*/
private predicate mk_AllocArgCons(Function fcn, HashCons hc, int i, HC_Alloc list, boolean aligned, FunctionCall fc) {
analyzableFunctionCall(fc) and
fc.getTarget() = fcn and
hc = hashCons(fc.getArgument(i).getFullyConverted()) and
(
exists(HashCons head, HC_Alloc tail |
list = HC_AllocArgCons(fcn, head, i - 1, tail, aligned) and
mk_AllocArgCons(fcn, head, i - 1, tail, aligned, fc) and
(
aligned = true and
i > 2
or
aligned = false and
i > 1
)
)
or
(
aligned = true and
i = 2
or
aligned = false and
i = 1
) and
list = HC_EmptyAllocArgs(fcn)
)
}
private predicate mk_HasInit(HashCons hc, NewOrNewArrayExpr new) {
hc = hashCons(new.(NewExpr).getInitializer()) or
hc = hashCons(new.(NewArrayExpr).getInitializer())
}
private predicate analyzableNewExpr(NewExpr new) {
strictcount(new.getAllocatedType()) = 1 and
(
not exists(new.getAllocatorCall())
or
strictcount(new.getAllocatorCall()) = 1
) and (
not exists(new.getInitializer())
or
strictcount(new.getInitializer()) = 1
)
}
private predicate mk_NewExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned, NewExpr new) {
analyzableNewExpr(new) and
t = new.getAllocatedType() and
(
new.hasAlignedAllocation() and
aligned = true
or
not new.hasAlignedAllocation() and
aligned = false
)
and
(
exists(FunctionCall fc, HashCons head, HC_Alloc tail |
fc = new.getAllocatorCall() and
alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and
mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc)
)
or
exists(FunctionCall fc |
fc = new.getAllocatorCall() and
(
aligned = true and
fc.getNumberOfArguments() = 2
or
aligned = false and
fc.getNumberOfArguments() = 1
) and
alloc = HC_EmptyAllocArgs(fc.getTarget())
)
or
not exists(new.getAllocatorCall()) and
alloc = HC_NoAlloc()
)
and
(
init = HC_HasInit(hashCons(new.getInitializer()))
or
not exists(new.getInitializer()) and
init = HC_NoInit()
)
}
private predicate analyzableNewArrayExpr(NewArrayExpr new) {
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
(
not exists(new.getAllocatorCall())
or
strictcount(new.getAllocatorCall().getFullyConverted()) = 1
) and (
not exists(new.getInitializer())
or
strictcount(new.getInitializer().getFullyConverted()) = 1
)
}
private predicate mk_NewArrayExpr(Type t, HC_Alloc alloc, HC_Init init, boolean aligned,
NewArrayExpr new) {
analyzableNewArrayExpr(new) and
t = new.getAllocatedType() and
(
new.hasAlignedAllocation() and
aligned = true
or
not new.hasAlignedAllocation() and
aligned = false
)
and
(
exists(FunctionCall fc, HashCons head, HC_Alloc tail |
fc = new.getAllocatorCall() and
alloc = HC_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned) and
mk_AllocArgCons(fc.getTarget(), head, fc.getNumberOfArguments() - 1, tail, aligned, fc)
)
or
exists(FunctionCall fc |
fc = new.getAllocatorCall() and
(
aligned = true and
fc.getNumberOfArguments() = 2
or
aligned = false and
fc.getNumberOfArguments() = 1
) and
alloc = HC_EmptyAllocArgs(fc.getTarget())
)
or
not exists(new.getAllocatorCall()) and
alloc = HC_NoAlloc()
)
and
(
init = HC_HasInit(hashCons(new.getInitializer()))
or
not exists(new.getInitializer()) and
init = HC_NoInit()
)
}
private predicate analyzableSizeofType(SizeofTypeOperator e) {
strictcount(e.getType().getUnspecifiedType()) = 1 and
strictcount(e.getTypeOperand()) = 1
}
private predicate mk_SizeofType(Type t, SizeofTypeOperator e) {
analyzableSizeofType(e) and
t = e.getTypeOperand()
}
private predicate analyzableSizeofExpr(Expr e) {
e instanceof SizeofExprOperator and
strictcount(e.getAChild().getFullyConverted()) = 1
}
private predicate mk_SizeofExpr(HashCons child, SizeofExprOperator e) {
analyzableSizeofExpr(e) and
child = hashCons(e.getAChild())
}
private predicate analyzableAlignofType(AlignofTypeOperator e) {
strictcount(e.getType().getUnspecifiedType()) = 1 and
strictcount(e.getTypeOperand()) = 1
}
private predicate mk_AlignofType(Type t, AlignofTypeOperator e) {
analyzableAlignofType(e) and
t = e.getTypeOperand()
}
private predicate analyzableAlignofExpr(AlignofExprOperator e) {
strictcount(e.getExprOperand()) = 1
}
private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) {
analyzableAlignofExpr(e) and
child = hashCons(e.getAChild())
}
/** Gets the hash-cons of expression `e`. */
cached HashCons hashCons(Expr e) {
exists (int val, Type t
@@ -514,6 +744,36 @@ cached HashCons hashCons(Expr e) {
result = HC_MemberFunctionCall(fcn, qual, args)
)
or
exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned
| mk_NewExpr(t, alloc, init, aligned, e) and
result = HC_NewExpr(t, alloc, init)
)
or
exists(Type t, HC_Alloc alloc, HC_Init init, boolean aligned
| mk_NewArrayExpr(t, alloc, init, aligned, e) and
result = HC_NewArrayExpr(t, alloc, init)
)
or
exists(Type t
| mk_SizeofType(t, e) and
result = HC_SizeofType(t)
)
or
exists(HashCons child
| mk_SizeofExpr(child, e) and
result = HC_SizeofExpr(child)
)
or
exists(Type t
| mk_AlignofType(t, e) and
result = HC_AlignofType(t)
)
or
exists(HashCons child
| mk_AlignofExpr(child, e) and
result = HC_AlignofExpr(child)
)
or
(
mk_Nullptr(e) and
result = HC_Nullptr()
@@ -545,5 +805,11 @@ predicate analyzableExpr(Expr e, string kind) {
(analyzableArrayAccess(e) and kind = "ArrayAccess") or
(analyzablePointerDereferenceExpr(e) and kind = "PointerDereferenceExpr") or
(analyzableNonmemberFunctionCall(e) and kind = "NonmemberFunctionCall") or
(analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall")
(analyzableMemberFunctionCall(e) and kind = "MemberFunctionCall") or
(analyzableNewExpr(e) and kind = "NewExpr") or
(analyzableNewArrayExpr(e) and kind = "NewArrayExpr") or
(analyzableSizeofType(e) and kind = "SizeofTypeOperator") or
(analyzableSizeofExpr(e) and kind = "SizeofExprOperator") or
(analyzableAlignofType(e) and kind = "AlignofTypeOperator") or
(analyzableAlignofExpr(e) and kind = "AlignofExprOperator")
}

View File

@@ -38,9 +38,9 @@
| test.cpp:92:11:92:11 | x | 92:c11-c11 93:c10-c10 |
| test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 |
| test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 |
| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 |
| test.cpp:103:10:103:11 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 247:c11-c11 248:c11-c11 270:c19-c19 271:c19-c19 |
| test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 |
| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 |
| test.cpp:105:7:105:7 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 249:c11-c11 270:c15-c15 270:c22-c22 271:c15-c15 271:c22-c22 272:c15-c15 |
| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 |
| test.cpp:110:15:110:17 | 1 | 110:c15-c17 111:c9-c11 |
| test.cpp:110:15:110:17 | (char *)... | 110:c15-c17 111:c9-c11 |
@@ -73,3 +73,27 @@
| test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 |
| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 |
| test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 |
| test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 |
| test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 253:c25-c35 254:c25-c35 |
| test.cpp:206:25:206:43 | alignof(int_holder) | 206:c25-c43 206:c3-c21 |
| test.cpp:208:27:208:27 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 |
| test.cpp:209:10:209:15 | holder | 209:c10-c15 209:c29-c34 |
| test.cpp:209:10:209:18 | (...) | 209:c10-c18 209:c29-c37 |
| test.cpp:209:17:209:17 | x | 209:c17-c17 209:c36-c36 |
| test.cpp:209:22:209:37 | sizeof(<expr>) | 209:c22-c37 209:c3-c18 |
| test.cpp:210:10:210:11 | (...) | 210:c10-c11 211:c11-c12 211:c24-c25 |
| test.cpp:211:16:211:25 | alignof(<expr>) | 211:c16-c25 211:c3-c12 |
| test.cpp:247:3:247:12 | new | 247:c3-c12 248:c3-c12 |
| test.cpp:253:16:253:36 | new[] | 253:c16-c36 254:c16-c36 |
| test.cpp:256:3:256:21 | new | 256:c3-c21 257:c3-c21 |
| test.cpp:256:7:256:10 | (void *)... | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 |
| test.cpp:256:7:256:10 | ptr1 | 256:c7-c10 257:c7-c10 260:c11-c14 261:c11-c14 |
| test.cpp:258:7:258:10 | (void *)... | 258:c7-c10 262:c11-c14 |
| test.cpp:258:7:258:10 | ptr2 | 258:c7-c10 262:c11-c14 |
| test.cpp:260:3:260:25 | new | 260:c3-c25 261:c3-c25 |
| test.cpp:260:7:260:8 | 32 | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 |
| test.cpp:260:7:260:8 | (size_t)... | 260:c7-c8 261:c7-c8 262:c7-c8 264:c7-c8 265:c7-c8 267:c7-c8 268:c7-c8 270:c7-c8 271:c7-c8 272:c7-c8 |
| test.cpp:264:3:264:19 | new | 264:c3-c19 265:c3-c19 |
| test.cpp:267:3:267:23 | new[] | 267:c3-c23 268:c3-c23 |
| test.cpp:267:21:267:22 | 10 | 267:c21-c22 268:c21-c22 92:c15-c16 |
| test.cpp:272:19:272:19 | 3 | 272:c19-c19 35:c16-c16 |

View File

@@ -184,3 +184,90 @@ int test13(int y) {
int test14(int y) {
return SQUARE(y);
}
typedef struct {
int x;
char y;
} padded_t;
typedef struct {
int x;
} int_holder;
typedef unsigned long size_t;
void *malloc(size_t size);
int test15(int x) {
sizeof(padded_t);
alignof(padded_t);
sizeof(padded_t);
sizeof(int_holder) + sizeof(int);
alignof(int_holder) + alignof(int_holder);
int_holder holder = {x: x};
sizeof(holder.x) + sizeof(holder.x);
sizeof(x);
alignof(x) + alignof(x);
}
static void *operator new(size_t size) {
return malloc(size);
}
static void *operator new(size_t size, void *placement) {
return placement;
}
static void *operator new(size_t size, size_t alignment, void *placement) {
return placement;
}
static void *operator new(size_t size, size_t alignment) {
return malloc(size);
}
static void *operator new[](size_t size) {
return malloc(size);
}
static void *operator new[](size_t size, void *placement) {
return placement;
}
static void *operator new[](size_t size, size_t alignment, void *placement) {
return placement;
}
static void *operator new[](size_t size, size_t alignment) {
return malloc(size);
}
void test16() {
new int(1);
new int(1);
new int(2);
int x;
char *ptr1 = new char[sizeof(int)];
char *ptr2 = new char[sizeof(int)];
new(ptr1) IntHolder;
new(ptr1) IntHolder;
new(ptr2) IntHolder;
new(32, ptr1) IntHolder;
new(32, ptr1) IntHolder;
new(32, ptr2) IntHolder;
new(32) IntHolder;
new(32) IntHolder;
new(32) IntHolder[10];
new(32) IntHolder[10];
new(32) int[2] {1, 2};
new(32) int[2] {1, 2};
new(32) int[2] {3, 4};
}