Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constant folding streamlining, bugfixes, and Doxygen comments #541

Merged
merged 3 commits into from
May 10, 2017
Merged

constant folding streamlining, bugfixes, and Doxygen comments #541

merged 3 commits into from
May 10, 2017

Conversation

jnfoster
Copy link
Contributor

@jnfoster jnfoster commented Apr 28, 2017

Do not merge yet!

Submitting this pull request for comments. More documentation and tidying are needed

The current version of constant folding has two bugs:

  1. It does 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 contains the 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 current check for Declaration_Constants (https://github.com/p4lang/p4c/blob/master/frontends/common/constantFolding.cpp#L76) is not correct.

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

  • It is populated with genuine constants in the IR::Declaration_Constant case
  • It is read in the IR::PathExpression (aka variable) case
  1. When it transforms the IR, the constant folding pass 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 is 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 solution proposed here is to change the typeMap to const (which streamlines the constant folding pass) and reconstruct it after type checking. In addition, we statically convert the header stacks length to the appropriate type.

@mihaibudiu
Copy link
Contributor

I realized that there are more cases to consider.

H[2] h;
H[h.size +1] h1;
H[h1.size + 1] h2;
Etc.

Also:

control c()(bit<32> sz) { H[sz] h; apply {}}

Copy link
Contributor

@mihaibudiu mihaibudiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a test case for the failure cases you have described. Please add new test cases in testdata.

Otherwise it looks fine.

bool typesKnown;
bool warnings; // if true emit warnings
// maps expressions and declarations to their constant values
std::map<const IR::Node*, const IR::Expression*> constants;
std::map<const IR::Declaration_Constant*, const IR::Expression*> constants;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to update the comment above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please update the reference outputs. This can be done by running each failed test with the -f flag - after you made sure that the produced output is the intended one. For example:
./p4/testdata/p4_16_samples/stack.p4.test -f will update the output produced by this test.

@cc10512
Copy link
Contributor

cc10512 commented May 5, 2017

you should rebase on master, not merge master.

@jnfoster
Copy link
Contributor Author

jnfoster commented May 5, 2017

The GitHub web interface triggers these merges...

@cc10512
Copy link
Contributor

cc10512 commented May 5, 2017

That's why I never trusted GUIs ...

@sethfowler
Copy link
Contributor

Yeah. I wish the Github web interface would let you "Update branch" using a rebase instead of a merge. It would save a lot of time.

@jnfoster
Copy link
Contributor Author

jnfoster commented May 5, 2017

  • Fixed comments.
  • Updated test outputs, which covers bug 1 above.
  • Added a new test case for bug 2 above.

return expr;
if (expr->is<IR::BoolLiteral>())
}
if (expr->is<IR::BoolLiteral>()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this could be just expr->is<IR::Literal>() to get Constant, BoolLiteral and StringLiteral

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String literals are not currently folded, so this would change the logic of the function. Leaving it as-is for now.

if (expr->is<IR::ListExpression>()) {
auto list = expr->to<IR::ListExpression>();
for (auto e : list->components)
if (getConstant(e) == nullptr)
if (getConstant(e) == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our style guidelines do not require braces for this case.
Since @ChrisDodd prefers not to squander vertical space, I have avoided using them; they take 1 extra line.

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>())))
!(*d->type->to<IR::Type_Bits>() == *cst->type->to<IR::Type_Bits>())))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should set-up your editor to delete spaces at the end of lines.

}
}
if (init != d->initializer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here no braces?

bool typesKnown;
bool warnings; // if true emit warnings
// maps expressions and declarations to their constant values
std::map<const IR::Node*, const IR::Expression*> constants;
// maps declaration constants to constant values
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment could have three slashes

@@ -0,0 +1,16 @@
#include <core.p4>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about some of the tests I suggested in comments, e.g.

H[h1.size+1] h2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In principle, this should be possible, but it would be a deeper change based on how the passes are currently organized:

  • We don't fold IR::Members until after type checking.

  • In particular, we need to know the type of the expression to create a constant with the appropriate width.

  • But the type checker requires header stacks to have a compile-time known constant as their size.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be either a positive or a negative test. If it is negative it should have a good error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added stack3.p4 as an error test.

@jnfoster jnfoster mentioned this pull request May 7, 2017
@jnfoster jnfoster changed the title [do not merge] constant folding streamlining and bugfixes constant folding streamlining, bugfixes, and Doxygen comments May 7, 2017
@jnfoster
Copy link
Contributor Author

jnfoster commented May 7, 2017

Okay, I think this is finally ready to go. Please let me know if you have more feedback.

return e;
return v;
if (decl->is<IR::Declaration_Constant>()) {
auto dc = decl->to<IR::Declaration_Constant>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylisticly I prefer combining these two lines as

if (auto dc = decl->to<IR::Declaration_Constant>()) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this doesn't compile. So I reverted.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot the = sign in your code. It would compile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mea culpa! I'll push one more time.

d = new IR::Declaration_Constant(d->srcInfo, d->name, d->annotations, d->type, init);
auto orig = getOriginal()->to<IR::Declaration_Constant>();
BUG_CHECK(orig, "getOriginal() did not return a Declaration_Constant");
constants.emplace(orig, init);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 lines could be replaced by

constants.emplace(getOriginal<IR::Declaration_Constant>(), init);

-- the templated version of getOriginal does the cast and the BUG_CHECK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Done.

@@ -0,0 +1,42 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the output generated by the compiler for this test?
There should be a file stack3.p4-stderr at the very least.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@jnfoster
Copy link
Contributor Author

jnfoster commented May 8, 2017 via email

@mihaibudiu
Copy link
Contributor

In general you should git add everything in testdata after running a test.

@jnfoster
Copy link
Contributor Author

jnfoster commented May 8, 2017 via email

Copy link
Contributor

@mihaibudiu mihaibudiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit only contains some changes in the control-plane folder, but no files in testdata.

@antoninbas
Copy link
Member

@jnfoster I doubt you meant to update the PI submodule ref pointer (you probably shouldn't)

@jnfoster
Copy link
Contributor Author

jnfoster commented May 8, 2017

Gah. I think this is from pushing the update branch button here and then pushing additional changes from the command line. Let me fix this...

@jnfoster
Copy link
Contributor Author

jnfoster commented May 8, 2017

Okay @sethfowler helped me detangle myself.

Morale: don't ever push GitHub's "Update branch" button or follow their command-line instructions!

../testdata/p4_16_errors/stack3.p4(35): error: header h[]: Size of header stack type should be a constant
h[x] s3;
^^^^
../testdata/p4_16_errors/stack3.p4(35): error: Could not find type of h[]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbudiu-vmw I'm confused about why the type of h can't be found. Is this indicating a deeper error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, if a sub-expression fails type-checking no type is installed in the typeMap, so all the expressions that involve it as a sub-expression will fail to type-check too. These additional error messages are somewhat redundant, but other compilers are equally confused.

@mihaibudiu
Copy link
Contributor

@jnfoster : this code does not build.

@jnfoster
Copy link
Contributor Author

Sorry about that. Clearly I had not run make check when I committed @ChrisDodd's suggested changes in. I just pushed a new version that walks it back and verified that it compiles and passes all tests on my machine. Should be fixed now.

jnfoster added 3 commits May 10, 2017 12:02
* Simplify handling of `constants` map. It is now populated in the visitor for `IR::Declaration_Cosntant` nodes and accessed by the visitor for `IR::PathExpression` nodes.

* Make `typeMap` `const`.

* Polish and streamline the code.
* Add a new failing test (stack3.p4)

* Update test outputs for other tests
@jnfoster jnfoster merged commit 07f93f8 into p4lang:master May 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants