Skip to content

Commit 70f5660

Browse files
author
Mihai Budiu
authored
Better alias analysis in side-effects pass (#922)
1 parent 0b22472 commit 70f5660

24 files changed

+452
-521
lines changed

frontends/p4/sideEffects.cpp

+190-21
Original file line numberDiff line numberDiff line change
@@ -310,40 +310,205 @@ class DismantleExpression : public Transform {
310310
const IR::Node* preorder(IR::LAnd* expression) override { return shortCircuit(expression); }
311311
const IR::Node* preorder(IR::LOr* expression) override { return shortCircuit(expression); }
312312

313-
// We don't want to compute the full read/write set here so we
314-
// overapproximate it as follows: all declarations that occur in
315-
// an expression.
316-
// TODO: this could be made more precise, perhaps using LocationSets.
317-
class ReadsWrites : public Inspector {
313+
/// This class represents the path to a location.
314+
/// Given a struct S { bit a; bit b; } and a variable S x;
315+
/// a path can be x.a, or just x. An array index is represented as a
316+
/// number (encoded as a string) or as "*", denoting an unknown index.
317+
struct LocationPath : public IHasDbPrint {
318+
const IR::IDeclaration* root;
319+
std::vector<cstring> path;
320+
321+
explicit LocationPath(const IR::IDeclaration* root): root(root) { CHECK_NULL(root); }
322+
323+
const LocationPath* append(cstring suffix) const {
324+
auto result = new LocationPath(root);
325+
result->path = path;
326+
result->path.push_back(suffix);
327+
return result;
328+
}
329+
330+
/// True if this path is a prefix of other or the other way around
331+
bool isPrefix(const LocationPath* other) const {
332+
// Due to the structure of the P4 language, two distinct
333+
// declarations can never alias.
334+
if (root != other->root)
335+
return false;
336+
size_t len = std::min(path.size(), other->path.size());
337+
for (size_t i = 0; i < len; i++) {
338+
if (path.at(i) == "*" || other->path.at(i) == "*")
339+
continue;
340+
if (path.at(i) != other->path.at(i))
341+
return false;
342+
}
343+
return true;
344+
}
345+
346+
void dbprint(std::ostream& out) const override {
347+
out << root->getName();
348+
for (auto p : path)
349+
out << "." << p;
350+
}
351+
};
352+
353+
/// We represent a set of location set as a set of LocationPath
354+
/// objects.
355+
class SetOfLocations : public IHasDbPrint {
318356
public:
319-
std::set<const IR::IDeclaration*> decls;
320-
ReferenceMap* refMap;
357+
std::set<const LocationPath*> paths;
358+
359+
SetOfLocations() = default;
360+
explicit SetOfLocations(const LocationPath* path) {
361+
add(path);
362+
}
363+
explicit SetOfLocations(const SetOfLocations* set): paths(set->paths) {}
364+
365+
void add(const LocationPath* path) { paths.emplace(path); }
366+
bool overlaps(const SetOfLocations* other) const {
367+
// Normally one of these sets has only one element, because
368+
// one of the two is a left-value, so this should be fast.
369+
for (auto s : paths) {
370+
for (auto so : other->paths) {
371+
if (s->isPrefix(so))
372+
return true;
373+
}
374+
}
375+
return false;
376+
}
377+
378+
const SetOfLocations* join(const SetOfLocations* other) const {
379+
auto result = new SetOfLocations(this);
380+
for (auto p : other->paths)
381+
result->add(p);
382+
return result;
383+
}
384+
385+
/// Append suffix to each location in the set
386+
const SetOfLocations* append(cstring suffix) const {
387+
auto result = new SetOfLocations();
388+
for (auto p : paths) {
389+
auto append = p->append(suffix);
390+
result->add(append);
391+
}
392+
return result;
393+
}
321394

322-
explicit ReadsWrites(ReferenceMap* refMap) : refMap(refMap)
395+
void dbprint(std::ostream& out) const override {
396+
for (auto p : paths)
397+
out << p << std::endl;
398+
}
399+
};
400+
401+
/// Computes the SetOfLocations read and written by an expression.
402+
/// This is invoked only for expressions that appear as arguments
403+
/// to method calls.
404+
class ReadsWrites : public Inspector {
405+
const ReferenceMap* refMap;
406+
std::map<const IR::Expression*, const SetOfLocations*> rw;
407+
408+
public:
409+
explicit ReadsWrites(const ReferenceMap* refMap) : refMap(refMap)
323410
{ setName("ReadsWrites"); }
324411

412+
void postorder(const IR::Operation_Binary* expression) override {
413+
auto left = ::get(rw, expression->left);
414+
auto right = ::get(rw, expression->right);
415+
rw.emplace(expression, left->join(right));
416+
}
417+
325418
void postorder(const IR::PathExpression* expression) override {
326419
auto decl = refMap->getDeclaration(expression->path);
327-
decls.emplace(decl);
420+
auto path = new LocationPath(decl);
421+
auto locs = new SetOfLocations(path);
422+
rw.emplace(expression, locs);
423+
}
424+
425+
void postorder(const IR::Operation_Unary* expression) override {
426+
auto e = ::get(rw, expression->expr);
427+
rw.emplace(expression, e);
428+
}
429+
430+
void postorder(const IR::Member* expression) override {
431+
auto e = ::get(rw, expression->expr);
432+
auto result = e->append(expression->member);
433+
rw.emplace(expression, result);
434+
}
435+
436+
void postorder(const IR::ArrayIndex* expression) override {
437+
auto e = ::get(rw, expression->left);
438+
const SetOfLocations* result;
439+
if (expression->right->is<IR::Constant>()) {
440+
int index = expression->right->to<IR::Constant>()->asInt();
441+
result = e->append(Util::toString(index));
442+
} else {
443+
result = e->append("*");
444+
}
445+
rw.emplace(expression, result);
446+
}
447+
448+
void postorder(const IR::Literal* expression) override {
449+
rw.emplace(expression, new SetOfLocations());
450+
}
451+
452+
void postorder(const IR::TypeNameExpression* expression) override {
453+
rw.emplace(expression, new SetOfLocations());
454+
}
455+
456+
void postorder(const IR::Operation_Ternary* expression) override {
457+
auto e0 = ::get(rw, expression->e0);
458+
auto e1 = ::get(rw, expression->e1);
459+
auto e2 = ::get(rw, expression->e2);
460+
rw.emplace(expression, e0->join(e1)->join(e2));
461+
}
462+
463+
void postorder(const IR::MethodCallExpression* expression) override {
464+
// The only expression that can appear here is h.isValid();
465+
// The ReadsWrites analysis is not called for other methods that
466+
// have side-effects -- these are always copied into temporaries.
467+
BUG_CHECK(expression->method->is<IR::Member>(),
468+
"%1%: expected isValid()", expression);
469+
auto member = expression->method->to<IR::Member>();
470+
BUG_CHECK(member->member == "isValid", "%1%: expected isValid()", expression);
471+
auto obj = member->expr;
472+
auto e = ::get(rw, obj);
473+
rw.emplace(expression, e->append("$valid"));
474+
}
475+
476+
void postorder(const IR::ConstructorCallExpression* expression) override {
477+
const SetOfLocations* result = new SetOfLocations();
478+
for (auto e : *expression->arguments) {
479+
auto s = ::get(rw, e);
480+
result = result->join(s);
481+
}
482+
rw.emplace(expression, result);
483+
}
484+
485+
void postorder(const IR::ListExpression* expression) override {
486+
const SetOfLocations* result = new SetOfLocations();
487+
for (auto e : expression->components) {
488+
auto s = ::get(rw, e);
489+
result = result->join(s);
490+
}
491+
rw.emplace(expression, result);
492+
}
493+
494+
const SetOfLocations* get(const IR::Expression* expression) {
495+
expression->apply(*this);
496+
auto result = ::get(rw, expression);
497+
CHECK_NULL(result);
498+
LOG3("SetOfLocations(" << expression << ")=" << result);
499+
return result;
328500
}
329501
};
330502

331503
// Conservative alias analysis. We implement this here because this pass
332504
// runs early in the front end, before enough information is present (eg.
333505
// def-use information) to do a precise alias analysis.
334506
bool mayAlias(const IR::Expression* left, const IR::Expression* right) const {
335-
ReadsWrites rwleft(refMap);
336-
(void)left->apply(rwleft);
337-
ReadsWrites rwright(refMap);
338-
(void)right->apply(rwright);
339-
340-
for (auto d : rwleft.decls) {
341-
if (rwright.decls.count(d) > 0) {
342-
LOG3(dbp(d) << " accessed by both " << dbp(left) << " and " << dbp(right));
343-
return true;
344-
}
345-
}
346-
return false;
507+
ReadsWrites rw(refMap);
508+
auto llocs = rw.get(left);
509+
auto rlocs = rw.get(right);
510+
LOG3("Checking overlap between " << llocs << " and " << rlocs);
511+
return llocs->overlaps(rlocs);
347512
}
348513

349514
/// Returns true if type is a header or a struct containing a header.
@@ -430,6 +595,10 @@ class DismantleExpression : public Transform {
430595
break;
431596
if (!p1->hasOut() && !p2->hasOut())
432597
continue;
598+
if (useTemporary.find(p1) != useTemporary.end())
599+
continue;
600+
if (useTemporary.find(p2) != useTemporary.end())
601+
continue;
433602
auto arg2 = desc.substitution.lookup(p2);
434603
if (mayAlias(arg1, arg2)) {
435604
LOG3("Using temporary for " << dbp(mce) <<

midend/actionSynthesis.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ bool DoSynthesizeActions::mustMove(const IR::AssignmentStatement *assign) {
108108
if (!mi->is<ExternMethod>())
109109
return true;
110110
auto em = mi->to<ExternMethod>();
111+
// TODO: this is wrong, there is no way a mid-end pass can depend on
112+
// v1model.
111113
auto &v1model = P4V1::V1Model::instance;
112114
if (em->originalExternType->name.name == v1model.ck16.name)
113115
return false;

testdata/p4_14_samples_outputs/action_inline2-frontend.p4

+1-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout
2727
}
2828

2929
control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
30-
bit<8> tmp;
3130
@name(".copy2") action copy2_0(inout bit<8> dest_0, bit<8> val_0) {
3231
dest_0 = val_0;
3332
}
@@ -38,9 +37,7 @@ control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_
3837
copy_0(dest_2, val_2);
3938
}
4039
@name(".setb1") action setb1_0(bit<9> port) {
41-
tmp = hdr.data.b1;
42-
setbyte_0(tmp, hdr.data.b2);
43-
hdr.data.b1 = tmp;
40+
setbyte_0(hdr.data.b1, hdr.data.b2);
4441
standard_metadata.egress_spec = port;
4542
}
4643
@name(".noop") action noop_0() {

testdata/p4_14_samples_outputs/flowlet_switching-frontend.p4

+11-23
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,13 @@ control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t
117117
}
118118

119119
control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
120-
bit<14> tmp;
121-
tuple<bit<32>, bit<32>, bit<8>, bit<16>, bit<16>, bit<16>> tmp_0;
122-
bit<16> tmp_1;
123-
bit<32> tmp_2;
124-
bit<32> tmp_3;
125-
bit<32> tmp_4;
126120
@name(".flowlet_id") register<bit<16>>(32w8192) flowlet_id_0;
127121
@name(".flowlet_lasttime") register<bit<32>>(32w8192) flowlet_lasttime_0;
128122
@name("._drop") action _drop_1() {
129123
mark_to_drop();
130124
}
131125
@name(".set_ecmp_select") action set_ecmp_select_0(bit<8> ecmp_base, bit<8> ecmp_count) {
132-
tmp_0 = { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort, meta.ingress_metadata.flowlet_id };
133-
hash<bit<14>, bit<10>, tuple<bit<32>, bit<32>, bit<8>, bit<16>, bit<16>, bit<16>>, bit<20>>(tmp, HashAlgorithm.crc16, (bit<10>)ecmp_base, tmp_0, (bit<20>)ecmp_count);
134-
meta.ingress_metadata.ecmp_offset = tmp;
126+
hash<bit<14>, bit<10>, tuple<bit<32>, bit<32>, bit<8>, bit<16>, bit<16>, bit<16>>, bit<20>>(meta.ingress_metadata.ecmp_offset, HashAlgorithm.crc16, (bit<10>)ecmp_base, { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort, meta.ingress_metadata.flowlet_id }, (bit<20>)ecmp_count);
135127
}
136128
@name(".set_nhop") action set_nhop_0(bit<32> nhop_ipv4, bit<9> port) {
137129
meta.ingress_metadata.nhop_ipv4 = nhop_ipv4;
@@ -140,13 +132,9 @@ control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_
140132
}
141133
@name(".lookup_flowlet_map") action lookup_flowlet_map_0() {
142134
hash<bit<13>, bit<13>, tuple<bit<32>, bit<32>, bit<8>, bit<16>, bit<16>>, bit<26>>(meta.ingress_metadata.flowlet_map_index, HashAlgorithm.crc16, 13w0, { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.ipv4.protocol, hdr.tcp.srcPort, hdr.tcp.dstPort }, 26w13);
143-
tmp_2 = (bit<32>)meta.ingress_metadata.flowlet_map_index;
144-
flowlet_id_0.read(tmp_1, tmp_2);
145-
meta.ingress_metadata.flowlet_id = tmp_1;
135+
flowlet_id_0.read(meta.ingress_metadata.flowlet_id, (bit<32>)meta.ingress_metadata.flowlet_map_index);
146136
meta.ingress_metadata.flow_ipg = (bit<32>)meta.intrinsic_metadata.ingress_global_timestamp;
147-
tmp_4 = (bit<32>)meta.ingress_metadata.flowlet_map_index;
148-
flowlet_lasttime_0.read(tmp_3, tmp_4);
149-
meta.ingress_metadata.flowlet_lasttime = tmp_3;
137+
flowlet_lasttime_0.read(meta.ingress_metadata.flowlet_lasttime, (bit<32>)meta.ingress_metadata.flowlet_map_index);
150138
meta.ingress_metadata.flow_ipg = meta.ingress_metadata.flow_ipg - meta.ingress_metadata.flowlet_lasttime;
151139
flowlet_lasttime_0.write((bit<32>)meta.ingress_metadata.flowlet_map_index, (bit<32>)meta.intrinsic_metadata.ingress_global_timestamp);
152140
}
@@ -226,23 +214,23 @@ control DeparserImpl(packet_out packet, in headers hdr) {
226214
}
227215

228216
control verifyChecksum(in headers hdr, inout metadata meta) {
229-
bit<16> tmp_5;
230-
bool tmp_6;
217+
bit<16> tmp;
218+
bool tmp_0;
231219
@name("ipv4_checksum") Checksum16() ipv4_checksum_0;
232220
apply {
233-
tmp_5 = ipv4_checksum_0.get<tuple<bit<4>, bit<4>, bit<8>, bit<16>, bit<16>, bit<3>, bit<13>, bit<8>, bit<8>, bit<32>, bit<32>>>({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr });
234-
tmp_6 = hdr.ipv4.hdrChecksum == tmp_5;
235-
if (tmp_6)
221+
tmp = ipv4_checksum_0.get<tuple<bit<4>, bit<4>, bit<8>, bit<16>, bit<16>, bit<3>, bit<13>, bit<8>, bit<8>, bit<32>, bit<32>>>({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr });
222+
tmp_0 = hdr.ipv4.hdrChecksum == tmp;
223+
if (tmp_0)
236224
mark_to_drop();
237225
}
238226
}
239227

240228
control computeChecksum(inout headers hdr, inout metadata meta) {
241-
bit<16> tmp_7;
229+
bit<16> tmp_1;
242230
@name("ipv4_checksum") Checksum16() ipv4_checksum_1;
243231
apply {
244-
tmp_7 = ipv4_checksum_1.get<tuple<bit<4>, bit<4>, bit<8>, bit<16>, bit<16>, bit<3>, bit<13>, bit<8>, bit<8>, bit<32>, bit<32>>>({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr });
245-
hdr.ipv4.hdrChecksum = tmp_7;
232+
tmp_1 = ipv4_checksum_1.get<tuple<bit<4>, bit<4>, bit<8>, bit<16>, bit<16>, bit<3>, bit<13>, bit<8>, bit<8>, bit<32>, bit<32>>>({ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, hdr.ipv4.identification, hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr });
233+
hdr.ipv4.hdrChecksum = tmp_1;
246234
}
247235
}
248236

0 commit comments

Comments
 (0)