Skip to content
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

Make the Tofino spec files independent of the generated IR. #5063

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Make the json loader and parser independent from the generated ir files.
Signed-off-by: fruffy <fruffy@nyu.edu>
  • Loading branch information
fruffy committed Dec 15, 2024
commit ad24e06fadee72c5054cb77feef18b8650e86d83
46 changes: 1 addition & 45 deletions frontends/common/constantParsing.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
#ifndef FRONTENDS_COMMON_CONSTANTPARSING_H_
#define FRONTENDS_COMMON_CONSTANTPARSING_H_

#include "lib/cstring.h"
#include "ir/unparsed_constant.h"

namespace P4::IR {
class Constant;
Expand All @@ -29,50 +29,6 @@ class SourceInfo;

namespace P4 {

/**
* An unparsed numeric constant. We produce these as token values during
* lexing. The parser is responsible for actually interpreting the raw text as a
* numeric value and transforming it into an IR::Constant using parseConstant().
*
* To illustrate how a numeric constant is represented using this struct,
* here is a breakdown of '16w0x12':
*
* ___
* / ___
* | /
* | bitwidth (if @hasWidth) | 16
* | \___
* |
* | ___
* | /
* | separator (if @hasWidth) | w
* | \___
* @text |
* | ___
* | /
* | ignored prefix of length @skip | 0x
* | \___
* |
* | ___
* | /
* | numeric value in base @base | w
* | \___
* \___
*
* Simple numeric constants like '5' are specified by setting @hasWidth to
* false and providing a @skip length of 0.
*/
struct UnparsedConstant {
cstring text; /// Raw P4 text which was recognized as a numeric constant.
unsigned skip; /// An ignored prefix of the numeric constant (e.g. '0x').
unsigned base; /// The base in which the constant is expressed.
bool hasWidth; /// If true, a bitwidth and separator are present.
};

std::ostream &operator<<(std::ostream &out, const UnparsedConstant &constant);

bool operator<(const UnparsedConstant &a, const UnparsedConstant &b);

/**
* Parses an UnparsedConstant @constant into an IR::Constant object, with
* location information taken from @srcInfo. If parsing fails, an IR::Constant
Expand Down
40 changes: 11 additions & 29 deletions ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.

set (IR_LIB_SRCS
bitrange.cpp
json_parser.cpp
)

add_library (ir_lib STATIC ${IR_LIB_SRCS})
target_link_libraries(ir_lib PRIVATE p4ctoolkit absl::flat_hash_map ${LIBGC_LIBRARIES})


set (IR_SRCS
annotations.cpp
base.cpp
bitrange.cpp
dbprint.cpp
dbprint-expression.cpp
dbprint-stmt.cpp
Expand All @@ -25,41 +33,15 @@ set (IR_SRCS
expression.cpp
ir.cpp
irutils.cpp
json_parser.cpp
loop-visitor.cpp
node.cpp
loop-visitor.cpp
pass_manager.cpp
pass_utils.cpp
type.cpp
visitor.cpp
write_context.cpp
)

set (IR_HDRS
annotations.h
configuration.h
dbprint.h
dump.h
id.h
indexed_vector.h
ir-inline.h
ir-tree-macros.h
ir.h
ir-traversal.h
ir-traversal-internal.h
irutils.h
json_generator.h
json_loader.h
json_parser.h
namemap.h
node.h
nodemap.h
pass_manager.h
pass_utils.h
vector.h
visitor.h
)

set (BASE_IR_DEF_FILES
${CMAKE_CURRENT_SOURCE_DIR}/base.def
${CMAKE_CURRENT_SOURCE_DIR}/type.def
Expand All @@ -70,7 +52,7 @@ set (BASE_IR_DEF_FILES
set (IR_DEF_FILES ${IR_DEF_FILES} ${BASE_IR_DEF_FILES} PARENT_SCOPE)

add_library (ir STATIC ${IR_SRCS})
target_link_libraries(ir PRIVATE absl::flat_hash_map ${LIBGC_LIBRARIES})
target_link_libraries(ir PUBLIC ir_lib PRIVATE absl::flat_hash_map ${LIBGC_LIBRARIES})


add_dependencies(ir genIR)
Expand Down
77 changes: 77 additions & 0 deletions ir/inode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
Copyright 2013-present Barefoot Networks, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef IR_INODE_H_
#define IR_INODE_H_

#include "lib/castable.h"
#include "lib/cstring.h"
#include "lib/exceptions.h"
#include "lib/rtti.h"
#include "lib/source_file.h"

namespace P4 {
class JSONGenerator;
class JSONLoader;
} // namespace P4

namespace P4::IR {

class Node;

/// SFINAE helper to check if given class has a `static_type_name`
/// method. Definite node classes have them while interfaces do not
template <class, class = void>
struct has_static_type_name : std::false_type {};

template <class T>
struct has_static_type_name<T, std::void_t<decltype(T::static_type_name())>> : std::true_type {};

template <class T>
inline constexpr bool has_static_type_name_v = has_static_type_name<T>::value;

// node interface
class INode : public Util::IHasSourceInfo, public IHasDbPrint, public ICastable {
public:
virtual ~INode() {}
virtual const Node *getNode() const = 0;
virtual Node *getNode() = 0;
virtual void toJSON(JSONGenerator &) const = 0;
virtual cstring node_type_name() const = 0;
virtual void validate() const {}

// default checkedTo implementation for nodes: just fallback to generic ICastable method
template <typename T>
std::enable_if_t<!has_static_type_name_v<T>, const T *> checkedTo() const {
return ICastable::checkedTo<T>();
}

// alternative checkedTo implementation that produces slightly better error message
// due to node_type_name() / static_type_name() being available
template <typename T>
std::enable_if_t<has_static_type_name_v<T>, const T *> checkedTo() const {
const auto *result = to<T>();
BUG_CHECK(result, "Cast failed: %1% with type %2% is not a %3%.", this, node_type_name(),
T::static_type_name());
return result;
}

DECLARE_TYPEINFO(INode);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@asl I have a question on the RTTI macros. I am trying to factor out some classes to avoid pulling in the entire IR code. Unfortunately, for IDeclaration and INode macros like DECLARE_TYPEINFO_WITH_TYPEID(INode, NodeKind::INode); require pulling in ir/ir-tree-macros.h which is generated.

Is there a way to avoid this while still being correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the typeid's should be unique. It is part of gen-tree-macro.h. The location is a bit weird, but it was emitted there exactly in order not to pull the whole IR header. It could be even split into separate .h file if gen-tree-macro.h is undesired.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, gen-tree-macro depends on the generator which is a little unpleasant. Although the code there is less complex than what is being put into ir-generated.h.
You effectively run into #5013.

Maybe there is a way to depend on gen-tree or just the rtti enum.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, one way to do this is to split enum into "static" and "generated" parts. Static would be for all classes not autogenerated (INode, IDeclaration, Vector), etc. and everything else will be outside the scope.

};

} // namespace P4::IR

#endif /* IR_INODE_H_ */
5 changes: 4 additions & 1 deletion ir/json_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ limitations under the License.
#include "lib/cstring.h"
#include "lib/ltbitmatrix.h"
#include "lib/match.h"
#include "lib/null.h"
#include "lib/ordered_map.h"
#include "lib/ordered_set.h"
#include "lib/safe_vector.h"
Expand Down Expand Up @@ -80,7 +81,9 @@ class JSONLoader {
if (id >= 0) {
if (node_refs.find(id) == node_refs.end()) {
if (auto fn = get(IR::unpacker_table, json->as<JsonObject>().get_type())) {
node_refs[id] = fn(*this);
auto *node = fn(*this)->to<IR::Node>();
CHECK_NULL(node);
node_refs[id] = node;
// Creating JsonObject from source_info read from jsonFile
// and setting SourceInfo for each node
// when "--fromJSON" flag is used
Expand Down
45 changes: 2 additions & 43 deletions ir/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ limitations under the License.

#include <iosfwd>

#include "ir-tree-macros.h"
#include "ir/gen-tree-macro.h"
#include "lib/castable.h"
#include "ir/inode.h"
#include "ir/ir-tree-macros.h"
#include "lib/cstring.h"
#include "lib/exceptions.h"
#include "lib/source_file.h"

namespace P4 {
Expand Down Expand Up @@ -51,46 +50,6 @@ class Vector; // IWYU pragma: keep
template <class T>
class IndexedVector; // IWYU pragma: keep

/// SFINAE helper to check if given class has a `static_type_name`
/// method. Definite node classes have them while interfaces do not
template <class, class = void>
struct has_static_type_name : std::false_type {};

template <class T>
struct has_static_type_name<T, std::void_t<decltype(T::static_type_name())>> : std::true_type {};

template <class T>
inline constexpr bool has_static_type_name_v = has_static_type_name<T>::value;

// node interface
class INode : public Util::IHasSourceInfo, public IHasDbPrint, public ICastable {
public:
virtual ~INode() {}
virtual const Node *getNode() const = 0;
virtual Node *getNode() = 0;
virtual void toJSON(JSONGenerator &) const = 0;
virtual cstring node_type_name() const = 0;
virtual void validate() const {}

// default checkedTo implementation for nodes: just fallback to generic ICastable method
template <typename T>
std::enable_if_t<!has_static_type_name_v<T>, const T *> checkedTo() const {
return ICastable::checkedTo<T>();
}

// alternative checkedTo implementation that produces slightly better error message
// due to node_type_name() / static_type_name() being available
template <typename T>
std::enable_if_t<has_static_type_name_v<T>, const T *> checkedTo() const {
const auto *result = to<T>();
BUG_CHECK(result, "Cast failed: %1% with type %2% is not a %3%.", this, node_type_name(),
T::static_type_name());
return result;
}

DECLARE_TYPEINFO_WITH_TYPEID(INode, NodeKind::INode);
};

class Node : public virtual INode {
public:
virtual bool apply_visitor_preorder(Modifier &v);
Expand Down
18 changes: 18 additions & 0 deletions ir/unpacker_table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef IR_UNPACKER_TABLE_H_
#define IR_UNPACKER_TABLE_H_

#include "ir/inode.h"
#include "lib/cstring.h"

namespace P4 {
class JSONLoader;

using NodeFactoryFn = IR::INode *(*)(JSONLoader &);
namespace IR {
extern std::map<cstring, NodeFactoryFn> unpacker_table;

} // namespace IR

} // namespace P4

#endif /* IR_UNPACKER_TABLE_H_ */
70 changes: 70 additions & 0 deletions ir/unparsed_constant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Copyright 2013-present Barefoot Networks, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef IR_UNPARSED_CONSTANT_H_
#define IR_UNPARSED_CONSTANT_H_

#include "lib/cstring.h"

namespace P4 {

/**
* An unparsed numeric constant. We produce these as token values during
* lexing. The parser is responsible for actually interpreting the raw text as a
* numeric value and transforming it into an IR::Constant using parseConstant().
*
* To illustrate how a numeric constant is represented using this struct,
* here is a breakdown of '16w0x12':
*
* ___
* / ___
* | /
* | bitwidth (if @hasWidth) | 16
* | \___
* |
* | ___
* | /
* | separator (if @hasWidth) | w
* | \___
* @text |
* | ___
* | /
* | ignored prefix of length @skip | 0x
* | \___
* |
* | ___
* | /
* | numeric value in base @base | w
* | \___
* \___
*
* Simple numeric constants like '5' are specified by setting @hasWidth to
* false and providing a @skip length of 0.
*/
struct UnparsedConstant {
cstring text; /// Raw P4 text which was recognized as a numeric constant.
unsigned skip; /// An ignored prefix of the numeric constant (e.g. '0x').
unsigned base; /// The base in which the constant is expressed.
bool hasWidth; /// If true, a bitwidth and separator are present.
};

std::ostream &operator<<(std::ostream &out, const UnparsedConstant &constant);

bool operator<(const UnparsedConstant &a, const UnparsedConstant &b);

} // namespace P4

#endif /* IR_UNPARSED_CONSTANT_H_ */
1 change: 0 additions & 1 deletion ir/visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ limitations under the License.
#include "absl/container/flat_hash_map.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "ir/ir-generated.h"
#include "lib/hash.h"

#if HAVE_CXXABI_H
Expand Down
Loading