From 472f460b4460f5fdf94e68cbc18db2fff3b131f4 Mon Sep 17 00:00:00 2001 From: Chris Dodd Date: Mon, 16 Sep 2024 23:39:24 +0000 Subject: [PATCH] Support for [lsb+:width] slices - allows for non-const lsb slices (width must still be const) Signed-off-by: Chris Dodd --- backends/p4tools/common/lib/symbolic_env.cpp | 2 +- .../modules/smith/common/statements.cpp | 2 +- .../testgen/core/small_step/expr_stepper.cpp | 2 +- .../testgen/core/small_step/expr_stepper.h | 2 +- frontends/common/constantFolding.cpp | 45 ++++++++++++ frontends/common/constantFolding.h | 1 + frontends/p4-14/fromv1.0/converters.cpp | 3 +- frontends/p4/alias.h | 2 +- frontends/p4/def_use.cpp | 2 +- frontends/p4/def_use.h | 2 +- frontends/p4/inlining.cpp | 2 +- frontends/p4/sideEffects.cpp | 2 +- frontends/p4/simplifyDefUse.cpp | 9 +-- frontends/p4/strengthReduction.cpp | 19 +++++ frontends/p4/strengthReduction.h | 1 + frontends/p4/toP4/toP4.cpp | 3 +- frontends/p4/toP4/toP4.h | 2 +- .../p4/typeChecking/readOnlyTypeInference.cpp | 1 + frontends/p4/typeChecking/typeCheckExpr.cpp | 72 +++++++++++++++++++ frontends/p4/typeChecking/typeChecker.h | 3 + frontends/p4/typeChecking/typeInference.cpp | 1 + frontends/parsers/p4/p4parser.ypp | 11 ++- ir/dbprint-expression.cpp | 7 ++ ir/expression.cpp | 5 ++ ir/expression.def | 34 +++++++-- ir/write_context.cpp | 4 +- midend/def_use.cpp | 7 +- midend/global_copyprop.cpp | 9 +-- midend/local_copyprop.cpp | 2 +- testdata/p4_16_samples/forloop5a.p4 | 30 ++++++++ .../p4_16_samples_outputs/forloop5a-first.p4 | 28 ++++++++ .../forloop5a-frontend.p4 | 30 ++++++++ .../p4_16_samples_outputs/forloop5a-midend.p4 | 29 ++++++++ testdata/p4_16_samples_outputs/forloop5a.p4 | 28 ++++++++ .../p4_16_samples_outputs/forloop5a.p4-stderr | 0 35 files changed, 369 insertions(+), 33 deletions(-) create mode 100644 testdata/p4_16_samples/forloop5a.p4 create mode 100644 testdata/p4_16_samples_outputs/forloop5a-first.p4 create mode 100644 testdata/p4_16_samples_outputs/forloop5a-frontend.p4 create mode 100644 testdata/p4_16_samples_outputs/forloop5a-midend.p4 create mode 100644 testdata/p4_16_samples_outputs/forloop5a.p4 create mode 100644 testdata/p4_16_samples_outputs/forloop5a.p4-stderr diff --git a/backends/p4tools/common/lib/symbolic_env.cpp b/backends/p4tools/common/lib/symbolic_env.cpp index b60bea00e20..4d544b8ba66 100644 --- a/backends/p4tools/common/lib/symbolic_env.cpp +++ b/backends/p4tools/common/lib/symbolic_env.cpp @@ -126,7 +126,7 @@ bool SymbolicEnv::isSymbolicValue(const IR::Node *node) { binary->is() || binary->is()) && isSymbolicValue(binary->left) && isSymbolicValue(binary->right); } - if (const auto *slice = expr->to()) { + if (const auto *slice = expr->to()) { return isSymbolicValue(slice->e0) && isSymbolicValue(slice->e1) && isSymbolicValue(slice->e2); } diff --git a/backends/p4tools/modules/smith/common/statements.cpp b/backends/p4tools/modules/smith/common/statements.cpp index d69b0d82af1..775871e5b92 100644 --- a/backends/p4tools/modules/smith/common/statements.cpp +++ b/backends/p4tools/modules/smith/common/statements.cpp @@ -156,7 +156,7 @@ void StatementGenerator::removeLval(const IR::Expression *left, const IR::Type * lvalStr = path->path->name.name; } else if (const auto *mem = left->to()) { lvalStr = mem->member.name; - } else if (const auto *slice = left->to()) { + } else if (const auto *slice = left->to()) { lvalStr = slice->e0->to()->path->name.name; } else if (const auto *arrIdx = left->to()) { lvalStr = arrIdx->left->to()->path->name.name; diff --git a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp index 4c4e71d79b7..e89562389d2 100644 --- a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp @@ -491,7 +491,7 @@ bool ExprStepper::preorder(const IR::StructExpression *structExpression) { return stepSymbolicValue(structExpression); } -bool ExprStepper::preorder(const IR::Slice *slice) { +bool ExprStepper::preorder(const IR::AbstractSlice *slice) { logStep(slice); if (!SymbolicEnv::isSymbolicValue(slice->e0)) { diff --git a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h index 6023d9948fa..6937d3602f5 100644 --- a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h +++ b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h @@ -292,7 +292,7 @@ class ExprStepper : public AbstractStepper { bool preorder(const IR::SelectExpression *selectExpression) override; bool preorder(const IR::BaseListExpression *listExpression) override; bool preorder(const IR::StructExpression *structExpression) override; - bool preorder(const IR::Slice *slice) override; + bool preorder(const IR::AbstractSlice *slice) override; bool preorder(const IR::P4Table *table) override; }; diff --git a/frontends/common/constantFolding.cpp b/frontends/common/constantFolding.cpp index 6cf142553cb..2ba8c18ae94 100644 --- a/frontends/common/constantFolding.cpp +++ b/frontends/common/constantFolding.cpp @@ -678,6 +678,51 @@ const IR::Node *DoConstantFolding::postorder(IR::Slice *e) { return new IR::Constant(e->srcInfo, resultType, value, cbase->base, true); } +const IR::Node *DoConstantFolding::postorder(IR::PlusSlice *e) { + auto *e0 = getConstant(e->e0); + auto *lsb = getConstant(e->e1); + auto *width = getConstant(e->e2); + if (!width) { + if (typesKnown) + error(ErrorType::ERR_EXPECTED, "%1%: slice indexes must be compile-time constants", + e->e2); + return e; + } + + if (!e0 || !lsb) return e; + + auto clsb = lsb->to(); + if (clsb == nullptr) { + error(ErrorType::ERR_EXPECTED, "%1%: expected an integer value", lsb); + return e; + } + auto cwidth = width->to(); + if (cwidth == nullptr) { + error(ErrorType::ERR_EXPECTED, "%1%: expected an integer value", width); + return e; + } + auto cbase = e0->to(); + if (cbase == nullptr) { + error(ErrorType::ERR_EXPECTED, "%1%: expected an integer value", e->e0); + return e; + } + + int w = cwidth->asInt(); + int l = clsb->asInt(); + if (l < 0) { + ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected slice indexes to be non-negative", + e->e2); + return e; + } + if (overflowWidth(e, l) || overflowWidth(e, l + w)) return e; + big_int value = cbase->value >> l; + big_int mask = 1; + mask = (mask << w) - 1; + value = value & mask; + auto resultType = IR::Type_Bits::get(w); + return new IR::Constant(e->srcInfo, resultType, value, cbase->base, true); +} + const IR::Node *DoConstantFolding::postorder(IR::Member *e) { if (!typesKnown) return e; auto orig = getOriginal(); diff --git a/frontends/common/constantFolding.h b/frontends/common/constantFolding.h index 9d1f9a678f6..e6f54a842c0 100644 --- a/frontends/common/constantFolding.h +++ b/frontends/common/constantFolding.h @@ -141,6 +141,7 @@ class DoConstantFolding : public Transform, public ResolutionContext { const IR::Node *postorder(IR::LAnd *e) override; const IR::Node *postorder(IR::LOr *e) override; const IR::Node *postorder(IR::Slice *e) override; + const IR::Node *postorder(IR::PlusSlice *e) override; const IR::Node *postorder(IR::Add *e) override; const IR::Node *postorder(IR::AddSat *e) override; const IR::Node *postorder(IR::Sub *e) override; diff --git a/frontends/p4-14/fromv1.0/converters.cpp b/frontends/p4-14/fromv1.0/converters.cpp index 1670171bdaf..d9cf4aef726 100644 --- a/frontends/p4-14/fromv1.0/converters.cpp +++ b/frontends/p4-14/fromv1.0/converters.cpp @@ -110,8 +110,7 @@ const IR::Node *ExpressionConverter::postorder(IR::Primitive *primitive) { auto typeargs = new IR::Vector(); typeargs->push_back(IR::Type_Bits::get(aval + bval)); auto lookahead = new IR::MethodCallExpression(method, typeargs); - auto result = new IR::Slice(primitive->srcInfo, lookahead, new IR::Constant(bval - 1), - new IR::Constant(0)); + auto result = new IR::Slice(primitive->srcInfo, lookahead, bval - 1, 0); result->type = IR::Type_Bits::get(bval); return result; } else if (primitive->name == "valid") { diff --git a/frontends/p4/alias.h b/frontends/p4/alias.h index 39dbcf464fe..84b28de568f 100644 --- a/frontends/p4/alias.h +++ b/frontends/p4/alias.h @@ -191,7 +191,7 @@ class ReadsWrites : public Inspector, public ResolutionContext { rw.emplace(expression, e0->join(e1)->join(e2)); } - void postorder(const IR::Slice *expression) override { + void postorder(const IR::AbstractSlice *expression) override { auto e = ::P4::get(rw, expression->e0); CHECK_NULL(e); rw.emplace(expression, e); diff --git a/frontends/p4/def_use.cpp b/frontends/p4/def_use.cpp index 0c15b27450a..7ea442e5c72 100644 --- a/frontends/p4/def_use.cpp +++ b/frontends/p4/def_use.cpp @@ -536,7 +536,7 @@ bool ComputeWriteSet::preorder(const IR::Literal *expression) { return false; } -bool ComputeWriteSet::preorder(const IR::Slice *expression) { +bool ComputeWriteSet::preorder(const IR::AbstractSlice *expression) { visit(expression->e0); expressionWrites(expression, lhs ? getWrites(expression->e0) : LocationSet::empty); return false; diff --git a/frontends/p4/def_use.h b/frontends/p4/def_use.h index 2a7f50eec85..dacdccefb77 100644 --- a/frontends/p4/def_use.h +++ b/frontends/p4/def_use.h @@ -624,7 +624,7 @@ class ComputeWriteSet : public Inspector, public IHasDbPrint { // expressions bool preorder(const IR::Literal *expression) override; - bool preorder(const IR::Slice *expression) override; + bool preorder(const IR::AbstractSlice *expression) override; bool preorder(const IR::TypeNameExpression *expression) override; bool preorder(const IR::PathExpression *expression) override; bool preorder(const IR::Member *expression) override; diff --git a/frontends/p4/inlining.cpp b/frontends/p4/inlining.cpp index 5f1e2b06d00..e8505276b65 100644 --- a/frontends/p4/inlining.cpp +++ b/frontends/p4/inlining.cpp @@ -58,7 +58,7 @@ class FindLocationSets : public Inspector { return false; } - bool preorder(const IR::Slice *expression) { + bool preorder(const IR::AbstractSlice *expression) { visit(expression->e0); auto base = get(expression->e0); set(expression, base); diff --git a/frontends/p4/sideEffects.cpp b/frontends/p4/sideEffects.cpp index 9e50ebd2527..6fc15859dde 100644 --- a/frontends/p4/sideEffects.cpp +++ b/frontends/p4/sideEffects.cpp @@ -435,7 +435,7 @@ const IR::Node *DoSimplifyExpressions::preorder(IR::MethodCallExpression *mce) { // If the parameter is out and the argument is a slice then // also use a temporary; makes the job of def-use analysis easier - if (arg->expression->is() && p->hasOut()) { + if (arg->expression->is() && p->hasOut()) { LOG3("Using temporary for " << dbp(mce) << " param " << dbp(p) << " since it is an out slice"); useTemporary.emplace(p); diff --git a/frontends/p4/simplifyDefUse.cpp b/frontends/p4/simplifyDefUse.cpp index c9166fd87a9..9a6d6861a43 100644 --- a/frontends/p4/simplifyDefUse.cpp +++ b/frontends/p4/simplifyDefUse.cpp @@ -644,7 +644,7 @@ class FindUninitialized : public Inspector { loc = new LocationSet(storage); else loc = LocationSet::empty; - } else if (auto slice = parent->to()) { + } else if (auto slice = parent->to()) { loc = checkHeaderFieldWrite(expr, slice->e0); } else { BUG("%1%: unexpected expression on LHS", parent); @@ -1380,13 +1380,14 @@ class FindUninitialized : public Inspector { return false; } - bool preorder(const IR::Slice *expression) override { + bool preorder(const IR::AbstractSlice *expression) override { LOG3("FU Visiting [" << expression->id << "]: " << expression); auto *slice_stmt = findContext(); - if (slice_stmt != nullptr && lhs) { + auto *slice = expression->to(); + if (slice_stmt != nullptr && lhs && slice) { // track this slice statement - hasUses.watchForOverwrites(expression); + hasUses.watchForOverwrites(slice); LOG4("Tracking " << dbp(slice_stmt) << " " << slice_stmt << " for potential overwrites"); } diff --git a/frontends/p4/strengthReduction.cpp b/frontends/p4/strengthReduction.cpp index fe4b559065b..e2668b1d389 100644 --- a/frontends/p4/strengthReduction.cpp +++ b/frontends/p4/strengthReduction.cpp @@ -448,4 +448,23 @@ const IR::Node *DoStrengthReduction::postorder(IR::Slice *expr) { return expr; } +const IR::Node *DoStrengthReduction::postorder(IR::PlusSlice *expr) { + if (expr->e1->is() && expr->e2->is()) { + auto *rv = new IR::Slice(expr->srcInfo, expr->e0, expr->getH(), expr->getL()); + return postorder(rv); + } + if (auto sh = expr->e0->to()) { + if (!sh->left->type->is()) return expr; + if (sh->left->type->to()->isSigned) return expr; + expr->e0 = sh->left; + expr->e1 = new IR::Add(sh->srcInfo, expr->e1, sh->right); + } + if (auto sh = expr->e0->to()) { + if (!sh->left->type->is()) return expr; + expr->e0 = sh->left; + expr->e1 = new IR::Sub(sh->srcInfo, expr->e1, sh->right); + } + return expr; +} + } // namespace P4 diff --git a/frontends/p4/strengthReduction.h b/frontends/p4/strengthReduction.h index 242c60f043c..01ef0b10048 100644 --- a/frontends/p4/strengthReduction.h +++ b/frontends/p4/strengthReduction.h @@ -104,6 +104,7 @@ class DoStrengthReduction final : public Transform { const IR::Node *postorder(IR::Mod *expr) override; const IR::Node *postorder(IR::Mux *expr) override; const IR::Node *postorder(IR::Slice *expr) override; + const IR::Node *postorder(IR::PlusSlice *expr) override; const IR::Node *postorder(IR::Mask *expr) override; const IR::Node *postorder(IR::Range *expr) override; const IR::Node *postorder(IR::Concat *expr) override; diff --git a/frontends/p4/toP4/toP4.cpp b/frontends/p4/toP4/toP4.cpp index 270c6bc8da6..92390092bb4 100644 --- a/frontends/p4/toP4/toP4.cpp +++ b/frontends/p4/toP4/toP4.cpp @@ -772,7 +772,7 @@ VECTOR_VISIT(IndexedVector, StatOrDecl) /////////////////////////////////////////// -bool ToP4::preorder(const IR::Slice *slice) { +bool ToP4::preorder(const IR::AbstractSlice *slice) { int prec = expressionPrecedence; bool useParens = prec > slice->getPrecedence(); if (useParens) builder.append("("); @@ -782,6 +782,7 @@ bool ToP4::preorder(const IR::Slice *slice) { builder.append("["); expressionPrecedence = DBPrint::Prec_Low; visit(slice->e1); + if (slice->is()) builder.append("+"); builder.append(":"); expressionPrecedence = DBPrint::Prec_Low; visit(slice->e2); diff --git a/frontends/p4/toP4/toP4.h b/frontends/p4/toP4/toP4.h index 52ee1cf78bf..312ee68c3d2 100644 --- a/frontends/p4/toP4/toP4.h +++ b/frontends/p4/toP4/toP4.h @@ -174,7 +174,7 @@ class ToP4 : public Inspector, ResolutionContext { bool preorder(const IR::Dots *e) override; bool preorder(const IR::NamedDots *e) override; bool preorder(const IR::Constant *c) override; - bool preorder(const IR::Slice *slice) override; + bool preorder(const IR::AbstractSlice *slice) override; bool preorder(const IR::BoolLiteral *b) override; bool preorder(const IR::StringLiteral *s) override; bool preorder(const IR::PathExpression *p) override; diff --git a/frontends/p4/typeChecking/readOnlyTypeInference.cpp b/frontends/p4/typeChecking/readOnlyTypeInference.cpp index 46d47675d96..bf12c0bd5a4 100644 --- a/frontends/p4/typeChecking/readOnlyTypeInference.cpp +++ b/frontends/p4/typeChecking/readOnlyTypeInference.cpp @@ -116,6 +116,7 @@ DEFINE_POSTORDER(IR::Cmpl) DEFINE_POSTORDER(IR::Cast) DEFINE_POSTORDER(IR::Mux) DEFINE_POSTORDER(IR::Slice) +DEFINE_POSTORDER(IR::PlusSlice) DEFINE_POSTORDER(IR::PathExpression) DEFINE_POSTORDER(IR::Member) DEFINE_POSTORDER(IR::TypeNameExpression) diff --git a/frontends/p4/typeChecking/typeCheckExpr.cpp b/frontends/p4/typeChecking/typeCheckExpr.cpp index fbea83a6fa5..bc2fcf49c1f 100644 --- a/frontends/p4/typeChecking/typeCheckExpr.cpp +++ b/frontends/p4/typeChecking/typeCheckExpr.cpp @@ -1452,6 +1452,78 @@ const IR::Node *TypeInferenceBase::postorder(const IR::Slice *expression) { return expression; } +const IR::Node *TypeInferenceBase::postorder(const IR::PlusSlice *expression) { + if (done()) return expression; + const IR::Type *type = getType(expression->e0); + if (type == nullptr) return expression; + + if (auto se = type->to()) type = getTypeType(se->type); + + if (!type->is()) { + typeError("%1%: bit extraction only defined for bit<> types", expression); + return expression; + } + + IR::PlusSlice *cloned = nullptr; + auto e1type = getType(expression->e1); + if (e1type && e1type->is()) { + auto ei = EnumInstance::resolve(expression->e1, typeMap); + CHECK_NULL(ei); + if (auto sei = ei->to(); sei && expression->e1 != sei->value) { + cloned = expression->clone(); + cloned->e1 = sei->value; + } + } + auto e2type = getType(expression->e2); + if (e2type && e2type->is()) { + auto ei = EnumInstance::resolve(expression->e2, typeMap); + CHECK_NULL(ei); + auto sei = ei->to(); + if (sei == nullptr) { + typeError("%1%: slice bit index values must be constants", expression->e2); + return expression; + } + + if (expression->e1 != sei->value) { + cloned = (cloned ? cloned : expression->clone()); + cloned->e2 = sei->value; + } + } + if (cloned) expression = cloned; + + if (!expression->e2->is()) { + typeError("%1%: slice bit index values must be constants", expression->e2); + return expression; + } + auto width = expression->e2->checkedTo(); + if (!width->fitsInt()) { + typeError("%1%: width too large", width); + return expression; + } + int w = width->asInt(); + if (w < 0) { + typeError("%1%: negative width %2%", expression, width); + return expression; + } + + const IR::Type *resultType = IR::Type_Bits::get(type->srcInfo, w, false); + resultType = canonicalize(resultType); + if (resultType == nullptr) return expression; + setType(getOriginal(), resultType); + setType(expression, resultType); + if (isLeftValue(expression->e0)) { + setLeftValue(expression); + setLeftValue(getOriginal()); + } + if (isCompileTimeConstant(expression->e0) && isCompileTimeConstant(expression->e1)) { + auto result = constantFold(expression); + setCompileTimeConstant(result); + setCompileTimeConstant(getOriginal()); + return result; + } + return expression; +} + const IR::Node *TypeInferenceBase::postorder(const IR::Dots *expression) { if (done()) return expression; setType(expression, IR::Type_Any::get()); diff --git a/frontends/p4/typeChecking/typeChecker.h b/frontends/p4/typeChecking/typeChecker.h index 58ea57ab552..306c55c8403 100644 --- a/frontends/p4/typeChecking/typeChecker.h +++ b/frontends/p4/typeChecking/typeChecker.h @@ -305,6 +305,7 @@ class TypeInferenceBase : public virtual Visitor, public ResolutionContext { const IR::Node *postorder(const IR::Cast *expression); const IR::Node *postorder(const IR::Mux *expression); const IR::Node *postorder(const IR::Slice *expression); + const IR::Node *postorder(const IR::PlusSlice *expression); const IR::Node *postorder(const IR::PathExpression *expression); const IR::Node *postorder(const IR::Member *expression); const IR::Node *postorder(const IR::TypeNameExpression *expression); @@ -447,6 +448,7 @@ class ReadOnlyTypeInference : public virtual Inspector, public TypeInferenceBase void postorder(const IR::Cast *expression) override; void postorder(const IR::Mux *expression) override; void postorder(const IR::Slice *expression) override; + void postorder(const IR::PlusSlice *expression) override; void postorder(const IR::PathExpression *expression) override; void postorder(const IR::Member *expression) override; void postorder(const IR::TypeNameExpression *expression) override; @@ -582,6 +584,7 @@ class TypeInference : public virtual Transform, public TypeInferenceBase { const IR::Node *postorder(IR::Cast *expression) override; const IR::Node *postorder(IR::Mux *expression) override; const IR::Node *postorder(IR::Slice *expression) override; + const IR::Node *postorder(IR::PlusSlice *expression) override; const IR::Node *postorder(IR::PathExpression *expression) override; const IR::Node *postorder(IR::Member *expression) override; const IR::Node *postorder(IR::TypeNameExpression *expression) override; diff --git a/frontends/p4/typeChecking/typeInference.cpp b/frontends/p4/typeChecking/typeInference.cpp index c008dbedf00..0a7202d6f3c 100644 --- a/frontends/p4/typeChecking/typeInference.cpp +++ b/frontends/p4/typeChecking/typeInference.cpp @@ -106,6 +106,7 @@ DEFINE_POSTORDER(IR::Cmpl) DEFINE_POSTORDER(IR::Cast) DEFINE_POSTORDER(IR::Mux) DEFINE_POSTORDER(IR::Slice) +DEFINE_POSTORDER(IR::PlusSlice) DEFINE_POSTORDER(IR::PathExpression) DEFINE_POSTORDER(IR::Member) DEFINE_POSTORDER(IR::TypeNameExpression) diff --git a/frontends/parsers/p4/p4parser.ypp b/frontends/parsers/p4/p4parser.ypp index 01efb79d36b..eda9d1fe6de 100644 --- a/frontends/parsers/p4/p4parser.ypp +++ b/frontends/parsers/p4/p4parser.ypp @@ -1604,6 +1604,7 @@ lvalue | lvalue dot_name %prec DOT { $$ = new IR::Member(@1 + @2, $1, *$2); } | lvalue "[" expression "]" { $$ = new IR::ArrayIndex(@1 + @4, $1, $3); } | lvalue "[" expression ":" expression "]" { $$ = new IR::Slice(@1 + @6, $1, $3, $5); } + | lvalue "[" expression "+" ":" expression "]" { $$ = new IR::PlusSlice(@1 + @7, $1, $3, $6); } | "(" lvalue ")" { $$ = $2; } ; @@ -1616,7 +1617,10 @@ expression | THIS { $$ = new IR::This(@1); } | prefixedNonTypeName { $$ = new IR::PathExpression($1); } | expression "[" expression "]" { $$ = new IR::ArrayIndex(@1 + @4, $1, $3); } - | expression "[" expression ":" expression "]" { $$ = new IR::Slice(@1 + @6, $1, $3, $5); } + | expression "[" expression ":" expression "]" + { $$ = new IR::Slice(@1 + @6, $1, $3, $5); } + | expression "[" expression "+" ":" expression "]" + { $$ = new IR::PlusSlice(@1 + @7, $1, $3, $6); } | "{" expressionList optTrailingComma "}" { $$ = new IR::ListExpression(@1 + @4, *$2); } | INVALID { $$ = new IR::Invalid(@1, IR::Type::Unknown::get()); } | "{" kvList optTrailingComma "}" { $$ = new IR::StructExpression( @@ -1680,7 +1684,10 @@ nonBraceExpression | THIS { $$ = new IR::This(@1); } | prefixedNonTypeName { $$ = new IR::PathExpression($1); } | nonBraceExpression "[" expression "]" { $$ = new IR::ArrayIndex(@1 + @4, $1, $3); } - | nonBraceExpression "[" expression ":" expression "]" { $$ = new IR::Slice(@1 + @6, $1, $3, $5); } + | nonBraceExpression "[" expression ":" expression "]" + { $$ = new IR::Slice(@1 + @6, $1, $3, $5); } + | nonBraceExpression "[" expression "+" ":" expression "]" + { $$ = new IR::PlusSlice(@1 + @7, $1, $3, $6); } | "(" expression ")" { $$ = $2; } | "!" expression %prec PREFIX { $$ = new IR::LNot(@1 + @2, $2); } | "~" expression %prec PREFIX { $$ = new IR::Cmpl(@1 + @2, $2); } diff --git a/ir/dbprint-expression.cpp b/ir/dbprint-expression.cpp index 07914eae2aa..33874bd5a4d 100644 --- a/ir/dbprint-expression.cpp +++ b/ir/dbprint-expression.cpp @@ -82,6 +82,13 @@ void IR::Slice::dbprint(std::ostream &out) const { if (prec == 0) out << ';'; } +void IR::PlusSlice::dbprint(std::ostream &out) const { + int prec = getprec(out); + out << setprec(Prec_Postfix) << e0 << "[" << setprec(Prec_Low) << e1 + << "+:" << setprec(Prec_Low) << e2 << setprec(prec) << ']'; + if (prec == 0) out << ';'; +} + void IR::Primitive::dbprint(std::ostream &out) const { const char *sep = ""; int prec = getprec(out); diff --git a/ir/expression.cpp b/ir/expression.cpp index c9314b5cc05..0ad9032e2e9 100644 --- a/ir/expression.cpp +++ b/ir/expression.cpp @@ -45,6 +45,11 @@ const IR::Expression *IR::Slice::make(const IR::Expression *e, unsigned lo, unsi BUG_CHECK(lo >= sl->getL() && hi <= sl->getH(), "MakeSlice slice on slice type mismatch"); e = sl->e0; } + if (auto sl = e->to()) { + auto *e2 = sl->e2; + if (lo > 0) e2 = new IR::Add(e2, new IR::Constant(lo)); + return new IR::PlusSlice(sl->e1, e2, hi - lo + 1); + } return new IR::Slice(e, hi, lo); } diff --git a/ir/expression.def b/ir/expression.def index d2e7255ac2b..dc8e00b71f2 100644 --- a/ir/expression.def +++ b/ir/expression.def @@ -325,19 +325,24 @@ class TypeNameExpression : Expression { "%1% unexpected type in TypeNameExpression", typeName); } } -class Slice : Operation_Ternary { +abstract AbstractSlice : Operation_Ternary { + virtual unsigned getH() const = 0; + virtual unsigned getL() const = 0; +} + +class Slice : AbstractSlice { precedence = DBPrint::Prec_Postfix; stringOp = "[:]"; toString{ return absl::StrCat(e0->toString().string_view(), "[", e1->toString().string_view(), ":", e2->toString().string_view(), "]"); } // After type checking e1 and e2 will be constants - unsigned getH() const { return e1->to()->asUnsigned(); } - unsigned getL() const { return e2->to()->asUnsigned(); } + unsigned getH() const override { return e1->checkedTo()->asUnsigned(); } + unsigned getL() const override { return e2->checkedTo()->asUnsigned(); } Slice(Expression a, int hi, int lo) - : Operation_Ternary(IR::Type::Bits::get(hi-lo+1), a, new Constant(hi), new Constant(lo)) {} + : AbstractSlice(IR::Type::Bits::get(hi-lo+1), a, new Constant(hi), new Constant(lo)) {} Slice(Util::SourceInfo si, Expression a, int hi, int lo) - : Operation_Ternary(si, IR::Type::Bits::get(hi-lo+1), a, new Constant(hi), new Constant(lo)) {} + : AbstractSlice(si, IR::Type::Bits::get(hi-lo+1), a, new Constant(hi), new Constant(lo)) {} Slice { if (type->is() && e1 && e1->is() && e2 && e2->is()) type = IR::Type::Bits::get(getH() - getL() + 1); } @@ -345,6 +350,25 @@ class Slice : Operation_Ternary { static Expression make(Expression a, unsigned hi, unsigned lo); } +class PlusSlice : AbstractSlice { + precedence = DBPrint::Prec_Postfix; + stringOp = "[+:]"; + toString{ return absl::StrCat(e0->toString().string_view(), + "[", e1->toString().string_view(), "+:", + e2->toString().string_view(), "]"); } + unsigned getH() const override { + BUG_CHECK(e1->is(), "non-const PlusSlice not handled"); + return e1->to()->asUnsigned() + e2->checkedTo()->asUnsigned() - 1; } + unsigned getL() const override { + BUG_CHECK(e1->is(), "non-const PlusSlice not handled"); + return e1->to()->asUnsigned(); } + PlusSlice(Expression a, Expression lo, int width) + : AbstractSlice(IR::Type::Bits::get(width), a, lo, new Constant(width)) {} + PlusSlice { + if (type->is() && e2 && e2->is()) + type = IR::Type::Bits::get(e2->to()->asUnsigned()); } +} + class Member : Operation_Unary { precedence = DBPrint::Prec_Postfix; ID member; diff --git a/ir/write_context.cpp b/ir/write_context.cpp index 2832e3b8107..142e31fc453 100644 --- a/ir/write_context.cpp +++ b/ir/write_context.cpp @@ -32,7 +32,7 @@ bool P4WriteContext::isWrite(bool root_value) { if (!ctxt || !ctxt->node) return root_value; while (ctxt->child_index == 0 && (ctxt->node->is() || ctxt->node->is() || - ctxt->node->is() || ctxt->node->is())) { + ctxt->node->is() || ctxt->node->is())) { ctxt = ctxt->parent; if (!ctxt || !ctxt->node) return root_value; } @@ -77,7 +77,7 @@ bool P4WriteContext::isRead(bool root_value) { if (!ctxt || !ctxt->node) return root_value; while (ctxt->child_index == 0 && (ctxt->node->is() || ctxt->node->is() || - ctxt->node->is() || ctxt->node->is())) { + ctxt->node->is() || ctxt->node->is())) { ctxt = ctxt->parent; if (!ctxt || !ctxt->node) return root_value; } diff --git a/midend/def_use.cpp b/midend/def_use.cpp index 00fdd802563..f6124c73dbc 100644 --- a/midend/def_use.cpp +++ b/midend/def_use.cpp @@ -454,7 +454,7 @@ void ComputeDefUse::set_live_from_type(def_info_t &di, const IR::Type *type) { } static const IR::Expression *get_primary(const IR::Expression *e, const Visitor::Context *ctxt) { - if (ctxt && (ctxt->node->is() || ctxt->node->is() || + if (ctxt && (ctxt->node->is() || ctxt->node->is() || ctxt->node->is())) { return get_primary(ctxt->node->to(), ctxt->parent); } else { @@ -607,6 +607,8 @@ const IR::Expression *ComputeDefUse::do_write(def_info_t &di, const IR::Expressi di.erase_slice(range); e = do_write(di.slices[range], sl, ctxt->parent); return e; + } else if (ctxt->node->is()) { + // writes an unknown part of the expression, rest (still) live } else if (auto *ai = ctxt->node->to()) { if (auto idx = ai->right->to()) { int i = idx->asInt(); @@ -619,8 +621,9 @@ const IR::Expression *ComputeDefUse::do_write(def_info_t &di, const IR::Expressi e = ai; } return e; + } else { + di.defs.clear(); } - di.defs.clear(); di.defs.insert(getLoc(e)); di.fields.clear(); di.slices.clear(); diff --git a/midend/global_copyprop.cpp b/midend/global_copyprop.cpp index eb4cb56c23a..4e46133b8e5 100644 --- a/midend/global_copyprop.cpp +++ b/midend/global_copyprop.cpp @@ -17,7 +17,7 @@ static cstring lValueName(const IR::Expression *exp) { if (auto base = lValueName(a->left)) return base + "[" + std::to_string(k->asInt()) + "]"; } - } else if (auto sl = exp->to()) { + } else if (auto sl = exp->to()) { if (auto e0 = lValueName(sl->e0)) return e0; } return cstring(); @@ -157,13 +157,14 @@ bool FindVariableValues::preorder(const IR::AssignmentStatement *stat) { removeVarsContaining(&vars, GlobalCopyProp::lValueName(stat->left)); // Set value if (auto lit = stat->right->to()) { - if (stat->left->is()) return false; + if (stat->left->is()) return false; vars[GlobalCopyProp::lValueName(stat->left)] = lit; LOG5(" Setting value: " << lit << ", for: " << stat->left); } else if (auto v = vars[GlobalCopyProp::lValueName(stat->right)]) { auto lit = v->to(); if (lit == nullptr) return false; - if (stat->left->is() || stat->right->is()) return false; + if (stat->left->is() || stat->right->is()) + return false; vars[GlobalCopyProp::lValueName(stat->left)] = lit; LOG5(" Setting value: " << lit << ", for: " << stat->left); } @@ -380,7 +381,7 @@ const IR::Node *DoGlobalCopyPropagation::preorder(IR::AssignmentStatement *stat) // Store the value for 'stat->left' if it is now a constant. If it is an assignment to an // identical literal as already stored in the 'vars' container the statement is removed. if (auto lit = stat->right->to()) { - if (stat->left->is()) return stat; + if (stat->left->is()) return stat; if ((*vars)[GlobalCopyProp::lValueName(stat->left)] && lit->equiv(*((*vars)[GlobalCopyProp::lValueName(stat->left)]))) return new IR::EmptyStatement(stat->srcInfo); diff --git a/midend/local_copyprop.cpp b/midend/local_copyprop.cpp index f7c393a86c3..ccb834d238d 100644 --- a/midend/local_copyprop.cpp +++ b/midend/local_copyprop.cpp @@ -32,7 +32,7 @@ using namespace literals; static const IR::Expression *lvalue_out(const IR::Expression *exp) { if (auto ai = exp->to()) return lvalue_out(ai->left); if (auto hsr = exp->to()) return lvalue_out(hsr->base()); - if (auto sl = exp->to()) return lvalue_out(sl->e0); + if (auto sl = exp->to()) return lvalue_out(sl->e0); if (auto mem = exp->to()) return lvalue_out(mem->expr); return exp; } diff --git a/testdata/p4_16_samples/forloop5a.p4 b/testdata/p4_16_samples/forloop5a.p4 new file mode 100644 index 00000000000..089a512bdc6 --- /dev/null +++ b/testdata/p4_16_samples/forloop5a.p4 @@ -0,0 +1,30 @@ +#include +control generic(inout M m); +package top(generic c); + + +header t1 { + bit<32> x; + bit<32> y; +} + +struct headers_t { + t1 t1; +} + +control c(inout headers_t hdrs) { + action a0() { + bit<32> result = 0; + for (bit<8> i = 0; i < 32; i = i + 8) { + result = result << 8; + result = result + (bit<32>)hdrs.t1.x[i+:8] + (bit<32>)hdrs.t1.y[i+:8]; + } + hdrs.t1.x = result; + } + + apply { + a0(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/forloop5a-first.p4 b/testdata/p4_16_samples_outputs/forloop5a-first.p4 new file mode 100644 index 00000000000..d2f380743cf --- /dev/null +++ b/testdata/p4_16_samples_outputs/forloop5a-first.p4 @@ -0,0 +1,28 @@ +#include + +control generic(inout M m); +package top(generic c); +header t1 { + bit<32> x; + bit<32> y; +} + +struct headers_t { + t1 t1; +} + +control c(inout headers_t hdrs) { + action a0() { + bit<32> result = 32w0; + for (bit<8> i = 8w0; i < 8w32; i = i + 8w8) { + result = result << 8; + result = result + (bit<32>)hdrs.t1.x[i+:8] + (bit<32>)hdrs.t1.y[i+:8]; + } + hdrs.t1.x = result; + } + apply { + a0(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/forloop5a-frontend.p4 b/testdata/p4_16_samples_outputs/forloop5a-frontend.p4 new file mode 100644 index 00000000000..3e1f9bcccd9 --- /dev/null +++ b/testdata/p4_16_samples_outputs/forloop5a-frontend.p4 @@ -0,0 +1,30 @@ +#include + +control generic(inout M m); +package top(generic c); +header t1 { + bit<32> x; + bit<32> y; +} + +struct headers_t { + t1 t1; +} + +control c(inout headers_t hdrs) { + @name("c.result") bit<32> result_0; + @name("c.i") bit<8> i_0; + @name("c.a0") action a0() { + result_0 = 32w0; + for (i_0 = 8w0; i_0 < 8w32; i_0 = i_0 + 8w8) { + result_0 = result_0 << 8; + result_0 = result_0 + (bit<32>)hdrs.t1.x[i_0+:8] + (bit<32>)hdrs.t1.y[i_0+:8]; + } + hdrs.t1.x = result_0; + } + apply { + a0(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/forloop5a-midend.p4 b/testdata/p4_16_samples_outputs/forloop5a-midend.p4 new file mode 100644 index 00000000000..8caeea27a13 --- /dev/null +++ b/testdata/p4_16_samples_outputs/forloop5a-midend.p4 @@ -0,0 +1,29 @@ +#include + +control generic(inout M m); +package top(generic c); +header t1 { + bit<32> x; + bit<32> y; +} + +struct headers_t { + t1 t1; +} + +control c(inout headers_t hdrs) { + @name("c.a0") action a0() { + hdrs.t1.x = ((((bit<32>)hdrs.t1.x[7:0] + (bit<32>)hdrs.t1.y[7:0] << 8) + (bit<32>)hdrs.t1.x[15:8] + (bit<32>)hdrs.t1.y[15:8] << 8) + (bit<32>)hdrs.t1.x[23:16] + (bit<32>)hdrs.t1.y[23:16] << 8) + (bit<32>)hdrs.t1.x[31:24] + (bit<32>)hdrs.t1.y[31:24]; + } + @hidden table tbl_a0 { + actions = { + a0(); + } + const default_action = a0(); + } + apply { + tbl_a0.apply(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/forloop5a.p4 b/testdata/p4_16_samples_outputs/forloop5a.p4 new file mode 100644 index 00000000000..7f19884653f --- /dev/null +++ b/testdata/p4_16_samples_outputs/forloop5a.p4 @@ -0,0 +1,28 @@ +#include + +control generic(inout M m); +package top(generic c); +header t1 { + bit<32> x; + bit<32> y; +} + +struct headers_t { + t1 t1; +} + +control c(inout headers_t hdrs) { + action a0() { + bit<32> result = 0; + for (bit<8> i = 0; i < 32; i = i + 8) { + result = result << 8; + result = result + (bit<32>)hdrs.t1.x[i+:8] + (bit<32>)hdrs.t1.y[i+:8]; + } + hdrs.t1.x = result; + } + apply { + a0(); + } +} + +top(c()) main; diff --git a/testdata/p4_16_samples_outputs/forloop5a.p4-stderr b/testdata/p4_16_samples_outputs/forloop5a.p4-stderr new file mode 100644 index 00000000000..e69de29bb2d