-
Notifications
You must be signed in to change notification settings - Fork 441
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
Added support for assert and assume statement (#1548) #1799
Conversation
* Added removeAssertAssume pass in frontend which will remove all assert and assume statements from program when BUILD_TYPE=RELEASE * Not supported in deparsers and verifyChecksum block with assumption that it is not needed * Added support for code gen for built-in methods in ebpf backend * Changed generating error code value in bmv2 backend to be generated like hex value into json file Signed-off-by: Enisa Hadzic <enisa.hadzic@rt-rk.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice, but I think we should make assert/assume part of the architecture files, and not part of core.p4.
Or part of some library.
@@ -251,7 +251,9 @@ void ExpressionConverter::postorder(const IR::Member* expression) { | |||
result->emplace("type", "hexstr"); | |||
auto decl = type->to<IR::Type_Error>()->getDeclByName(expression->member.name); | |||
auto errorValue = structure->errorCodesMap.at(decl); | |||
result->emplace("value", Util::toString(errorValue)); | |||
// this generates error constant like hex value | |||
auto reprValue = stringRepr(errorValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is a bug fix you should probably file a separate PR for it, including a test case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for all comments.
I think that it is a bug fix, because when I extended core.p4 errors, I had noticed that two tests had failed: checksum3-bmv2.p4 and checksum2-bmv2.p4. These tests reported out_of_range exception when make check is done, because tests added new errors which will have values 9 and 10, but it didn't generate error values as hex values (0x9 and 0xa) in json file. For example, in case of error value 10 it was generated like:
"type" : "hexstr",
"value" : "10"
which will be read as hex value 16, not 10 as it should be.
Now it is generate like:
"type" : "hexstr",
"value" : "0xa"
I will include a test for this case and make it as a seperate PR.
@@ -187,6 +187,22 @@ Util::IJson* ParserConverter::convertParserStatement(const IR::StatOrDecl* stat) | |||
params->append(jexpr); | |||
} | |||
return result; | |||
} else if (extfn->method->name.name == "assert") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the code is almost the same, can you unify it?
@@ -997,4 +999,42 @@ void ExternConverter_Digest::convertExternInstance( | |||
UNUSED const IR::ExternBlock* eb, UNUSED const bool& emitExterns) | |||
{ /* TODO */ } | |||
|
|||
Util::IJson* ExternConverter_assert::convertExternFunction( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lots of code duplicated here as well. Can you refactor?
ExternConverter_assert ExternConverter_assert::singleton; | ||
ExternConverter_assume ExternConverter_assume::singleton; | ||
|
||
Util::IJson* ExternConverter_assert::convertExternFunction( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and again. Could this code go to common?
builder->emitIndent(); | ||
builder->append("if ("); | ||
visit(function->expr->arguments->at(0)->expression); | ||
builder->append(" == false) "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be rather !expr
return false; | ||
} | ||
} else if (mi->is<P4::BuiltInMethod>()) { | ||
// Added this because compiler didn't recognize assert(hdr.ethernet.isValid()), for expample |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is a bug maybe you can file a separate PR and a test case for it.
When does the compiler not recognize isValid, only within assert?
isValid should be handled by the expressionConverter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compiler didn't recognize isValid in assert, but also it didn't recognize setValid and setInvalid method calls for that ebpf backend when they are called from a parser state.
Also, I notice that compiler doesn't recognize verify call in parser state for ebpf backend, but I didn't add code generation for that case. In that case following error is reported:
error: Unexpected method call in parser verify
verify(false, error.NoError);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If I understood it well, it should be enabled to call methods, that are mentioned above, in parser state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible that verify is not supported yet in the back-end.
But isValid should work; if you have a counter-example please file another issue.
@@ -54,6 +54,67 @@ class ActionTranslationVisitor : public CodeGenInspector { | |||
return false; | |||
} | |||
|
|||
bool preorder(const IR::MethodCallExpression* expression) { | |||
auto mi = P4::MethodInstance::resolve(expression, refMap, typeMap); | |||
if (mi->is<P4::ExternFunction>()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code should be refactored and should appear only once.
@@ -21,7 +21,8 @@ namespace EBPF { | |||
|
|||
|
|||
void KernelSamplesTarget::emitIncludes(Util::SourceCodeBuilder* builder) const { | |||
builder->append("#include \"ebpf_kernel.h\"\n"); | |||
builder->append("#include \"ebpf_kernel.h\"\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there stdio.h in the kernel?
@@ -30,6 +30,8 @@ enum class StandardExceptions { | |||
OverwritingHeader, | |||
HeaderTooShort, | |||
ParserTimeout, | |||
AssertError, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a big assumption: that we are changing code.p4.
I would rather expect that assert and assume are library functions - at least for a while.
Putting them in core.p4 may break programs that happen to use identifiers with these names.
This is a subject for the LDWG.
@@ -148,6 +149,9 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P | |||
new TypeInference(&refMap, &typeMap, false), // insert casts | |||
new ValidateMatchAnnotations(&typeMap), | |||
new DefaultArguments(&refMap, &typeMap), // add default argument values to parameters | |||
#ifdef NDEBUG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all, this NDEBUG is for the compiler source code. You should remove assert/assume only if the p4 compiler is invoked with NDEBUG, not gcc.
Second, this is too early to remove the calls. If the calls have side-effects they should still happen. So the removal should happen after the SideEffects pass.
Third, I am not sure we actually want this part of the front-end; perhaps the mid-end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understood you well, p4 compiler should remove assert/assume if it is invoked with -DNDEBUG (and default) option and leave it if it is invoked with -UNDEBUG. I moved this pass to be the first in the mid-end, before any of the optimizations is done. Feel free to correct me if I misunderstood it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What Mihai means is that when you write #ifdef NDEBUG
, you are using a preprocessor flag that is meant for the C++ compiler with which you compile p4c. The assert
/ assume
statements should be removed if the p4c compiler is invoked with -DNDEBUG
. When I compile a C / C++ program, I choose whether asserts are removed by providing -DNEBUG
or not. Whether the C / C++ compiler was itself compiled with -DNEBUG
is completely irrelevant. The p4c invocation p4c -DNEBUG <path to P4 program>
should remove the assert
/ assume
statements. I would argue that using a preprocessor flag for this is probably not ideal. What about p4c -ndebug <path to P4 program>
instead?
auto linePosition = srcInfo.toPositionString(); | ||
builder->emitIndent(); | ||
builder->appendFormat( | ||
"fprintf(stderr, \"Assertion error: ' % s ' failed, file ' % s ', line % s \\n\");", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tested this with kernel code? Can you just call fprintf from an ebpf program running in the kernel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to test it, but I couldn't manage to do it because I got some errors when I tried to compile .c file that is p4c output. So I did it according to the rest of code gen for ebpf backend.
Maybe I didn't understand well how to test it, so I would be thankful if you can give me more information how to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you get errors compiling a C file it's a bug in the ebpf back-end - please file an issue.
The ebpf testing tries to test the files in multiple ways, depending on your kernel version.
But the ebpf code is supposed to run in the kernel, so it may not use fprintf.
The more important question is how to handle an assertion failure in ebpf.
Usually assume gives preconditions (which are assumed to be true before you enter a piece of code), and assert enforces postconditions (which are true when you exit a piece of code). Usually a static analyzer (a prover) tries to prove the postconditions assuming that the preconditions hold. The prover cannot prove the precoditions - it will take them for granted. That's the difference between assume and assert. This behavior is true for static analyses. The (dynamic) runtime behavior of both statements is the same, though: if the condition is false, then something is very wrong. For assume it means that the assumption was incorrect. For asserts it would mean that the proof made by the prover was wrong. |
Hello everyone, I have done changes according to your suggestions. Unfortunately I didn't manage to test implementation for ebpf backend, so I made a new PR with support for bmv2 backend only. Please take a look, here is link to new PR #1859, meanwhile I will try to resolve problem with ebpf. |
Added removeAssertAssume pass in frontend which will remove
all assert and assume statements from program when BUILD_TYPE=RELEASE
Not supported in deparsers and verifyChecksum block with assumption
that it is not needed
Added support for code gen for built-in methods in ebpf backend
Changed generating error code value in bmv2 backend to be
generated like hex value into json file
Signed-off-by: Enisa Hadzic enisa.hadzic@rt-rk.com