-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathsimple.cpp
More file actions
171 lines (139 loc) · 3.27 KB
/
simple.cpp
File metadata and controls
171 lines (139 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
namespace Simple
{
int user_input()
{
return 42;
}
void sink(int x)
{
}
class Foo
{
int a_;
int b_;
public:
int a() { return a_; }
int b() { return b_; }
void setA(int a) { a_ = a; }
void setB(int b) { b_ = b; }
Foo(int a, int b) : a_(a), b_(b){};
};
void bar(Foo &f)
{
sink(f.a()); // $ ast=39:12 ast=41:12 ir=39:12 ir=41:12
sink(f.b()); // $ ast=40:12 ast=42:12 ir=40:12 ir=42:12
}
void foo()
{
Foo f(0, 0);
Foo g(0, 0);
Foo h(0, 0);
Foo i(0, 0);
f.setA(user_input());
g.setB(user_input());
h.setA(user_input());
h.setB(user_input());
// Only a() should alert
bar(f);
// Only b() should alert
bar(g);
// Both a() and b() should alert
bar(h);
// Nothing should alert
bar(i);
}
struct A
{
int i;
};
void single_field_test()
{
A a;
a.i = user_input();
A a2 = a;
sink(a2.i); // $ ast,ir
}
struct C {
int f1;
};
struct C2
{
C f2;
int getf2f1() {
return f2.f1;
}
void m() {
f2.f1 = user_input();
sink(getf2f1()); // $ ast,ir
}
};
typedef A A_typedef;
void single_field_test_typedef(A_typedef a)
{
a.i = user_input();
A_typedef a2 = a;
sink(a2.i); // $ ast,ir
}
namespace TestAdditionalCallTargets {
using TakesIntReturnsVoid = void(*)(int);
template<TakesIntReturnsVoid F>
void call_template_argument(int);
void call_sink(int x) {
sink(x); // $ ir
}
void test_additional_call_targets() {
int x = user_input();
call_template_argument<call_sink>(x);
}
}
void post_update_to_phi_input(bool b)
{
A a;
if(b) {
a.i = user_input();
}
sink(a.i); // $ ast,ir
}
void write_to_param(int* p) {
*p = user_input();
}
void alias_with_fields(bool b) {
A a;
int* q;
if(b) {
q = &a.i;
} else {
q = nullptr;
}
write_to_param(q);
sink(a.i); // $ MISSING: ast,ir
}
template<typename T>
union U_with_two_instantiations_of_different_size {
int x;
T y;
};
struct LargeStruct {
int data[64];
};
void test_union_with_two_instantiations_of_different_sizes() {
// A union's fields is partitioned into "chunks" for field-flow in order to
// improve performance (so that a write to a field of a union does not flow
// to too many reads that don't happen at runtime). The partitioning is based
// the size of the types in the union. So a write to a field of size k only
// flows to a read of size k.
// Since field-flow is based on uninstantiated types a field can have
// multiple sizes if the union is instantiated with types of
// different sizes. So to compute the partition we pick the maximum size.
// Because of this there are `Content`s corresponding to the union
// `U_with_two_instantiations_of_different_size<T>`: The one for size
// `sizeof(int)`, and the one for size `sizeof(LargeStruct)` (because
// `LargeStruct` is larger than `int`). So the write to `x` writes to the
// `Content` for size `sizeof(int)`, and the read of `y` reads from the
// `Content` for size `sizeof(LargeStruct)`.
U_with_two_instantiations_of_different_size<int> u_int;
U_with_two_instantiations_of_different_size<LargeStruct> u_very_large;
u_int.x = user_input();
sink(u_int.y); // $ MISSING: ir
}
} // namespace Simple