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 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 From 3ca46d877452ebef3ec6f36fad7eb9f01e02a98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 13:48:24 +0100 Subject: [PATCH 09/18] Fix pass invoke --- lib/passes/TypeARTPass.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index 23fc1505..d51e428c 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -413,7 +413,11 @@ llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() { } 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) { From 59049cc5a2b37b3c5da78d5b94777818cdb48b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 14:02:50 +0100 Subject: [PATCH 10/18] Fix for LLVM-18; pass invocation --- lib/passes/TypeARTPass.cpp | 13 +++++++------ lib/passes/filter/MetaCG.h | 7 ++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index d51e428c..2cdd6d59 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -405,26 +405,27 @@ 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"); + 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()))); + MPM.addPass(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"); + 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()))); + MPM.addPass(typeart::pass::TypeArtPass(parameters.get())); }); pass_builder.registerPipelineParsingCallback( [](StringRef name, ModulePassManager& module_pm, ArrayRef) { diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index 8d564666..2222e4c5 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -14,6 +14,7 @@ #define METACG_H #include +#include #include #include #include @@ -147,20 +148,20 @@ struct Md { template llvm::Expected as(llvm::StringRef const name) const { if (v.getAsNull()) { - return llvm::createStringError("No metadata object"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), "No metadata object"); } json::Path::Root root{}; auto p = v.getAsObject()->get(name); if (!p) { - return llvm::createStringError("Member not found"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), "Member not found"); } if (T r{}; fromJSON(*p, r, root)) { return r; } - return llvm::createStringError("Failed to deserialize metadata"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), "Failed to deserialize metadata"); } private: From 6cf05b3a2cad6361a20786b9c101c348cadc0d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 14:34:49 +0100 Subject: [PATCH 11/18] Fixes for LLVM 14 --- lib/passes/TypeARTPass.cpp | 76 +++++++++++---------- lib/passes/configuration/PassBuilderUtil.h | 4 +- lib/passes/configuration/TypeARTOptions.cpp | 4 +- lib/passes/filter/ACGFilter.cpp | 6 +- lib/passes/filter/CGForwardFilter.cpp | 6 +- lib/passes/filter/MetaCG.h | 19 ++++++ 6 files changed, 68 insertions(+), 47 deletions(-) diff --git a/lib/passes/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index 2cdd6d59..c6320fbf 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -403,46 +403,48 @@ 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(parameters.get())); - }); + 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(parameters.get())); + }); #if LLVM_VERSION_MAJOR > 19 - pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel, ThinOrFullLTOPhase) { + pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel, ThinOrFullLTOPhase) { #else - pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) { + 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(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; - }); - }}; + 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(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/configuration/PassBuilderUtil.h b/lib/passes/configuration/PassBuilderUtil.h index bebeda9d..8908e2f5 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 1e908992..dc7b29bc 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 9092f841..b0e76aa7 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -31,14 +31,14 @@ 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); + LOG_DEBUG("Starting node with parameter [" << idx << "]: " << mcg.forId(id)->name.value_or("")); } 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); + LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name.value_or("")); if (const auto fn = mcg.forId(current); fn && fn->name) { if (matcher.match(*fn->name)) { @@ -71,7 +71,7 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons for (const auto& out : *outs) { for (const auto callee : out.callees) { enqueue(callee, out.idx); - LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name); + LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name.value_or("")); } } } diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 43169860..059ab617 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -32,14 +32,14 @@ FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes for (const auto id : nodes) { enqueue(id); - LOG_DEBUG("Starting node with parameter: " << mcg.forId(id)->name); + LOG_DEBUG("Starting node with parameter: " << mcg.forId(id)->name.value_or("")); } 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); + LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name.value_or("")); if (const auto fn = mcg.forId(current); fn && fn->name) { if (matcher.match(*fn->name)) { @@ -68,7 +68,7 @@ FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes for (const auto& [callee, _] : current_node->callees) { enqueue(callee); - LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name); + LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name.value_or("")); } } diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index 2222e4c5..d75d7d88 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -16,10 +16,29 @@ #include #include #include +#include #include #include #include +#if LLVM_VERSION_MAJOR <= 14 +namespace llvm::json { +template +bool fromJSON(const Value& E, std::optional& Out, Path P) { + if (E.kind() == Value::Null) { + Out = std::nullopt; + return true; + } + T V; + if (fromJSON(E, V, P)) { + Out = std::move(V); + return true; + } + return false; +} +} // namespace llvm::json +#endif + template <> struct std::hash> { size_t operator()(std::pair const& p) const noexcept { From ba08e1328156f22fed3fea4f55433d1de0d452a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 14:38:14 +0100 Subject: [PATCH 12/18] Format --- lib/mpi_interceptor/InterceptorFunctions.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/mpi_interceptor/InterceptorFunctions.h b/lib/mpi_interceptor/InterceptorFunctions.h index c4f276eb..7c2f5cd5 100644 --- a/lib/mpi_interceptor/InterceptorFunctions.h +++ b/lib/mpi_interceptor/InterceptorFunctions.h @@ -42,7 +42,7 @@ typedef struct MPISemCounter { _Atomic size_t error; } MPICounter; -static MPICounter mcounter = {0, 0, 0}; +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, @@ -117,7 +117,7 @@ TYPEART_NO_EXPORT int ta_check_buffer(const char* mpi_name, const void* called_f return 0; } - const void *ret_addr; + const void* ret_addr; typeart_status_v = typeart_get_return_address(buf, &ret_addr); if (typeart_status_v != TYPEART_OK) { return 0; @@ -132,13 +132,13 @@ TYPEART_NO_EXPORT int ta_check_buffer(const char* mpi_name, const void* called_f 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); + 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); + 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); @@ -173,7 +173,7 @@ 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"); + const char* var = getenv("TYPEART_LOG_BUF_SRCLOC"); if (var && strncmp(var, "TRUE", 4) == 0) log_buffer_srcloc = 1; } From bfbb78e2a507b6d45899ead5e68ec032c3e88244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 17:03:03 +0100 Subject: [PATCH 13/18] Use matchers and correlate sig --- cmake/modules/target-util.cmake | 3 +++ lib/passes/analysis/MemInstFinder.cpp | 12 ++++++++++-- lib/passes/filter/ACGFilter.cpp | 23 +++++++++++++++++++++-- lib/passes/filter/ACGFilter.h | 6 ++++-- lib/passes/filter/CGForwardFilter.cpp | 26 +++++++++++++++++++++++--- lib/passes/filter/CGForwardFilter.h | 6 ++++-- test/pass/filter/05_correlate_sig.c | 2 +- test/pass/filter/12_omp_correlate.c | 4 ++-- test/pass/filter/13_omp_loops.c | 4 ++-- 9 files changed, 70 insertions(+), 16 deletions(-) diff --git a/cmake/modules/target-util.cmake b/cmake/modules/target-util.cmake index 9b2ba9c0..40e7a785 100644 --- a/cmake/modules/target-util.cmake +++ b/cmake/modules/target-util.cmake @@ -11,6 +11,9 @@ function(typeart_target_compile_options target) -Wformat=2 -Wundef -Werror=float-equal ) endif() + target_compile_options(${target} PRIVATE + -fno-rtti + ) target_compile_definitions(${target} PRIVATE "LLVM_VERSION_MAJOR=${LLVM_VERSION_MAJOR}") diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index 134e1076..674a11b3 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -114,7 +114,11 @@ static std::unique_ptr make_filter(const MemInstFinderC std::exit(1); } - return std::make_unique(std::move(mcg.get()), Regex{util::glob2regex(glob), Regex::NoFlags}); + auto matcher = std::make_unique(util::glob2regex(glob)); + const auto deep_glob = config[config::ConfigStdArgs::filter_glob_deep]; + auto deep_matcher = std::make_unique(util::glob2regex(deep_glob)); + + return std::make_unique(std::move(mcg.get()), std::move(matcher), std::move(deep_matcher)); } else if (filter_id == FilterImplementation::acg) { LOG_DEBUG("Return Argflow filter"); @@ -131,7 +135,11 @@ static std::unique_ptr make_filter(const MemInstFinderC std::exit(1); } - return std::make_unique(std::move(mcg.get()), Regex{util::glob2regex(glob), Regex::NoFlags}); + auto matcher = std::make_unique(util::glob2regex(glob)); + const auto deep_glob = config[config::ConfigStdArgs::filter_glob_deep]; + auto deep_matcher = std::make_unique(util::glob2regex(deep_glob)); + + return std::make_unique(std::move(mcg.get()), std::move(matcher), std::move(deep_matcher)); } else { LOG_DEBUG("Return default filter") auto matcher = std::make_unique(util::glob2regex(glob)); diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index b0e76aa7..32311399 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -12,11 +12,14 @@ #include "ACGFilter.h" +#include "Matcher.h" + #include namespace typeart::filter { -AcgFilterImpl::AcgFilterImpl(metacg::Mcg&& cg, Regex&& match) : mcg{std::move(cg)}, matcher{std::move(match)} { +AcgFilterImpl::AcgFilterImpl(metacg::Mcg&& cg, std::unique_ptr&& m, std::unique_ptr&& deep) + : mcg{std::move(cg)}, matcher{std::move(m)}, deep_matcher{std::move(deep)} { } FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, const size_t idx) { @@ -41,7 +44,7 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name.value_or("")); if (const auto fn = mcg.forId(current); fn && fn->name) { - if (matcher.match(*fn->name)) { + if (matcher->matchName(*fn->name) == Matcher::MatchResult::Match) { // Keep if the function matches the matcher LOG_DEBUG("-> Matches matcher, keeping"); return FilterAnalysis::Keep; @@ -166,6 +169,22 @@ FilterAnalysis AcgFilterImpl::indirect(const CallSite current, const Path& p) { } FilterAnalysis AcgFilterImpl::def(const 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; + } + } const auto arg = *p.getEndPrev(); assert(arg && "Argument is missing"); diff --git a/lib/passes/filter/ACGFilter.h b/lib/passes/filter/ACGFilter.h index acaf4bef..5dc919c2 100644 --- a/lib/passes/filter/ACGFilter.h +++ b/lib/passes/filter/ACGFilter.h @@ -37,7 +37,7 @@ struct ArgflowFilterTrait { struct AcgFilterImpl { using Support = ArgflowFilterTrait; - AcgFilterImpl(metacg::Mcg&&, Regex&&); + AcgFilterImpl(metacg::Mcg&&, std::unique_ptr&& m, std::unique_ptr&& deep); FilterAnalysis precheck(Value*, Function*, const FPath&); FilterAnalysis indirect(CallSite, const Path&); @@ -48,7 +48,9 @@ struct AcgFilterImpl { FilterAnalysis reachesMatching(ArrayRef, size_t); metacg::Mcg mcg; - Regex matcher; + // Regex matcher; + std::unique_ptr matcher; + std::unique_ptr deep_matcher; FunctionOracleMatcher oracle; }; diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 059ab617..f3187918 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -12,12 +12,16 @@ #include "CGForwardFilter.h" +#include "Matcher.h" + #include +#include namespace typeart::filter { -CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, Regex&& match) - : mcg{std::move(cg)}, matcher{std::move(match)} { +CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, std::unique_ptr&& m, + std::unique_ptr&& deep) + : mcg{std::move(cg)}, matcher{std::move(m)}, deep_matcher{std::move(deep)} { } FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes) { @@ -42,7 +46,7 @@ FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef nodes LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name.value_or("")); if (const auto fn = mcg.forId(current); fn && fn->name) { - if (matcher.match(*fn->name)) { + if (matcher->matchName(*fn->name) == Matcher::MatchResult::Match) { // Keep if the function matches the matcher LOG_DEBUG("-> Matches matcher, keeping"); return FilterAnalysis::Keep; @@ -148,6 +152,22 @@ FilterAnalysis CGForwardFilterImpl::indirect(const CallSite current, const Path& } FilterAnalysis CGForwardFilterImpl::def(const 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; + } + } if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { return reachesMatching({*node}); } else { diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h index 55740c38..62678b69 100644 --- a/lib/passes/filter/CGForwardFilter.h +++ b/lib/passes/filter/CGForwardFilter.h @@ -37,7 +37,7 @@ struct CGForwardFilterTrait { struct CGForwardFilterImpl { using Support = CGForwardFilterTrait; - CGForwardFilterImpl(metacg::Mcg&&, Regex&&); + CGForwardFilterImpl(metacg::Mcg&&, std::unique_ptr&& m, std::unique_ptr&& deep); FilterAnalysis precheck(Value*, Function*, const FPath&); FilterAnalysis indirect(CallSite, const Path&); @@ -48,7 +48,9 @@ struct CGForwardFilterImpl { FilterAnalysis reachesMatching(ArrayRef); metacg::Mcg mcg; - Regex matcher; + // Regex matcher; + std::unique_ptr matcher; + std::unique_ptr deep_matcher; FunctionOracleMatcher oracle; }; diff --git a/test/pass/filter/05_correlate_sig.c b/test/pass/filter/05_correlate_sig.c index fb320a11..9133265e 100644 --- a/test/pass/filter/05_correlate_sig.c +++ b/test/pass/filter/05_correlate_sig.c @@ -25,4 +25,4 @@ void foo() { // ACG: // CHECK-acg: > Stack Memory // CHECK-acg: Alloca : 5.00 -// CHECK-acg: Stack call filtered % : 100.00 +// CHECK-acg: Stack call filtered % : 80.00 diff --git a/test/pass/filter/12_omp_correlate.c b/test/pass/filter/12_omp_correlate.c index 899e605c..9d73775f 100644 --- a/test/pass/filter/12_omp_correlate.c +++ b/test/pass/filter/12_omp_correlate.c @@ -1,11 +1,11 @@ // clang-format off // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-analysis-filter-pointer-alloca=false -S 2>&1 | %filecheck %s // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-analysis-filter-pointer-alloca=false -S 2>&1 | %filecheck %s --check-prefix=CHECK-opt -// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.ipcg --typeart-analysis-filter-pointer-alloca=false -S 2>&1 | %filecheck %s --check-prefix=CHECK-exp-cg +// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.mcg --typeart-analysis-filter-pointer-alloca=false -S 2>&1 | %filecheck %s --check-prefix=CHECK-exp-cg // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-analysis-filter-pointer-alloca=false -S | %filecheck %s --check-prefix=check-inst // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-analysis-filter-pointer-alloca=false -S | %filecheck %s --check-prefix=check-inst -// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.ipcg --typeart-analysis-filter-pointer-alloca=false -S | %filecheck %s --check-prefix=check-inst +// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.mcg --typeart-analysis-filter-pointer-alloca=false -S | %filecheck %s --check-prefix=check-inst // REQUIRES: llvm-14 // REQUIRES: openmp // clang-format on diff --git a/test/pass/filter/13_omp_loops.c b/test/pass/filter/13_omp_loops.c index 6859f1a1..df998c8c 100644 --- a/test/pass/filter/13_omp_loops.c +++ b/test/pass/filter/13_omp_loops.c @@ -1,11 +1,11 @@ // clang-format off // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %apply-typeart --typeart-stack=true --typeart-filter=true -S 2>&1 | %filecheck %s // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true -S 2>&1 | %filecheck %s -// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.ipcg -S 2>&1 +// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.mcg -S 2>&1 // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %apply-typeart --typeart-stack=true --typeart-filter=true -S | %filecheck %s --check-prefix=check-inst // RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true -S | %filecheck %s --check-prefix=check-inst -// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.ipcg -S | %filecheck %s --check-prefix=check-inst +// RUN: %c-to-llvm -fno-discard-value-names %omp_c_flags %s | %opt -O2 -S | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=cg --typeart-filter-cg-file=%p/05_cg.mcg -S | %filecheck %s --check-prefix=check-inst // REQUIRES: llvm-14 // REQUIRES: openmp // clang-format on From 4594bc77c4f3c788cc0b86e0bfc3481974cfb21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 17:44:43 +0100 Subject: [PATCH 14/18] llvm hash function --- lib/passes/filter/MetaCG.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/passes/filter/MetaCG.h b/lib/passes/filter/MetaCG.h index d75d7d88..70d14f99 100644 --- a/lib/passes/filter/MetaCG.h +++ b/lib/passes/filter/MetaCG.h @@ -14,6 +14,7 @@ #define METACG_H #include +#include #include #include #include @@ -42,9 +43,7 @@ bool fromJSON(const Value& E, std::optional& Out, Path P) { 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; + return llvm::hash_value(p); } }; From dbd50208a3adf2e8e2298d97f4dacad73d4bde7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Fri, 13 Feb 2026 16:36:32 +0100 Subject: [PATCH 15/18] Handle functions not in CG --- lib/passes/filter/ACGFilter.cpp | 17 +++++++++++++++-- lib/passes/filter/CGForwardFilter.cpp | 17 +++++++++++++++-- lib/passes/filter/Matcher.h | 6 +++--- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index 32311399..74d19839 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -198,9 +198,22 @@ FilterAnalysis AcgFilterImpl::def(const CallSite current, const Path& p) { if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { return reachesMatching({*node}, idx); } else { + // Fn not in CG? ask the oracle first, e.g., for __typeart instrumentation: + const auto oracle_match = oracle.match(current); + switch (oracle_match) { + case Matcher::MatchResult::ShouldSkip: { + return FilterAnalysis::Skip; + } + case Matcher::MatchResult::ShouldContinue: { + return FilterAnalysis::Continue; + } + default: + break; + } + // Be conservative if the function is not recorded in the call graph - LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName()); - return FilterAnalysis::Continue; + LOG_DEBUG("Unrecorded function, keeping: " << current.getCalledFunction()->getName()); + return FilterAnalysis::Keep; } } diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index f3187918..41ffcf1e 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -171,9 +171,22 @@ FilterAnalysis CGForwardFilterImpl::def(const CallSite current, const Path& p) { if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) { return reachesMatching({*node}); } else { + // Fn not in CG? ask the oracle first, e.g., for __typeart instrumentation: + const auto oracle_match = oracle.match(current); + switch (oracle_match) { + case Matcher::MatchResult::ShouldSkip: { + return FilterAnalysis::Skip; + } + case Matcher::MatchResult::ShouldContinue: { + return FilterAnalysis::Continue; + } + default: + break; + } + // Be conservative if the function is not recorded in the call graph - LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName()); - return FilterAnalysis::Continue; + LOG_DEBUG("Unrecorded function, keeping: " << current.getCalledFunction()->getName()); + return FilterAnalysis::Keep; } } diff --git a/lib/passes/filter/Matcher.h b/lib/passes/filter/Matcher.h index 6e93d915..9a6b1270 100644 --- a/lib/passes/filter/Matcher.h +++ b/lib/passes/filter/Matcher.h @@ -73,9 +73,9 @@ class FunctionOracleMatcher final : public Matcher { 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"}}; + llvm::SmallDenseSet skip_set{ + {"printf"}, {"sprintf"}, {"snprintf"}, {"fprintf"}, {"puts"}, {"__cxa_atexit"}, {"fopen"}, {"fwrite"}, + {"fclose"}, {"scanf"}, {"strtol"}, {"srand"}, {"strtod"}, {"__getdelim"}, {"__isoc23_strtol"}, {"bcmp"}}; public: MatchResult matchName(llvm::StringRef const name) const override { From 7845cb58548e956e1e5dfcd9b2fb5255a8af7f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 17 Feb 2026 11:47:51 +0100 Subject: [PATCH 16/18] Test argflow and malformed CG --- lib/passes/analysis/MemInstFinder.cpp | 4 ++-- test/lit.cfg | 2 ++ test/pass/filter/04_cg.c | 4 ++-- test/pass/filter/04_cg.mcg | 34 +++++++++++++++++++++++++-- test/pass/filter/26_malformed_cg.mcg | 31 ++++++++++++++++++++++++ test/pass/filter/26_malformed_json.c | 13 ++++++++++ 6 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 test/pass/filter/26_malformed_cg.mcg create mode 100644 test/pass/filter/26_malformed_json.c diff --git a/lib/passes/analysis/MemInstFinder.cpp b/lib/passes/analysis/MemInstFinder.cpp index 674a11b3..47b53dde 100644 --- a/lib/passes/analysis/MemInstFinder.cpp +++ b/lib/passes/analysis/MemInstFinder.cpp @@ -110,7 +110,7 @@ static std::unique_ptr make_filter(const MemInstFinderC auto mcg = metacg::parse((*buf)->getBuffer()); if (!mcg) { - LOG_FATAL(mcg.takeError() << '\n'); + llvm::handleAllErrors(mcg.takeError(), [](const llvm::ErrorInfoBase& E) { LOG_FATAL(E.message()); }); std::exit(1); } @@ -131,7 +131,7 @@ static std::unique_ptr make_filter(const MemInstFinderC auto mcg = metacg::parse((*buf)->getBuffer()); if (!mcg) { - LOG_FATAL(mcg.takeError() << '\n'); + llvm::handleAllErrors(mcg.takeError(), [](const llvm::ErrorInfoBase& E) { LOG_FATAL(E.message()); }); std::exit(1); } diff --git a/test/lit.cfg b/test/lit.cfg index fc8dbd45..ab62837e 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -135,6 +135,8 @@ config.substitutions.append(('%omp_cpp_flags', openmp_cxx_flags)) # config.substitutions.append(('%arg_heap', '-typeart-heap')) config.substitutions.append(('%apply-typeart', '{} {} -load-pass-plugin {} -passes="{}"'.format(typeart_types_d, opt, transform_pass, std_plugin_args_newpm))) +# for testing pass fatals/exit 1: +config.substitutions.append(('%not-apply-typeart', '{} not {} -load-pass-plugin {} -passes="{}"'.format(typeart_types_d, opt, transform_pass, std_plugin_args_newpm))) config.substitutions.append(('%c-to-llvm', '{} {}'.format(clang_cc, to_llvm_args))) config.substitutions.append(('%cpp-to-llvm', '{} {}'.format(clang_cpp, to_llvm_args))) diff --git a/test/pass/filter/04_cg.c b/test/pass/filter/04_cg.c index 28f911bf..0db95d55 100644 --- a/test/pass/filter/04_cg.c +++ b/test/pass/filter/04_cg.c @@ -3,7 +3,7 @@ // 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 -extern void bar(int* ptr); // reaches MPI, see 04_cg.ipcg +extern void bar(int* ptr); // reaches MPI: foo->bar->MPI_Send(ptr), see 04_cg.ipcg extern void aar(int* ptr); // does not reach MPI void foo() { @@ -14,7 +14,7 @@ void foo() { // ACG: // CHECK: > Stack Memory // CHECK-NEXT: Alloca : 2.00 -// CHECK-NEXT: Stack call filtered % : 100.00 +// CHECK-NEXT: Stack call filtered % : 50.00 // Standard filter // CHECK-default: > Stack Memory diff --git a/test/pass/filter/04_cg.mcg b/test/pass/filter/04_cg.mcg index ac40bafe..9f3d1535 100644 --- a/test/pass/filter/04_cg.mcg +++ b/test/pass/filter/04_cg.mcg @@ -13,10 +13,33 @@ "origin": "04_cg.c" }, "1": { - "callees": {}, + "callees": { + "3": {} + }, "functionName": "bar", "hasBody": false, - "meta": {}, + "meta": { + "argflow": { + "args": [ + { + "idx": 0, + "outs": [ + { + "by_ref": true, + "callees": [ + 3 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 9 + } + } + ] + } + ] + } + }, "origin": null }, "2": { @@ -25,6 +48,13 @@ "hasBody": false, "meta": {}, "origin": null + }, + "3": { + "callees": {}, + "functionName": "MPI_Send", + "hasBody": false, + "meta": {}, + "origin": null } } }, diff --git a/test/pass/filter/26_malformed_cg.mcg b/test/pass/filter/26_malformed_cg.mcg new file mode 100644 index 00000000..b11a2979 --- /dev/null +++ b/test/pass/filter/26_malformed_cg.mcg @@ -0,0 +1,31 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { + "1": {} + }, + "functionName": "foo", + "hasBody": true, + "meta": {}, + "origin": "26_malformed_json.c" + } + "1": { + "callees": {}, + "functionName": "bar", + "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/26_malformed_json.c b/test/pass/filter/26_malformed_json.c new file mode 100644 index 00000000..019ac928 --- /dev/null +++ b/test/pass/filter/26_malformed_json.c @@ -0,0 +1,13 @@ +// clang-format off +// RUN: %c-to-llvm %s | %not-apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/26_malformed_cg.mcg 2>&1 | %filecheck %s +// clang-format on + +// comma is missing between nodes +// CHECK: Fatal + +extern void bar(int* ptr); + +void foo() { + int a; + bar(&a); +} From e662c6c4bd362b377ca89999c1048bb394aaae7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 17 Feb 2026 13:42:37 +0100 Subject: [PATCH 17/18] Testing --- .github/workflows/basic-ci.yml | 2 +- lib/passes/filter/ACGFilter.cpp | 3 + test/pass/filter/27_cg.mcg | 87 ++++++++++++++++ test/pass/filter/27_cg_chain.c | 25 +++++ test/pass/filter/28_inline.llin | 168 ++++++++++++++++++++++++++++++ test/pass/filter/29_inline.llin | 167 ++++++++++++++++++++++++++++++ test/pass/filter/30_inline.llin | 178 ++++++++++++++++++++++++++++++++ test/pass/filter/31_fptr.c | 35 +++++++ test/pass/filter/31_fptr.mcg | 119 +++++++++++++++++++++ 9 files changed, 783 insertions(+), 1 deletion(-) create mode 100644 test/pass/filter/27_cg.mcg create mode 100644 test/pass/filter/27_cg_chain.c create mode 100644 test/pass/filter/28_inline.llin create mode 100644 test/pass/filter/29_inline.llin create mode 100644 test/pass/filter/30_inline.llin create mode 100644 test/pass/filter/31_fptr.c create mode 100644 test/pass/filter/31_fptr.mcg diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 852ff663..a023e85e 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -90,7 +90,7 @@ jobs: run: sudo apt-get update - name: Install LLVM - run: sudo apt-get install libllvm${{ matrix.platform.llvm-version }} llvm-${{ matrix.platform.llvm-version }} llvm-${{ matrix.platform.llvm-version }}-dev + run: sudo apt-get install libllvm${{ matrix.platform.llvm-version }} llvm-${{ matrix.platform.llvm-version }} llvm-${{ matrix.platform.llvm-version }}-dev llvm-${{ matrix.platform.llvm-version }}-tools - name: Install LLVM OpenMP runtime run: sudo apt-get install libomp-${{ matrix.platform.llvm-version }}-dev diff --git a/lib/passes/filter/ACGFilter.cpp b/lib/passes/filter/ACGFilter.cpp index 74d19839..7625d3eb 100644 --- a/lib/passes/filter/ACGFilter.cpp +++ b/lib/passes/filter/ACGFilter.cpp @@ -73,6 +73,9 @@ FilterAnalysis AcgFilterImpl::reachesMatching(const ArrayRef nodes, cons for (const auto& out : *outs) { for (const auto callee : out.callees) { + // if (!out.by_ref) { + // continue; + // } enqueue(callee, out.idx); LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name.value_or("")); } diff --git a/test/pass/filter/27_cg.mcg b/test/pass/filter/27_cg.mcg new file mode 100644 index 00000000..e692ada7 --- /dev/null +++ b/test/pass/filter/27_cg.mcg @@ -0,0 +1,87 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "MPI_mock", + "hasBody": true, + "meta": {}, + "origin": "04_cg_inline.c" + }, + "1": { + "callees": { + "0": {} + }, + "functionName": "forward_mock", + "hasBody": true, + "meta": {}, + "origin": "04_cg_inline.c" + }, + "2": { + "callees": { + "0": {}, + "1": {} + }, + "functionName": "bar", + "hasBody": true, + "meta": { + "argflow": { + "args": [ + { + "idx": 0, + "outs": [ + { + "by_ref": true, + "callees": [ + 1 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 13 + } + } + ] + }, + { + "idx": 1, + "outs": [ + { + "by_ref": true, + "callees": [ + 0 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 14 + } + } + ] + } + ] + } + }, + "origin": "04_cg_inline.c" + }, + "3": { + "callees": { + "2": {} + }, + "functionName": "foo", + "hasBody": true, + "meta": {}, + "origin": "04_cg_inline.c" + } + } + }, + "_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/27_cg_chain.c b/test/pass/filter/27_cg_chain.c new file mode 100644 index 00000000..7a88603e --- /dev/null +++ b/test/pass/filter/27_cg_chain.c @@ -0,0 +1,25 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/27_cg.mcg 2>&1 | %filecheck %s +// clang-format on +void MPI_mock(void* buf) { +} + +int forward_mock(int* a) { + float c = 2.f; + MPI_mock(&c); + return *a; +} +__attribute__((always_inline)) void bar(int* ptr, double* bptr) { + forward_mock(ptr); + MPI_mock(bptr); +} + +void foo() { + int a = 0; // a -> bar(a,..) -> forward_mock(a) -> OK + double b = 1.; // b -> bar(...,b) -> MPI_mock(b) + bar(&a, &b); +} +// ACG: +// CHECK: > Stack Memory +// CHECK-NEXT: Alloca : +// CHECK-NEXT: Stack call filtered % : 33 diff --git a/test/pass/filter/28_inline.llin b/test/pass/filter/28_inline.llin new file mode 100644 index 00000000..087fa0e8 --- /dev/null +++ b/test/pass/filter/28_inline.llin @@ -0,0 +1,168 @@ +; RUN: %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/27_cg.mcg < %s -S 2>&1 | %filecheck %s +; bar was inline here +; REQUIRES: llvm-21 + +; CHECK: > Stack Memory +; CHECK-NEXT: Alloca : +; CHECK-NEXT: Stack call filtered % : 33 + + +; ModuleID = 'out_inline.ll' +source_filename = "27_cg_chain.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local void @MPI_mock(ptr noundef %buf) #0 !dbg !9 { +entry: + %buf.addr = alloca ptr, align 8 + store ptr %buf, ptr %buf.addr, align 8, !tbaa !16 + #dbg_declare(ptr %buf.addr, !15, !DIExpression(), !20) + ret void, !dbg !21 +} + +; Function Attrs: nounwind uwtable +define dso_local i32 @forward_mock(ptr noundef %a) #0 !dbg !22 { +entry: + %a.addr = alloca ptr, align 8 + %c = alloca float, align 4 + store ptr %a, ptr %a.addr, align 8, !tbaa !31 + #dbg_declare(ptr %a.addr, !28, !DIExpression(), !33) + call void @llvm.lifetime.start.p0(i64 4, ptr %c) #3, !dbg !34 + #dbg_declare(ptr %c, !29, !DIExpression(), !35) + store float 2.000000e+00, ptr %c, align 4, !dbg !35, !tbaa !36 + call void @MPI_mock(ptr noundef %c), !dbg !38 + %0 = load ptr, ptr %a.addr, align 8, !dbg !39, !tbaa !31 + %1 = load i32, ptr %0, align 4, !dbg !40, !tbaa !41 + call void @llvm.lifetime.end.p0(i64 4, ptr %c) #3, !dbg !43 + ret i32 %1, !dbg !44 +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #1 + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr captures(none)) #1 + +; Function Attrs: nounwind uwtable +define dso_local void @foo() #0 !dbg !62 { +entry: + %ptr.addr.i = alloca ptr, align 8 + %bptr.addr.i = alloca ptr, align 8 + %a = alloca i32, align 4 + %b = alloca double, align 8 + call void @llvm.lifetime.start.p0(i64 4, ptr %a) #3, !dbg !68 + #dbg_declare(ptr %a, !66, !DIExpression(), !69) + store i32 0, ptr %a, align 4, !dbg !69, !tbaa !41 + call void @llvm.lifetime.start.p0(i64 8, ptr %b) #3, !dbg !70 + #dbg_declare(ptr %b, !67, !DIExpression(), !71) + store double 1.000000e+00, ptr %b, align 8, !dbg !71, !tbaa !72 + call void @llvm.lifetime.start.p0(i64 8, ptr %ptr.addr.i) + call void @llvm.lifetime.start.p0(i64 8, ptr %bptr.addr.i) + store ptr %a, ptr %ptr.addr.i, align 8, !tbaa !31 + #dbg_declare(ptr %ptr.addr.i, !51, !DIExpression(), !74) + store ptr %b, ptr %bptr.addr.i, align 8, !tbaa !54 + #dbg_declare(ptr %bptr.addr.i, !52, !DIExpression(), !76) + %0 = load ptr, ptr %ptr.addr.i, align 8, !dbg !77, !tbaa !31 + %call.i = call i32 @forward_mock(ptr noundef %0), !dbg !78 + %1 = load ptr, ptr %bptr.addr.i, align 8, !dbg !79, !tbaa !54 + call void @MPI_mock(ptr noundef %1), !dbg !80 + call void @llvm.lifetime.end.p0(i64 8, ptr %ptr.addr.i), !dbg !81 + call void @llvm.lifetime.end.p0(i64 8, ptr %bptr.addr.i), !dbg !81 + call void @llvm.lifetime.end.p0(i64 8, ptr %b) #3, !dbg !82 + call void @llvm.lifetime.end.p0(i64 4, ptr %a) #3, !dbg !82 + ret void, !dbg !82 +} + +attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #2 = { alwaysinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.1.5", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "27_cg_chain.c", directory: "", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{!"clang version 21.1.5"} +!9 = distinct !DISubprogram(name: "MPI_mock", scope: !10, file: !10, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!10 = !DIFile(filename: "27_cg_chain.c", directory: "", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!14 = !{!15} +!15 = !DILocalVariable(name: "buf", arg: 1, scope: !9, file: !10, line: 4, type: !13) +!16 = !{!17, !17, i64 0} +!17 = !{!"any pointer", !18, i64 0} +!18 = !{!"omnipotent char", !19, i64 0} +!19 = !{!"Simple C/C++ TBAA"} +!20 = !DILocation(line: 4, column: 21, scope: !9) +!21 = !DILocation(line: 5, column: 1, scope: !9) +!22 = distinct !DISubprogram(name: "forward_mock", scope: !10, file: !10, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !27) +!23 = !DISubroutineType(types: !24) +!24 = !{!25, !26} +!25 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) +!27 = !{!28, !29} +!28 = !DILocalVariable(name: "a", arg: 1, scope: !22, file: !10, line: 7, type: !26) +!29 = !DILocalVariable(name: "c", scope: !22, file: !10, line: 8, type: !30) +!30 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!31 = !{!32, !32, i64 0} +!32 = !{!"p1 int", !17, i64 0} +!33 = !DILocation(line: 7, column: 23, scope: !22) +!34 = !DILocation(line: 8, column: 3, scope: !22) +!35 = !DILocation(line: 8, column: 9, scope: !22) +!36 = !{!37, !37, i64 0} +!37 = !{!"float", !18, i64 0} +!38 = !DILocation(line: 9, column: 3, scope: !22) +!39 = !DILocation(line: 10, column: 11, scope: !22) +!40 = !DILocation(line: 10, column: 10, scope: !22) +!41 = !{!42, !42, i64 0} +!42 = !{!"int", !18, i64 0} +!43 = !DILocation(line: 11, column: 1, scope: !22) +!44 = !DILocation(line: 10, column: 3, scope: !22) +!45 = distinct !DISubprogram(name: "bar", scope: !10, file: !10, line: 12, type: !46, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !50) +!46 = !DISubroutineType(types: !47) +!47 = !{null, !26, !48} +!48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) +!49 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!50 = !{!51, !52} +!51 = !DILocalVariable(name: "ptr", arg: 1, scope: !45, file: !10, line: 12, type: !26) +!52 = !DILocalVariable(name: "bptr", arg: 2, scope: !45, file: !10, line: 12, type: !48) +!53 = !DILocation(line: 12, column: 46, scope: !45) +!54 = !{!55, !55, i64 0} +!55 = !{!"p1 double", !17, i64 0} +!56 = !DILocation(line: 12, column: 59, scope: !45) +!57 = !DILocation(line: 13, column: 16, scope: !45) +!58 = !DILocation(line: 13, column: 3, scope: !45) +!59 = !DILocation(line: 14, column: 12, scope: !45) +!60 = !DILocation(line: 14, column: 3, scope: !45) +!61 = !DILocation(line: 15, column: 1, scope: !45) +!62 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 17, type: !63, scopeLine: 17, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !65) +!63 = !DISubroutineType(types: !64) +!64 = !{null} +!65 = !{!66, !67} +!66 = !DILocalVariable(name: "a", scope: !62, file: !10, line: 18, type: !25) +!67 = !DILocalVariable(name: "b", scope: !62, file: !10, line: 19, type: !49) +!68 = !DILocation(line: 18, column: 3, scope: !62) +!69 = !DILocation(line: 18, column: 7, scope: !62) +!70 = !DILocation(line: 19, column: 3, scope: !62) +!71 = !DILocation(line: 19, column: 10, scope: !62) +!72 = !{!73, !73, i64 0} +!73 = !{!"double", !18, i64 0} +!74 = !DILocation(line: 12, column: 46, scope: !45, inlinedAt: !75) +!75 = distinct !DILocation(line: 20, column: 3, scope: !62) +!76 = !DILocation(line: 12, column: 59, scope: !45, inlinedAt: !75) +!77 = !DILocation(line: 13, column: 16, scope: !45, inlinedAt: !75) +!78 = !DILocation(line: 13, column: 3, scope: !45, inlinedAt: !75) +!79 = !DILocation(line: 14, column: 12, scope: !45, inlinedAt: !75) +!80 = !DILocation(line: 14, column: 3, scope: !45, inlinedAt: !75) +!81 = !DILocation(line: 15, column: 1, scope: !45, inlinedAt: !75) +!82 = !DILocation(line: 21, column: 1, scope: !62) diff --git a/test/pass/filter/29_inline.llin b/test/pass/filter/29_inline.llin new file mode 100644 index 00000000..e64ccb56 --- /dev/null +++ b/test/pass/filter/29_inline.llin @@ -0,0 +1,167 @@ +; RUN: %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/27_cg.mcg < %s -S 2>&1 | %filecheck %s +; forward_mock was inlined +; REQUIRES: llvm-21 + +; CHECK: > Stack Memory +; CHECK-NEXT: Alloca : +; CHECK-NEXT: Stack call filtered % : 33 + + +; ModuleID = 'out_inline.ll' +source_filename = "27_cg_chain.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local void @MPI_mock(ptr noundef %buf) #0 !dbg !9 { +entry: + %buf.addr = alloca ptr, align 8 + store ptr %buf, ptr %buf.addr, align 8, !tbaa !16 + #dbg_declare(ptr %buf.addr, !15, !DIExpression(), !20) + ret void, !dbg !21 +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #2 + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr captures(none)) #2 + +; Function Attrs: nounwind uwtable +define dso_local void @bar(ptr noundef %ptr, ptr noundef %bptr) #0 !dbg !45 { +entry: + %a.addr.i = alloca ptr, align 8 + %c.i = alloca float, align 4 + %ptr.addr = alloca ptr, align 8 + %bptr.addr = alloca ptr, align 8 + store ptr %ptr, ptr %ptr.addr, align 8, !tbaa !31 + #dbg_declare(ptr %ptr.addr, !51, !DIExpression(), !53) + store ptr %bptr, ptr %bptr.addr, align 8, !tbaa !54 + #dbg_declare(ptr %bptr.addr, !52, !DIExpression(), !56) + %0 = load ptr, ptr %ptr.addr, align 8, !dbg !57, !tbaa !31 + call void @llvm.lifetime.start.p0(i64 8, ptr %a.addr.i) + store ptr %0, ptr %a.addr.i, align 8, !tbaa !31 + #dbg_declare(ptr %a.addr.i, !28, !DIExpression(), !58) + call void @llvm.lifetime.start.p0(i64 4, ptr %c.i) #3, !dbg !60 + #dbg_declare(ptr %c.i, !29, !DIExpression(), !61) + store float 2.000000e+00, ptr %c.i, align 4, !dbg !61, !tbaa !36 + call void @MPI_mock(ptr noundef %c.i), !dbg !62 + %1 = load ptr, ptr %a.addr.i, align 8, !dbg !63, !tbaa !31 + %2 = load i32, ptr %1, align 4, !dbg !64, !tbaa !41 + call void @llvm.lifetime.end.p0(i64 4, ptr %c.i) #3, !dbg !65 + call void @llvm.lifetime.end.p0(i64 8, ptr %a.addr.i), !dbg !66 + %3 = load ptr, ptr %bptr.addr, align 8, !dbg !67, !tbaa !54 + call void @MPI_mock(ptr noundef %3), !dbg !68 + ret void, !dbg !69 +} + +; Function Attrs: nounwind uwtable +define dso_local void @foo() #0 !dbg !70 { +entry: + %a = alloca i32, align 4 + %b = alloca double, align 8 + call void @llvm.lifetime.start.p0(i64 4, ptr %a) #3, !dbg !76 + #dbg_declare(ptr %a, !74, !DIExpression(), !77) + store i32 0, ptr %a, align 4, !dbg !77, !tbaa !41 + call void @llvm.lifetime.start.p0(i64 8, ptr %b) #3, !dbg !78 + #dbg_declare(ptr %b, !75, !DIExpression(), !79) + store double 1.000000e+00, ptr %b, align 8, !dbg !79, !tbaa !80 + call void @bar(ptr noundef %a, ptr noundef %b), !dbg !82 + call void @llvm.lifetime.end.p0(i64 8, ptr %b) #3, !dbg !83 + call void @llvm.lifetime.end.p0(i64 4, ptr %a) #3, !dbg !83 + ret void, !dbg !83 +} + +attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { alwaysinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.1.5", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "27_cg_chain.c", directory: "/build_21", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{!"clang version 21.1.5"} +!9 = distinct !DISubprogram(name: "MPI_mock", scope: !10, file: !10, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!10 = !DIFile(filename: "27_cg_chain.c", directory: "", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!14 = !{!15} +!15 = !DILocalVariable(name: "buf", arg: 1, scope: !9, file: !10, line: 4, type: !13) +!16 = !{!17, !17, i64 0} +!17 = !{!"any pointer", !18, i64 0} +!18 = !{!"omnipotent char", !19, i64 0} +!19 = !{!"Simple C/C++ TBAA"} +!20 = !DILocation(line: 4, column: 21, scope: !9) +!21 = !DILocation(line: 5, column: 1, scope: !9) +!22 = distinct !DISubprogram(name: "forward_mock", scope: !10, file: !10, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !27) +!23 = !DISubroutineType(types: !24) +!24 = !{!25, !26} +!25 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) +!27 = !{!28, !29} +!28 = !DILocalVariable(name: "a", arg: 1, scope: !22, file: !10, line: 7, type: !26) +!29 = !DILocalVariable(name: "c", scope: !22, file: !10, line: 8, type: !30) +!30 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!31 = !{!32, !32, i64 0} +!32 = !{!"p1 int", !17, i64 0} +!33 = !DILocation(line: 7, column: 23, scope: !22) +!34 = !DILocation(line: 8, column: 3, scope: !22) +!35 = !DILocation(line: 8, column: 9, scope: !22) +!36 = !{!37, !37, i64 0} +!37 = !{!"float", !18, i64 0} +!38 = !DILocation(line: 9, column: 3, scope: !22) +!39 = !DILocation(line: 10, column: 11, scope: !22) +!40 = !DILocation(line: 10, column: 10, scope: !22) +!41 = !{!42, !42, i64 0} +!42 = !{!"int", !18, i64 0} +!43 = !DILocation(line: 11, column: 1, scope: !22) +!44 = !DILocation(line: 10, column: 3, scope: !22) +!45 = distinct !DISubprogram(name: "bar", scope: !10, file: !10, line: 12, type: !46, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !50) +!46 = !DISubroutineType(types: !47) +!47 = !{null, !26, !48} +!48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) +!49 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!50 = !{!51, !52} +!51 = !DILocalVariable(name: "ptr", arg: 1, scope: !45, file: !10, line: 12, type: !26) +!52 = !DILocalVariable(name: "bptr", arg: 2, scope: !45, file: !10, line: 12, type: !48) +!53 = !DILocation(line: 12, column: 46, scope: !45) +!54 = !{!55, !55, i64 0} +!55 = !{!"p1 double", !17, i64 0} +!56 = !DILocation(line: 12, column: 59, scope: !45) +!57 = !DILocation(line: 13, column: 16, scope: !45) +!58 = !DILocation(line: 7, column: 23, scope: !22, inlinedAt: !59) +!59 = distinct !DILocation(line: 13, column: 3, scope: !45) +!60 = !DILocation(line: 8, column: 3, scope: !22, inlinedAt: !59) +!61 = !DILocation(line: 8, column: 9, scope: !22, inlinedAt: !59) +!62 = !DILocation(line: 9, column: 3, scope: !22, inlinedAt: !59) +!63 = !DILocation(line: 10, column: 11, scope: !22, inlinedAt: !59) +!64 = !DILocation(line: 10, column: 10, scope: !22, inlinedAt: !59) +!65 = !DILocation(line: 11, column: 1, scope: !22, inlinedAt: !59) +!66 = !DILocation(line: 10, column: 3, scope: !22, inlinedAt: !59) +!67 = !DILocation(line: 14, column: 12, scope: !45) +!68 = !DILocation(line: 14, column: 3, scope: !45) +!69 = !DILocation(line: 15, column: 1, scope: !45) +!70 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 17, type: !71, scopeLine: 17, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !73) +!71 = !DISubroutineType(types: !72) +!72 = !{null} +!73 = !{!74, !75} +!74 = !DILocalVariable(name: "a", scope: !70, file: !10, line: 18, type: !25) +!75 = !DILocalVariable(name: "b", scope: !70, file: !10, line: 19, type: !49) +!76 = !DILocation(line: 18, column: 3, scope: !70) +!77 = !DILocation(line: 18, column: 7, scope: !70) +!78 = !DILocation(line: 19, column: 3, scope: !70) +!79 = !DILocation(line: 19, column: 10, scope: !70) +!80 = !{!81, !81, i64 0} +!81 = !{!"double", !18, i64 0} +!82 = !DILocation(line: 20, column: 3, scope: !70) +!83 = !DILocation(line: 21, column: 1, scope: !70) diff --git a/test/pass/filter/30_inline.llin b/test/pass/filter/30_inline.llin new file mode 100644 index 00000000..02053485 --- /dev/null +++ b/test/pass/filter/30_inline.llin @@ -0,0 +1,178 @@ +; RUN: %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/27_cg.mcg < %s -S 2>&1 | %filecheck %s +; everything was inline here +; REQUIRES: llvm-21 + +; CHECK: > Stack Memory +; CHECK-NEXT: Alloca : +; CHECK-NEXT: Stack call filtered % : 33 + +; ModuleID = 'out_inline.ll' +source_filename = "27_cg_chain.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local void @MPI_mock(ptr noundef %buf) #0 !dbg !9 { +entry: + %buf.addr = alloca ptr, align 8 + store ptr %buf, ptr %buf.addr, align 8, !tbaa !16 + #dbg_declare(ptr %buf.addr, !15, !DIExpression(), !20) + ret void, !dbg !21 +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #2 + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr captures(none)) #2 + +; Function Attrs: nounwind uwtable +define dso_local void @foo() #0 !dbg !70 { +entry: + %a.addr.i.i = alloca ptr, align 8 + %c.i.i = alloca float, align 4 + %ptr.addr.i = alloca ptr, align 8 + %bptr.addr.i = alloca ptr, align 8 + %a = alloca i32, align 4 + %b = alloca double, align 8 + call void @llvm.lifetime.start.p0(i64 4, ptr %a) #3, !dbg !76 + #dbg_declare(ptr %a, !74, !DIExpression(), !77) + store i32 0, ptr %a, align 4, !dbg !77, !tbaa !41 + call void @llvm.lifetime.start.p0(i64 8, ptr %b) #3, !dbg !78 + #dbg_declare(ptr %b, !75, !DIExpression(), !79) + store double 1.000000e+00, ptr %b, align 8, !dbg !79, !tbaa !80 + call void @llvm.lifetime.start.p0(i64 8, ptr %ptr.addr.i) + call void @llvm.lifetime.start.p0(i64 8, ptr %bptr.addr.i) + store ptr %a, ptr %ptr.addr.i, align 8, !tbaa !31 + #dbg_declare(ptr %ptr.addr.i, !51, !DIExpression(), !82) + store ptr %b, ptr %bptr.addr.i, align 8, !tbaa !54 + #dbg_declare(ptr %bptr.addr.i, !52, !DIExpression(), !84) + %0 = load ptr, ptr %ptr.addr.i, align 8, !dbg !85, !tbaa !31 + call void @llvm.lifetime.start.p0(i64 8, ptr %a.addr.i.i) + store ptr %0, ptr %a.addr.i.i, align 8, !tbaa !31 + #dbg_declare(ptr %a.addr.i.i, !28, !DIExpression(), !86) + call void @llvm.lifetime.start.p0(i64 4, ptr %c.i.i) #3, !dbg !88 + #dbg_declare(ptr %c.i.i, !29, !DIExpression(), !89) + store float 2.000000e+00, ptr %c.i.i, align 4, !dbg !89, !tbaa !36 + call void @MPI_mock(ptr noundef %c.i.i), !dbg !90 + %1 = load ptr, ptr %a.addr.i.i, align 8, !dbg !91, !tbaa !31 + %2 = load i32, ptr %1, align 4, !dbg !92, !tbaa !41 + call void @llvm.lifetime.end.p0(i64 4, ptr %c.i.i) #3, !dbg !93 + call void @llvm.lifetime.end.p0(i64 8, ptr %a.addr.i.i), !dbg !94 + %3 = load ptr, ptr %bptr.addr.i, align 8, !dbg !95, !tbaa !54 + call void @MPI_mock(ptr noundef %3), !dbg !96 + call void @llvm.lifetime.end.p0(i64 8, ptr %ptr.addr.i), !dbg !97 + call void @llvm.lifetime.end.p0(i64 8, ptr %bptr.addr.i), !dbg !97 + call void @llvm.lifetime.end.p0(i64 8, ptr %b) #3, !dbg !98 + call void @llvm.lifetime.end.p0(i64 4, ptr %a) #3, !dbg !98 + ret void, !dbg !98 +} + +attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { alwaysinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.1.5", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "27_cg_chain.c", directory: "", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{!"clang version 21.1.5"} +!9 = distinct !DISubprogram(name: "MPI_mock", scope: !10, file: !10, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!10 = !DIFile(filename: "27_cg_chain.c", directory: "", checksumkind: CSK_MD5, checksum: "56e794fba168ff0c21cdc975f0546576") +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!14 = !{!15} +!15 = !DILocalVariable(name: "buf", arg: 1, scope: !9, file: !10, line: 4, type: !13) +!16 = !{!17, !17, i64 0} +!17 = !{!"any pointer", !18, i64 0} +!18 = !{!"omnipotent char", !19, i64 0} +!19 = !{!"Simple C/C++ TBAA"} +!20 = !DILocation(line: 4, column: 21, scope: !9) +!21 = !DILocation(line: 5, column: 1, scope: !9) +!22 = distinct !DISubprogram(name: "forward_mock", scope: !10, file: !10, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !27) +!23 = !DISubroutineType(types: !24) +!24 = !{!25, !26} +!25 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64) +!27 = !{!28, !29} +!28 = !DILocalVariable(name: "a", arg: 1, scope: !22, file: !10, line: 7, type: !26) +!29 = !DILocalVariable(name: "c", scope: !22, file: !10, line: 8, type: !30) +!30 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!31 = !{!32, !32, i64 0} +!32 = !{!"p1 int", !17, i64 0} +!33 = !DILocation(line: 7, column: 23, scope: !22) +!34 = !DILocation(line: 8, column: 3, scope: !22) +!35 = !DILocation(line: 8, column: 9, scope: !22) +!36 = !{!37, !37, i64 0} +!37 = !{!"float", !18, i64 0} +!38 = !DILocation(line: 9, column: 3, scope: !22) +!39 = !DILocation(line: 10, column: 11, scope: !22) +!40 = !DILocation(line: 10, column: 10, scope: !22) +!41 = !{!42, !42, i64 0} +!42 = !{!"int", !18, i64 0} +!43 = !DILocation(line: 11, column: 1, scope: !22) +!44 = !DILocation(line: 10, column: 3, scope: !22) +!45 = distinct !DISubprogram(name: "bar", scope: !10, file: !10, line: 12, type: !46, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !50) +!46 = !DISubroutineType(types: !47) +!47 = !{null, !26, !48} +!48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !49, size: 64) +!49 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!50 = !{!51, !52} +!51 = !DILocalVariable(name: "ptr", arg: 1, scope: !45, file: !10, line: 12, type: !26) +!52 = !DILocalVariable(name: "bptr", arg: 2, scope: !45, file: !10, line: 12, type: !48) +!53 = !DILocation(line: 12, column: 46, scope: !45) +!54 = !{!55, !55, i64 0} +!55 = !{!"p1 double", !17, i64 0} +!56 = !DILocation(line: 12, column: 59, scope: !45) +!57 = !DILocation(line: 13, column: 16, scope: !45) +!58 = !DILocation(line: 7, column: 23, scope: !22, inlinedAt: !59) +!59 = distinct !DILocation(line: 13, column: 3, scope: !45) +!60 = !DILocation(line: 8, column: 3, scope: !22, inlinedAt: !59) +!61 = !DILocation(line: 8, column: 9, scope: !22, inlinedAt: !59) +!62 = !DILocation(line: 9, column: 3, scope: !22, inlinedAt: !59) +!63 = !DILocation(line: 10, column: 11, scope: !22, inlinedAt: !59) +!64 = !DILocation(line: 10, column: 10, scope: !22, inlinedAt: !59) +!65 = !DILocation(line: 11, column: 1, scope: !22, inlinedAt: !59) +!66 = !DILocation(line: 10, column: 3, scope: !22, inlinedAt: !59) +!67 = !DILocation(line: 14, column: 12, scope: !45) +!68 = !DILocation(line: 14, column: 3, scope: !45) +!69 = !DILocation(line: 15, column: 1, scope: !45) +!70 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 17, type: !71, scopeLine: 17, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !73) +!71 = !DISubroutineType(types: !72) +!72 = !{null} +!73 = !{!74, !75} +!74 = !DILocalVariable(name: "a", scope: !70, file: !10, line: 18, type: !25) +!75 = !DILocalVariable(name: "b", scope: !70, file: !10, line: 19, type: !49) +!76 = !DILocation(line: 18, column: 3, scope: !70) +!77 = !DILocation(line: 18, column: 7, scope: !70) +!78 = !DILocation(line: 19, column: 3, scope: !70) +!79 = !DILocation(line: 19, column: 10, scope: !70) +!80 = !{!81, !81, i64 0} +!81 = !{!"double", !18, i64 0} +!82 = !DILocation(line: 12, column: 46, scope: !45, inlinedAt: !83) +!83 = distinct !DILocation(line: 20, column: 3, scope: !70) +!84 = !DILocation(line: 12, column: 59, scope: !45, inlinedAt: !83) +!85 = !DILocation(line: 13, column: 16, scope: !45, inlinedAt: !83) +!86 = !DILocation(line: 7, column: 23, scope: !22, inlinedAt: !87) +!87 = distinct !DILocation(line: 13, column: 3, scope: !45, inlinedAt: !83) +!88 = !DILocation(line: 8, column: 3, scope: !22, inlinedAt: !87) +!89 = !DILocation(line: 8, column: 9, scope: !22, inlinedAt: !87) +!90 = !DILocation(line: 9, column: 3, scope: !22, inlinedAt: !87) +!91 = !DILocation(line: 10, column: 11, scope: !22, inlinedAt: !87) +!92 = !DILocation(line: 10, column: 10, scope: !22, inlinedAt: !87) +!93 = !DILocation(line: 11, column: 1, scope: !22, inlinedAt: !87) +!94 = !DILocation(line: 10, column: 3, scope: !22, inlinedAt: !87) +!95 = !DILocation(line: 14, column: 12, scope: !45, inlinedAt: !83) +!96 = !DILocation(line: 14, column: 3, scope: !45, inlinedAt: !83) +!97 = !DILocation(line: 15, column: 1, scope: !45, inlinedAt: !83) +!98 = !DILocation(line: 21, column: 1, scope: !70) diff --git a/test/pass/filter/31_fptr.c b/test/pass/filter/31_fptr.c new file mode 100644 index 00000000..a69587eb --- /dev/null +++ b/test/pass/filter/31_fptr.c @@ -0,0 +1,35 @@ +// clang-format off +// RUN: %c-to-llvm %s | %apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/31_fptr.mcg 2>&1 | %filecheck %s +// clang-format on + +void MPI_mock(void* buf) { +} + +int never_called(int* never) { + MPI_mock(never); + return 0; +} + +int forward_mock(int* a) { + float c = 2.f; + MPI_mock(&c); + return *a; +} + +int (*forward_mock_ptr)(int*) = forward_mock; + +void bar(int* ptr, double* bptr) { + forward_mock_ptr(ptr); // in mcg: can be (forward_mock | never_called) -> cannot filter ptr + MPI_mock(bptr); +} + +void foo() { + int a = 0; // a -> bar(a,..) -> forward_mock_ptr(a) + double b = 1.; // b -> bar(...,b) -> MPI_mock(b) + bar(&a, &b); +} + +// ACG: +// CHECK: > Stack Memory +// CHECK-NEXT: Alloca : +// CHECK-NEXT: Stack call filtered % : 0 \ No newline at end of file diff --git a/test/pass/filter/31_fptr.mcg b/test/pass/filter/31_fptr.mcg new file mode 100644 index 00000000..7b25ebd0 --- /dev/null +++ b/test/pass/filter/31_fptr.mcg @@ -0,0 +1,119 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "MPI_mock", + "hasBody": true, + "meta": {}, + "origin": "31_fptr.c" + }, + "1": { + "callees": { + "0": {} + }, + "functionName": "never_called", + "hasBody": true, + "meta": { + "argflow": { + "args": [ + { + "idx": 0, + "outs": [ + { + "by_ref": true, + "callees": [ + 0 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 9 + } + } + ] + } + ] + } + }, + "origin": "31_fptr.c" + }, + "2": { + "callees": { + "0": {} + }, + "functionName": "forward_mock", + "hasBody": true, + "meta": {}, + "origin": "31_fptr.c" + }, + "3": { + "callees": { + "0": {}, + "1": {}, + "2": {} + }, + "functionName": "bar", + "hasBody": true, + "meta": { + "argflow": { + "args": [ + { + "idx": 0, + "outs": [ + { + "by_ref": true, + "callees": [ + 1, + 2 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 22 + } + } + ] + }, + { + "idx": 1, + "outs": [ + { + "by_ref": true, + "callees": [ + 0 + ], + "idx": 0, + "loc": { + "col": 3, + "line": 23 + } + } + ] + } + ] + } + }, + "origin": "31_fptr.c" + }, + "4": { + "callees": { + "3": {} + }, + "functionName": "foo", + "hasBody": true, + "meta": {}, + "origin": "31_fptr.c" + } + } + }, + "_MetaCG": { + "generator": { + "name": "GenCC", + "sha": "NO_GIT_SHA_AVAILABLE", + "version": "0.1" + }, + "version": "4.0" + } +} \ No newline at end of file From 794c66344199b318f3ebdb61d05b57777535a0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 17 Feb 2026 13:57:31 +0100 Subject: [PATCH 18/18] Try to find not --- test/CMakeLists.txt | 1 + test/lit.cfg | 6 +++++- test/lit.site.cfg.in | 1 + test/pass/filter/26_malformed_json.c | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 13ae0faa..a9047bfe 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ typeart_find_llvm_progs(TYPEART_FILECHECK_EXEC "FileCheck-${LLVM_VERSION_MAJOR};FileCheck" ABORT_IF_MISSING) +typeart_find_llvm_progs(TYPEART_NOT_EXEC "not-${LLVM_VERSION_MAJOR};not") find_package(Python3 COMPONENTS Interpreter) diff --git a/test/lit.cfg b/test/lit.cfg index ab62837e..bb22e3c3 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -58,6 +58,9 @@ if config.has_legacy_wrapper: if config.is_ci: config.available_features.add('ci') +if config.not_exe: + config.available_features.add('not') + profile_files = getattr(config, 'profile_file', None) typeart_base_lib_dir= getattr(config, 'typeart_base_lib_dir', None) typeart_lib_root = getattr(config, 'typeart_lib_dir', None) @@ -136,7 +139,8 @@ config.substitutions.append(('%omp_cpp_flags', openmp_cxx_flags)) config.substitutions.append(('%apply-typeart', '{} {} -load-pass-plugin {} -passes="{}"'.format(typeart_types_d, opt, transform_pass, std_plugin_args_newpm))) # for testing pass fatals/exit 1: -config.substitutions.append(('%not-apply-typeart', '{} not {} -load-pass-plugin {} -passes="{}"'.format(typeart_types_d, opt, transform_pass, std_plugin_args_newpm))) +if config.not_exe: + config.substitutions.append(('%not-apply-typeart', '{} {} {} -load-pass-plugin {} -passes="{}"'.format(typeart_types_d, config.not_exe ,opt, transform_pass, std_plugin_args_newpm))) config.substitutions.append(('%c-to-llvm', '{} {}'.format(clang_cc, to_llvm_args))) config.substitutions.append(('%cpp-to-llvm', '{} {}'.format(clang_cpp, to_llvm_args))) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 7a37f7cc..741c4d08 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -12,6 +12,7 @@ config.opt = "@TYPEART_OPT_EXEC@" config.opt_args = "@TYPEART_OPT_ARGS@" config.llc = "@TYPEART_LLC_EXEC@" config.filecheck = "@TYPEART_FILECHECK_EXEC@" +config.not_exe = "@TYPEART_NOT_EXEC@" config.typeart_project_dir = "@TYPEARTPASS_PROJECT_DIR@" config.typeart_base_lib_dir = "@TYPEARTPASS_BASE_LIBRARY_DIR@" diff --git a/test/pass/filter/26_malformed_json.c b/test/pass/filter/26_malformed_json.c index 019ac928..9a9d44db 100644 --- a/test/pass/filter/26_malformed_json.c +++ b/test/pass/filter/26_malformed_json.c @@ -2,6 +2,8 @@ // RUN: %c-to-llvm %s | %not-apply-typeart --typeart-stack=true --typeart-filter=true --typeart-filter-implementation=acg --typeart-filter-cg-file=%p/26_malformed_cg.mcg 2>&1 | %filecheck %s // clang-format on +// REQUIRES: not + // comma is missing between nodes // CHECK: Fatal