Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

124 lines
3.8 KiB
C++

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 tvm/ir/scope_stack.h
* \brief A generic scope stack for managing hierarchical state during IR visiting.
*/
#ifndef TVM_IR_SCOPE_STACK_H_
#define TVM_IR_SCOPE_STACK_H_
#include <tvm/ffi/error.h>
#include <deque>
#include <type_traits>
namespace tvm {
/*!
* \brief A scope stack for maintaining hierarchical state during IR visiting.
*
* During IR tree traversal, visitors often need to track scope-local state
* (e.g., active constraints, variable bindings) that should be automatically
* cleaned up when leaving a scope. ScopeStack provides this via WithNewScope,
* which pushes a new element on entry and pops it on exit.
*
* \code
* ScopeStack<WithGroup<ConstraintContext>> constraints;
*
* // In VisitStmt_(ForNode):
* return constraints.WithNewScope([&]() -> Stmt {
* constraints.Current().Emplace(analyzer, condition);
* return StmtExprMutator::VisitStmt_(op);
* });
* \endcode
*
* \tparam T The element type stored on the stack. Must be default-constructible.
*/
template <typename T>
class ScopeStack {
public:
/*! \brief Construct with one initial scope level. */
ScopeStack() { stack_.emplace_back(); }
/*! \brief Return the number of active scopes. */
size_t size() const { return stack_.size(); }
/*! \brief Return true if no scopes are active. */
bool empty() const { return stack_.empty(); }
/*!
* \brief Access the current (innermost) scope element.
*
* The returned reference is stable across push_back/pop_back because
* std::deque guarantees pointer stability for these operations.
*
* \return Mutable reference to the top element.
*/
T& Current() {
TVM_FFI_ICHECK(!stack_.empty());
return stack_.back();
}
/*! \brief Const access to the current (innermost) scope element. */
const T& Current() const {
TVM_FFI_ICHECK(!stack_.empty());
return stack_.back();
}
/*!
* \brief Execute body within a new scope.
*
* Pushes a new T onto the stack, executes the body, then pops it.
*
* \param body A callable to execute within the scope.
* \return The return value of body(), if non-void.
*/
template <typename F>
auto WithNewScope(F&& body) -> decltype(body()) {
stack_.emplace_back();
struct Guard {
std::deque<T>* stack;
~Guard() noexcept(false) { stack->pop_back(); }
} guard{&stack_};
if constexpr (std::is_void_v<decltype(body())>) {
body();
} else {
return body();
}
}
private:
/*!
* \brief The scope stack.
*
* We use std::deque rather than std::vector for pointer stability:
* references returned by Current() remain valid across push/pop operations.
* This is critical because methods called on Current() (e.g., Emplace on
* a WithGroup) may trigger re-entrant code that pushes new scopes onto
* the same stack. With std::vector the internal buffer reallocation would
* invalidate the reference, causing use-after-free.
*/
std::deque<T> stack_;
};
} // namespace tvm
#endif // TVM_IR_SCOPE_STACK_H_