-
Notifications
You must be signed in to change notification settings - Fork 449
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
Add utilities for running modifications inside nested IR nodes #4940
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
c0c0a4a
Add ir-traversal
vlstill ef46311
Docs for ir-traversal
vlstill a56325d
Further docs tweaks
vlstill 04068f4
Code tweaks
vlstill 2e4ce5e
Fix formatting
vlstill 390aa70
Add tests, few fixes in impl
vlstill 2e0a857
Fix a typo & make lint happy
vlstill b51b88c
Format
vlstill 93bf62b
Put the namespace Detail into a separate file
vlstill 73a71c8
Exemplify IR::Traversal on few places in testgen
vlstill fb2436e
Try using quote includes
vlstill 569bd03
Merge branch 'main' into vstill/ir-traversal
vlstill File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
Copyright 2024-present Intel Corporation. | ||
|
||
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. | ||
*/ | ||
/** | ||
* \file | ||
* \brief IR traversing utilities, internal implementation details, not to be used directly. | ||
* \date 2024 | ||
* \author Vladimír Štill | ||
*/ | ||
|
||
#ifndef IR_IR_TRAVERSAL_INTERNAL_H_ | ||
#define IR_IR_TRAVERSAL_INTERNAL_H_ | ||
|
||
#include "lib/exceptions.h" | ||
#include "lib/rtti_utils.h" | ||
|
||
#ifndef IR_IR_TRAVERSAL_INTERNAL_ENABLE | ||
#error "This file must not be used directly" | ||
#endif | ||
|
||
namespace P4::IR::Traversal::Detail { | ||
|
||
/// @brief Internal, don't use directly. | ||
/// The class exists so that the functions can be mutually recursive. | ||
struct Traverse { | ||
template <typename Obj, typename T> | ||
static const Obj *modify(const Obj *, Assign<const T *> asgn) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
return asgn.value; | ||
} | ||
|
||
template <typename Obj, typename T> | ||
static const Obj *modify(Obj *, Assign<const T *> asgn) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
return asgn.value; | ||
} | ||
|
||
template <typename Obj, typename T> | ||
static Obj *modify(Obj *obj, Assign<T> &&asgn) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
*obj = std::move(asgn.value); | ||
return obj; | ||
} | ||
|
||
template <typename Obj, typename T> | ||
static Obj *modify(Obj *obj, const Assign<T> &asgn) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
*obj = asgn.value; | ||
return obj; | ||
} | ||
|
||
template <typename Obj, typename Fn> | ||
static decltype(auto) modify(Obj *obj, Fn fn) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
return fn(obj); | ||
} | ||
|
||
template <typename Obj, typename... Selectors> | ||
static Obj *modify(Obj *obj, Index idx, Selectors &&...selectors) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
BUG_CHECK(obj->size() > idx.value, "Index %1% out of bounds of %2%", idx.value, obj); | ||
modifyRef((*obj)[idx.value], std::forward<Selectors>(selectors)...); | ||
return obj; | ||
} | ||
|
||
template <typename Obj, typename To, typename... Selectors> | ||
static To *modify(Obj *obj, RTTI::Detail::ToType<To> cast, Selectors &&...selectors) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
auto *casted = cast(obj); | ||
BUG_CHECK(casted, "Cast of %1% failed", obj); | ||
return modify(casted, std::forward<Selectors>(selectors)...); | ||
} | ||
|
||
template <typename Obj, typename T, typename Sub, typename... Selectors> | ||
static Obj *modify(Obj *obj, Sub T::*member, Selectors &&...selectors) { | ||
static_assert(!std::is_const_v<Obj>, "Cannot modify constant object"); | ||
modifyRef(obj->*member, std::forward<Selectors>(selectors)...); | ||
return obj; | ||
} | ||
|
||
template <typename T, typename... Selectors> | ||
static void modifyRef(T &ref, Selectors &&...selectors) { | ||
if constexpr (std::is_pointer_v<T>) { | ||
ref = modify(ref->clone(), std::forward<Selectors>(selectors)...); | ||
} else { | ||
auto *res = modify(&ref, std::forward<Selectors>(selectors)...); | ||
if (&ref != res) { | ||
ref = *res; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
} // namespace P4::IR::Traversal::Detail | ||
|
||
#endif // IR_IR_TRAVERSAL_INTERNAL_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
Copyright 2024-present Intel Corporation. | ||
|
||
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. | ||
*/ | ||
/** | ||
* \file | ||
* \brief IR traversing utilities. This is mainly useful for modifying a sub-component of an IR | ||
* node. | ||
* \date 2024 | ||
* \author Vladimír Štill | ||
*/ | ||
|
||
#ifndef IR_IR_TRAVERSAL_H_ | ||
#define IR_IR_TRAVERSAL_H_ | ||
#define IR_IR_TRAVERSAL_INTERNAL_ENABLE | ||
|
||
#include <cstddef> | ||
|
||
namespace P4::IR::Traversal { | ||
|
||
/// @brief A selector used at the end of selector chain to assign to the current sub-object. | ||
/// e.g. `modify(obj, &IR::AssignmentStatement::left, Assign(var))` will set the LHS of assignment. | ||
/// @tparam T the parameter is usually derived by the C++ compiler. | ||
template <typename T> | ||
struct Assign { | ||
explicit Assign(const T &value) : value(value) {} | ||
T value; | ||
}; | ||
|
||
/// @brief Select an index of a integer-indexed sub-object. %This is useful e.g. to select first | ||
/// parameter of a method call and in similar cases involving @ref IR::Vector or @ref | ||
/// IR::IndexedVector. | ||
/// | ||
/// @code | ||
/// modify(mce, &IR::MethodCallExpression::arguments, Index(0), &IR::Argument::expression, | ||
/// Assign(val)) | ||
/// @endcode | ||
/// This snippet assigns `val` into the first argument of a method call. | ||
struct Index { | ||
explicit Index(size_t value) : value(value) {} | ||
size_t value; | ||
}; | ||
|
||
} // namespace P4::IR::Traversal | ||
|
||
// Put the internals out of the main file to increase its readability. | ||
#include "ir-traversal-internal.h" | ||
|
||
namespace P4::IR::Traversal { | ||
|
||
/// @brief Given an object @p obj and a series of selector (ending with a modifier), modify the @p | ||
/// obj's sub object at the selected path. %This is useful for deeper modification of objects that | ||
/// is not based on object types (in that case please use visitors) but on object structure (e.g. | ||
/// member paths of C++ objects). | ||
/// @param obj An object to modify. | ||
/// @param ...selectors A series of selectors ending either with Assign, or with a modifier | ||
/// function (typically a lambda). | ||
/// @return Modified object. This will usually be the same object as @p obj (unless the only | ||
/// selector is Assign with a pointer). | ||
/// | ||
/// @section sec_selectors Path Selectors | ||
/// | ||
/// Selection of the sub-objects is perfomed mainly using member pointers, but other selectors are | ||
/// also possible. | ||
/// | ||
/// - Object member pointer (e.g. `&IR::Expression::type`, `&IR::AssignmentStatement::left`) -- | ||
/// these are used to select members of object. Note that since the IR sometimes uses inline | ||
/// members and sometimes uses pointer members these are transparently handled the same -- this | ||
/// behaves as-if at any point the current object was a pointer to value that is to be processed | ||
/// by the following selectors. | ||
/// - @ref `IR::Traversal::Index` -- used for indexing e.g. @ref `IR::Vector` or @ref | ||
/// `IR::IndexedVector`. | ||
/// - @ref `RTTI::to<T>` -- used to cast to a more specific type. This is necessary to access | ||
/// members of the more specific type (e.g. if you know that a RHS of `IR::AssignmentStatement` is | ||
/// `IR::Lss`, you can use `RTTI::to<IR::Lss>` and then access members of `IR::Lss`. Note that the | ||
/// cast is not applied to anything -- we are passing a cast object. | ||
/// | ||
/// @section sec_modifiers Modifiers | ||
/// | ||
/// Modifier is a last component of the chain, it modifies the selected object. | ||
/// - @ref `Traversal::Assign` -- a simple modifier that assigns/replaces the currently selected | ||
/// value. If the argument is a pointer, the value is replaced, if the argument is non-pointer, | ||
/// the selected value is assigned. | ||
/// - freeform modifier object -- usually a lambda. This object will receive pointer to non-const | ||
/// value of the currently selected object and can modify it in any way (e.g. increment value). | ||
/// | ||
/// For example, given an extern call, you can modify the type of the first argument by adding a | ||
/// cast to it like this: | ||
/// @code | ||
/// modify(call, &IR::MethodCallStatement::methodCall, &IR::MethodCallExpression::arguments, | ||
/// Index(0), &IR::Argument::expression, [&](const IR::Expression *expr) | ||
/// { | ||
/// return new IR::Cast(expr->srcInfo, idxType, expr); | ||
/// }); | ||
/// @endcode | ||
/// | ||
/// Similarly, you can set the type of the first argument in the type of the extern like this: | ||
/// @code | ||
/// modify(call, &IR::MethodCallStatement::methodCall, &IR::MethodCallExpression::method, | ||
/// &IR::Expression::type, RTTI::to<IR::Type_Method>, &IR::Type_Method::parameters, | ||
/// &IR::ParameterList::parameters, Index(0), &IR::Parameter::type, Assign(idxType)); | ||
/// @endcode | ||
/// | ||
/// Any time a cast fails or an index is out of range the modification triggers a `BUG`. | ||
/// | ||
/// Please note that any modification is applied only to the selected path, that is, if the same | ||
/// node occurs multiple times in the IR DAG, and one of them is "focused" by the selector and | ||
/// modified, the other instance is unchanged (because the "focused" instance is cloned). | ||
template <typename Obj, typename... Selectors> | ||
Obj *modify(Obj *obj, Selectors &&...selectors) { | ||
return Detail::Traverse::modify(obj, std::forward<Selectors>(selectors)...); | ||
} | ||
|
||
/// @brief Similar to modify, but accepts constant argument which is cloned. Therefore, the result | ||
/// is a different object then @p obj. @sa `Traversal::modify`. | ||
template <typename Obj, typename... Selectors> | ||
Obj *apply(const Obj *obj, Selectors &&...selectors) { | ||
return Detail::Traverse::modify(obj->clone(), std::forward<Selectors>(selectors)...); | ||
} | ||
|
||
} // namespace P4::IR::Traversal | ||
|
||
#undef IR_IR_TRAVERSAL_INTERNAL_ENABLE | ||
#endif // IR_IR_TRAVERSAL_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely an improvement and helps avoiding accidental bugs by getting an index or variable wrong.