blob: 6d8056ffec0d19e94b4b357d87a54e6f660130c6 [file] [log] [blame]
// Copyright 2024 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef SRC_TINT_LANG_CORE_IR_REFERENCED_FUNCTIONS_H_
#define SRC_TINT_LANG_CORE_IR_REFERENCED_FUNCTIONS_H_
#include "src/tint/lang/core/ir/control_instruction.h"
#include "src/tint/lang/core/ir/module.h"
#include "src/tint/lang/core/ir/return.h"
#include "src/tint/lang/core/ir/user_call.h"
#include "src/tint/utils/containers/hashmap.h"
#include "src/tint/utils/rtti/switch.h"
/// Utility that helps guarantee the same const-ness is applied to both types.
template <class Src, class Dst>
using TranscribeConst = std::conditional_t<std::is_const<Src>{}, std::add_const_t<Dst>, Dst>;
namespace tint::core::ir {
/// ReferencedFunctions is a helper to determine the set of functions that are transitively
/// referenced by functions in a module. References are determined lazily and cached for future
/// requests.
///
/// Note:
/// The template param M is used to ensure that inputs and outputs of this class have the same
/// const-ness. If 'Module' is supplied then the internal operations and output will not be
/// const, which is needed for transforms. Whereas if the param is 'const Module' the internals
/// and outputs will be const, which is needed for the IR validator.
/// Note:
/// Changes to the module can invalidate the cached data. This is intended to be created by
/// operations that need this information, and discarded when they complete. Tracking this
/// information inside the IR module would add overhead any time an instruction is added or
/// removed from the module. Since only a few operations need this information, it is expected
/// to be more efficient to generate it on demand.
template <typename M>
class ReferencedFunctions {
// Replace this with concepts when C++20 is available.
static_assert(std::is_same<std::remove_cv_t<M>, Module>());
public:
/// Short form aliases for types that have the same constant-ness as M.
using BlockT = TranscribeConst<M, Block>;
using FunctionT = TranscribeConst<M, Function>;
/// A set of a functions referenced by a function.
using FunctionSet = Hashset<FunctionT*, 16>;
/// Constructor.
/// @param ir the module
explicit ReferencedFunctions(M& ir) {
// Loop over functions, recording the blocks that they are called from.
for (auto func : ir.functions) {
if (!func) {
continue;
}
func->ForEachUseUnsorted([&](const Usage& use) {
if (auto* call = use.instruction->As<UserCall>()) {
block_to_direct_calls_.GetOrAddZero(call->Block()).Add(func);
} else {
TINT_ASSERT(use.instruction->Is<Return>());
}
});
}
}
/// Get the set of transitively referenced functions for a function.
/// @param func the function
/// @returns the set of transitively reference functions
FunctionSet& TransitiveReferences(FunctionT* func) {
return transitive_references_.GetOrAdd(func, [&] {
FunctionSet functions;
if (!func) {
return functions;
}
// Walk blocks in the function to find function calls.
Vector<BlockT*, 64> block_queue{func->Block()};
while (!block_queue.IsEmpty()) {
auto* next = block_queue.Pop();
if (!next) {
continue;
}
// Add directly referenced functions.
if (auto itr = block_to_direct_calls_.Get(next)) {
for (auto& callee : *itr) {
if (functions.Add(callee)) {
// Add functions transitively referenced by the callee.
const auto& callee_functions = TransitiveReferences(callee);
for (auto& transitive_func : callee_functions) {
functions.Add(transitive_func);
}
}
}
}
// Loop over instructions in the block to find nested blocks.
for (auto* inst : *next) {
if (auto* ctrl = inst->template As<ControlInstruction>()) {
// Add nested blocks to the queue.
ctrl->ForeachBlock([&](BlockT* blk) { block_queue.Push(blk); });
}
}
}
return functions;
});
}
private:
/// A map from blocks to their directly called functions.
Hashmap<BlockT*, FunctionSet, 64> block_to_direct_calls_{};
/// A map from functions to their transitively referenced functions.
Hashmap<FunctionT*, FunctionSet, 8> transitive_references_;
};
} // namespace tint::core::ir
#endif // SRC_TINT_LANG_CORE_IR_REFERENCED_FUNCTIONS_H_