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

Syntax sugar for creating IR::Expressions #196

Closed
wants to merge 1 commit into from

Conversation

ChrisDodd
Copy link
Contributor

  • IR::ERef type encapsulating a const IR::Expression * for overloading
  • overloaded operators and methods for creating new expressions
  • set types & srcInfo of newly created expressiong based on operands

- IR::ERef type encapsulating a `const IR::Expression *` for overloading
- overloaded operators and methods for creating new expressions
- set types & srcInfo of newly created expressions based on operands
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.

While this looks pretty it has two major shortcomings:

  • you are losing sourceInfo for many constructs. This will cause problems with giving good error messages and implementing debuggers
  • you are mixing some type checking logic with the IR classes. I think that typeChecking should be a visitor.

| '!' expression %prec PREFIX { $$ = new IR::LNot(@1, $2); }
| '~' expression %prec PREFIX { $$ = new IR::Cmpl(@1, $2); }
| '-' expression %prec PREFIX { $$ = new IR::Neg(@1, $2); }
| NOT expression %prec PREFIX { $$ = !$2; }
Copy link
Contributor

Choose a reason for hiding this comment

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

The grammar was partly serving as documentation of the IR; now developers will have to read the ERef class too.

You are losing the sourceInfo. Error messages will not be able to point to the source code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

source info is pulled from the operands by default -- it should be unchanged from what it was before (all tests indicate that it is unchanged)

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, then I withdraw this objection.
Should have read more carefully ERef.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, sourceInfo was pointing at the operation sign only, but maybe that's a minor issue.
The error looks like this:

a+b
 ^

and not like this:

a+b
^^^

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is an odd difference between the P4_14 and _16 frontends that I hadn't noticed before -- the P4_14 frontend always set the source info to the full expression (not just the operator token). I'm not sure which is better aesthetically for error messages. We could change it back to explicity setting the source info to just the operator char if desired.

Copy link
Contributor

Choose a reason for hiding this comment

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

P4-16 was the same originally, but this causes problems with long expressions that span multiple lines; the error output does not look as sharp - so I moved to pointing just the operator instead of also pointing the operands.

new IR::Constant(aval));
result->type = IR::Type_Bits::get(bval);
IR::ERef lookahead = method(typeargs);
IR::ERef result = lookahead.Slice(aval + bval - 1, aval);
Copy link
Contributor

Choose a reason for hiding this comment

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

You are losing the source info in the slice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

source info is unchanged here as far as I can see...

new IR::Vector<IR::Expression>());
IR::ERef base = primitive->operands.at(0);
IR::ERef method = base.Member(primitive->srcInfo, IR::Type_Header::isValid);
IR::ERef result = method();
Copy link
Contributor

Choose a reason for hiding this comment

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

You are losing the sourceInfo in the method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unchanged here as well

structure->emptyTypeArguments,
new IR::Vector<IR::Expression>());
IR::ERef tbl = new IR::PathExpression(newname);
IR::ERef method = tbl.Member(IR::IApply::applyMethodName);
Copy link
Contributor

Choose a reason for hiding this comment

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

All these are losing the sourceInfo.

@@ -452,8 +452,8 @@ simpleExpressionList

simpleKeysetExpression
: expression { $$ = $1; }
| expression MASK expression { $$ = new IR::Mask(@1 + @3, $1, $3); }
| expression RANGE expression { $$ = new IR::Range(@1 + @3, $1, $3); }
| expression MASK expression { $$ = $1.Mask($3); }
Copy link
Contributor

Choose a reason for hiding this comment

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

These are losing the sourceInfo.

: lvalue '(' argumentList ')' ';' { auto mc = new IR::MethodCallExpression(@1 + @4, $1,
new IR::Vector<IR::Type>(), $3);
$$ = new IR::MethodCallStatement(@1 + @4, mc); }
: lvalue '(' argumentList ')' ';' { $$ = new IR::MethodCallStatement(@1 + @4, $1($3)); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here for the methodCallExpression.

@@ -66,10 +66,12 @@ class Sub : Operation_Binary {
class Shl : Operation_Binary {
stringOp = "<<";
precedence = DBPrint::Prec_Shl;
Shl { if (type->is<Type::Unknown>() && left) type = left->type; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that this logic belongs to the IR class; it is maybe part of a typeChecker.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We want the types set for expressions in all cases, so it should be part of the constructor. Making it a separate pass just leads to holes where the type info is incorrect.

Copy link
Contributor

Choose a reason for hiding this comment

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

The P4-16 front-end and all mid-end passes I wrote never use the type in the expression; they always uses the typeMap. The only use for that type is when converting from p4-14 - the type is set by the p4-14 front-end and read by the p4-16 front-end.

@ChrisDodd
Copy link
Contributor Author

ChrisDodd commented Dec 20, 2016 via email

@mihaibudiu
Copy link
Contributor

Changing all existing passes will be a major endeavor. The simplest solution is to write a pass before the back-end which does the conversion you want and consume this lower-level IR in the back-end. Also, in the back-end you don't worry about generating P4 again.

Note that types do change while compiling, for example when enums are converted to ints, or when parameters are removed (then function signatures change). So the typechecker is not a no-op in the mid-end.


public:
ERef(const Expression *e) : expr(e) {} // NOLINT(runtime/explicit)
ERef(const Expression &e) : expr(&e) {} // NOLINT(runtime/explicit)
Copy link
Contributor

Choose a reason for hiding this comment

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

Are Expression objects always allocated via new? If you pass a temporary here you'll be storing a pointer to the stack. That's a pretty big footgun. Presumably if they are always allocated via new, the constructor that takes a pointer is sufficient, so I'd suggest only providing that one or else performing an actual copy here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, they should always be allocated by new. This was an attempt to get around the need for explicit IR::ERef constructor calls all over the place, but it doesn't really work.


namespace IR {

class ERef {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd mark this class final.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking that I wanted ERef_specific to be a subclass of ERef to avoid erasing the Expression subclass type, but that doesn't really work either.

@ChrisDodd
Copy link
Contributor Author

I think I'm going to withdraw this pull request as being only half-baked. Experimenting with using ERef in the backend, it seems far too cumbersome, as all the Expression references in the IR are const Expression * rather than ERef, so you end up needing many explicit IR::ERef calls, making the result no more readable than all the explicit new calls. Avoiding that will likely require integrating this with the IR generator to use some sort of explicit reference type in the generated IR classes rather than raw pointers.

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.

3 participants