diff --git a/lib/mpi_interceptor/InterceptorFunctions.h b/lib/mpi_interceptor/InterceptorFunctions.h index c1ccb341..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) { @@ -115,6 +116,33 @@ 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; + } + + 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) { + ++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); @@ -144,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/lib/passes/Commandline.cpp b/lib/passes/Commandline.cpp index 3ea91a4e..f6366e47 100644 --- a/lib/passes/Commandline.cpp +++ b/lib/passes/Commandline.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include using namespace llvm; @@ -126,10 +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")), + 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/TypeARTPass.cpp b/lib/passes/TypeARTPass.cpp index 3d7a8b3c..23fc1505 100644 --- a/lib/passes/TypeARTPass.cpp +++ b/lib/passes/TypeARTPass.cpp @@ -403,48 +403,41 @@ 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.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 1260db13..134e1076 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 @@ -96,15 +99,39 @@ 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); + } + + 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]; + const auto buf = MemoryBuffer::getFile(std::move(cg_file), true); + if (!buf) { + LOG_FATAL("Failed to load MCG file"); + std::exit(1); + } + + auto mcg = metacg::parse((*buf)->getBuffer()); + if (!mcg) { + LOG_FATAL(mcg.takeError() << '\n'); 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)); + + 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)); 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; diff --git a/lib/passes/compat/CallSite.h b/lib/passes/compat/CallSite.h index 9a4f821a..2b03c6ab 100644 --- a/lib/passes/compat/CallSite.h +++ b/lib/passes/compat/CallSite.h @@ -15,12 +15,14 @@ #ifndef COMPAT_LLVM_IR_CALLSITE_H #define COMPAT_LLVM_IR_CALLSITE_H +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Instruction.h" #include #include #include #include +#include namespace llvm { class CallSite { @@ -53,6 +55,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 +89,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..9846def4 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/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 db1a76eb..1e908992 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); @@ -172,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 new file mode 100644 index 00000000..9092f841 --- /dev/null +++ b/lib/passes/filter/ACGFilter.cpp @@ -0,0 +1,192 @@ +// 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 +// + +#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.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); + + 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 outs = mcg.outputs(current, cur_idx); + if (!outs) { + 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); + } + } + } + + 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::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; + } + + const auto idx = std::distance(current.args().begin(), find(current.args(), arg)); + + const auto* callLoc = current.getLocation(); + if (!callLoc) { + LOG_DEBUG("No call location, 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) { + 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 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; + } + + 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; + } + + 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 + LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName()); + return FilterAnalysis::Continue; + } +} + +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..acaf4bef --- /dev/null +++ b/lib/passes/filter/ACGFilter.h @@ -0,0 +1,59 @@ +// 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 ARGFLOWFILTER_H +#define ARGFLOWFILTER_H + +#include "FilterBase.h" +#include "Matcher.h" +#include "MetaCG.h" +#include "compat/CallSite.h" + +namespace typeart::filter { + +namespace omp { +struct OmpContext; +} + +struct DefaultSearch; + +struct ArgflowFilterTrait { + 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; +}; + +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&); + + private: + FilterAnalysis reachesMatching(ArrayRef, size_t); + + metacg::Mcg mcg; + Regex matcher; + FunctionOracleMatcher oracle; +}; + +using AcgFilter = BaseFilter; + +} // namespace typeart::filter + +#endif // ARGFLOWFILTER_H diff --git a/lib/passes/filter/CGForwardFilter.cpp b/lib/passes/filter/CGForwardFilter.cpp index 2570f5aa..43169860 100644 --- a/lib/passes/filter/CGForwardFilter.cpp +++ b/lib/passes/filter/CGForwardFilter.cpp @@ -12,118 +12,153 @@ #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 "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" - -#include - -namespace llvm { -class Function; -} // namespace llvm +#include namespace typeart::filter { -CGFilterImpl::CGFilterImpl(const std::string& filter_str, std::unique_ptr&& cgraph) - : CGFilterImpl(filter_str, std::move(cgraph), nullptr) { +CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, Regex&& match) + : mcg{std::move(cg)}, matcher{std::move(match)} { } -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)) { +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) { + workq.push_back(it); + } + }; + + 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"); + + 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); + + // Filter if we're in a leaf function if (analysis.empty()) { 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 (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; } + } - 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; - } - } + return FilterAnalysis::Continue; +} + +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 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; + // 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; } - return FilterAnalysis::Continue; -} + const auto parentNode = mcg.byName(parentScope->getName()); + if (!parentNode) { + LOG_DEBUG("Failed to get parent node, continuing"); + 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; - } + 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; } - const auto searchCG = [&](auto from) { - if (call_graph) { - return call_graph->reachable(std::string{from->getName()}, filter); + for (const auto& local : md->locals) { + if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) { + callees.append(local.callees.begin(), local.callees.end()); } - return CGInterface::ReachabilityResult::unknown; - }; + } - const auto reached = searchCG(current.getCalledFunction()); + for (const auto& [callee, _] : parent->callees) { + callees.push_back(callee); + } - 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; + return reachesMatching(callees); +} + +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; } } -FilterAnalysis CGFilterImpl::def(CallSite current, const Path& p) { - return decl(current, p); +FilterAnalysis CGForwardFilterImpl::decl(const CallSite current, const Path& p) { + return def(current, p); } -} // namespace typeart::filter \ No newline at end of file +} // namespace typeart::filter diff --git a/lib/passes/filter/CGForwardFilter.h b/lib/passes/filter/CGForwardFilter.h index 3f017dc1..55740c38 100644 --- a/lib/passes/filter/CGForwardFilter.h +++ b/lib/passes/filter/CGForwardFilter.h @@ -15,59 +15,45 @@ #include "FilterBase.h" #include "Matcher.h" +#include "MetaCG.h" #include "compat/CallSite.h" -#include "filter/CGInterface.h" -#include "filter/IRPath.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 +} -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 -#endif // TYPEART_CGFORWARDFILTER_H \ No newline at end of file +#endif // TYPEART_CGFORWARDFILTER_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/Matcher.h b/lib/passes/filter/Matcher.h index d58653ec..6e93d915 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,49 +56,48 @@ 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; } }; class FunctionOracleMatcher final : public Matcher { const MemOps mem_operations{}; - llvm::SmallDenseSet continue_set{{"sqrt"}, {"cos"}, {"sin"}, {"pow"}, {"fabs"}, - {"abs"}, {"log"}, {"fscanf"}, {"cbrt"}, {"gettimeofday"}}; + 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"}}; 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 new file mode 100644 index 00000000..8d564666 --- /dev/null +++ b/lib/passes/filter/MetaCG.h @@ -0,0 +1,316 @@ +// 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 +#include +#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; +}; + +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); +} + +/// MetaCGv4 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 + llvm::Expected as(llvm::StringRef const name) const { + 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 (T r{}; fromJSON(*p, r, root)) { + return r; + } + + return llvm::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 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, 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); +} + +/// Represents the callgraph's nodes and their associated IDs +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) { + return false; + } + + for (auto const& [k, v] : *outer) { + auto k_str = k.str(); + + 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{}) { + r.insert({hash, node}); + } else { + return false; + } + } + + return true; +} + +/// MetaCGv4 callgraph +struct CallGraph { + Md meta; + Nodes nodes; +}; + +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); +} + +/// 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 {}; + } + + std::optional forId(size_t id) const { + 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()) { + return {}; + } + + auto argflow = this->graph.nodes.at(node).meta.as("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) { + return out; + } + } + + return {}; + } + + 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 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); + return *parsed; + } else { + return llvm::Expected{parsed.takeError()}; + } +} + +} // namespace typeart::filter::metacg + +#endif // METACG_H 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