|
| 1 | +/* |
| 2 | +Copyright 2025 Nvidia, Inc. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +#include "checkArgAlias.h" |
| 18 | + |
| 19 | +#include "methodInstance.h" |
| 20 | + |
| 21 | +namespace P4 { |
| 22 | + |
| 23 | +// find the enclosing 'location' expression from the current context. That |
| 24 | +// is, any Member(s) or ArrayIndex(s) that are a parents of the current node. |
| 25 | +const IR::Expression *CheckArgAlias::FindCaptures::getRefCtxt() { |
| 26 | + auto *ctxt = getChildContext(); |
| 27 | + for (; auto *p = ctxt->parent; ctxt = p) { |
| 28 | + if (p->node->is<IR::Member>()) continue; |
| 29 | + if (p->node->is<IR::ArrayIndex>() && p->child_index == 0) continue; |
| 30 | + break; |
| 31 | + } |
| 32 | + return ctxt->node->to<IR::Expression>(); |
| 33 | +} |
| 34 | + |
| 35 | +bool CheckArgAlias::FindCaptures::preorder(const IR::PathExpression *pe) { |
| 36 | + const Context *ctxt = nullptr; |
| 37 | + auto last = visiting.end(); |
| 38 | + --last; |
| 39 | + while (auto scope = findOrigCtxt<IR::INamespace>(ctxt)) { |
| 40 | + auto rv = lookup(scope, pe->path->name, ResolutionType::Any); |
| 41 | + if (!rv.empty()) break; |
| 42 | + if (last->second == scope->getNode()) --last; |
| 43 | + } |
| 44 | + auto expr = getRefCtxt(); |
| 45 | + for (++last; last < visiting.end(); ++last) { |
| 46 | + LOG4(expr << " escapes from " << DBPrint::Brief << last->first); |
| 47 | + result[last->first][pe->path->name].push_back(expr); |
| 48 | + } |
| 49 | + return true; |
| 50 | +} |
| 51 | + |
| 52 | +bool CheckArgAlias::FindCaptures::preorder(const IR::IApply *ia) { |
| 53 | + LOG3("CheckArgAlias::preorder(IA): " << DBPrint::Brief << ia); |
| 54 | + auto *d = ia->to<IR::IDeclaration>(); |
| 55 | + visiting.push_back(std::make_pair(d, d->getNode())); |
| 56 | + return true; |
| 57 | +} |
| 58 | + |
| 59 | +bool CheckArgAlias::FindCaptures::preorder(const IR::IFunctional *fn) { |
| 60 | + LOG3("CheckArgAlias::preorder(IF): " << DBPrint::Brief << fn); |
| 61 | + auto *d = fn->to<IR::IDeclaration>(); |
| 62 | + visiting.push_back(std::make_pair(d, d->getNode())); |
| 63 | + return true; |
| 64 | +} |
| 65 | + |
| 66 | +const IR::Expression *CheckArgAlias::Check::baseExpr(const IR::Expression *e, int *depth) { |
| 67 | + if (depth) ++*depth; |
| 68 | + if (auto *mem = e->to<IR::Member>()) return baseExpr(mem->expr, depth); |
| 69 | + if (auto *ai = e->to<IR::ArrayIndex>()) return baseExpr(ai->left, depth); |
| 70 | + return e; |
| 71 | +} |
| 72 | + |
| 73 | +int CheckArgAlias::Check::baseExprDepth(const IR::Expression *e) { |
| 74 | + int depth = 0; |
| 75 | + baseExpr(e, &depth); |
| 76 | + return depth; |
| 77 | +} |
| 78 | + |
| 79 | +bool CheckArgAlias::Check::overlaps(const IR::Expression *e1, const IR::Expression *e2) { |
| 80 | + while (auto *ai = e1->to<IR::ArrayIndex>()) e1 = ai->left; |
| 81 | + while (auto *ai = e2->to<IR::ArrayIndex>()) e2 = ai->left; |
| 82 | + auto *m1 = e1->to<IR::Member>(); |
| 83 | + auto *m2 = e2->to<IR::Member>(); |
| 84 | + if (m1 && m2) { |
| 85 | + if (m1->expr->type == m2->expr->type) return m1->member == m2->member; |
| 86 | + if (baseExprDepth(m1->expr) > baseExprDepth(m2->expr)) |
| 87 | + return overlaps(m1->expr, e2); |
| 88 | + else |
| 89 | + return overlaps(e1, m2->expr); |
| 90 | + } else if (m1) { |
| 91 | + return overlaps(m1->expr, e2); |
| 92 | + } else if (m2) { |
| 93 | + return overlaps(e1, m2->expr); |
| 94 | + } |
| 95 | + BUG_CHECK(e1->is<IR::PathExpression>(), "'%1%' not a PathExpression", e1); |
| 96 | + BUG_CHECK(e2->is<IR::PathExpression>(), "'%1%' not a PathExpression", e2); |
| 97 | + BUG_CHECK(e1->equiv(*e2), "'%1%' and '%2%' are different", e1, e2); |
| 98 | + return e1->equiv(*e2); |
| 99 | +} |
| 100 | + |
| 101 | +bool CheckArgAlias::Check::preorder(const IR::MethodCallExpression *mce) { |
| 102 | + LOG3("CheckArgAlias::Check::preorder(MCE): " << mce); |
| 103 | + auto *mi = MethodInstance::resolve(mce, this, self.typeMap); |
| 104 | + BUG_CHECK(mi, "MethodInstance::resolve failed"); |
| 105 | + if (!self.captures.any(mi->callee())) return true; |
| 106 | + auto arg = mi->expr->arguments->begin(); |
| 107 | + auto *params = mi->actualMethodType->parameters; |
| 108 | + for (auto it = params->begin(); it < params->end(); ++it, ++arg) { |
| 109 | + if (arg == mi->expr->arguments->end()) { |
| 110 | + // at the end of the argument list -- rest of params must be directionless |
| 111 | + // and this must be an action call |
| 112 | + BUG_CHECK(mi->is<P4::ActionCall>(), "not an action call?"); |
| 113 | + break; |
| 114 | + } |
| 115 | + auto *param = *it; |
| 116 | + if (param->direction == IR::Direction::None) continue; |
| 117 | + if (param->direction == IR::Direction::In) continue; |
| 118 | + auto *exp = (*arg)->expression; |
| 119 | + while (auto *sl = exp->to<IR::AbstractSlice>()) exp = sl->e0; |
| 120 | + auto *pe = baseExpr(exp)->to<IR::PathExpression>(); |
| 121 | + BUG_CHECK(pe, "not a PathExpression"); |
| 122 | + for (auto *cap : self.captures(mi->callee(), pe->path->name)) { |
| 123 | + if (overlaps(exp, cap)) { |
| 124 | + error(ErrorType::ERR_INVALID, "%1%%2% binds value accessed by callee %3%%4%", |
| 125 | + param->direction == IR::Direction::InOut ? "inout argument" : "out argument", |
| 126 | + (*arg)->srcInfo, mi->callee(), cap->srcInfo); |
| 127 | + // only trigger once per call |
| 128 | + return true; |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + return true; |
| 133 | +} |
| 134 | + |
| 135 | +} // namespace P4 |
0 commit comments