• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tudasc / TypeART / 21639007075

03 Feb 2026 04:44PM UTC coverage: 88.438% (-1.8%) from 90.245%
21639007075

Pull #183

github

web-flow
Merge 4594bc77c into 8ae76f97d
Pull Request #183: Argument-granular whole program dataflow filtering

209 of 363 new or added lines in 10 files covered. (57.58%)

8 existing lines in 3 files now uncovered.

4903 of 5544 relevant lines covered (88.44%)

32492.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

27.71
/lib/passes/filter/CGForwardFilter.cpp
1
// TypeART library
2
//
3
// Copyright (c) 2017-2025 TypeART Authors
4
// Distributed under the BSD 3-Clause license.
5
// (See accompanying file LICENSE.txt or copy at
6
// https://opensource.org/licenses/BSD-3-Clause)
7
//
8
// Project home: https://github.com/tudasc/TypeART
9
//
10
// SPDX-License-Identifier: BSD-3-Clause
11
//
12

13
#include "CGForwardFilter.h"
14

15
#include "Matcher.h"
16

17
#include <llvm/ADT/SmallSet.h>
18
#include <utility>
19

20
namespace typeart::filter {
21

22
CGForwardFilterImpl::CGForwardFilterImpl(metacg::Mcg&& cg, std::unique_ptr<Matcher>&& m,
32✔
23
                                         std::unique_ptr<Matcher>&& deep)
24
    : mcg{std::move(cg)}, matcher{std::move(m)}, deep_matcher{std::move(deep)} {
16✔
25
}
16✔
26

NEW
27
FilterAnalysis CGForwardFilterImpl::reachesMatching(const ArrayRef<size_t> nodes) {
×
NEW
28
  SmallVector<size_t, 64> workq{};
×
NEW
29
  SmallSet<size_t, 32> seen{};
×
30

NEW
31
  const auto enqueue = [&seen, &workq](const size_t it) {
×
NEW
32
    if (const auto [_, inserted] = seen.insert(it); inserted) {
×
NEW
33
      workq.push_back(it);
×
NEW
34
    }
×
NEW
35
  };
×
36

NEW
37
  for (const auto id : nodes) {
×
NEW
38
    enqueue(id);
×
39
    LOG_DEBUG("Starting node with parameter: " << mcg.forId(id)->name.value_or(""));
40
  }
41

NEW
42
  while (!workq.empty()) {
×
43
    // Grab the current node ID and translate it into its corresponding function descriptor to check
44
    // if its name matches our matcher.
NEW
45
    const auto current = workq.pop_back_val();
×
46
    LOG_DEBUG("> Inspecting node: " << mcg.forId(current)->name.value_or(""));
47

NEW
48
    if (const auto fn = mcg.forId(current); fn && fn->name) {
×
NEW
49
      if (matcher->matchName(*fn->name) == Matcher::MatchResult::Match) {
×
50
        // Keep if the function matches the matcher
51
        LOG_DEBUG("-> Matches matcher, keeping");
NEW
52
        return FilterAnalysis::Keep;
×
NEW
53
      } else if (const auto r = oracle.matchName(*fn->name); r != Matcher::MatchResult::NoMatch) {
×
54
        // Ignore any known skippable functions
NEW
55
        switch (r) {
×
56
          case Matcher::MatchResult::ShouldSkip:
57
          case Matcher::MatchResult::ShouldContinue:
58
            LOG_DEBUG("-> Known function, skipping");
NEW
59
            continue;
×
60

61
          default:;
NEW
62
        }
×
NEW
63
      }
×
NEW
64
    } else if (fn && !fn->has_body) {
×
65
      // We have to be conservative if we reach an unknown function without a body
66
      LOG_DEBUG("-> Function has no body, keeping");
NEW
67
      return FilterAnalysis::Keep;
×
68
    }
69

NEW
70
    const auto current_node = mcg.forId(current);
×
NEW
71
    assert(current_node && "MCG is broken");
×
72

NEW
73
    for (const auto& [callee, _] : current_node->callees) {
×
NEW
74
      enqueue(callee);
×
75
      LOG_DEBUG("-> Enqueued callee: " << mcg.forId(callee)->name.value_or(""));
76
    }
NEW
77
  }
×
78

NEW
79
  return FilterAnalysis::Continue;
×
UNCOV
80
}
×
81

82
FilterAnalysis CGForwardFilterImpl::precheck(Value* in, Function* start, const FPath&) {
208✔
83
  if (!start) {
208✔
UNCOV
84
    return FilterAnalysis::Continue;
×
85
  }
86

87
  FunctionAnalysis analysis{};
208✔
88
  analysis.analyze(start);
208✔
89

90
  // Filter if we're in a leaf function
91
  if (analysis.empty()) {
208✔
92
    return FilterAnalysis::Filter;
×
93
  }
94

95
  if (isTempAlloc(in)) {
208✔
96
    LOG_DEBUG("Alloca is a temporary " << *in);
NEW
97
    return FilterAnalysis::Filter;
×
98
  }
99

100
  if (AllocaInst* alloc = dyn_cast<AllocaInst>(in)) {
208✔
101
    if (alloc->getAllocatedType()->isStructTy() && omp::OmpContext::allocaReachesTask(alloc)) {
120✔
102
      LOG_DEBUG("Alloca reaches task call " << *alloc)
UNCOV
103
      return FilterAnalysis::Filter;
×
104
    }
105
  }
120✔
106

107
  return FilterAnalysis::Continue;
208✔
108
}
208✔
109

NEW
110
FilterAnalysis CGForwardFilterImpl::indirect(const CallSite current, const Path& p) {
×
NEW
111
  SmallVector<size_t, 16> callees{};
×
112

NEW
113
  const auto* callLoc = current.getLocation();
×
NEW
114
  if (!callLoc) {
×
115
    LOG_DEBUG("No call location, continuing");
NEW
116
    return FilterAnalysis::Continue;
×
117
  }
118

119
  // Resolve the parent scope via debug metadata as it should stay consistent even through inlining
NEW
120
  const auto* parentScope = dyn_cast<DISubprogram>(callLoc->getScope());
×
NEW
121
  if (!parentScope) {
×
122
    LOG_DEBUG("Failed to get parent scope, continuing");
NEW
123
    return FilterAnalysis::Continue;
×
124
  }
125

NEW
126
  const auto parentNode = mcg.byName(parentScope->getName());
×
NEW
127
  if (!parentNode) {
×
128
    LOG_DEBUG("Failed to get parent node, continuing");
NEW
129
    return FilterAnalysis::Continue;
×
130
  }
131

NEW
132
  const auto parent = mcg.forId(*parentNode);
×
NEW
133
  assert(parent && "Malformed MCG");
×
134

NEW
135
  auto md = parent->meta.as<metacg::MdLocals>("localflow");
×
NEW
136
  if (!md) {
×
137
    LOG_DEBUG("Failed to get localflow for node, continuing");
NEW
138
    return FilterAnalysis::Continue;
×
139
  }
140

NEW
141
  for (const auto& local : md->locals) {
×
NEW
142
    if (local.loc == metacg::SrcLoc{callLoc->getColumn(), callLoc->getLine()}) {
×
NEW
143
      callees.append(local.callees.begin(), local.callees.end());
×
UNCOV
144
    }
×
145
  }
146

NEW
147
  for (const auto& [callee, _] : parent->callees) {
×
NEW
148
    callees.push_back(callee);
×
149
  }
150

NEW
151
  return reachesMatching(callees);
×
UNCOV
152
}
×
153

154
FilterAnalysis CGForwardFilterImpl::def(const CallSite current, const Path& p) {
80✔
155
  if (deep_matcher && deep_matcher->match(current) == Matcher::MatchResult::Match) {
80✔
156
#if LLVM_VERSION_MAJOR < 15
157
    auto result = correlate2void(current, p);
80✔
158
#else
159
    auto result = correlate2pointer(current, p);
160
#endif
161
    switch (result) {
80✔
162
      case ArgCorrelation::GlobalMismatch:
163
        [[fallthrough]];
164
      case ArgCorrelation::ExactMismatch:
165
        LOG_DEBUG("Correlated, continue search");
166
        return FilterAnalysis::Continue;
64✔
167
      default:
168
        return FilterAnalysis::Keep;
16✔
169
    }
170
  }
NEW
171
  if (const auto node = mcg.byName(current.getCalledFunction()->getName()); node) {
×
NEW
172
    return reachesMatching({*node});
×
173
  } else {
174
    // Be conservative if the function is not recorded in the call graph
175
    LOG_DEBUG("Unrecorded function, continuing: " << current.getCalledFunction()->getName());
NEW
176
    return FilterAnalysis::Continue;
×
177
  }
178
}
80✔
179

180
FilterAnalysis CGForwardFilterImpl::decl(const CallSite current, const Path& p) {
80✔
181
  return def(current, p);
80✔
182
}
183

184
}  // namespace typeart::filter
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc