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

tudasc / TypeART / 23985353175

04 Apr 2026 06:52PM UTC coverage: 90.264%. First build
23985353175

Pull #185

github

web-flow
Merge baebfc1d5 into b87694974
Pull Request #185: Support LLVM 22

111 of 118 new or added lines in 4 files covered. (94.07%)

4886 of 5413 relevant lines covered (90.26%)

39010.96 hits per line

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

85.77
/lib/passes/TypeARTPass.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 "Commandline.h"
14
#include "TypeARTConfiguration.h"
15
#include "analysis/MemInstFinder.h"
16
#include "configuration/Configuration.h"
17
#include "configuration/EnvironmentConfiguration.h"
18
#include "configuration/FileConfiguration.h"
19
#include "configuration/PassBuilderUtil.h"
20
#include "configuration/PassConfiguration.h"
21
#include "configuration/TypeARTOptions.h"
22
#include "instrumentation/CallBackFunctionInserter.h"
23
#include "instrumentation/MemOpArgCollector.h"
24
#include "instrumentation/MemOpInstrumentation.h"
25
#include "instrumentation/TypeARTFunctions.h"
26
#include "instrumentation/TypeIDProvider.h"
27
#include "support/ConfigurationBase.h"
28
#include "support/Logger.h"
29
#include "support/ModuleDumper.h"
30
#include "support/Table.h"
31
#include "support/Util.h"
32
#include "typegen/TypeGenerator.h"
33

34
#include "llvm/ADT/DenseMap.h"
1,664✔
35
#include "llvm/ADT/STLExtras.h"
1,664✔
36
#include "llvm/ADT/ScopeExit.h"
1,664✔
37
#include "llvm/ADT/SmallVector.h"
38
#include "llvm/ADT/Statistic.h"
39
#include "llvm/ADT/StringRef.h"
40
#include "llvm/IR/DataLayout.h"
41
#include "llvm/IR/Function.h"
42
#include "llvm/IR/LegacyPassManager.h"
43
#include "llvm/IR/Module.h"
44
#include "llvm/IR/PassManager.h"
45
#include "llvm/Pass.h"
46
#include "llvm/Passes/PassBuilder.h"
47
#if LLVM_VERSION_MAJOR < 22
1,664✔
48
#include "llvm/Passes/PassPlugin.h"
1,688✔
49
#else
50
#include "llvm/Plugins/PassPlugin.h"
51
#endif
52
#include "llvm/Support/CommandLine.h"
53
#include "llvm/Support/raw_ostream.h"
54

55
#include <cassert>
56
#include <cstddef>
57
#include <llvm/ADT/DenseSet.h>
58
#include <llvm/Config/llvm-config.h>
59
#include <llvm/IR/Constant.h>
60
#include <llvm/Support/Error.h>
61
#include <memory>
4,992✔
62
#include <optional>
1,664✔
63
#include <sstream>
64
#include <string>
65
#include <utility>
66

67
namespace llvm {
68
class BasicBlock;
69
}  // namespace llvm
70

71
using namespace llvm;
72

73
extern llvm::cl::OptionCategory typeart_category;
74

75
static cl::opt<std::string> cl_typeart_configuration_file(
5,139✔
76
    "typeart-config", cl::init(""),
5,139✔
77
    cl::desc(
5,139✔
78
        "Location of the configuration file to configure the TypeART pass. Commandline arguments are prioritized."),
5,139✔
79
    cl::cat(typeart_category));
10,278✔
80

81
#define DEBUG_TYPE "typeart"
82

83
ALWAYS_ENABLED_STATISTIC(NumInstrumentedMallocs, "Number of instrumented mallocs");
84
ALWAYS_ENABLED_STATISTIC(NumInstrumentedFrees, "Number of instrumented frees");
85
ALWAYS_ENABLED_STATISTIC(NumInstrumentedAlloca, "Number of instrumented (stack) allocas");
86
ALWAYS_ENABLED_STATISTIC(NumInstrumentedGlobal, "Number of instrumented globals");
87

88
namespace typeart::pass {
89

90
std::optional<std::string> get_configuration_file_path() {
5,742✔
91
  if (!cl_typeart_configuration_file.empty()) {
5,742✔
92
    LOG_DEBUG("Using cl::opt for config file " << cl_typeart_configuration_file.getValue());
93
    return cl_typeart_configuration_file.getValue();
×
94
  }
95
  const char* config_file = std::getenv("TYPEART_CONFIG_FILE");
5,742✔
96
  if (config_file != nullptr) {
5,742✔
97
    LOG_DEBUG("Using env var for types file " << config_file)
98
    return std::string{config_file};
30✔
99
  }
100
  LOG_INFO("No configuration file set.")
5,712✔
101
  return {};
5,712✔
102
}
5,742✔
103

104
class TypeArtPass : public llvm::PassInfoMixin<TypeArtPass> {
105
  std::optional<config::TypeARTConfigOptions> pass_opts{std::nullopt};
×
106
  std::unique_ptr<config::Configuration> pass_config;
107

108
  std::unique_ptr<analysis::MemInstFinder> meminst_finder;
109
  std::unique_ptr<TypeGenerator> typeManager;
110
  InstrumentationHelper instrumentation_helper;
111
  std::unique_ptr<TAFunctionQuery> functions;
112
  std::unique_ptr<InstrumentationContext> instrumentation_context;
113

114
  const config::Configuration& configuration() const {
160,369✔
115
    return *pass_config;
160,369✔
116
  }
117

118
 public:
119
  TypeArtPass() = default;
×
120
  explicit TypeArtPass(config::TypeARTConfigOptions opts) : pass_opts(opts) {
10,278✔
121
    // LOG_INFO("Created with \n" << opts)
122
  }
10,278✔
123

124
  bool doInitialization(Module& m) {
5,736✔
125
    auto config_file_path = get_configuration_file_path();
5,736✔
126

127
    const auto init = config_file_path.has_value()
5,736✔
128
                          ? config::TypeARTConfigInit{config_file_path.value()}
30✔
129
                          : config::TypeARTConfigInit{{}, config::TypeARTConfigInit::FileConfigurationMode::Empty};
3,282✔
130

131
    auto typeart_config = [&](const auto& init_value) {
9,048✔
132
      if (init_value.mode == config::TypeARTConfigInit::FileConfigurationMode::Empty) {
5,736✔
133
        return config::make_typeart_configuration_from_opts(pass_opts.value_or(config::TypeARTConfigOptions{}));
22,824✔
134
      }
135
      return config::make_typeart_configuration(init_value);
30✔
136
    }(init);
5,736✔
137

138
    if (typeart_config) {
5,736✔
139
      LOG_INFO("Emitting TypeART configuration content\n" << typeart_config.get()->getOptions())
5,730✔
140
      pass_config = std::move(*typeart_config);
5,730✔
141
    } else {
5,730✔
142
      LOG_FATAL("Could not load TypeART configuration.")
6✔
143
      std::exit(EXIT_FAILURE);
6✔
144
    }
145

146
    meminst_finder = analysis::create_finder(configuration());
3,306✔
147

148
    const std::string types_file =
149
        configuration().getValueOr(config::ConfigStdArgs::types, {config::ConfigStdArgValues::types});
3,306✔
150

151
    const TypegenImplementation typesgen_parser =
3,306✔
152
        configuration().getValueOr(config::ConfigStdArgs::typegen, {config::ConfigStdArgValues::typegen});
3,306✔
153
    typeManager = make_typegen(types_file, typesgen_parser);
3,306✔
154

155
    LOG_DEBUG("Propagating type infos.");
156
    const auto [loaded, error] = typeManager->load();
3,306✔
157
    if (loaded) {
5,730✔
158
      LOG_DEBUG("Existing type configuration successfully loaded from " << types_file);
159
    } else {
1,305✔
160
      LOG_DEBUG("No valid existing type configuration found: " << types_file << ". Reason: " << error.message());
161
    }
162

163
    instrumentation_helper.setModule(m);
3,306✔
164
    ModuleData mdata{&m};
3,306✔
165
    const auto has_cu_types = typeManager->registerModule(mdata);
3,306✔
166

167
    declareInstrumentationFunctions(m);
3,306✔
168
    {
169
      auto type_id_handler = get_type_id_handler(m, &typeManager->getTypeDatabase(), configuration(), functions.get());
5,730✔
170
      // const bool heap   = configuration()[config::ConfigStdArgs::heap];
171
      if (has_cu_types) {
5,730✔
172
        LOG_DEBUG("Registering compilation unit types list")
173
        type_id_handler->registerModule(mdata);
1,952✔
174
      }
1,952✔
175

176
      auto arg_collector =
177
          std::make_unique<MemOpArgCollector>(configuration(), typeManager.get(), instrumentation_helper);
5,730✔
178
      // const bool instrument_stack_lifetime = configuration()[config::ConfigStdArgs::stack_lifetime];
179
      auto cb_provider    = make_callback_inserter(configuration(), std::move(type_id_handler), functions.get());
5,730✔
180
      auto mem_instrument = std::make_unique<MemOpInstrumentation>(configuration(), functions.get(),
11,460✔
181
                                                                   instrumentation_helper, std::move(cb_provider));
5,730✔
182

183
      instrumentation_context =
5,730✔
184
          std::make_unique<InstrumentationContext>(std::move(arg_collector), std::move(mem_instrument));
5,730✔
185
    }
3,306✔
186
    return true;
187
  }
3,306✔
188

189
  bool doFinalization() {
5,730✔
190
    /*
191
     * Persist the accumulated type definition information for this module.
192
     */
193
    // TODO: inline/hybrid types not supported in non-opaque mode
194
    const bool emit_type_file_always     = bool(LLVM_VERSION_MAJOR < 15);
5,730✔
195
    TypeSerializationImplementation mode = configuration()[config::ConfigStdArgs::type_serialization];
5,730✔
196
    if (emit_type_file_always || mode == TypeSerializationImplementation::FILE) {
4,042✔
197
      const std::string types_file = configuration()[config::ConfigStdArgs::types];
2,258✔
198
      LOG_DEBUG("Writing type file to " << types_file);
199

200
      const auto [stored, error] = typeManager->store();
2,258✔
201
      if (stored) {
2,258✔
202
        LOG_DEBUG("Success!");
203
      } else {
2,258✔
204
        LOG_FATAL("Failed writing type config to " << types_file << ". Reason: " << error.message());
×
205
      }
206
    }
2,258✔
207
    const bool print_stats = configuration()[config::ConfigStdArgs::stats];
5,730✔
208
    if (print_stats) {
5,730✔
209
      auto& out = llvm::errs();
5,667✔
210
      printStats(out);
5,667✔
211
    }
5,667✔
212
    return false;
5,730✔
213
  }
214

215
  void declareInstrumentationFunctions(Module& m) {
5,730✔
216
    functions = declare_instrumentation_functions(m, configuration());
5,730✔
217
  }
5,730✔
218

219
  void printStats(llvm::raw_ostream& out){
5,667✔
220
#if LLVM_VERSION_MAJOR < 22
221
      const auto scope_exit_cleanup_counter = llvm::make_scope_exit([&]() {
9,740✔
222
#else
223
      llvm::scope_exit scope_exit_cleanup_counter([&]() {
1,594✔
224
#endif
225
        NumInstrumentedAlloca  = 0;
5,667✔
226
        NumInstrumentedFrees   = 0;
5,667✔
227
        NumInstrumentedGlobal  = 0;
5,667✔
228
        NumInstrumentedMallocs = 0;
5,667✔
229
      });
5,667✔
230
  meminst_finder->printStats(out);
5,667✔
231

232
  const auto get_ta_mode = [&]() {
11,334✔
233
    const bool heap   = configuration()[config::ConfigStdArgs::heap];
5,667✔
234
    const bool stack  = configuration()[config::ConfigStdArgs::stack];
5,667✔
235
    const bool global = configuration()[config::ConfigStdArgs::global];
5,667✔
236

237
    if (heap) {
5,667✔
238
      if (stack) {
3,871✔
239
        return " [Heap & Stack]";
960✔
240
      }
241
      return " [Heap]";
2,911✔
242
    }
243

244
    if (stack) {
1,796✔
245
      return " [Stack]";
1,796✔
246
    }
247

NEW
248
    if (global) {
×
NEW
249
      return " [Global]";
×
250
    }
251

NEW
252
    LOG_ERROR("Did not find heap or stack, or combination thereof!");
×
NEW
253
    assert((heap || stack || global) && "Needs stack, heap, global or combination thereof");
×
NEW
254
    return " [Unknown]";
×
255
  };
5,667✔
256

257
  Table stats("TypeArtPass");
5,667✔
258
  stats.wrap_header_ = true;
5,667✔
259
  stats.title_ += get_ta_mode();
5,667✔
260
  stats.put(Row::make("Malloc", NumInstrumentedMallocs.getValue()));
5,667✔
261
  stats.put(Row::make("Free", NumInstrumentedFrees.getValue()));
5,667✔
262
  stats.put(Row::make("Alloca", NumInstrumentedAlloca.getValue()));
5,667✔
263
  stats.put(Row::make("Global", NumInstrumentedGlobal.getValue()));
5,667✔
264

265
  std::ostringstream stream;
5,667✔
266
  stats.print(stream);
5,667✔
267
  out << stream.str();
5,667✔
268
}
5,667✔
269

270
llvm::PreservedAnalyses
271
run(llvm::Module& m, llvm::ModuleAnalysisManager&) {
5,730✔
272
  bool changed{false};
5,730✔
273
  changed |= doInitialization(m);
5,730✔
274
  const bool heap = configuration()[config::ConfigStdArgs::heap];  // Must happen after doInit
5,730✔
275
  dump_module(m, heap ? util::module::ModulePhase::kBase : util::module::ModulePhase::kOpt);
5,730✔
276
  changed |= runOnModule(m);
5,730✔
277
  dump_module(m, heap ? util::module::ModulePhase::kHeap : util::module::ModulePhase::kStack);
5,730✔
278
  changed |= doFinalization();
5,730✔
279
  return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
5,730✔
280
}
281

282
bool runOnModule(llvm::Module& m) {
5,730✔
283
  meminst_finder->runOnModule(m);
5,730✔
284
  const bool instrument_global = configuration()[config::ConfigStdArgs::global];
5,730✔
285
  bool globals_were_instrumented{false};
5,730✔
286
  if (instrument_global) {
5,730✔
287
    // declareInstrumentationFunctions(m);
288

289
    const auto& globalsList = meminst_finder->getModuleGlobals();
2,753✔
290
    if (!globalsList.empty()) {
2,753✔
291
      const auto global_count = instrumentation_context->handleGlobal(globalsList);
1,078✔
292
      NumInstrumentedGlobal += global_count;
1,078✔
293
      globals_were_instrumented = global_count > 0;
1,078✔
294
    }
1,078✔
295
  }
2,753✔
296

297
  llvm::DenseSet<const llvm::Constant*> tor_funcs;
5,730✔
298
  {
299
    const auto collect_funcs = [&tor_funcs](const auto* constant) -> bool {
7,220✔
300
      if (llvm::isa<llvm::Function>(constant)) {
1,490✔
301
        tor_funcs.insert(constant);
1,490✔
302
      }
1,490✔
303
      return false;
1,490✔
304
    };
305

306
    util::for_each_cdtor("llvm.global_ctors", m, collect_funcs);
5,730✔
307
    util::for_each_cdtor("llvm.global_dtors", m, collect_funcs);
5,730✔
308
  }
309

310
  const auto instrumented_function = llvm::count_if(m.functions(), [&](auto& f) {
176,734✔
311
                                       if (tor_funcs.contains(&f)) {
165,274✔
312
                                         LOG_DEBUG("Function is in LLVM global ctor or dtor " << f.getName())
313
                                         return false;
1,490✔
314
                                       }
315
                                       return runOnFunc(f);
163,784✔
316
                                     }) > 0;
171,004✔
317
  return instrumented_function || globals_were_instrumented;
5,730✔
318
}
5,730✔
319

320
bool runOnFunc(llvm::Function& f) {
163,784✔
321
  using namespace typeart;
322

323
  if (f.isDeclaration() || util::starts_with_any_of(f.getName(), "__typeart", "typeart", "__sanitizer", "__tysan")) {
163,784✔
324
    return false;
127,609✔
325
  }
326

327
  if (!meminst_finder->hasFunctionData(f)) {
36,175✔
NEW
328
    LOG_WARNING("No allocation data could be retrieved for function: " << f.getName());
×
NEW
329
    return false;
×
330
  }
331

332
  LOG_DEBUG("Running on function: " << f.getName())
333

334
  // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are
335
  // nullpointer/invalidated
336
  // declareInstrumentationFunctions(*f.getParent());
337

338
  bool mod{false};
36,175✔
339
//  auto& c = f.getContext();
340
#if LLVM_VERSION_MAJOR > 19
341
  DataLayout dl(f.getParent()->getDataLayout());
14,955✔
342
#else
343
  DataLayout dl(f.getParent());
21,220✔
344
#endif
345

346
  const auto& fData   = meminst_finder->getFunctionData(f);
36,175✔
347
  const auto& mallocs = fData.mallocs;
36,175✔
348
  const auto& allocas = fData.allocas;
36,175✔
349
  const auto& frees   = fData.frees;
36,175✔
350

351
  const bool instrument_heap  = configuration()[config::ConfigStdArgs::heap];
36,175✔
352
  const bool instrument_stack = configuration()[config::ConfigStdArgs::stack];
36,175✔
353

354
  if (instrument_heap) {
36,175✔
355
    // instrument collected calls of bb:
356
    const auto heap_count = instrumentation_context->handleHeap(mallocs);
25,128✔
357
    const auto free_count = instrumentation_context->handleFree(frees);
25,128✔
358

359
    NumInstrumentedMallocs += heap_count;
25,128✔
360
    NumInstrumentedFrees += free_count;
25,128✔
361

362
    mod |= heap_count > 0 || free_count > 0;
25,128✔
363
  }
25,128✔
364

365
  if (instrument_stack) {
36,175✔
366
    const auto stack_count = instrumentation_context->handleStack(allocas);
13,657✔
367
    NumInstrumentedAlloca += stack_count;
13,657✔
368
    mod |= stack_count > 0;
13,657✔
369
  }
13,657✔
370

371
  return mod;
36,175✔
372
}
163,784✔
373
};  // namespace typeart::pass
374

375
class LegacyTypeArtPass : public llvm::ModulePass {
376
 private:
377
  TypeArtPass pass_impl_;
378

379
 public:
380
  static char ID;  // NOLINT
381

382
  LegacyTypeArtPass() : ModulePass(ID) {};
×
383

384
  bool doInitialization(llvm::Module&) override;
385

386
  bool runOnModule(llvm::Module& module) override;
387

388
  bool doFinalization(llvm::Module&) override;
389

390
  ~LegacyTypeArtPass() override = default;
×
391
};
392

393
bool LegacyTypeArtPass::doInitialization(llvm::Module& m) {
×
394
  return pass_impl_.doInitialization(m);
×
395
}
396

397
bool LegacyTypeArtPass::runOnModule(llvm::Module& module) {
×
398
  bool changed{false};
×
399
  changed |= pass_impl_.runOnModule(module);
×
400
  return changed;
×
401
}
402

403
bool LegacyTypeArtPass::doFinalization(llvm::Module&) {
×
404
  return pass_impl_.doFinalization();
×
405
  ;
406
}
407

408
}  // namespace typeart::pass
409

410
//.....................
411
// New PM
412
//.....................
413
llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() {
5,139✔
414
  using namespace llvm;
415
  return {
10,278✔
416
    LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
10,278✔
417
      pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) {
5,742✔
418
        auto parameters = typeart::util::pass::parsePassParameters(
603✔
419
            typeart::config::pass::parse_typeart_config, "typeart<heap;stats;type-serialization=hybrid>", "typeart");
603✔
420
        if (!parameters) {
603✔
421
          LOG_FATAL("Error parsing heap params: " << parameters.takeError())
×
422
          return;
×
423
        }
424
        MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
603✔
425
      });
603✔
426
#if LLVM_VERSION_MAJOR > 19
427
      pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel, ThinOrFullLTOPhase) {
2,430✔
428
#else
429
      pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) {
3,312✔
430
#endif
431
        auto parameters = typeart::util::pass::parsePassParameters(
603✔
432
            typeart::config::pass::parse_typeart_config, "typeart<no-heap;stack;stats;type-serialization=hybrid>",
603✔
433
            "typeart");
603✔
434
        if (!parameters) {
603✔
435
          LOG_FATAL("Error parsing stack params: " << parameters.takeError())
×
436
          return;
×
437
        }
438
        MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
603✔
439
      });
603✔
440
      pass_builder.registerPipelineParsingCallback([](StringRef name, ModulePassManager& module_pm,
14,211✔
441
                                                      ArrayRef<PassBuilder::PipelineElement>) {
442
        if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) {
9,072✔
443
          auto parameters =
444
              typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, name, "typeart");
9,072✔
445
          if (!parameters) {
9,072✔
446
            LOG_FATAL("Error parsing params: " << parameters.takeError())
×
447
            return false;
×
448
          }
449
          module_pm.addPass(typeart::pass::TypeArtPass(parameters.get()));
9,072✔
450
          return true;
9,072✔
451
        }
9,072✔
452
        LOG_FATAL("Not a valid parametrized pass name: " << name)
×
453
        return false;
×
454
      });
9,072✔
455
    }
5,139✔
456
  };
457
}
458

459
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
5,139✔
460
  return getTypeartPassPluginInfo();
5,139✔
461
}
462

463
//.....................
464
// Old PM
465
//.....................
466
char typeart::pass::LegacyTypeArtPass::ID = 0;  // NOLINT
467

468
static RegisterPass<typeart::pass::LegacyTypeArtPass> x("typeart", "TypeArt Pass");  // NOLINT
5,139✔
469

470
ModulePass* createTypeArtPass() {
×
471
  return new typeart::pass::LegacyTypeArtPass();
×
472
}
473

474
extern "C" void AddTypeArtPass(LLVMPassManagerRef pass_manager) {
×
475
  unwrap(pass_manager)->add(createTypeArtPass());
×
476
}
×
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