Skip to content

Commit 3e1d4aa

Browse files
committed
Allow implict read/write of extern instances
Signed-off-by: Chris Dodd <[email protected]>
1 parent caafda0 commit 3e1d4aa

28 files changed

+11492
-25
lines changed

backends/p4test/midend.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ limitations under the License.
5050
#include "midend/flattenUnions.h"
5151
#include "midend/global_copyprop.h"
5252
#include "midend/hsIndexSimplify.h"
53+
#include "midend/implicitReadWrite.h"
5354
#include "midend/local_copyprop.h"
5455
#include "midend/midEndLast.h"
5556
#include "midend/nestedStructs.h"
@@ -90,6 +91,7 @@ MidEnd::MidEnd(P4TestOptions &options, std::ostream *outStream) {
9091

9192
addPasses(
9293
{new P4::DumpPipe("MidEnd start"),
94+
new P4::ImplicitReadWrite(&typeMap),
9395
options.ndebug ? new P4::RemoveAssertAssume(&typeMap) : nullptr,
9496
new P4::RemoveMiss(&typeMap),
9597
new P4::EliminateNewtype(&typeMap),

frontends/p4/simplifyDefUse.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,10 @@ class FindUninitialized : public Inspector {
706706
typeMap->setType(src_member, ftype);
707707
processHeadersInAssignment(dst_member, src_member, ftype, ftype);
708708
}
709+
} else if (typeMap->externImplicitReadType(src_type)) {
710+
for (const auto *s : headerDefs->getStorageLocation(dst)) {
711+
headerDefs->setValueToStorage(s, TernaryBool::Yes);
712+
}
709713
} else {
710714
BUG("%1%: unexpected expression on RHS", src);
711715
}
@@ -1469,6 +1473,30 @@ class RemoveUnused : public Transform {
14691473
ReferenceMap *refMap;
14701474
TypeMap *typeMap;
14711475

1476+
// FIXME -- this is very messy -- should be a better way to detect this
1477+
// Perhaps via P4::SideEffects
1478+
bool isExternType(const IR::Expression *e) {
1479+
auto *type = typeMap ? typeMap->getType(e, false) : nullptr;
1480+
if (!type) type = e->type;
1481+
if (auto *tsc = type->to<IR::Type_SpecializedCanonical>()) type = tsc->substituted;
1482+
if (auto *ts = type->to<IR::Type_Specialized>()) type = ts->baseType;
1483+
return type->is<IR::Type_Extern>();
1484+
}
1485+
1486+
bool implicitExernAssign(const IR::Expression *exp) {
1487+
while (!isExternType(exp)) {
1488+
if (auto *sl = exp->to<IR::AbstractSlice>())
1489+
exp = sl->e0;
1490+
else if (auto *m = exp->to<IR::Member>())
1491+
exp = m->expr;
1492+
else if (auto *ai = exp->to<IR::ArrayIndex>())
1493+
exp = ai->left;
1494+
else
1495+
return false;
1496+
}
1497+
return true;
1498+
}
1499+
14721500
public:
14731501
explicit RemoveUnused(const HasUses &hasUses, ReferenceMap *refMap, TypeMap *typeMap)
14741502
: hasUses(hasUses), refMap(refMap), typeMap(typeMap) {
@@ -1477,7 +1505,7 @@ class RemoveUnused : public Transform {
14771505
setName("RemoveUnused");
14781506
}
14791507
const IR::Node *postorder(IR::BaseAssignmentStatement *statement) override {
1480-
if (!hasUses.hasUses(getOriginal())) {
1508+
if (!hasUses.hasUses(getOriginal()) && !implicitExernAssign(statement->left)) {
14811509
Log::TempIndent indent;
14821510
LOG3("Removing statement " << getOriginal() << " " << statement << indent);
14831511
SideEffects se(typeMap);

frontends/p4/typeChecking/typeCheckExpr.cpp

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,19 +1313,29 @@ const IR::Node *TypeInferenceBase::postorder(const IR::PathExpression *expressio
13131313
} else if (decl->is<IR::Declaration_Variable>()) {
13141314
setLeftValue(expression);
13151315
setLeftValue(getOriginal<IR::Expression>());
1316-
} else if (decl->is<IR::Parameter>()) {
1317-
auto paramDecl = decl->to<IR::Parameter>();
1316+
} else if (auto paramDecl = decl->to<IR::Parameter>()) {
13181317
if (paramDecl->direction == IR::Direction::InOut ||
13191318
paramDecl->direction == IR::Direction::Out) {
13201319
setLeftValue(expression);
13211320
setLeftValue(getOriginal<IR::Expression>());
13221321
} else if (paramDecl->direction == IR::Direction::None) {
13231322
setCompileTimeConstant(expression);
13241323
setCompileTimeConstant(getOriginal<IR::Expression>());
1324+
if (typeMap->externImplicitAssignType(paramDecl->type, true)) {
1325+
setLeftValue(expression);
1326+
setLeftValue(getOriginal<IR::Expression>());
1327+
}
13251328
}
1326-
} else if (decl->is<IR::Declaration_Constant>() || decl->is<IR::Declaration_Instance>()) {
1329+
} else if (decl->is<IR::Declaration_Constant>()) {
1330+
setCompileTimeConstant(expression);
1331+
setCompileTimeConstant(getOriginal<IR::Expression>());
1332+
} else if (auto di = decl->to<IR::Declaration_Instance>()) {
13271333
setCompileTimeConstant(expression);
13281334
setCompileTimeConstant(getOriginal<IR::Expression>());
1335+
if (typeMap->externImplicitAssignType(di->type, true)) {
1336+
setLeftValue(expression);
1337+
setLeftValue(getOriginal<IR::Expression>());
1338+
}
13291339
} else if (decl->is<IR::Method>() || decl->is<IR::Function>()) {
13301340
type = getType(decl->getNode());
13311341
// Each method invocation uses fresh type variables
@@ -1598,28 +1608,36 @@ const IR::Node *TypeInferenceBase::postorder(const IR::Member *expression) {
15981608
if (auto ts = type->to<IR::Type_SpecializedCanonical>()) type = ts->substituted;
15991609

16001610
if (auto *ext = type->to<IR::Type_Extern>()) {
1601-
auto call = findContext<IR::MethodCallExpression>();
1602-
if (call == nullptr) {
1603-
typeError("%1%: Methods can only be called", expression);
1611+
setCompileTimeConstant(expression);
1612+
setCompileTimeConstant(getOriginal<IR::Expression>());
1613+
if (auto call = getParent<IR::MethodCallExpression>()) {
1614+
auto method = ext->lookupMethod(expression->member, call->arguments);
1615+
if (method == nullptr) {
1616+
typeError("%1%: extern %2% does not have method matching this call", expression,
1617+
ext->name);
1618+
return expression;
1619+
}
1620+
1621+
const IR::Type *methodType = getType(method);
1622+
if (methodType == nullptr) return expression;
1623+
// Each method invocation uses fresh type variables
1624+
methodType = cloneWithFreshTypeVariables(methodType->to<IR::IMayBeGenericType>());
1625+
1626+
setType(getOriginal(), methodType);
1627+
setType(expression, methodType);
16041628
return expression;
1605-
}
1606-
auto method = ext->lookupMethod(expression->member, call->arguments);
1607-
if (method == nullptr) {
1608-
typeError("%1%: extern %2% does not have method matching this call", expression,
1609-
ext->name);
1629+
} else if (auto cvt = typeMap->externImplicitReadType(ext)) {
1630+
if (cvt->is<IR::Type_StructLike>()) {
1631+
type = cvt;
1632+
// assume we'll convert to that and fall through
1633+
} else {
1634+
typeError("%1%: Methods can only be called", expression);
1635+
return expression;
1636+
}
1637+
} else {
1638+
typeError("%1%: Methods can only be called", expression);
16101639
return expression;
16111640
}
1612-
1613-
const IR::Type *methodType = getType(method);
1614-
if (methodType == nullptr) return expression;
1615-
// Each method invocation uses fresh type variables
1616-
methodType = cloneWithFreshTypeVariables(methodType->to<IR::IMayBeGenericType>());
1617-
1618-
setType(getOriginal(), methodType);
1619-
setType(expression, methodType);
1620-
setCompileTimeConstant(expression);
1621-
setCompileTimeConstant(getOriginal<IR::Expression>());
1622-
return expression;
16231641
}
16241642

16251643
bool inMethod = getParent<IR::MethodCallExpression>() != nullptr;

frontends/p4/typeChecking/typeChecker.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ const IR::Type *TypeInferenceBase::specialize(const IR::IMayBeGenericType *type,
229229
}
230230

231231
// May return nullptr if a type error occurs.
232+
// FIXME -- this seems to be broken in that it sometimes creates duplicate struct
233+
// types (new IR::Struct objects tht are identical to the existing type). This will
234+
// break any code that assumens types can be compared by pointer (which much code does)
232235
const IR::Type *TypeInferenceBase::canonicalize(const IR::Type *type) {
233236
if (type == nullptr) return nullptr;
234237

@@ -548,6 +551,8 @@ const IR::Expression *TypeInferenceBase::assignment(const IR::Node *errorPositio
548551
if (destType->is<IR::Type_Dontcare>()) return sourceExpression;
549552
const IR::Type *initType = getType(sourceExpression);
550553
if (initType == nullptr) return sourceExpression;
554+
if (auto implicit = typeMap->externImplicitAssignType(destType)) destType = implicit;
555+
if (auto implicit = typeMap->externImplicitReadType(initType)) initType = implicit;
551556

552557
auto tvs = unifyCast(errorPosition, destType, initType,
553558
"Source expression '%1%' produces a result of type '%2%' which cannot be "

frontends/p4/typeChecking/typeUnification.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ namespace P4 {
2424

2525
using namespace literals;
2626

27+
const IR::Type *unspecialize(const IR::Type *type) {
28+
if (auto tsc = type->to<IR::Type_SpecializedCanonical>()) type = tsc->substituted;
29+
if (auto ts = type->to<IR::Type_Specialized>()) type = ts->baseType;
30+
return type;
31+
}
32+
2733
/// Unifies a call with a prototype.
2834
bool TypeUnification::unifyCall(const BinaryConstraint *constraint) {
2935
// These are canonical types.
@@ -91,7 +97,8 @@ bool TypeUnification::unifyCall(const BinaryConstraint *constraint) {
9197
return constraint->reportError(
9298
constraints->getCurrentSubstitution(),
9399
"%1%: Read-only value used for out/inout parameter '%2%'", arg->srcInfo, param);
94-
else if (param->direction == IR::Direction::None && !arg->compileTimeConstant)
100+
else if (param->direction == IR::Direction::None && !arg->compileTimeConstant &&
101+
!unspecialize(arg->type)->is<IR::Type_Extern>())
95102
return constraint->reportError(constraints->getCurrentSubstitution(),
96103
"%1%: argument used for directionless parameter '%2%' "
97104
"must be a compile-time constant",
@@ -416,8 +423,13 @@ bool TypeUnification::unify(const BinaryConstraint *constraint) {
416423
constraints->add(new EqualityConstraint(dotsField->type, partial, constraint));
417424
}
418425
return true;
426+
} else if (auto implicit = typeMap->externImplicitReadType(src)) {
427+
// FIXME -- we're supposed to be able to compare types for equality by
428+
// just comparing pointers, but it seems like typechecking internally
429+
// creates duplicate struct types. This seems like a bug waiting to
430+
// happen, since if any such type leaks, later passes may fail mysteriously
431+
if (implicit->equiv(*dest)) return true;
419432
}
420-
421433
return constraint->reportError(constraints->getCurrentSubstitution());
422434
} else if (dest->is<IR::Type_Base>()) {
423435
if (dest->is<IR::Type_Bits>() && src->is<IR::Type_InfInt>()) {
@@ -430,6 +442,10 @@ bool TypeUnification::unify(const BinaryConstraint *constraint) {
430442
constraints->add(constraint->create(dest, src));
431443
return true;
432444
}
445+
if (auto implicit = typeMap->externImplicitReadType(src)) {
446+
if (*implicit == *dest) return true;
447+
if (implicit->is<IR::Type_SerEnum>()) src = implicit;
448+
}
433449
if (auto senum = src->to<IR::Type_SerEnum>()) {
434450
if (constraint->is<P4::CanBeImplicitlyCastConstraint>()) {
435451
if (dest->is<IR::Type_Bits>()) {

frontends/p4/typeMap.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ limitations under the License.
1818

1919
namespace P4 {
2020

21+
using namespace literals;
22+
2123
bool TypeMap::typeIsEmpty(const IR::Type *type) const {
2224
if (auto bt = type->to<IR::Type_Bits>()) {
2325
return bt->size == 0;
@@ -400,4 +402,45 @@ int TypeMap::widthBits(const IR::Type *type, const IR::Node *errorPosition, bool
400402
return -1;
401403
}
402404

405+
const IR::Type *TypeMap::externImplicitAssignType(const IR::Type *type, bool skipIndex) const {
406+
if (auto mappedType = getType(type)) type = mappedType;
407+
if (auto typeType = type->to<IR::Type_Type>()) type = typeType->type;
408+
if (skipIndex)
409+
while (auto arrType = type->to<IR::Type_Array>()) type = arrType->elementType;
410+
if (auto tsc = type->to<IR::Type_SpecializedCanonical>()) type = tsc->substituted;
411+
if (auto ts = type->to<IR::Type_Specialized>()) type = ts->baseType;
412+
if (auto et = type->to<IR::Type_Extern>()) {
413+
for (auto m : et->methods) {
414+
if (m->hasAnnotation("implicit"_cs) && m->type->returnType->is<IR::Type_Void>() &&
415+
m->getParameters()->size() == 1) {
416+
type = m->getParameters()->getParameter(0)->type;
417+
if (auto mappedType = getType(type)) type = mappedType;
418+
if (auto typeType = type->to<IR::Type_Type>()) type = typeType->type;
419+
return type;
420+
}
421+
}
422+
}
423+
return nullptr;
424+
}
425+
426+
const IR::Type *TypeMap::externImplicitReadType(const IR::Type *type, bool skipIndex) const {
427+
if (auto mappedType = getType(type)) type = mappedType;
428+
if (auto typeType = type->to<IR::Type_Type>()) type = typeType->type;
429+
if (skipIndex)
430+
while (auto arrType = type->to<IR::Type_Array>()) type = arrType->elementType;
431+
if (auto tsc = type->to<IR::Type_SpecializedCanonical>()) type = tsc->substituted;
432+
if (auto ts = type->to<IR::Type_Specialized>()) type = ts->baseType;
433+
if (auto et = type->to<IR::Type_Extern>()) {
434+
for (auto m : et->methods) {
435+
if (m->hasAnnotation("implicit"_cs) && m->getParameters()->size() == 0) {
436+
type = m->type->returnType;
437+
if (auto mappedType = getType(type)) type = mappedType;
438+
if (auto typeType = type->to<IR::Type_Type>()) type = typeType->type;
439+
return type;
440+
}
441+
}
442+
}
443+
return nullptr;
444+
}
445+
403446
} // namespace P4

frontends/p4/typeMap.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ class TypeMap final : public ProgramMap {
108108

109109
/// True is type occupies no storage.
110110
bool typeIsEmpty(const IR::Type *type) const;
111+
112+
// helper functions for implicitly assignable/readable externs, needed by typeChecking
113+
// and typeUnification
114+
// FIXME -- we currently only allow one implicit assign and one implicit read per
115+
// extern, as unification can't deal with resolving more than one (overload resolution)
116+
const IR::Type *externImplicitAssignType(const IR::Type *, bool skipIndex = false) const;
117+
const IR::Type *externImplicitReadType(const IR::Type *, bool skipIndex = false) const;
111118
};
112119
} // namespace P4
113120

midend/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ set (MIDEND_SRCS
3737
flattenLogMsg.cpp
3838
flattenUnions.cpp
3939
hsIndexSimplify.cpp
40+
implicitReadWrite.cpp
4041
interpreter.cpp
4142
global_copyprop.cpp
4243
local_copyprop.cpp
@@ -93,6 +94,7 @@ set (MIDEND_HDRS
9394
flattenInterfaceStructs.h
9495
flattenUnions.h
9596
has_side_effects.h
97+
implicitReadWrite.h
9698
interpreter.h
9799
global_copyprop.h
98100
local_copyprop.h

0 commit comments

Comments
 (0)