From 0f664d22405f36fdc8b3705bf0f35f9ed33c9d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Fri, 1 Aug 2025 13:54:02 +0200 Subject: [PATCH 1/8] ACG filter argument --- lib/passes/analysis/MemInstFinder.cpp | 13 ++++++++++++- lib/passes/analysis/MemInstFinder.h | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index 1260db13..1498d68a 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -105,7 +105,18 @@ static std::unique_ptr make_filter(const MemInstFinderC auto json_cg = JSONCG::getJSON(cg_file); auto matcher = std::make_unique(util::glob2regex(glob)); return std::make_unique(glob, std::move(json_cg), std::move(matcher)); - } else { + } else if (filter_id == FilterImplementation::acg) { + const std::string acg_file = config[config::ConfigStdArgs::filter_cg_file]; + if (acg_file.empty()) { + LOG_FATAL("ACG File not set!"); + std::exit(1); + } + LOG_DEBUG("Return ACG filter with CG file @ " << acg_file) + + return std::make_unique(); // TODO + } + + else { LOG_DEBUG("Return default filter") auto matcher = std::make_unique(util::glob2regex(glob)); const auto deep_glob = config[config::ConfigStdArgs::filter_glob_deep]; diff --git a/lib/passes/analysis/MemInstFinder.h b/lib/passes/analysis/MemInstFinder.h index a11cf88f..b5df1e67 100644 --- a/lib/passes/analysis/MemInstFinder.h +++ b/lib/passes/analysis/MemInstFinder.h @@ -30,7 +30,7 @@ class Configuration; namespace typeart::analysis { -enum class FilterImplementation { none, standard, cg }; +enum class FilterImplementation { none, standard, cg, acg }; struct FunctionData { MallocDataList mallocs; From 1bcc4ebb7c40e2703028ad7b2ffdc4d20564ed5f Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:15:52 +0100 Subject: [PATCH 2/8] Resolve conflicts --- lib/passes/Commandline.cpp | 4 +- lib/passes/TypeARTPass.cpp | 86 +++--- lib/passes/analysis/MemInstFinder.cpp | 24 +- lib/passes/compat/CallSite.h | 19 ++ lib/passes/configuration/OptionsUtil.h | 1 + lib/passes/configuration/TypeARTOptions.cpp | 1 + lib/passes/filter/ACGFilter.cpp | 138 +++++++++ lib/passes/filter/ACGFilter.h | 59 ++++ lib/passes/filter/CMakeLists.txt | 3 + lib/passes/filter/MetaCG.h | 320 ++++++++++++++++++++ 10 files changed, 604 insertions(+), 51 deletions(-) create mode 100644 lib/passes/filter/ACGFilter.cpp create mode 100644 lib/passes/filter/ACGFilter.h create mode 100644 lib/passes/filter/MetaCG.h diff --git a/lib/passes/Commandline.cpp b/lib/passes/Commandline.cpp index 3ea91a4e..09e0f355 100644 --- a/lib/passes/Commandline.cpp +++ b/lib/passes/Commandline.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace llvm; @@ -129,7 +130,8 @@ static cl::opt cl_typeart_call_filter_i cl::values(clEnumValN(typeart::analysis::FilterImplementation::none, "none", "No filter"), clEnumValN(typeart::analysis::FilterImplementation::standard, "std", "Standard forward filter (default)"), - clEnumValN(typeart::analysis::FilterImplementation::cg, "cg", "Call-graph-based filter")), + clEnumValN(typeart::analysis::FilterImplementation::cg, "cg", "Call-graph-based filter"), + clEnumValN(typeart::analysis::FilterImplementation::acg, "acg", "MetaCG with argument flow based filter")), cl::Hidden, cl::init(typeart::analysis::FilterImplementation::standard), cl::cat(typeart_analysis_category)); static cl::opt cl_typeart_call_filter_glob( diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index 3d7a8b3c..954bca5b 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -403,48 +403,50 @@ bool LegacyTypeArtPass::doFinalization(llvm::Module&) { //..................... llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() { using namespace llvm; - return { - LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) { - pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) { - auto parameters = typeart::util::pass::parsePassParameters( - typeart::config::pass::parse_typeart_config, "typeart", "typeart"); - if (!parameters) { - LOG_FATAL("Error parsing heap params: " << parameters.takeError()) - return; - } - MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); - }); -#if LLVM_VERSION_MAJOR > 19 - pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel, ThinOrFullLTOPhase) { -#else - pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) { -#endif - auto parameters = typeart::util::pass::parsePassParameters( - typeart::config::pass::parse_typeart_config, "typeart", - "typeart"); - if (!parameters) { - LOG_FATAL("Error parsing stack params: " << parameters.takeError()) - return; - } - MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); - }); - pass_builder.registerPipelineParsingCallback([](StringRef name, ModulePassManager& module_pm, - ArrayRef) { - if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) { - auto parameters = - typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, name, "typeart"); - if (!parameters) { - LOG_FATAL("Error parsing params: " << parameters.takeError()) - return false; - } - module_pm.addPass(typeart::pass::TypeArtPass(parameters.get())); - return true; - } - LOG_FATAL("Not a valid parametrized pass name: " << name) - return false; - }); - } - }; + return {LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) { + pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) { + auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, + "typeart", "typeart"); + if (!parameters) { + LOG_FATAL("Error parsing heap params: " << parameters.takeError()) + return; + } + MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); + }); + pass_builder.registerFullLinkTimeOptimizationLastEPCallback([](ModulePassManager& MPM, OptimizationLevel) { + auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, + "typeart", "typeart"); + if (!parameters) { + LOG_FATAL("Error parsing heap params: " << parameters.takeError()) + return; + } + MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); + }); + pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) { + auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, + "typeart", "typeart"); + if (!parameters) { + LOG_FATAL("Error parsing stack params: " << parameters.takeError()) + return; + } + MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); + }); + pass_builder.registerPipelineParsingCallback( + [](StringRef name, ModulePassManager& module_pm, ArrayRef) { + if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) { + auto parameters = typeart::util::pass::parsePassParameters( + typeart::config::pass::parse_typeart_config, name, "typeart"); + if (!parameters) { + LOG_FATAL("Error parsing params: " << parameters.takeError()) + return false; + } + module_pm.addPass(typeart::pass::TypeArtPass(parameters.get())); + return true; + } + LOG_FATAL("Not a valid parametrized pass name: " << name) + return false; + }); + }}; } extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index 1498d68a..dbd0b15e 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -17,10 +17,12 @@ #include "analysis/MemOpData.h" #include "configuration/Configuration.h" #include "configuration/TypeARTOptions.h" +#include "filter/ACGFilter.h" #include "filter/CGForwardFilter.h" #include "filter/CGInterface.h" #include "filter/Filter.h" #include "filter/Matcher.h" +#include "filter/MetaCG.h" #include "filter/StdForwardFilter.h" #include "support/ConfigurationBase.h" #include "support/Logger.h" @@ -44,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -106,17 +109,22 @@ static std::unique_ptr make_filter(const MemInstFinderC auto matcher = std::make_unique(util::glob2regex(glob)); return std::make_unique(glob, std::move(json_cg), std::move(matcher)); } else if (filter_id == FilterImplementation::acg) { - const std::string acg_file = config[config::ConfigStdArgs::filter_cg_file]; - if (acg_file.empty()) { - LOG_FATAL("ACG File not set!"); + LOG_DEBUG("Return Argflow filter"); + std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; + const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); + if (!buf) { + LOG_FATAL("Failed to load MCG file"); std::exit(1); } - LOG_DEBUG("Return ACG filter with CG file @ " << acg_file) - - return std::make_unique(); // TODO - } - else { + auto mcg = metacg::parse((*buf)->getBuffer()); + if (!mcg) { + LOG_FATAL(mcg.takeError() << '\n'); + std::exit(1); + } + + return std::make_unique(std::move(mcg.get()), Regex{util::glob2regex(glob), Regex::NoFlags}); + } else { LOG_DEBUG("Return default filter") auto matcher = std::make_unique(util::glob2regex(glob)); const auto deep_glob = config[config::ConfigStdArgs::filter_glob_deep]; diff --git a/lib/passes/compat/CallSite.h b/lib/passes/compat/CallSite.h index 9a4f821a..e64a97dc 100644 --- a/lib/passes/compat/CallSite.h +++ b/lib/passes/compat/CallSite.h @@ -15,6 +15,7 @@ #ifndef COMPAT_LLVM_IR_CALLSITE_H #define COMPAT_LLVM_IR_CALLSITE_H +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Instruction.h" #include @@ -22,6 +23,8 @@ #include #include +#include + namespace llvm { class CallSite { private: @@ -53,6 +56,10 @@ class CallSite { return true; } + [[nodiscard]] Function* getFunction() const { + return instruction_->getFunction(); + } + [[nodiscard]] llvm::Function* getCalledFunction() const { if (auto* call_base = llvm::dyn_cast_or_null(instruction_)) { return call_base->getCalledFunction(); @@ -83,6 +90,18 @@ class CallSite { auto* call_base = llvm::cast(instruction_); return call_base->getIntrinsicID(); } + + [[nodiscard]] const DILocation* getLocation() const { + SmallVector> mds; + instruction_->getAllMetadata(mds); + + for (const auto& [_, md] : mds) { + if (const auto* loc = dyn_cast(md); loc) + return loc; + } + + return nullptr; + } }; } // namespace llvm diff --git a/lib/passes/configuration/OptionsUtil.h b/lib/passes/configuration/OptionsUtil.h index f44ac0c4..1f35e5a1 100644 --- a/lib/passes/configuration/OptionsUtil.h +++ b/lib/passes/configuration/OptionsUtil.h @@ -43,6 +43,7 @@ ClType string_to_enum(llvm::StringRef cl_value) { if constexpr (std::is_same_v) { auto val = llvm::StringSwitch(cl_value) .Case("cg", FilterImplementation::cg) + .Case("acg", FilterImplementation::acg) .Case("none", FilterImplementation::none) .Case("std", FilterImplementation::standard) .Default(FilterImplementation::standard); diff --git a/lib/passes/configuration/TypeARTOptions.cpp b/lib/passes/configuration/TypeARTOptions.cpp index db1a76eb..dc7b29bc 100644 --- a/lib/passes/configuration/TypeARTOptions.cpp +++ b/lib/passes/configuration/TypeARTOptions.cpp @@ -37,6 +37,7 @@ struct llvm::yaml::ScalarEnumerationTraits struct llvm::yaml::ScalarEnumerationTraits { static void enumeration(IO& io, typeart::analysis::FilterImplementation& value) { + io.enumCase(value, "acg", typeart::analysis::FilterImplementation::acg); io.enumCase(value, "cg", typeart::analysis::FilterImplementation::cg); io.enumCase(value, "std", typeart::analysis::FilterImplementation::standard); io.enumCase(value, "none", typeart::analysis::FilterImplementation::none); diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp new file mode 100644 index 00000000..e2dd5ba5 --- /dev/null +++ b/lib/passes/filter/ACGFilter.cpp @@ -0,0 +1,138 @@ +// TypeART library +// +// Copyright (c) 2017-2023 TypeART Authors +// Distributed under the BSD 3-Clause license. +// (See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// +// Project home: https://github.com/tudasc/TypeART +// +// SPDX-License-Identifier: BSD-3-Clause +// + +#include "ACGFilter.h" + +#include + +namespace typeart::filter { + +AcgFilterImpl::AcgFilterImpl(metacg::Mcg&& cg, Regex&& match) + : mcg{std::move(cg)}, matcher{std::move(match)} +{} + +FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, const size_t idx) { + SmallVector, 64> workq{}; + SmallSet seen{}; + + const auto enqueue = [&seen, &workq](const size_t it, const size_t i) { + if (const auto [_, inserted] = seen.insert(it); inserted) + workq.push_back({it, i}); + }; + + for (const auto id : nodes) { + enqueue(id, idx); + LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.graph.nodes.forId(id)->name << '\n'); + } + + while (!workq.empty()) { + // Grab the current node ID and translate it into its corresponding function descriptor to check + // if its name matches our matcher. + const auto [current, cur_idx] = workq.pop_back_val(); + LOG_DEBUG("> Inspecting node: " << mcg.graph.nodes.forId(current)->name << '\n'); + if (const auto fn = mcg.graph.nodes.forId(current); fn && matcher.match(fn->name)) { + LOG_DEBUG("-> Matches matcher, keeping\n"); + return FilterAnalysis::Keep; + } else if (fn && !fn->has_body) + return FilterAnalysis::Keep; + + const auto outs = mcg.graph.nodes.outputs(current, cur_idx); + if (!outs) { + LOG_DEBUG("-> Failed to get outputs\n"); + continue; + } + + for (const auto& out : *outs) + for (const auto callee : out.callees) { + enqueue(callee, out.idx); + LOG_DEBUG("-> Enqueued callee: " << mcg.graph.nodes.forId(callee)->name << '\n'); + } + } + + return FilterAnalysis::Continue; +} + +FilterAnalysis AcgFilterImpl::precheck(Value* in, Function* start, const FPath&) { + if (!start) + return FilterAnalysis::Continue; + + FunctionAnalysis analysis{}; + analysis.analyze(start); + // Filter if we're in a leaf function + if (analysis.empty()) + return FilterAnalysis::Filter; + + if (isTempAlloc(in)) { + LOG_DEBUG("Alloca is a temporary " << *in); + return FilterAnalysis::Filter; + } + + if (AllocaInst* alloc = dyn_cast(in)) { + if (alloc->getAllocatedType()->isStructTy() && omp::OmpContext::allocaReachesTask(alloc)) { + LOG_DEBUG("Alloca reaches task call " << *alloc) + return FilterAnalysis::Filter; + } + } + + return FilterAnalysis::Continue; +} + +FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { + const auto arg = *p.getEndPrev(); + assert(arg && "Argument is missing"); + + if (!is_contained(current.args(), arg)) + return FilterAnalysis::Continue; + + // Calculate the argument position + const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); + + SmallVector callees{}; + + // If `current` does not have a called function, we instead look through the DI to find the source location + // of this call, then compare all recorded calls in `current`'s parent function's function descriptor to + // get a list of potential call targets. + if (!current.getCalledFunction()) { + const auto parentNode = mcg.graph.nodes.byName(current.getFunction()->getName()); + if (!parentNode) + return FilterAnalysis::Keep; + + const auto parent = mcg.graph.nodes.forId(*parentNode); + assert(parent && "Malformed MCG"); + + auto md = parent->meta.as("localflow"); + if (!md) + return FilterAnalysis::Keep; + + for (const auto& local : md->locals) { + // TODO: How to potentially translate current.getLocation() to non-inlined mcg src loc + if (const auto* diLoc = current.getLocation(); + local.loc == metacg::SrcLoc{diLoc ? diLoc->getColumn() : 0, diLoc ? diLoc->getLine() : 0}) + callees.append(local.callees.begin(), local.callees.end()); + } + + const auto outs = mcg.graph.nodes.outputs(*parentNode, idx); + for (const auto& out : *outs) + callees.append(out.callees.begin(), out.callees.end()); + } else if (const auto node = mcg.graph.nodes.byName(current.getCalledFunction()->getName()); node) + callees.push_back(*node); + else + // Be conservative if the function is not recorded in the call graph + return FilterAnalysis::Keep; + + // Determine if any of the potential callees can pass the parameters at index `idx` to a matching function + return reachesMatching(callees, idx); +} + +FilterAnalysis AcgFilterImpl::decl(const CallSite current, const Path& p) { return def(current, p); } + +} // namespace typeart::filter diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h new file mode 100644 index 00000000..e217c513 --- /dev/null +++ b/lib/passes/filter/ACGFilter.h @@ -0,0 +1,59 @@ +// TypeART library +// +// Copyright (c) 2017-2023 TypeART Authors +// Distributed under the BSD 3-Clause license. +// (See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// +// Project home: https://github.com/tudasc/TypeART +// +// SPDX-License-Identifier: BSD-3-Clause +// + +#ifndef ARGFLOWFILTER_H +#define ARGFLOWFILTER_H + +#include "compat/CallSite.h" +#include "FilterBase.h" +#include "Matcher.h" +#include "MetaCG.h" + +namespace typeart::filter { + +namespace omp { + struct OmpContext; +} + +struct DefaultSearch; + +struct ArgflowFilterTrait { + constexpr static bool Indirect = false; + constexpr static bool Intrinsic = false; + constexpr static bool Declaration = true; + constexpr static bool Definition = true; + constexpr static bool PreCheck = true; +}; + +class CGInterface; + +struct AcgFilterImpl { + using Support = ArgflowFilterTrait; + + AcgFilterImpl(metacg::Mcg&&, Regex&&); + + FilterAnalysis precheck(Value*, Function*, const FPath&); + FilterAnalysis decl(CallSite, const Path&); + FilterAnalysis def(CallSite, const Path&); + +private: + FilterAnalysis reachesMatching(ArrayRef, size_t); + + metacg::Mcg mcg; + Regex matcher; +}; + +using AcgFilter = BaseFilter; + +} // namespace typeart::filter + +#endif //ARGFLOWFILTER_H diff --git a/lib/passes/filter/CMakeLists.txt b/lib/passes/filter/CMakeLists.txt index bf7e3af2..18b9cd3a 100644 --- a/lib/passes/filter/CMakeLists.txt +++ b/lib/passes/filter/CMakeLists.txt @@ -5,6 +5,9 @@ set(FILTER_SOURCES FilterBase.h CGForwardFilter.h CGForwardFilter.cpp + MetaCG.h + ACGFilter.h + ACGFilter.cpp StdForwardFilter.h StdForwardFilter.cpp FilterUtil.cpp diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h new file mode 100644 index 00000000..98a0a092 --- /dev/null +++ b/lib/passes/filter/MetaCG.h @@ -0,0 +1,320 @@ +// TypeART library +// +// Copyright (c) 2017-2025 TypeART Authors +// Distributed under the BSD 3-Clause license. +// (See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/BSD-3-Clause) +// +// Project home: https://github.com/tudasc/TypeART +// +// SPDX-License-Identifier: BSD-3-Clause +// + +#ifndef METACG_H +#define METACG_H + +#include + +#include + +template <> +struct std::hash> { + size_t operator()(std::pair const& p) const noexcept { + size_t h1 = std::hash{}(std::get<0>(p)); + size_t h2 = std::hash{}(std::get<1>(p)); + return h1 ^ h2 << 1; + } +}; + +namespace typeart::filter::metacg { + +/// Holds information about the generator used to serialize the callgraph +struct Generator { std::string name, sha, version; }; + +inline bool fromJSON(json::Value const& json, Generator& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("name", r.name) && o.map("sha", r.sha) && o.map("version", r.version); +} + +/// MetaCGv3 header +struct Header { + Generator gen; + std::string version; +}; + +inline bool fromJSON(json::Value const& json, Header& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("generator", r.gen) && o.map("version", r.version); +} + +/// Represents a source location +struct SrcLoc { + auto operator==(const SrcLoc& rhs) const { return col == rhs.col && line == rhs.line; } + + size_t col, line; +}; + +inline bool fromJSON(llvm::json::Value const& json, SrcLoc& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("col", r.col) && o.map("line", r.line); +} + +/// Call instances metadata +struct MdCalls { std::vector locs; }; + +inline bool fromJSON(json::Value const& json, MdCalls& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("locs", r.locs); +} + +/// Represents an output parameter an incoming argument flows into +struct MdArgOutput { + bool by_ref; + std::vector callees; + size_t idx; + SrcLoc loc; +}; + +inline bool fromJSON(json::Value const& json, MdArgOutput& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o + && o.map("by_ref", r.by_ref) + && o.map("callees", r.callees) + && o.map("idx", r.idx) + && o.map("loc", r.loc); +} + +/// Represents an incoming argument +struct MdArg { + size_t idx; + std::vector outs; +}; + +inline bool fromJSON(json::Value const& json, MdArg& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("idx", r.idx) && o.map("outs", r.outs); +} + +/// Represents a local that flows into a callee +struct MdLocal { + std::vector callees; + SrcLoc loc; +}; + +inline bool fromJSON(json::Value const& json, MdLocal& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("callees", r.callees) && o.map("loc", r.loc); +} + +/// Toplevel node metadata to annotate each published parameter +struct MdArgflow { + std::vector args; +}; + +inline bool fromJSON(json::Value const& json, MdArgflow& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("args", r.args); +} + +/// Toplevel node metadata to annotate published locals +struct MdLocals { + std::vector locals; +}; + +inline bool fromJSON(json::Value const& json, MdLocals& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("locals", r.locals); +} + +/// Represents a generic metadata entry, required as metadata entries are inherently polymorphic +/// so the concrete deserialization is left to the caller. +struct Md { + friend bool fromJSON(json::Value const&, Md&, json::Path const&); + + Md() = default; + + explicit Md(json::Value const& val) : v{val} {} + + /// Performs a lookup into the metadata object with the given key `name` and attempts to deserialize + /// it into the requested type. + template + Expected as(StringRef const name) const { + if (v.getAsNull()) + return createStringError("No metadata object"); + + json::Path::Root root{}; + if (T r{}; fromJSON(*(v.getAsObject()->get(name)), r, root)) + return r; + + return createStringError("Failed to deserialize metadata"); + } + +private: + json::Value v{nullptr}; +}; + +inline bool fromJSON(json::Value const& json, Md& r, json::Path const&) { + r.v = json; + return true; +} + +/// Represents a function in the callgraph +struct Fn { + std::string name, origin; + bool has_body; + Md meta; +}; + +inline bool fromJSON(json::Value const& json, Fn& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o + && o.map("functionName", r.name) + && o.map("origin", r.origin) + && o.map("hasBody", r.has_body) + && o.map("meta", r.meta); +} + +/// Represents the edges of the callgraph +struct EdgeContainer { + friend struct CallGraph; + + friend bool fromJSON(json::Value const& json, EdgeContainer& r, json::Path const& path); + + EdgeContainer() = default; + +private: + std::unordered_map, Md> underlying{}; +}; + +inline bool fromJSON(json::Value const& json, EdgeContainer& r, json::Path const& path) { + auto const outer = json.getAsArray(); + if (!outer) + return false; + + for (json::Value const& v: *outer) { + auto const inner = v.getAsArray(); + if (!inner) + return false; + + auto const first = (*inner)[0].getAsArray(); + auto const second = (*inner)[1]; + if (!first) + return false; + + r.underlying.insert({ + {*(*first)[0].getAsUINT64(), *(*first)[1].getAsUINT64()}, + Md{second} + }); + } + + return true; +} + +/// Represents the nodes of the callgraph +struct NodeContainer { + friend struct CallGraph; + + NodeContainer() = default; + + friend bool fromJSON(json::Value const& json, NodeContainer& r, json::Path const& path); + + /// Translates a function name to its corresponding node ID if it exists + std::optional byName(StringRef const name) const { + for (auto const& [id, f]: underlying) { + if (f.name == name) + return id; + } + + return {}; + } + + /// Translates a node identifier to a function descriptor + std::optional forId(size_t const id) const { + for (auto const& [idx, f]: underlying) { + if (idx == id) + return f; + } + + return {}; + } + + /// Returns the parameter outputs for `node`'s incoming argument at position `idx` + std::optional> outputs(size_t node, size_t idx) const { + if (underlying.find(node) == underlying.end()) + return {}; + + auto argflow = underlying.at(node).meta.as("argflow"); + if (!argflow) + return {}; + + for (auto const& [id, out]: argflow->args) { + if (id == idx) + return out; + } + + return {}; + } + +private: + std::unordered_map underlying{}; +}; + +inline bool fromJSON(json::Value const& json, NodeContainer& r, json::Path const& path) { + auto const outer = json.getAsArray(); + if (!outer) + return false; + + for (json::Value const& v: *outer) { + auto const inner = v.getAsArray(); + if (!inner) + return false; + + auto const first = (*inner)[0].getAsUINT64(); + if (!first) + return false; + + Fn f{}; + if (auto const second = fromJSON((*inner)[1], f, path); !second) + return false; + + r.underlying.insert({*first, f}); + } + + return true; +} + +/// Represents the complete callgraph +struct CallGraph { + EdgeContainer edges; + NodeContainer nodes; +}; + +inline bool fromJSON(json::Value const& json, CallGraph& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("edges", r.edges) && o.map("nodes", r.nodes); +} + +/// Top-level MetaCGv3 object container +struct Mcg { + CallGraph graph; + Header hdr; +}; + +inline bool fromJSON(json::Value const& json, Mcg& r,json::Path const& path) { + json::ObjectMapper o{json, path}; + return o && o.map("_CG", r.graph) && o.map("_MetaCG", r.hdr); +} + +/// Deserializes the MetaCGv3 format from text +inline Expected parse(StringRef const json) { + if (auto parsed = json::parse(json); parsed) { + LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << '\n' << "Version: " << parsed->hdr.version << '\n'); + return *parsed; + } + else + return Expected{parsed.takeError()}; +} + +} // namespace typeart::filter::metacg + +#endif //METACG_H From 479176e3dbb1740e1e3fbd16951d4c9f2cc96c5b Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:38:04 +0200 Subject: [PATCH 3/8] Update to MetaCG v4 & fix parent scope handling for dataflow --- lib/passes/filter/ACGFilter.cpp | 47 ++++++--- lib/passes/filter/ACGFilter.h | 1 - lib/passes/filter/MetaCG.h | 182 +++++++++++++++----------------- 3 files changed, 113 insertions(+), 117 deletions(-) diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index e2dd5ba5..705fb8ed 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -31,31 +31,34 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons for (const auto id : nodes) { enqueue(id, idx); - LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.graph.nodes.forId(id)->name << '\n'); + LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.forId(id)->name << '\n'); } while (!workq.empty()) { // Grab the current node ID and translate it into its corresponding function descriptor to check // if its name matches our matcher. const auto [current, cur_idx] = workq.pop_back_val(); - LOG_DEBUG("> Inspecting node: " << mcg.graph.nodes.forId(current)->name << '\n'); - if (const auto fn = mcg.graph.nodes.forId(current); fn && matcher.match(fn->name)) { + LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name << '\n'); + if (const auto fn = mcg.forId(current); fn && fn->name && matcher.match(*fn->name)) { LOG_DEBUG("-> Matches matcher, keeping\n"); return FilterAnalysis::Keep; - } else if (fn && !fn->has_body) + } else if (fn && !fn->has_body) { + LOG_DEBUG("-> Function has no body, keeping\n"); return FilterAnalysis::Keep; + } - const auto outs = mcg.graph.nodes.outputs(current, cur_idx); + const auto outs = mcg.outputs(current, cur_idx); if (!outs) { LOG_DEBUG("-> Failed to get outputs\n"); continue; } - for (const auto& out : *outs) + for (const auto& out : *outs) { for (const auto callee : out.callees) { enqueue(callee, out.idx); - LOG_DEBUG("-> Enqueued callee: " << mcg.graph.nodes.forId(callee)->name << '\n'); + LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name << '\n'); } + } } return FilterAnalysis::Continue; @@ -67,6 +70,7 @@ FilterAnalysis AcgFilterImpl::precheck(Value* in, Function* start, const FPath&) FunctionAnalysis analysis{}; analysis.analyze(start); + // Filter if we're in a leaf function if (analysis.empty()) return FilterAnalysis::Filter; @@ -102,32 +106,41 @@ FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { // of this call, then compare all recorded calls in `current`'s parent function's function descriptor to // get a list of potential call targets. if (!current.getCalledFunction()) { - const auto parentNode = mcg.graph.nodes.byName(current.getFunction()->getName()); + const auto* callLoc = current.getLocation(); + if (!callLoc) + return FilterAnalysis::Keep; + + // Resolve the parent scope via debug metadata as it should stay consistent even through inlining + const auto* parentScope = dyn_cast(callLoc->getScope()); + if (!parentScope) + return FilterAnalysis::Keep; + + const auto parentNode = mcg.byName(parentScope->getName()); if (!parentNode) return FilterAnalysis::Keep; - const auto parent = mcg.graph.nodes.forId(*parentNode); + const auto parent = mcg.forId(*parentNode); assert(parent && "Malformed MCG"); auto md = parent->meta.as("localflow"); if (!md) return FilterAnalysis::Keep; - for (const auto& local : md->locals) { - // TODO: How to potentially translate current.getLocation() to non-inlined mcg src loc - if (const auto* diLoc = current.getLocation(); - local.loc == metacg::SrcLoc{diLoc ? diLoc->getColumn() : 0, diLoc ? diLoc->getLine() : 0}) + for (const auto& local : md->locals) + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) callees.append(local.callees.begin(), local.callees.end()); - } - const auto outs = mcg.graph.nodes.outputs(*parentNode, idx); + const auto outs = mcg.outputs(*parentNode, idx); for (const auto& out : *outs) callees.append(out.callees.begin(), out.callees.end()); - } else if (const auto node = mcg.graph.nodes.byName(current.getCalledFunction()->getName()); node) + } + // Otherwise simply add the called function to the list of callees to search from + else if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { callees.push_back(*node); - else + } else { // Be conservative if the function is not recorded in the call graph return FilterAnalysis::Keep; + } // Determine if any of the potential callees can pass the parameters at index `idx` to a matching function return reachesMatching(callees, idx); diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h index e217c513..3a1d1672 100644 --- a/lib/passes/filter/ACGFilter.h +++ b/lib/passes/filter/ACGFilter.h @@ -15,7 +15,6 @@ #include "compat/CallSite.h" #include "FilterBase.h" -#include "Matcher.h" #include "MetaCG.h" namespace typeart::filter { diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index 98a0a092..c4cd1ef1 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -15,6 +15,10 @@ #include +#include + +#include +#include #include template <> @@ -31,12 +35,14 @@ namespace typeart::filter::metacg { /// Holds information about the generator used to serialize the callgraph struct Generator { std::string name, sha, version; }; +namespace json = llvm::json; + inline bool fromJSON(json::Value const& json, Generator& r, json::Path const& path) { json::ObjectMapper o{json, path}; return o && o.map("name", r.name) && o.map("sha", r.sha) && o.map("version", r.version); } -/// MetaCGv3 header +/// MetaCGv4 header struct Header { Generator gen; std::string version; @@ -138,15 +144,19 @@ struct Md { /// Performs a lookup into the metadata object with the given key `name` and attempts to deserialize /// it into the requested type. template - Expected as(StringRef const name) const { + llvm::Expected as(llvm::StringRef const name) const { if (v.getAsNull()) - return createStringError("No metadata object"); + return llvm::createStringError("No metadata object"); json::Path::Root root{}; - if (T r{}; fromJSON(*(v.getAsObject()->get(name)), r, root)) + auto p = v.getAsObject()->get(name); + if (!p) + return llvm::createStringError("Member not found"); + + if (T r{}; fromJSON(*p, r, root)) return r; - return createStringError("Failed to deserialize metadata"); + return llvm::createStringError("Failed to deserialize metadata"); } private: @@ -158,92 +168,107 @@ inline bool fromJSON(json::Value const& json, Md& r, json::Path const&) { return true; } -/// Represents a function in the callgraph -struct Fn { - std::string name, origin; +/// Represents edges from a node to its callees and associated edge metadata +using Edges = std::unordered_map; + +inline bool fromJSON(json::Value const& json, Edges& r, json::Path const& path) { + auto const outer = json.getAsObject(); + if (!outer) + return false; + + for (auto const& [k, v] : *outer) { + auto k_str = k.str(); + + size_t hash{}; + if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) + r.insert({hash, Md{v}}); + else + return false; + } + + return true; +} + +/// Represents a node in the callgraph +struct Node { + std::optional name; + std::optional origin; bool has_body; Md meta; + Edges callees; }; -inline bool fromJSON(json::Value const& json, Fn& r, json::Path const& path) { +inline bool fromJSON(json::Value const& json, Node& r, json::Path const& path) { json::ObjectMapper o{json, path}; return o && o.map("functionName", r.name) && o.map("origin", r.origin) && o.map("hasBody", r.has_body) - && o.map("meta", r.meta); + && o.map("meta", r.meta) + && o.map("callees", r.callees); } -/// Represents the edges of the callgraph -struct EdgeContainer { - friend struct CallGraph; - - friend bool fromJSON(json::Value const& json, EdgeContainer& r, json::Path const& path); +/// Represents the callgraph's nodes and their associated IDs +using Nodes = std::unordered_map; - EdgeContainer() = default; - -private: - std::unordered_map, Md> underlying{}; -}; - -inline bool fromJSON(json::Value const& json, EdgeContainer& r, json::Path const& path) { - auto const outer = json.getAsArray(); +inline bool fromJSON(json::Value const& json, Nodes& r, json::Path const& path) { + auto const outer = json.getAsObject(); if (!outer) return false; - for (json::Value const& v: *outer) { - auto const inner = v.getAsArray(); - if (!inner) - return false; + for (auto const& [k, v] : *outer) { + auto k_str = k.str(); - auto const first = (*inner)[0].getAsArray(); - auto const second = (*inner)[1]; - if (!first) + Node node {}; + if (auto const parsed = fromJSON(v, node, path); !parsed) return false; - r.underlying.insert({ - {*(*first)[0].getAsUINT64(), *(*first)[1].getAsUINT64()}, - Md{second} - }); + size_t hash{}; + if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) + r.insert({hash, node}); + else + return false; } return true; } -/// Represents the nodes of the callgraph -struct NodeContainer { - friend struct CallGraph; - - NodeContainer() = default; +/// MetaCGv4 callgraph +struct CallGraph { + Md meta; + Nodes nodes; +}; - friend bool fromJSON(json::Value const& json, NodeContainer& r, json::Path const& path); +inline bool fromJSON(json::Value const& json, CallGraph& r, json::Path const& path) { + json::ObjectMapper o{json, path}; + return o + && o.map("meta", r.meta) + && o.map("nodes", r.nodes); +} - /// Translates a function name to its corresponding node ID if it exists - std::optional byName(StringRef const name) const { - for (auto const& [id, f]: underlying) { - if (f.name == name) - return id; - } +/// Top-level MetaCGv4 object container +struct Mcg { + std::optional byName(std::string_view const name) const { + for (auto const& [hash, node] : this->graph.nodes) + if (node.name == name) + return hash; return {}; } - /// Translates a node identifier to a function descriptor - std::optional forId(size_t const id) const { - for (auto const& [idx, f]: underlying) { - if (idx == id) - return f; - } + std::optional forId(size_t id) const { + for (auto const& [hash, node] : this->graph.nodes) + if (hash == id) + return node; return {}; } - /// Returns the parameter outputs for `node`'s incoming argument at position `idx` std::optional> outputs(size_t node, size_t idx) const { - if (underlying.find(node) == underlying.end()) + if (this->graph.nodes.find(node) == this->graph.nodes.end()) return {}; - auto argflow = underlying.at(node).meta.as("argflow"); + auto argflow = this->graph.nodes.at(node).meta.as("argflow"); if (!argflow) return {}; @@ -255,47 +280,6 @@ struct NodeContainer { return {}; } -private: - std::unordered_map underlying{}; -}; - -inline bool fromJSON(json::Value const& json, NodeContainer& r, json::Path const& path) { - auto const outer = json.getAsArray(); - if (!outer) - return false; - - for (json::Value const& v: *outer) { - auto const inner = v.getAsArray(); - if (!inner) - return false; - - auto const first = (*inner)[0].getAsUINT64(); - if (!first) - return false; - - Fn f{}; - if (auto const second = fromJSON((*inner)[1], f, path); !second) - return false; - - r.underlying.insert({*first, f}); - } - - return true; -} - -/// Represents the complete callgraph -struct CallGraph { - EdgeContainer edges; - NodeContainer nodes; -}; - -inline bool fromJSON(json::Value const& json, CallGraph& r, json::Path const& path) { - json::ObjectMapper o{json, path}; - return o && o.map("edges", r.edges) && o.map("nodes", r.nodes); -} - -/// Top-level MetaCGv3 object container -struct Mcg { CallGraph graph; Header hdr; }; @@ -305,14 +289,14 @@ inline bool fromJSON(json::Value const& json, Mcg& r,json::Path const& path) { return o && o.map("_CG", r.graph) && o.map("_MetaCG", r.hdr); } -/// Deserializes the MetaCGv3 format from text -inline Expected parse(StringRef const json) { +/// Deserializes the MetaCGv4 format from text +inline llvm::Expected parse(llvm::StringRef const json) { if (auto parsed = json::parse(json); parsed) { LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << '\n' << "Version: " << parsed->hdr.version << '\n'); return *parsed; } else - return Expected{parsed.takeError()}; + return llvm::Expected{parsed.takeError()}; } } // namespace typeart::filter::metacg From 0210148c88ad494e5b947cb8d2194b4ec086e9f0 Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 1 Dec 2025 15:24:33 +0100 Subject: [PATCH 4/8] Update ACG filter & switch CGForward filter to new MCG format --- lib/mpi_interceptor/InterceptorFunctions.h | 23 +++ lib/passes/analysis/MemInstFinder.cpp | 22 ++- lib/passes/filter/ACGFilter.cpp | 119 +++++++----- lib/passes/filter/ACGFilter.h | 7 +- lib/passes/filter/CGForwardFilter.cpp | 199 ++++++++++++--------- lib/passes/filter/CGForwardFilter.h | 62 +++---- lib/passes/filter/Matcher.h | 68 +++---- lib/passes/filter/MetaCG.h | 2 +- 8 files changed, 292 insertions(+), 210 deletions(-) diff --git a/lib/mpi_interceptor/InterceptorFunctions.h b/lib/mpi_interceptor/InterceptorFunctions.h index c1ccb341..0b54e641 100644 --- a/lib/mpi_interceptor/InterceptorFunctions.h +++ b/lib/mpi_interceptor/InterceptorFunctions.h @@ -115,6 +115,29 @@ TYPEART_NO_EXPORT int ta_check_buffer(const char* mpi_name, const void* called_f ta_print_loc(called_from); return 0; } + + const void *ret_addr; + typeart_status_v = typeart_get_return_address(buf, &ret_addr); + if (typeart_status_v != TYPEART_OK) { + return 0; + } + + typeart_source_location src_loc; + typeart_status_v = typeart_get_source_location(ret_addr, &src_loc); + if (typeart_status_v != TYPEART_OK) { + ++mcounter.error; + const char* msg = ta_get_error_message(typeart_status_v); + printf("R[%d][Error][%d] %s: failed to get source location for buffer %p at loc %p - %s\n", + rank, const_adr, mpi_name, buf, called_from, msg); + return 0; + } + + printf("R[%d][Info][%d] %s: buffer %p @ %s:%s:%u\n", rank, const_adr, mpi_name, buf, + src_loc.file, src_loc.function, src_loc.line); + + free(src_loc.file); + free(src_loc.function); + // if (mpi_count > count) { // TODO: Count check not really sensible without taking the MPI type into account // printf("R[%d][Error][%d] Call '%s' buffer %p too small\n", rank, const_adr, mpi_name, buf); diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index dbd0b15e..6f1330c5 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -99,15 +99,23 @@ static std::unique_ptr make_filter(const MemInstFinderC LOG_DEBUG("Return no-op filter") return std::make_unique(); } else if (filter_id == FilterImplementation::cg) { - const std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; - if (cg_file.empty()) { - LOG_FATAL("CG File not set!"); + LOG_DEBUG("Return CGForward filter"); + + std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; + const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); + if (!buf) { + LOG_FATAL("Failed to load MCG file"); std::exit(1); } - LOG_DEBUG("Return CG filter with CG file @ " << cg_file) - auto json_cg = JSONCG::getJSON(cg_file); - auto matcher = std::make_unique(util::glob2regex(glob)); - return std::make_unique(glob, std::move(json_cg), std::move(matcher)); + + auto mcg = metacg::parse((*buf)->getBuffer()); + if (!mcg) { + LOG_FATAL(mcg.takeError() << '\n'); + std::exit(1); + } + + return std::make_unique(std::move(mcg.get()), Regex{util::glob2regex(glob), Regex::NoFlags}); + } else if (filter_id == FilterImplementation::acg) { LOG_DEBUG("Return Argflow filter"); std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index 705fb8ed..ff485510 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -31,32 +31,47 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons for (const auto id : nodes) { enqueue(id, idx); - LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.forId(id)->name << '\n'); + LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.forId(id)->name); } while (!workq.empty()) { // Grab the current node ID and translate it into its corresponding function descriptor to check // if its name matches our matcher. const auto [current, cur_idx] = workq.pop_back_val(); - LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name << '\n'); - if (const auto fn = mcg.forId(current); fn && fn->name && matcher.match(*fn->name)) { - LOG_DEBUG("-> Matches matcher, keeping\n"); - return FilterAnalysis::Keep; + LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name); + + if (const auto fn = mcg.forId(current); fn && fn->name) { + if (matcher.match(*fn->name)) { + // Keep if the function matches the matcher + LOG_DEBUG("-> Matches matcher, keeping"); + return FilterAnalysis::Keep; + } else if (const auto r = oracle.matchName(*fn->name); r != Matcher::MatchResult::NoMatch) { + // Ignore any known skippable functions + switch (r) { + case Matcher::MatchResult::ShouldSkip: + case Matcher::MatchResult::ShouldContinue: + LOG_DEBUG("-> Known function, skipping"); + continue; + + default: ; + } + } } else if (fn && !fn->has_body) { - LOG_DEBUG("-> Function has no body, keeping\n"); + // We have to be conservative if we reach an unknown function without a body + LOG_DEBUG("-> Function has no body, keeping"); return FilterAnalysis::Keep; } const auto outs = mcg.outputs(current, cur_idx); if (!outs) { - LOG_DEBUG("-> Failed to get outputs\n"); + LOG_DEBUG("-> Failed to get outputs, possibly leaf"); continue; } for (const auto& out : *outs) { for (const auto callee : out.callees) { enqueue(callee, out.idx); - LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name << '\n'); + LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name); } } } @@ -90,60 +105,76 @@ FilterAnalysis AcgFilterImpl::precheck(Value* in, Function* start, const FPath&) return FilterAnalysis::Continue; } -FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { +FilterAnalysis AcgFilterImpl::indirect(const CallSite current, const Path& p) { + SmallVector callees{}; + const auto arg = *p.getEndPrev(); assert(arg && "Argument is missing"); if (!is_contained(current.args(), arg)) return FilterAnalysis::Continue; - // Calculate the argument position const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); - SmallVector callees{}; + const auto* callLoc = current.getLocation(); + if (!callLoc) { + LOG_DEBUG("No call location, continuing"); + return FilterAnalysis::Continue; + } - // If `current` does not have a called function, we instead look through the DI to find the source location - // of this call, then compare all recorded calls in `current`'s parent function's function descriptor to - // get a list of potential call targets. - if (!current.getCalledFunction()) { - const auto* callLoc = current.getLocation(); - if (!callLoc) - return FilterAnalysis::Keep; + // Resolve the parent scope via debug metadata as it should stay consistent even through inlining + const auto* parentScope = dyn_cast(callLoc->getScope()); + if (!parentScope) { + LOG_DEBUG("Failed to get parent scope, continuing"); + return FilterAnalysis::Continue; + } - // Resolve the parent scope via debug metadata as it should stay consistent even through inlining - const auto* parentScope = dyn_cast(callLoc->getScope()); - if (!parentScope) - return FilterAnalysis::Keep; + const auto parentNode = mcg.byName(parentScope->getName()); + if (!parentNode) { + LOG_DEBUG("Failed to get parent node, continuing"); + return FilterAnalysis::Continue; + } - const auto parentNode = mcg.byName(parentScope->getName()); - if (!parentNode) - return FilterAnalysis::Keep; + const auto parent = mcg.forId(*parentNode); + assert(parent && "Malformed MCG"); - const auto parent = mcg.forId(*parentNode); - assert(parent && "Malformed MCG"); + auto md = parent->meta.as("localflow"); + if (!md) { + LOG_DEBUG("Failed to get localflow for node, continuing"); + return FilterAnalysis::Continue; + } - auto md = parent->meta.as("localflow"); - if (!md) - return FilterAnalysis::Keep; + for (const auto& local : md->locals) + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) + callees.append(local.callees.begin(), local.callees.end()); - for (const auto& local : md->locals) - if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) - callees.append(local.callees.begin(), local.callees.end()); + const auto outs = mcg.outputs(*parentNode, idx); + if (!outs) + return FilterAnalysis::Continue; - const auto outs = mcg.outputs(*parentNode, idx); - for (const auto& out : *outs) - callees.append(out.callees.begin(), out.callees.end()); - } - // Otherwise simply add the called function to the list of callees to search from - else if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { - callees.push_back(*node); + for (const auto& out : *outs) + callees.append(out.callees.begin(), out.callees.end()); + + return reachesMatching(callees, idx); +} + +FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { + const auto arg = *p.getEndPrev(); + assert(arg && "Argument is missing"); + + if (!is_contained(current.args(), arg)) + return FilterAnalysis::Continue; + + // Calculate the argument position + const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); + + if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { + return reachesMatching({*node}, idx); } else { // Be conservative if the function is not recorded in the call graph - return FilterAnalysis::Keep; + LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName()); + return FilterAnalysis::Continue; } - - // Determine if any of the potential callees can pass the parameters at index `idx` to a matching function - return reachesMatching(callees, idx); } FilterAnalysis AcgFilterImpl::decl(const CallSite current, const Path& p) { return def(current, p); } diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h index 3a1d1672..7fc58d15 100644 --- a/lib/passes/filter/ACGFilter.h +++ b/lib/passes/filter/ACGFilter.h @@ -15,6 +15,7 @@ #include "compat/CallSite.h" #include "FilterBase.h" +#include "Matcher.h" #include "MetaCG.h" namespace typeart::filter { @@ -26,21 +27,20 @@ namespace omp { struct DefaultSearch; struct ArgflowFilterTrait { - constexpr static bool Indirect = false; + constexpr static bool Indirect = true; constexpr static bool Intrinsic = false; constexpr static bool Declaration = true; constexpr static bool Definition = true; constexpr static bool PreCheck = true; }; -class CGInterface; - struct AcgFilterImpl { using Support = ArgflowFilterTrait; AcgFilterImpl(metacg::Mcg&&, Regex&&); FilterAnalysis precheck(Value*, Function*, const FPath&); + FilterAnalysis indirect(CallSite, const Path&); FilterAnalysis decl(CallSite, const Path&); FilterAnalysis def(CallSite, const Path&); @@ -49,6 +49,7 @@ struct AcgFilterImpl { metacg::Mcg mcg; Regex matcher; + FunctionOracleMatcher oracle; }; using AcgFilter = BaseFilter; diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 2570f5aa..7983322f 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2025 TypeART Authors +// Copyright (c) 2017-2023 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) @@ -12,118 +12,145 @@ #include "CGForwardFilter.h" -#include "CGInterface.h" -#include "Matcher.h" -#include "OmpUtil.h" -#include "filter/FilterBase.h" -#include "filter/FilterUtil.h" -#include "support/Logger.h" -#include "support/Util.h" +#include -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Type.h" -#include "llvm/IR/Value.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/raw_ostream.h" +namespace typeart::filter { -#include +CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, Regex&& match) + : mcg{std::move(cg)}, matcher{std::move(match)} +{} -namespace llvm { -class Function; -} // namespace llvm +FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes) { + SmallVector workq{}; + SmallSet seen{}; -namespace typeart::filter { + const auto enqueue = [&seen, &workq](const size_t it) { + if (const auto [_, inserted] = seen.insert(it); inserted) + workq.push_back(it); + }; -CGFilterImpl::CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph) - : CGFilterImpl(filter_str, std::move(cgraph), nullptr) { -} + for (const auto id : nodes) { + enqueue(id); + LOG_DEBUG("Starting node with parameter: " << mcg.forId(id)->name); + } + + while (!workq.empty()) { + // Grab the current node ID and translate it into its corresponding function descriptor to check + // if its name matches our matcher. + const auto current = workq.pop_back_val(); + LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name); + + if (const auto fn = mcg.forId(current); fn && fn->name) { + if (matcher.match(*fn->name)) { + // Keep if the function matches the matcher + LOG_DEBUG("-> Matches matcher, keeping"); + return FilterAnalysis::Keep; + } else if (const auto r = oracle.matchName(*fn->name); r != Matcher::MatchResult::NoMatch) { + // Ignore any known skippable functions + switch (r) { + case Matcher::MatchResult::ShouldSkip: + case Matcher::MatchResult::ShouldContinue: + LOG_DEBUG("-> Known function, skipping"); + continue; + + default: ; + } + } + } else if (fn && !fn->has_body) { + // We have to be conservative if we reach an unknown function without a body + LOG_DEBUG("-> Function has no body, keeping"); + return FilterAnalysis::Keep; + } + + const auto current_node = mcg.forId(current); + assert(current_node && "MCG is broken"); -CGFilterImpl::CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph, - std::unique_ptr&& matcher) - : filter(util::glob2regex(filter_str)), call_graph(std::move(cgraph)), deep_matcher(std::move(matcher)) { + for (const auto& [callee, _] : current_node->callees) { + enqueue(callee); + LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name); + } + } + + return FilterAnalysis::Continue; } -FilterAnalysis CGFilterImpl::precheck(Value* in, Function* start, const FPath& fpath) { - if (start == nullptr) { +FilterAnalysis CGForwardFilterImpl::precheck(Value* in, Function* start, const FPath&) { + if (!start) return FilterAnalysis::Continue; - } - FunctionAnalysis analysis; + FunctionAnalysis analysis{}; analysis.analyze(start); - if (analysis.empty()) { + + // Filter if we're in a leaf function + if (analysis.empty()) + return FilterAnalysis::Filter; + + if (isTempAlloc(in)) { + LOG_DEBUG("Alloca is a temporary " << *in); return FilterAnalysis::Filter; } - if (fpath.empty()) { - // These conditions (temp alloc and alloca reaches task) - // are only interesting if filter just started (aka fpath is empty) - if (isTempAlloc(in)) { - LOG_DEBUG("Alloca is a temporary " << *in); + if (AllocaInst* alloc = dyn_cast(in)) { + if (alloc->getAllocatedType()->isStructTy() && omp::OmpContext::allocaReachesTask(alloc)) { + LOG_DEBUG("Alloca reaches task call " << *alloc) return FilterAnalysis::Filter; } - - if (llvm::AllocaInst* alloc = llvm::dyn_cast(in)) { - if (alloc->getAllocatedType()->isStructTy() && omp::OmpContext::allocaReachesTask(alloc)) { - LOG_DEBUG("Alloca reaches task call " << *alloc) - return FilterAnalysis::Filter; - } - } - } - - const auto has_omp_task = - llvm::any_of(analysis.calls.decl, [](const auto& csite) { return omp::OmpContext::isOmpTaskRelated(csite); }); - if (has_omp_task) { - // FIXME we cannot handle complex data flow of tasks at this point, hence, this check - LOG_DEBUG("Keep value " << *in << ". Detected omp task call."); - return FilterAnalysis::Keep; } return FilterAnalysis::Continue; } -FilterAnalysis CGFilterImpl::decl(CallSite current, const Path& p) { - if (deep_matcher && deep_matcher->match(current) == Matcher::MatchResult::Match) { -#if LLVM_VERSION_MAJOR < 15 - auto result = correlate2void(current, p); -#else - auto result = correlate2pointer(current, p); -#endif - switch (result) { - case ArgCorrelation::GlobalMismatch: - [[fallthrough]]; - case ArgCorrelation::ExactMismatch: - LOG_DEBUG("Correlated, continue search"); - return FilterAnalysis::Continue; - default: - return FilterAnalysis::Keep; - } +FilterAnalysis CGForwardFilterImpl::indirect(const CallSite current, const Path& p) { + SmallVector callees{}; + + const auto* callLoc = current.getLocation(); + if (!callLoc) { + LOG_DEBUG("No call location, continuing"); + return FilterAnalysis::Continue; } - const auto searchCG = [&](auto from) { - if (call_graph) { - return call_graph->reachable(std::string{from->getName()}, filter); - } - return CGInterface::ReachabilityResult::unknown; - }; + // Resolve the parent scope via debug metadata as it should stay consistent even through inlining + const auto* parentScope = dyn_cast(callLoc->getScope()); + if (!parentScope) { + LOG_DEBUG("Failed to get parent scope, continuing"); + return FilterAnalysis::Continue; + } + + const auto parentNode = mcg.byName(parentScope->getName()); + if (!parentNode) { + LOG_DEBUG("Failed to get parent node, continuing"); + return FilterAnalysis::Continue; + } - const auto reached = searchCG(current.getCalledFunction()); + const auto parent = mcg.forId(*parentNode); + assert(parent && "Malformed MCG"); - switch (reached) { - case CGInterface::ReachabilityResult::reaches: - return FilterAnalysis::Keep; - case CGInterface::ReachabilityResult::never_reaches: - return FilterAnalysis::Skip; - case CGInterface::ReachabilityResult::maybe_reaches: - return FilterAnalysis::Filter; - default: - return FilterAnalysis::Continue; + auto md = parent->meta.as("localflow"); + if (!md) { + LOG_DEBUG("Failed to get localflow for node, continuing"); + return FilterAnalysis::Continue; } + + for (const auto& local : md->locals) + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) + callees.append(local.callees.begin(), local.callees.end()); + + for (const auto& [callee, _] : parent->callees) + callees.push_back(callee); + + return reachesMatching(callees); } -FilterAnalysis CGFilterImpl::def(CallSite current, const Path& p) { - return decl(current, p); +FilterAnalysis CGForwardFilterImpl::def(const CallSite current, const Path& p) { + if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { + return reachesMatching({*node}); + } else { + // Be conservative if the function is not recorded in the call graph + LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName()); + return FilterAnalysis::Continue; + } } -} // namespace typeart::filter \ No newline at end of file +FilterAnalysis CGForwardFilterImpl::decl(const CallSite current, const Path& p) { return def(current, p); } + +} // namespace typeart::filter diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h index 3f017dc1..4fa36d9c 100644 --- a/lib/passes/filter/CGForwardFilter.h +++ b/lib/passes/filter/CGForwardFilter.h @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2025 TypeART Authors +// Copyright (c) 2017-2023 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) @@ -13,61 +13,47 @@ #ifndef TYPEART_CGFORWARDFILTER_H #define TYPEART_CGFORWARDFILTER_H +#include "compat/CallSite.h" #include "FilterBase.h" #include "Matcher.h" -#include "compat/CallSite.h" -#include "filter/CGInterface.h" -#include "filter/IRPath.h" +#include "MetaCG.h" -#include -#include +namespace typeart::filter { -namespace llvm { -class Function; -class Value; -} // namespace llvm -namespace typeart { -namespace filter { namespace omp { -struct OmpContext; -} // namespace omp -struct DefaultSearch; -} // namespace filter -} // namespace typeart + struct OmpContext; +} -namespace typeart::filter { +struct DefaultSearch; -struct CGFilterTrait { - constexpr static bool Indirect = false; +struct CGForwardFilterTrait { + constexpr static bool Indirect = true; constexpr static bool Intrinsic = false; constexpr static bool Declaration = true; constexpr static bool Definition = true; constexpr static bool PreCheck = true; }; -class CGInterface; - -struct CGFilterImpl { - using Support = CGFilterTrait; - - std::string filter; - std::unique_ptr call_graph; - std::unique_ptr deep_matcher; - - CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph); +struct CGForwardFilterImpl { + using Support = CGForwardFilterTrait; - CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph, - std::unique_ptr&& matcher); + CGForwardFilterImpl(metacg::Mcg&&, Regex&&); - FilterAnalysis precheck(Value* in, Function* start, const FPath&); + FilterAnalysis precheck(Value*, Function*, const FPath&); + FilterAnalysis indirect(CallSite, const Path&); + FilterAnalysis decl(CallSite, const Path&); + FilterAnalysis def(CallSite, const Path&); - FilterAnalysis decl(CallSite current, const Path& p); +private: + FilterAnalysis reachesMatching(ArrayRef); - FilterAnalysis def(CallSite current, const Path& p); + metacg::Mcg mcg; + Regex matcher; + FunctionOracleMatcher oracle; }; -using CGForwardFilter = BaseFilter; +using CGForwardFilter = BaseFilter; -} // namespace typeart::filter +} // namespace typeart::filter -#endif // TYPEART_CGFORWARDFILTER_H \ No newline at end of file +#endif // TYPEART_CGFORWARDFILTER_H diff --git a/lib/passes/filter/Matcher.h b/lib/passes/filter/Matcher.h index d58653ec..f05e85aa 100644 --- a/lib/passes/filter/Matcher.h +++ b/lib/passes/filter/Matcher.h @@ -30,14 +30,21 @@ class Matcher { Matcher& operator=(const Matcher&) = default; Matcher& operator=(Matcher&&) = default; - virtual MatchResult match(llvm::CallSite) const = 0; + virtual MatchResult match(llvm::CallSite const c) const { + if (c.getCalledFunction() != nullptr) + return this->matchName(c.getCalledFunction()->getName()); + + return MatchResult::NoMatch; + } + + virtual MatchResult matchName(llvm::StringRef) const = 0; virtual ~Matcher() = default; }; class NoMatcher final : public Matcher { public: - MatchResult match(llvm::CallSite) const { + MatchResult matchName(llvm::StringRef) const { return MatchResult::NoMatch; }; }; @@ -49,15 +56,14 @@ class DefaultStringMatcher final : public Matcher { explicit DefaultStringMatcher(const std::string& regex) : matcher(regex, llvm::Regex::NoFlags) { } - MatchResult match(llvm::CallSite c) const override { - const auto f = c.getCalledFunction(); - if (f != nullptr) { - const auto f_name = util::demangle(f->getName()); - const bool matched = matcher.match(f_name); - if (matched) { - return MatchResult::Match; - } + MatchResult matchName(llvm::StringRef const name) const override { + const auto f_name = util::demangle(name); + const bool matched = matcher.match(f_name); + + if (matched) { + return MatchResult::Match; } + return MatchResult::NoMatch; } }; @@ -65,33 +71,33 @@ class DefaultStringMatcher final : public Matcher { class FunctionOracleMatcher final : public Matcher { const MemOps mem_operations{}; llvm::SmallDenseSet continue_set{{"sqrt"}, {"cos"}, {"sin"}, {"pow"}, {"fabs"}, - {"abs"}, {"log"}, {"fscanf"}, {"cbrt"}, {"gettimeofday"}}; + {"abs"}, {"log"}, {"fscanf"}, {"cbrt"}, {"gettimeofday"}, + {"strcpy"}, {"strlen"}}; llvm::SmallDenseSet skip_set{{"printf"}, {"sprintf"}, {"snprintf"}, {"fprintf"}, {"puts"}, {"__cxa_atexit"}, {"fopen"}, {"fclose"}, {"scanf"}, {"strtol"}, {"srand"}}; public: - MatchResult match(llvm::CallSite c) const override { - const auto f = c.getCalledFunction(); - if (f != nullptr) { - const auto f_name = util::demangle(f->getName()); - llvm::StringRef f_name_ref{f_name}; - if (continue_set.count(f_name) > 0) { - return MatchResult::ShouldContinue; - } - if (skip_set.count(f_name) > 0) { - return MatchResult::ShouldSkip; - } - if (util::starts_with_any_of(f_name_ref, "__typeart_")) { - return MatchResult::ShouldSkip; - } - if (mem_operations.kind(f_name)) { - return MatchResult::ShouldSkip; - } - if (util::starts_with_any_of(f_name_ref, "__ubsan", "__asan", "__msan", "__tysan", "__dfsan", "__tsan")) { - return MatchResult::ShouldContinue; - } + MatchResult matchName(llvm::StringRef const name) const override { + const auto f_name = util::demangle(name); + llvm::StringRef f_name_ref{f_name}; + + if (continue_set.count(f_name) > 0) { + return MatchResult::ShouldContinue; } + if (skip_set.count(f_name) > 0) { + return MatchResult::ShouldSkip; + } + if (util::starts_with_any_of(f_name_ref, "__typeart_")) { + return MatchResult::ShouldSkip; + } + if (mem_operations.kind(f_name)) { + return MatchResult::ShouldSkip; + } + if (util::starts_with_any_of(f_name_ref, "__ubsan", "__asan", "__msan", "llvm.", "__tysan", "__dfsan", "__tsan")) { + return MatchResult::ShouldContinue; + } + return MatchResult::NoMatch; } }; diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index c4cd1ef1..26c2aea5 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -292,7 +292,7 @@ inline bool fromJSON(json::Value const& json, Mcg& r,json::Path const& path) { /// Deserializes the MetaCGv4 format from text inline llvm::Expected parse(llvm::StringRef const json) { if (auto parsed = json::parse(json); parsed) { - LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << '\n' << "Version: " << parsed->hdr.version << '\n'); + LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << " version: " << parsed->hdr.version); return *parsed; } else From 064cec70740de1719d8df905c461ae9a6102dd5f Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 8 Dec 2025 15:51:19 +0100 Subject: [PATCH 5/8] Format --- lib/passes/Commandline.cpp | 10 +- lib/passes/analysis/MemInstFinder.cpp | 4 +- lib/passes/compat/CallSite.h | 3 +- lib/passes/configuration/OptionsUtil.h | 2 +- lib/passes/configuration/PassBuilderUtil.h | 4 +- lib/passes/configuration/TypeARTOptions.cpp | 4 +- lib/passes/filter/ACGFilter.cpp | 48 +++++---- lib/passes/filter/ACGFilter.h | 10 +- lib/passes/filter/CGForwardFilter.cpp | 36 ++++--- lib/passes/filter/CGForwardFilter.h | 8 +- lib/passes/filter/Matcher.h | 10 +- lib/passes/filter/MetaCG.h | 110 +++++++++++--------- 12 files changed, 139 insertions(+), 110 deletions(-) diff --git a/lib/passes/Commandline.cpp b/lib/passes/Commandline.cpp index 09e0f355..f6366e47 100644 --- a/lib/passes/Commandline.cpp +++ b/lib/passes/Commandline.cpp @@ -127,11 +127,11 @@ static cl::opt cl_typeart_call_filter(CommandlineS static cl::opt cl_typeart_call_filter_implementation( CommandlineStdArgs::filter_impl, cl::desc(ConfigStdArgDescriptions::filter_impl), - cl::values(clEnumValN(typeart::analysis::FilterImplementation::none, "none", "No filter"), - clEnumValN(typeart::analysis::FilterImplementation::standard, "std", - "Standard forward filter (default)"), - clEnumValN(typeart::analysis::FilterImplementation::cg, "cg", "Call-graph-based filter"), - clEnumValN(typeart::analysis::FilterImplementation::acg, "acg", "MetaCG with argument flow based filter")), + cl::values( + clEnumValN(typeart::analysis::FilterImplementation::none, "none", "No filter"), + clEnumValN(typeart::analysis::FilterImplementation::standard, "std", "Standard forward filter (default)"), + clEnumValN(typeart::analysis::FilterImplementation::cg, "cg", "Call-graph-based filter"), + clEnumValN(typeart::analysis::FilterImplementation::acg, "acg", "MetaCG with argument flow based filter")), cl::Hidden, cl::init(typeart::analysis::FilterImplementation::standard), cl::cat(typeart_analysis_category)); static cl::opt cl_typeart_call_filter_glob( diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index 6f1330c5..134e1076 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -102,7 +102,7 @@ static std::unique_ptr make_filter(const MemInstFinderC LOG_DEBUG("Return CGForward filter"); std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; - const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); + const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); if (!buf) { LOG_FATAL("Failed to load MCG file"); std::exit(1); @@ -119,7 +119,7 @@ static std::unique_ptr make_filter(const MemInstFinderC } else if (filter_id == FilterImplementation::acg) { LOG_DEBUG("Return Argflow filter"); std::string cg_file = config[config::ConfigStdArgs::filter_cg_file]; - const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); + const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); if (!buf) { LOG_FATAL("Failed to load MCG file"); std::exit(1); diff --git a/lib/passes/compat/CallSite.h b/lib/passes/compat/CallSite.h index e64a97dc..2b03c6ab 100644 --- a/lib/passes/compat/CallSite.h +++ b/lib/passes/compat/CallSite.h @@ -22,7 +22,6 @@ #include #include #include - #include namespace llvm { @@ -92,7 +91,7 @@ class CallSite { } [[nodiscard]] const DILocation* getLocation() const { - SmallVector> mds; + SmallVector> mds; instruction_->getAllMetadata(mds); for (const auto& [_, md] : mds) { diff --git a/lib/passes/configuration/OptionsUtil.h b/lib/passes/configuration/OptionsUtil.h index 1f35e5a1..9846def4 100644 --- a/lib/passes/configuration/OptionsUtil.h +++ b/lib/passes/configuration/OptionsUtil.h @@ -43,7 +43,7 @@ ClType string_to_enum(llvm::StringRef cl_value) { if constexpr (std::is_same_v) { auto val = llvm::StringSwitch(cl_value) .Case("cg", FilterImplementation::cg) - .Case("acg", FilterImplementation::acg) + .Case("acg", FilterImplementation::acg) .Case("none", FilterImplementation::none) .Case("std", FilterImplementation::standard) .Default(FilterImplementation::standard); diff --git a/lib/passes/configuration/PassBuilderUtil.h b/lib/passes/configuration/PassBuilderUtil.h index 8908e2f5..bebeda9d 100644 --- a/lib/passes/configuration/PassBuilderUtil.h +++ b/lib/passes/configuration/PassBuilderUtil.h @@ -52,8 +52,8 @@ inline bool checkParametrizedPassName(llvm::StringRef Name, llvm::StringRef Pass /// Expected<> template class. /// template -inline auto parsePassParameters(ParametersParseCallableT&& Parser, llvm::StringRef Name, - llvm::StringRef PassName) -> decltype(Parser(llvm::StringRef{})) { +inline auto parsePassParameters(ParametersParseCallableT&& Parser, llvm::StringRef Name, llvm::StringRef PassName) + -> decltype(Parser(llvm::StringRef{})) { using namespace llvm; using ParametersT = typename decltype(Parser(StringRef{}))::value_type; diff --git a/lib/passes/configuration/TypeARTOptions.cpp b/lib/passes/configuration/TypeARTOptions.cpp index dc7b29bc..1e908992 100644 --- a/lib/passes/configuration/TypeARTOptions.cpp +++ b/lib/passes/configuration/TypeARTOptions.cpp @@ -173,8 +173,8 @@ TypeARTConfigOptions config_to_options(const Configuration& configuration) { } template -auto make_entry(std::string_view key, - const T& field_value) -> std::pair { +auto make_entry(std::string_view key, const T& field_value) + -> std::pair { if constexpr (std::is_enum_v) { return {key, config::OptionValue{static_cast(field_value)}}; } else { diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index ff485510..782c7a7d 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -16,17 +16,17 @@ namespace typeart::filter { -AcgFilterImpl::AcgFilterImpl(metacg::Mcg&& cg, Regex&& match) - : mcg{std::move(cg)}, matcher{std::move(match)} -{} +AcgFilterImpl::AcgFilterImpl(metacg::Mcg&& cg, Regex&& match) : mcg{std::move(cg)}, matcher{std::move(match)} { +} FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, const size_t idx) { SmallVector, 64> workq{}; SmallSet seen{}; const auto enqueue = [&seen, &workq](const size_t it, const size_t i) { - if (const auto [_, inserted] = seen.insert(it); inserted) + if (const auto [_, inserted] = seen.insert(it); inserted) { workq.push_back({it, i}); + } }; for (const auto id : nodes) { @@ -48,12 +48,12 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons } else if (const auto r = oracle.matchName(*fn->name); r != Matcher::MatchResult::NoMatch) { // Ignore any known skippable functions switch (r) { - case Matcher::MatchResult::ShouldSkip: - case Matcher::MatchResult::ShouldContinue: - LOG_DEBUG("-> Known function, skipping"); - continue; + case Matcher::MatchResult::ShouldSkip: + case Matcher::MatchResult::ShouldContinue: + LOG_DEBUG("-> Known function, skipping"); + continue; - default: ; + default:; } } } else if (fn && !fn->has_body) { @@ -80,15 +80,17 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons } FilterAnalysis AcgFilterImpl::precheck(Value* in, Function* start, const FPath&) { - if (!start) + if (!start) { return FilterAnalysis::Continue; + } FunctionAnalysis analysis{}; analysis.analyze(start); // Filter if we're in a leaf function - if (analysis.empty()) + if (analysis.empty()) { return FilterAnalysis::Filter; + } if (isTempAlloc(in)) { LOG_DEBUG("Alloca is a temporary " << *in); @@ -111,8 +113,9 @@ FilterAnalysis AcgFilterImpl::indirect(const CallSite current, const Path& p) { const auto arg = *p.getEndPrev(); assert(arg && "Argument is missing"); - if (!is_contained(current.args(), arg)) + if (!is_contained(current.args(), arg)) { return FilterAnalysis::Continue; + } const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); @@ -144,16 +147,20 @@ FilterAnalysis AcgFilterImpl::indirect(const CallSite current, const Path& p) { return FilterAnalysis::Continue; } - for (const auto& local : md->locals) - if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) + for (const auto& local : md->locals) { + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) { callees.append(local.callees.begin(), local.callees.end()); + } + } const auto outs = mcg.outputs(*parentNode, idx); - if (!outs) + if (!outs) { return FilterAnalysis::Continue; + } - for (const auto& out : *outs) + for (const auto& out : *outs) { callees.append(out.callees.begin(), out.callees.end()); + } return reachesMatching(callees, idx); } @@ -162,8 +169,9 @@ FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { const auto arg = *p.getEndPrev(); assert(arg && "Argument is missing"); - if (!is_contained(current.args(), arg)) + if (!is_contained(current.args(), arg)) { return FilterAnalysis::Continue; + } // Calculate the argument position const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); @@ -177,6 +185,8 @@ FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { } } -FilterAnalysis AcgFilterImpl::decl(const CallSite current, const Path& p) { return def(current, p); } +FilterAnalysis AcgFilterImpl::decl(const CallSite current, const Path& p) { + return def(current, p); +} -} // namespace typeart::filter +} // namespace typeart::filter diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h index 7fc58d15..c364f29f 100644 --- a/lib/passes/filter/ACGFilter.h +++ b/lib/passes/filter/ACGFilter.h @@ -13,15 +13,15 @@ #ifndef ARGFLOWFILTER_H #define ARGFLOWFILTER_H -#include "compat/CallSite.h" #include "FilterBase.h" #include "Matcher.h" #include "MetaCG.h" +#include "compat/CallSite.h" namespace typeart::filter { namespace omp { - struct OmpContext; +struct OmpContext; } struct DefaultSearch; @@ -44,7 +44,7 @@ struct AcgFilterImpl { FilterAnalysis decl(CallSite, const Path&); FilterAnalysis def(CallSite, const Path&); -private: + private: FilterAnalysis reachesMatching(ArrayRef, size_t); metacg::Mcg mcg; @@ -54,6 +54,6 @@ struct AcgFilterImpl { using AcgFilter = BaseFilter; -} // namespace typeart::filter +} // namespace typeart::filter -#endif //ARGFLOWFILTER_H +#endif // ARGFLOWFILTER_H diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 7983322f..6d5c3058 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -17,16 +17,17 @@ namespace typeart::filter { CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, Regex&& match) - : mcg{std::move(cg)}, matcher{std::move(match)} -{} + : mcg{std::move(cg)}, matcher{std::move(match)} { +} FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes) { SmallVector workq{}; SmallSet seen{}; const auto enqueue = [&seen, &workq](const size_t it) { - if (const auto [_, inserted] = seen.insert(it); inserted) + if (const auto [_, inserted] = seen.insert(it); inserted) { workq.push_back(it); + } }; for (const auto id : nodes) { @@ -48,12 +49,12 @@ FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes } else if (const auto r = oracle.matchName(*fn->name); r != Matcher::MatchResult::NoMatch) { // Ignore any known skippable functions switch (r) { - case Matcher::MatchResult::ShouldSkip: - case Matcher::MatchResult::ShouldContinue: - LOG_DEBUG("-> Known function, skipping"); - continue; + case Matcher::MatchResult::ShouldSkip: + case Matcher::MatchResult::ShouldContinue: + LOG_DEBUG("-> Known function, skipping"); + continue; - default: ; + default:; } } } else if (fn && !fn->has_body) { @@ -75,15 +76,17 @@ FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes } FilterAnalysis CGForwardFilterImpl::precheck(Value* in, Function* start, const FPath&) { - if (!start) + if (!start) { return FilterAnalysis::Continue; + } FunctionAnalysis analysis{}; analysis.analyze(start); // Filter if we're in a leaf function - if (analysis.empty()) + if (analysis.empty()) { return FilterAnalysis::Filter; + } if (isTempAlloc(in)) { LOG_DEBUG("Alloca is a temporary " << *in); @@ -131,12 +134,15 @@ FilterAnalysis CGForwardFilterImpl::indirect(const CallSite current, const Path& return FilterAnalysis::Continue; } - for (const auto& local : md->locals) - if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) + for (const auto& local : md->locals) { + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) { callees.append(local.callees.begin(), local.callees.end()); + } + } - for (const auto& [callee, _] : parent->callees) + for (const auto& [callee, _] : parent->callees) { callees.push_back(callee); + } return reachesMatching(callees); } @@ -151,6 +157,8 @@ FilterAnalysis CGForwardFilterImpl::def(const CallSite current, const Path& p) { } } -FilterAnalysis CGForwardFilterImpl::decl(const CallSite current, const Path& p) { return def(current, p); } +FilterAnalysis CGForwardFilterImpl::decl(const CallSite current, const Path& p) { + return def(current, p); +} } // namespace typeart::filter diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h index 4fa36d9c..79a98f76 100644 --- a/lib/passes/filter/CGForwardFilter.h +++ b/lib/passes/filter/CGForwardFilter.h @@ -13,15 +13,15 @@ #ifndef TYPEART_CGFORWARDFILTER_H #define TYPEART_CGFORWARDFILTER_H -#include "compat/CallSite.h" #include "FilterBase.h" #include "Matcher.h" #include "MetaCG.h" +#include "compat/CallSite.h" namespace typeart::filter { namespace omp { - struct OmpContext; +struct OmpContext; } struct DefaultSearch; @@ -44,7 +44,7 @@ struct CGForwardFilterImpl { FilterAnalysis decl(CallSite, const Path&); FilterAnalysis def(CallSite, const Path&); -private: + private: FilterAnalysis reachesMatching(ArrayRef); metacg::Mcg mcg; @@ -54,6 +54,6 @@ struct CGForwardFilterImpl { using CGForwardFilter = BaseFilter; -} // namespace typeart::filter +} // namespace typeart::filter #endif // TYPEART_CGFORWARDFILTER_H diff --git a/lib/passes/filter/Matcher.h b/lib/passes/filter/Matcher.h index f05e85aa..6e93d915 100644 --- a/lib/passes/filter/Matcher.h +++ b/lib/passes/filter/Matcher.h @@ -34,7 +34,7 @@ class Matcher { if (c.getCalledFunction() != nullptr) return this->matchName(c.getCalledFunction()->getName()); - return MatchResult::NoMatch; + return MatchResult::NoMatch; } virtual MatchResult matchName(llvm::StringRef) const = 0; @@ -70,9 +70,9 @@ class DefaultStringMatcher final : public Matcher { class FunctionOracleMatcher final : public Matcher { const MemOps mem_operations{}; - llvm::SmallDenseSet continue_set{{"sqrt"}, {"cos"}, {"sin"}, {"pow"}, {"fabs"}, - {"abs"}, {"log"}, {"fscanf"}, {"cbrt"}, {"gettimeofday"}, - {"strcpy"}, {"strlen"}}; + llvm::SmallDenseSet continue_set{{"sqrt"}, {"cos"}, {"sin"}, {"pow"}, + {"fabs"}, {"abs"}, {"log"}, {"fscanf"}, + {"cbrt"}, {"gettimeofday"}, {"strcpy"}, {"strlen"}}; llvm::SmallDenseSet skip_set{{"printf"}, {"sprintf"}, {"snprintf"}, {"fprintf"}, {"puts"}, {"__cxa_atexit"}, {"fopen"}, {"fclose"}, {"scanf"}, {"strtol"}, {"srand"}}; @@ -81,7 +81,7 @@ class FunctionOracleMatcher final : public Matcher { MatchResult matchName(llvm::StringRef const name) const override { const auto f_name = util::demangle(name); llvm::StringRef f_name_ref{f_name}; - + if (continue_set.count(f_name) > 0) { return MatchResult::ShouldContinue; } diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index 26c2aea5..8d564666 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -13,12 +13,10 @@ #ifndef METACG_H #define METACG_H +#include #include - -#include - #include -#include +#include #include template <> @@ -33,7 +31,9 @@ struct std::hash> { namespace typeart::filter::metacg { /// Holds information about the generator used to serialize the callgraph -struct Generator { std::string name, sha, version; }; +struct Generator { + std::string name, sha, version; +}; namespace json = llvm::json; @@ -55,7 +55,9 @@ inline bool fromJSON(json::Value const& json, Header& r, json::Path const& path) /// Represents a source location struct SrcLoc { - auto operator==(const SrcLoc& rhs) const { return col == rhs.col && line == rhs.line; } + auto operator==(const SrcLoc& rhs) const { + return col == rhs.col && line == rhs.line; + } size_t col, line; }; @@ -66,7 +68,9 @@ inline bool fromJSON(llvm::json::Value const& json, SrcLoc& r, json::Path const& } /// Call instances metadata -struct MdCalls { std::vector locs; }; +struct MdCalls { + std::vector locs; +}; inline bool fromJSON(json::Value const& json, MdCalls& r, json::Path const& path) { json::ObjectMapper o{json, path}; @@ -83,11 +87,7 @@ struct MdArgOutput { inline bool fromJSON(json::Value const& json, MdArgOutput& r, json::Path const& path) { json::ObjectMapper o{json, path}; - return o - && o.map("by_ref", r.by_ref) - && o.map("callees", r.callees) - && o.map("idx", r.idx) - && o.map("loc", r.loc); + return o && o.map("by_ref", r.by_ref) && o.map("callees", r.callees) && o.map("idx", r.idx) && o.map("loc", r.loc); } /// Represents an incoming argument @@ -139,27 +139,31 @@ struct Md { Md() = default; - explicit Md(json::Value const& val) : v{val} {} + explicit Md(json::Value const& val) : v{val} { + } /// Performs a lookup into the metadata object with the given key `name` and attempts to deserialize /// it into the requested type. template llvm::Expected as(llvm::StringRef const name) const { - if (v.getAsNull()) + if (v.getAsNull()) { return llvm::createStringError("No metadata object"); + } json::Path::Root root{}; auto p = v.getAsObject()->get(name); - if (!p) - return llvm::createStringError("Member not found"); + if (!p) { + return llvm::createStringError("Member not found"); + } - if (T r{}; fromJSON(*p, r, root)) + if (T r{}; fromJSON(*p, r, root)) { return r; + } return llvm::createStringError("Failed to deserialize metadata"); } -private: + private: json::Value v{nullptr}; }; @@ -173,17 +177,19 @@ using Edges = std::unordered_map; inline bool fromJSON(json::Value const& json, Edges& r, json::Path const& path) { auto const outer = json.getAsObject(); - if (!outer) + if (!outer) { return false; + } for (auto const& [k, v] : *outer) { auto k_str = k.str(); size_t hash{}; - if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) + if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) { r.insert({hash, Md{v}}); - else + } else { return false; + } } return true; @@ -200,12 +206,8 @@ struct Node { inline bool fromJSON(json::Value const& json, Node& r, json::Path const& path) { json::ObjectMapper o{json, path}; - return o - && o.map("functionName", r.name) - && o.map("origin", r.origin) - && o.map("hasBody", r.has_body) - && o.map("meta", r.meta) - && o.map("callees", r.callees); + return o && o.map("functionName", r.name) && o.map("origin", r.origin) && o.map("hasBody", r.has_body) && + o.map("meta", r.meta) && o.map("callees", r.callees); } /// Represents the callgraph's nodes and their associated IDs @@ -213,21 +215,24 @@ using Nodes = std::unordered_map; inline bool fromJSON(json::Value const& json, Nodes& r, json::Path const& path) { auto const outer = json.getAsObject(); - if (!outer) + if (!outer) { return false; + } for (auto const& [k, v] : *outer) { auto k_str = k.str(); - Node node {}; - if (auto const parsed = fromJSON(v, node, path); !parsed) + Node node{}; + if (auto const parsed = fromJSON(v, node, path); !parsed) { return false; + } size_t hash{}; - if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) + if (std::from_chars(k_str.data(), k_str.data() + k_str.size(), hash).ec == std::errc{}) { r.insert({hash, node}); - else + } else { return false; + } } return true; @@ -241,40 +246,47 @@ struct CallGraph { inline bool fromJSON(json::Value const& json, CallGraph& r, json::Path const& path) { json::ObjectMapper o{json, path}; - return o - && o.map("meta", r.meta) - && o.map("nodes", r.nodes); + return o && o.map("meta", r.meta) && o.map("nodes", r.nodes); } /// Top-level MetaCGv4 object container struct Mcg { std::optional byName(std::string_view const name) const { - for (auto const& [hash, node] : this->graph.nodes) - if (node.name == name) + for (auto const& [hash, node] : this->graph.nodes) { + if (node.name == name) { return hash; + } + } return {}; } std::optional forId(size_t id) const { - for (auto const& [hash, node] : this->graph.nodes) - if (hash == id) + for (auto const& [hash, node] : this->graph.nodes) { + if (hash == id) { return node; + } + } return {}; } std::optional> outputs(size_t node, size_t idx) const { - if (this->graph.nodes.find(node) == this->graph.nodes.end()) + if (this->graph.nodes.find(node) == this->graph.nodes.end()) { return {}; + } auto argflow = this->graph.nodes.at(node).meta.as("argflow"); - if (!argflow) + if (auto error = argflow.takeError()) { + auto error_msg = llvm::toString(std::move(error)); + LOG_DEBUG("Node " << node << " contains no argflow: " << error_msg); return {}; + } - for (auto const& [id, out]: argflow->args) { - if (id == idx) + for (auto const& [id, out] : argflow->args) { + if (id == idx) { return out; + } } return {}; @@ -284,7 +296,7 @@ struct Mcg { Header hdr; }; -inline bool fromJSON(json::Value const& json, Mcg& r,json::Path const& path) { +inline bool fromJSON(json::Value const& json, Mcg& r, json::Path const& path) { json::ObjectMapper o{json, path}; return o && o.map("_CG", r.graph) && o.map("_MetaCG", r.hdr); } @@ -292,13 +304,13 @@ inline bool fromJSON(json::Value const& json, Mcg& r,json::Path const& path) { /// Deserializes the MetaCGv4 format from text inline llvm::Expected parse(llvm::StringRef const json) { if (auto parsed = json::parse(json); parsed) { - LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << " version: " << parsed->hdr.version); + LOG_DEBUG("Generated by: " << parsed->hdr.gen.name << " version: " << parsed->hdr.version); return *parsed; - } - else + } else { return llvm::Expected{parsed.takeError()}; + } } -} // namespace typeart::filter::metacg +} // namespace typeart::filter::metacg -#endif //METACG_H +#endif // METACG_H From 2f0b024ced9886869af3b63f76880ad140e09616 Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 8 Dec 2025 15:52:33 +0100 Subject: [PATCH 6/8] Remove spurious LTO passbuilder callback --- lib/passes/TypeARTPass.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index 954bca5b..23fc1505 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -413,15 +413,6 @@ llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() { } MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); }); - pass_builder.registerFullLinkTimeOptimizationLastEPCallback([](ModulePassManager& MPM, OptimizationLevel) { - auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, - "typeart", "typeart"); - if (!parameters) { - LOG_FATAL("Error parsing heap params: " << parameters.takeError()) - return; - } - MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get()))); - }); pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) { auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, "typeart", "typeart"); From c35d1e12e627f804936f6ccf4f3d8b7910fc5b16 Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 8 Dec 2025 15:53:43 +0100 Subject: [PATCH 7/8] Update license headers --- lib/passes/filter/ACGFilter.cpp | 2 +- lib/passes/filter/ACGFilter.h | 2 +- lib/passes/filter/CGForwardFilter.cpp | 2 +- lib/passes/filter/CGForwardFilter.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index 782c7a7d..9092f841 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2023 TypeART Authors +// Copyright (c) 2017-2025 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h index c364f29f..acaf4bef 100644 --- a/lib/passes/filter/ACGFilter.h +++ b/lib/passes/filter/ACGFilter.h @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2023 TypeART Authors +// Copyright (c) 2017-2025 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 6d5c3058..43169860 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2023 TypeART Authors +// Copyright (c) 2017-2025 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h index 79a98f76..55740c38 100644 --- a/lib/passes/filter/CGForwardFilter.h +++ b/lib/passes/filter/CGForwardFilter.h @@ -1,6 +1,6 @@ // TypeART library // -// Copyright (c) 2017-2023 TypeART Authors +// Copyright (c) 2017-2025 TypeART Authors // Distributed under the BSD 3-Clause license. // (See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/BSD-3-Clause) From 6627c4596d7ab40462936815287325b5c9ed326d Mon Sep 17 00:00:00 2001 From: Laurin-Luis Lehning <65224843+e820@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:44:19 +0100 Subject: [PATCH 8/8] Adjust tests to use MCG --- lib/mpi_interceptor/InterceptorFunctions.h | 11 ++++++ lib/mpi_interceptor/mpi_interceptor_tmpl.impl | 7 ++++ test/pass/filter/04_cg.c | 8 ++-- test/pass/filter/04_cg.ipcg | 38 ------------------ test/pass/filter/04_cg.mcg | 39 +++++++++++++++++++ test/pass/filter/05_cg.ipcg | 13 ------- test/pass/filter/05_cg.mcg | 39 +++++++++++++++++++ test/pass/filter/05_correlate_sig.c | 10 ++--- 8 files changed, 105 insertions(+), 60 deletions(-) delete mode 100644 test/pass/filter/04_cg.ipcg create mode 100644 test/pass/filter/04_cg.mcg delete mode 100644 test/pass/filter/05_cg.ipcg create mode 100644 test/pass/filter/05_cg.mcg diff --git a/lib/mpi_interceptor/InterceptorFunctions.h b/lib/mpi_interceptor/InterceptorFunctions.h index 0b54e641..c4f276eb 100644 --- a/lib/mpi_interceptor/InterceptorFunctions.h +++ b/lib/mpi_interceptor/InterceptorFunctions.h @@ -43,6 +43,7 @@ typedef struct MPISemCounter { } MPICounter; static MPICounter mcounter = {0, 0, 0}; +static int log_buffer_srcloc = 0; TYPEART_NO_EXPORT void ta_check_send(const char* name, const void* called_from, const void* sendbuf, int count, MPI_Datatype dtype) { @@ -122,6 +123,10 @@ TYPEART_NO_EXPORT int ta_check_buffer(const char* mpi_name, const void* called_f return 0; } + if (log_buffer_srcloc == 0) { + return 0; + } + typeart_source_location src_loc; typeart_status_v = typeart_get_source_location(ret_addr, &src_loc); if (typeart_status_v != TYPEART_OK) { @@ -167,6 +172,12 @@ TYPEART_NO_EXPORT void ta_print_loc(const void* call_adr) { } } +TYPEART_NO_EXPORT void ta_init() { + const char *var = getenv("TYPEART_LOG_BUF_SRCLOC"); + if (var && strncmp(var, "TRUE", 4) == 0) + log_buffer_srcloc = 1; +} + TYPEART_NO_EXPORT void ta_exit() { // Called at MPI_Finalize time int rank = 0; diff --git a/lib/mpi_interceptor/mpi_interceptor_tmpl.impl b/lib/mpi_interceptor/mpi_interceptor_tmpl.impl index 5df24932..8f418a80 100644 --- a/lib/mpi_interceptor/mpi_interceptor_tmpl.impl +++ b/lib/mpi_interceptor/mpi_interceptor_tmpl.impl @@ -21,6 +21,13 @@ extern "C" { #include "InterceptorFunctions.h" +{{fn fn_name MPI_Init}} +{ + ta_init(); + return P{{fn_name}}({{args}}); +} +{{endfn}} + {{fn fn_name MPI_Finalize}} { ta_exit(); diff --git a/test/pass/filter/04_cg.c b/test/pass/filter/04_cg.c index c4fff36d..28f911bf 100644 --- a/test/pass/filter/04_cg.c +++ b/test/pass/filter/04_cg.c @@ -1,5 +1,5 @@ // clang-format off -// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/04_cg.ipcg -S 2>&1 | %filecheck %s +// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/04_cg.mcg -S 2>&1 | %filecheck %s // RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true -S 2>&1 | %filecheck %s --check-prefix=CHECK-default // clang-format on @@ -11,12 +11,12 @@ void foo() { bar(&a); aar(&b); } -// CG: +// ACG: // CHECK: > Stack Memory // CHECK-NEXT: Alloca : 2.00 -// CHECK-NEXT: Stack call filtered % : 50.00 +// CHECK-NEXT: Stack call filtered % : 100.00 // Standard filter // CHECK-default: > Stack Memory // CHECK-default-NEXT: Alloca : 2.00 -// CHECK-default-NEXT: Stack call filtered % : 0.00 \ No newline at end of file +// CHECK-default-NEXT: Stack call filtered % : 0.00 diff --git a/test/pass/filter/04_cg.ipcg b/test/pass/filter/04_cg.ipcg deleted file mode 100644 index ba0371ab..00000000 --- a/test/pass/filter/04_cg.ipcg +++ /dev/null @@ -1,38 +0,0 @@ -{ - "bar": { - "callees": ["MPI_Send"], - "doesOverride": false, - "hasBody": false, - "isVirtual": false, - "numStatements": 0, - "overriddenBy": [], - "overriddenFunctions": [], - "parents": [ - "foo" - ] - }, - "aar": { - "callees": [], - "doesOverride": false, - "hasBody": false, - "isVirtual": false, - "numStatements": 0, - "overriddenBy": [], - "overriddenFunctions": [], - "parents": [ - "foo" - ] - }, - "foo": { - "callees": [ - "bar" - ], - "doesOverride": false, - "hasBody": true, - "isVirtual": false, - "numStatements": 1, - "overriddenBy": [], - "overriddenFunctions": [], - "parents": [] - } -} \ No newline at end of file diff --git a/test/pass/filter/04_cg.mcg b/test/pass/filter/04_cg.mcg new file mode 100644 index 00000000..ac40bafe --- /dev/null +++ b/test/pass/filter/04_cg.mcg @@ -0,0 +1,39 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { + "1": {}, + "2": {} + }, + "functionName": "foo", + "hasBody": true, + "meta": {}, + "origin": "04_cg.c" + }, + "1": { + "callees": {}, + "functionName": "bar", + "hasBody": false, + "meta": {}, + "origin": null + }, + "2": { + "callees": {}, + "functionName": "aar", + "hasBody": false, + "meta": {}, + "origin": null + } + } + }, + "_MetaCG": { + "generator": { + "name": "GenCC", + "sha": "NO_GIT_SHA_AVAILABLE", + "version": "0.1" + }, + "version": "4.0" + } +} \ No newline at end of file diff --git a/test/pass/filter/05_cg.ipcg b/test/pass/filter/05_cg.ipcg deleted file mode 100644 index b3f54cd8..00000000 --- a/test/pass/filter/05_cg.ipcg +++ /dev/null @@ -1,13 +0,0 @@ -{ - "foo": { - "callees": [ - "MPI_Mock", "MPI_Send" - ], - "doesOverride": false, - "hasBody": true, - "isVirtual": false, - "overriddenBy": [], - "overriddenFunctions": [], - "parents": [] - } -} \ No newline at end of file diff --git a/test/pass/filter/05_cg.mcg b/test/pass/filter/05_cg.mcg new file mode 100644 index 00000000..b3f41132 --- /dev/null +++ b/test/pass/filter/05_cg.mcg @@ -0,0 +1,39 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { + "1": {}, + "2": {} + }, + "functionName": "foo", + "hasBody": true, + "meta": {}, + "origin": "05_correlate_sig.c" + }, + "1": { + "callees": {}, + "functionName": "MPI_Mock", + "hasBody": false, + "meta": {}, + "origin": null + }, + "2": { + "callees": {}, + "functionName": "MPI_Send", + "hasBody": false, + "meta": {}, + "origin": null + } + } + }, + "_MetaCG": { + "generator": { + "name": "GenCC", + "sha": "NO_GIT_SHA_AVAILABLE", + "version": "0.1" + }, + "version": "4.0" + } +} \ No newline at end of file diff --git a/test/pass/filter/05_correlate_sig.c b/test/pass/filter/05_correlate_sig.c index 03f55bab..fb320a11 100644 --- a/test/pass/filter/05_correlate_sig.c +++ b/test/pass/filter/05_correlate_sig.c @@ -1,6 +1,6 @@ // clang-format off // RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true -S 2>&1 | %filecheck %s --check-prefix=CHECK-exp-default -// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.ipcg -S 2>&1 | %filecheck %s --check-prefix=CHECK-exp-cg +// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/05_cg.mcg -S 2>&1 | %filecheck %s --check-prefix=CHECK-acg // clang-format on extern void MPI_Mock(int, int, int); @@ -22,7 +22,7 @@ void foo() { // CHECK-exp-default-NEXT: Alloca : 5.00 // CHECK-exp-default-NEXT: Stack call filtered % : 80.00 -// CG experimental filter -// CHECK-exp-cg: > Stack Memory -// CHECK-exp-cg-NEXT: Alloca : 5.00 -// CHECK-exp-cg-NEXT: Stack call filtered % : 80.00 \ No newline at end of file +// ACG: +// CHECK-acg: > Stack Memory +// CHECK-acg: Alloca : 5.00 +// CHECK-acg: Stack call filtered % : 100.00