Skip to content

Commit

Permalink
constant folding streamlining, bug fixes, and Doxygen comments (#541)
Browse files Browse the repository at this point in the history
The previous version of constant folding had two bugs:

It did not fold constants in all cases, or check that declaration constants are initialized with genuine constants. For example, on the input program,
```
const T t = { 32s10, 32s20 };
const S s = { { 32s15, 32s25}, t };
const int<32> x = t.t1;
const int<32> y = s.s1.t2;
const int<32> w = .t.t1;
const T t1 = (T)s.s1;
```
constant folding (with type information) yields:
```
const T t = { 32s10, 32s20 };
const S s = { { 32s15, 32s25 }, t };
const int<32> x = 32s10;
const int<32> y = 32s25;
const int<32> w = 32s10;
const T t1 = { 32s15, 32s25 };
```
Note that the RHS of the declaration of s is not a constant -- it references a variable t.

The root cause of this behavior is due to the use of auxiliary data structure constants: the pass does not ensure that all expressions that are inserted into this map are genuine constants, yet it assumes that getConstants, which returns arbitrary entries from constants, are always a constant. Hence, the check for Declaration_Constants is not correct.

The fix implemented here is to streamline the use of constants so it is used in exactly two places:

1. It is populated with genuine constants in the IR::Declaration_Constant case
2. It is read in the IR::PathExpression (aka variable) case

In addition, when the pass transforms the IR, it attempts to patch the typeMap with type information about the newly inserted nodes. However, the types it inserts are not always correct -- e.g., in particular, in the IR::Member case, when header stack sizes are folded, the type map is updated with `origtype`, the type of the expression, but the arbitrary-precision integer may require a cast. Hence, subsequent type-checking passes will rely on the information in the typeMap and assume that no cast is required. However, if the typeMap is ever cleared, then the typeChecker will mutate the node, triggering an error.
This error was not triggered by any of our current tests -- in examples like p4_16_samples/stack.p4, the IR::Member is dead code and is eliminated before the typeMap is cleared. Hence, the error never manifests. However, a close variant can trigger the error:
```
#include <core.p4>
header h { }
control c(out bit<32> x) {
    apply {
        h[4] stack;
        bit<32> sz = stack.size;
        x = sz;
    }
}
control Simpler(out bit<32> x);
package top(Simpler ctr);
top(c()) main;
```
The fix implemented here is to change the `typeMap` to `const` and reconstruct it after type checking. In addition, we statically convert header stacks sizes to the appropriate type.

This commit adds a new unit tests that checks for failures involving constant folding and header stacks.

It also adds Doxygen documentation.
  • Loading branch information
jnfoster authored May 10, 2017
1 parent db47de5 commit 07f93f8
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 110 deletions.
133 changes: 37 additions & 96 deletions frontends/common/constantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ namespace P4 {

const IR::Expression* DoConstantFolding::getConstant(const IR::Expression* expr) const {
CHECK_NULL(expr);
auto cst = get(constants, expr);
if (cst != nullptr)
return cst;
if (expr->is<IR::Constant>())
return expr;
if (expr->is<IR::BoolLiteral>())
Expand All @@ -35,38 +32,33 @@ const IR::Expression* DoConstantFolding::getConstant(const IR::Expression* expr)
for (auto e : list->components)
if (getConstant(e) == nullptr)
return nullptr;
return list;
return expr;
}
if (typesKnown) {
auto ei = EnumInstance::resolve(expr, typeMap);
if (ei != nullptr)
return expr;
}

return nullptr;
}

// This has to be called from a visitor method - it calls getOriginal()
void DoConstantFolding::setConstant(const IR::Node* node, const IR::Expression* result) {
LOG2("Folding " << node << " to " << result << " (" << result->id << ")");
auto orig = getOriginal();
constants.emplace(node, result);
constants.emplace(orig, result);
}

const IR::Node* DoConstantFolding::postorder(IR::PathExpression* e) {
if (refMap == nullptr)
return e;
auto decl = refMap->getDeclaration(e->path);
if (decl == nullptr)
return e;
auto v = get(constants, decl->getNode());
if (v == nullptr)
return e;
setConstant(e, v);
if (v->is<IR::ListExpression>())
return e;
return v;
if (auto dc = decl->to<IR::Declaration_Constant>()) {
auto cst = get(constants, dc);
if (cst == nullptr)
cst = getConstant(dc->initializer);
if (cst == nullptr)
return e;
if (!typesKnown && cst->is<IR::ListExpression>())
return e;
return cst;
}
return e;
}

const IR::Node* DoConstantFolding::postorder(IR::Declaration_Constant* d) {
Expand All @@ -76,27 +68,24 @@ const IR::Node* DoConstantFolding::postorder(IR::Declaration_Constant* d) {
::error("%1%: Cannot evaluate initializer for constant", d->initializer);
return d;
}

if (typesKnown) {
// If we typechecked we're safe
setConstant(d, init);
} else {
// In fact, this declaration may imply a cast, so the actual value of
// d is not init, but (d->type)init. The typechecker inserts casts,
// but if we run this before typechecking we have to be more conservative.
if (!typesKnown) {
// This declaration may imply a cast, so the actual value of d
// is not init, but (d->type)init. The typechecker inserts
// casts, but if we run this before typechecking we have to be
// more conservative.
if (init->is<IR::Constant>()) {
auto cst = init->to<IR::Constant>();
if (d->type->is<IR::Type_Bits>()) {
if (cst->type->is<IR::Type_InfInt>() ||
(cst->type->is<IR::Type_Bits>() &&
!(*d->type->to<IR::Type_Bits>() == *cst->type->to<IR::Type_Bits>())))
init = new IR::Constant(init->srcInfo, d->type, cst->value, cst->base);
setConstant(d, init);
}
}
if (init != d->initializer)
d = new IR::Declaration_Constant(d->srcInfo, d->name, d->annotations, d->type, init);
}
if (init != d->initializer)
d = new IR::Declaration_Constant(d->srcInfo, d->name, d->annotations, d->type, init);
constants.emplace(getOriginal<IR::Declaration_Constant>(), init);
return d;
}

Expand Down Expand Up @@ -124,9 +113,7 @@ const IR::Node* DoConstantFolding::postorder(IR::Cmpl* e) {
}

mpz_class value = ~cst->value;
auto result = new IR::Constant(cst->srcInfo, t, value, cst->base, true);
setConstant(e, result);
return result;
return new IR::Constant(cst->srcInfo, t, value, cst->base, true);
}

const IR::Node* DoConstantFolding::postorder(IR::Neg* e) {
Expand All @@ -151,9 +138,7 @@ const IR::Node* DoConstantFolding::postorder(IR::Neg* e) {
}

mpz_class value = -cst->value;
auto result = new IR::Constant(cst->srcInfo, t, value, cst->base, true);
setConstant(e, result);
return result;
return new IR::Constant(cst->srcInfo, t, value, cst->base, true);
}

const IR::Constant*
Expand Down Expand Up @@ -260,19 +245,15 @@ DoConstantFolding::compare(const IR::Operation_Binary* e) {
return e;
}
bool bresult = (left->value == right->value) == eqTest;
auto result = new IR::BoolLiteral(e->srcInfo, bresult);
setConstant(e, result);
return result;
return new IR::BoolLiteral(e->srcInfo, bresult);
} else if (typesKnown) {
auto le = EnumInstance::resolve(eleft, typeMap);
auto re = EnumInstance::resolve(eright, typeMap);
if (le != nullptr && re != nullptr) {
BUG_CHECK(le->type == re->type,
"%1%: different enum types in comparison", e);
bool bresult = (le->name == re->name) == eqTest;
auto result = new IR::BoolLiteral(e->srcInfo, bresult);
setConstant(e, result);
return result;
return new IR::BoolLiteral(e->srcInfo, bresult);
}
}

Expand Down Expand Up @@ -349,13 +330,10 @@ DoConstantFolding::binary(const IR::Operation_Binary* e,
}
}

const IR::Expression* result;
if (e->is<IR::Operation_Relation>())
result = new IR::BoolLiteral(e->srcInfo, value != 0);
return new IR::BoolLiteral(e->srcInfo, value != 0);
else
result = new IR::Constant(e->srcInfo, resultType, value, left->base, true);
setConstant(e, result);
return result;
return new IR::Constant(e->srcInfo, resultType, value, left->base, true);
}

const IR::Node* DoConstantFolding::postorder(IR::LAnd* e) {
Expand All @@ -368,16 +346,10 @@ const IR::Node* DoConstantFolding::postorder(IR::LAnd* e) {
::error("%1%: Expected a boolean value", left);
return e;
}

if (lcst->value) {
setConstant(e, e->right);
return e->right;
}

// Short-circuit folding
auto result = new IR::BoolLiteral(left->srcInfo, false);
setConstant(e, result);
return result;
return new IR::BoolLiteral(left->srcInfo, false);
}

const IR::Node* DoConstantFolding::postorder(IR::LOr* e) {
Expand All @@ -390,16 +362,10 @@ const IR::Node* DoConstantFolding::postorder(IR::LOr* e) {
::error("%1%: Expected a boolean value", left);
return e;
}

if (!lcst->value) {
setConstant(e, e->right);
return e->right;
}

// Short-circuit folding
auto result = new IR::BoolLiteral(left->srcInfo, true);
setConstant(e, result);
return result;
return new IR::BoolLiteral(left->srcInfo, true);
}

const IR::Node* DoConstantFolding::postorder(IR::Slice* e) {
Expand Down Expand Up @@ -451,9 +417,7 @@ const IR::Node* DoConstantFolding::postorder(IR::Slice* e) {
auto resultType = typeMap->getType(getOriginal(), true);
if (!resultType->is<IR::Type_Bits>())
BUG("Type of slice is not Type_Bits, but %1%", resultType);
auto result = new IR::Constant(e->srcInfo, resultType, value, cbase->base, true);
setConstant(e, result);
return result;
return new IR::Constant(e->srcInfo, resultType, value, cbase->base, true);
}

const IR::Node* DoConstantFolding::postorder(IR::Member* e) {
Expand All @@ -467,7 +431,7 @@ const IR::Node* DoConstantFolding::postorder(IR::Member* e) {
if (type->is<IR::Type_Stack>() && e->member == IR::Type_Stack::arraySize) {
auto st = type->to<IR::Type_Stack>();
auto size = st->getSize();
result = new IR::Constant(st->size->srcInfo, size);
result = new IR::Constant(st->size->srcInfo, origtype, size);
} else {
auto expr = getConstant(e->expr);
if (expr == nullptr)
Expand All @@ -494,9 +458,6 @@ const IR::Node* DoConstantFolding::postorder(IR::Member* e) {
BUG("Could not find field %1% in type %2%", e->member, type);
result = list->components.at(index)->clone();
}
typeMap->setType(result, origtype);
typeMap->setCompileTimeConstant(result);
setConstant(e, result);
return result;
}

Expand Down Expand Up @@ -526,9 +487,7 @@ const IR::Node* DoConstantFolding::postorder(IR::Concat* e) {

auto resultType = IR::Type_Bits::get(lt->size + rt->size, lt->isSigned);
mpz_class value = Util::shift_left(left->value, static_cast<unsigned>(rt->size)) + right->value;
auto result = new IR::Constant(e->srcInfo, resultType, value, left->base);
setConstant(e, result);
return result;
return new IR::Constant(e->srcInfo, resultType, value, left->base);
}

const IR::Node* DoConstantFolding::postorder(IR::LNot* e) {
Expand All @@ -541,10 +500,7 @@ const IR::Node* DoConstantFolding::postorder(IR::LNot* e) {
::error("%1%: Expected a boolean value", op);
return e;
}

auto result = new IR::BoolLiteral(cst->srcInfo, !cst->value);
setConstant(e, result);
return result;
return new IR::BoolLiteral(cst->srcInfo, !cst->value);
}

const IR::Node* DoConstantFolding::postorder(IR::Mux* e) {
Expand Down Expand Up @@ -577,7 +533,6 @@ const IR::Node* DoConstantFolding::shift(const IR::Operation_Binary* e) {

if (sgn(cr->value) == 0) {
// ::warning("%1% with zero", e);
setConstant(e, e->left);
return e->left;
}

Expand All @@ -604,9 +559,7 @@ const IR::Node* DoConstantFolding::shift(const IR::Operation_Binary* e) {
value = Util::shift_left(value, shift);
else
value = Util::shift_right(value, shift);
auto result = new IR::Constant(e->srcInfo, left->type, value, cl->base);
setConstant(e, result);
return result;
return new IR::Constant(e->srcInfo, left->type, value, cl->base);
}

const IR::Node *DoConstantFolding::postorder(IR::Cast *e) {
Expand All @@ -624,15 +577,11 @@ const IR::Node *DoConstantFolding::postorder(IR::Cast *e) {
auto type = etype->to<IR::Type_Bits>();
if (expr->is<IR::Constant>()) {
auto arg = expr->to<IR::Constant>();
auto result = cast(arg, arg->base, type);
setConstant(e, result);
return result;
return cast(arg, arg->base, type);
} else if (expr -> is<IR::BoolLiteral>()) {
auto arg = expr->to<IR::BoolLiteral>();
int v = arg->value ? 1 : 0;
auto result = new IR::Constant(e->srcInfo, type, v, 10);
setConstant(e, result);
return result;
return new IR::Constant(e->srcInfo, type, v, 10);
} else {
return e;
}
Expand Down Expand Up @@ -662,19 +611,11 @@ const IR::Node *DoConstantFolding::postorder(IR::Cast *e) {
::error("%1%: Only 0 and 1 can be cast to booleans", e);
return e;
}
auto lit = new IR::BoolLiteral(e->srcInfo, v == 1);
setConstant(e, lit);
return lit;
return new IR::BoolLiteral(e->srcInfo, v == 1);
}
} else if (etype->is<IR::Type_StructLike>()) {
auto result = expr->clone();
auto origtype = typeMap->getType(getOriginal());
typeMap->setType(result, origtype);
typeMap->setCompileTimeConstant(result);
setConstant(e, result);
return result;
return expr->clone();
}

return e;
}

Expand Down
Loading

0 comments on commit 07f93f8

Please sign in to comment.