in flexsai/p4/backend/json_stage/expression.cpp [231:399]
void ExpressionConverter::postorder(const IR::Member* expression) {
// TODO: deal with references that return bool
auto result = new Util::JsonObject();
auto parentType = backend->getTypeMap()->getType(expression->expr, true);
cstring fieldName = expression->member.name;
auto type = backend->getTypeMap()->getType(expression, true);
if (parentType->is<IR::Type_StructLike>()) {
auto st = parentType->to<IR::Type_StructLike>();
auto field = st->getField(expression->member);
if (field != nullptr)
// field could be a method call, i.e., isValid.
fieldName = field->controlPlaneName();
}
{
if (type->is<IR::Type_Error>() && expression->expr->is<IR::TypeNameExpression>()) {
// this deals with constants that have type 'error'
result->emplace("type", "hexstr");
auto decl = type->to<IR::Type_Error>()->getDeclByName(expression->member.name);
ErrorCodesMap errCodes = backend->getErrorCodesMap();
auto errorValue = errCodes.at(decl);
result->emplace("value", Util::toString(errorValue));
map.emplace(expression, result);
return;
}
}
auto param = enclosingParamReference(expression->expr);
if (param != nullptr) {
if (backend->isStandardMetadataParameter(param)) {
result->emplace("type", "field");
auto e = mkArrayField(result, "value");
e->append(SAI::SaiModelProperties::jsonMetadataParameterName);
e->append(fieldName);
} else {
if (type->is<IR::Type_Stack>()) {
result->emplace("type", "header_stack");
result->emplace("value", fieldName);
} else if (type->is<IR::Type_HeaderUnion>()) {
result->emplace("type", "header_union");
result->emplace("value", fieldName);
} else if (parentType->is<IR::Type_HeaderUnion>()) {
auto l = get(expression->expr);
cstring nestedField = fieldName;
if (l->is<Util::JsonObject>()) {
auto lv = l->to<Util::JsonObject>()->get("value");
if (lv->is<Util::JsonValue>()) {
// header in union reference ["u", "f"] => "u.f"
cstring prefix = lv->to<Util::JsonValue>()->getString();
nestedField = prefix + "." + nestedField;
}
}
result->emplace("type", "header");
result->emplace("value", nestedField);
} else if (parentType->is<IR::Type_StructLike>() &&
(type->is<IR::Type_Bits>() || type->is<IR::Type_Error>() ||
type->is<IR::Type_Boolean>())) {
auto field = parentType->to<IR::Type_StructLike>()->getField(
expression->member);
LOG3("looking up field " << field);
CHECK_NULL(field);
auto name = ::get(backend->scalarMetadataFields, field);
CHECK_NULL(name);
if (type->is<IR::Type_Bits>() || type->is<IR::Type_Error>() ||
leftValue || simpleExpressionsOnly) {
result->emplace("type", "field");
auto e = mkArrayField(result, "value");
e->append(scalarsName);
e->append(name);
} else if (type->is<IR::Type_Boolean>()) {
// Boolean variables are stored as ints, so we
// have to insert a conversion when reading such a
// variable
result->emplace("type", "expression");
auto e = new Util::JsonObject();
result->emplace("value", e);
e->emplace("op", "d2b"); // data to Boolean cast
e->emplace("left", Util::JsonValue::null);
auto r = new Util::JsonObject();
e->emplace("right", r);
r->emplace("type", "field");
auto a = mkArrayField(r, "value");
a->append(scalarsName);
a->append(name);
}
} else {
// This may be wrong, but the caller will handle it properly
// (e.g., this can be a method, such as packet.lookahead)
result->emplace("type", "header");
result->emplace("value", fieldName);
}
}
} else {
bool done = false;
if (expression->expr->is<IR::Member>()) {
// array.next.field => type: "stack_field", value: [ array, field ]
auto mem = expression->expr->to<IR::Member>();
auto memtype = backend->getTypeMap()->getType(mem->expr, true);
if (memtype->is<IR::Type_Stack>() && mem->member == IR::Type_Stack::last) {
auto l = get(mem->expr);
CHECK_NULL(l);
result->emplace("type", "stack_field");
auto e = mkArrayField(result, "value");
if (l->is<Util::JsonObject>())
e->append(l->to<Util::JsonObject>()->get("value"));
else
e->append(l);
e->append(fieldName);
done = true;
}
}
if (!done) {
auto l = get(expression->expr);
CHECK_NULL(l);
if (parentType->is<IR::Type_HeaderUnion>()) {
BUG_CHECK(l->is<Util::JsonObject>(), "Not a JsonObject");
auto lv = l->to<Util::JsonObject>()->get("value");
BUG_CHECK(lv->is<Util::JsonValue>(), "Not a JsonValue");
fieldName = lv->to<Util::JsonValue>()->getString() + "." + fieldName;
// Each header in a union is allocated a separate header instance.
// Refer to that instance directly.
result->emplace("type", "header");
result->emplace("value", fieldName);
} else {
result->emplace("type", "field");
auto e = mkArrayField(result, "value");
if (l->is<Util::JsonObject>()) {
auto lv = l->to<Util::JsonObject>()->get("value");
if (lv->is<Util::JsonArray>()) {
// TODO: is this case still necessary after eliminating nested structs?
// nested struct reference [ ["m", "f"], "x" ] => [ "m", "f.x" ]
auto array = lv->to<Util::JsonArray>();
BUG_CHECK(array->size() == 2, "expected 2 elements");
auto first = array->at(0);
auto second = array->at(1);
BUG_CHECK(second->is<Util::JsonValue>(), "expected a value");
e->append(first);
cstring nestedField = second->to<Util::JsonValue>()->getString();
nestedField += "." + fieldName;
e->append(nestedField);
} else if (lv->is<Util::JsonValue>()) {
e->append(lv);
e->append(fieldName);
} else {
BUG("%1%: Unexpected json", lv);
}
} else {
e->append(l);
e->append(fieldName);
}
if (!simpleExpressionsOnly && !leftValue && type->is<IR::Type_Boolean>()) {
auto cast = new Util::JsonObject();
auto value = new Util::JsonObject();
cast->emplace("type", "expression");
cast->emplace("value", value);
value->emplace("op", "d2b"); // data to Boolean cast
value->emplace("left", Util::JsonValue::null);
value->emplace("right", result);
result = cast;
}
}
}
}
map.emplace(expression, result);
}