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

tudasc / TypeART / 19190635272

08 Nov 2025 08:36AM UTC coverage: 90.337% (+1.4%) from 88.924%
19190635272

Pull #173

github

web-flow
Merge e6d559e53 into f4d0ba95b
Pull Request #173: Global variables as type descriptors

597 of 629 new or added lines in 25 files covered. (94.91%)

2 existing lines in 2 files now uncovered.

4768 of 5278 relevant lines covered (90.34%)

241911.16 hits per line

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

84.82
/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,688✔
35
#include "llvm/ADT/STLExtras.h"
1,688✔
36
#include "llvm/ADT/ScopeExit.h"
1,688✔
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
#include "llvm/Passes/PassPlugin.h"
1,688✔
48
#include "llvm/Support/CommandLine.h"
1,712✔
49
#include "llvm/Support/raw_ostream.h"
50

51
#include <cassert>
52
#include <cstddef>
53
#include <llvm/Config/llvm-config.h>
54
#include <llvm/Support/Error.h>
55
#include <memory>
56
#include <optional>
57
#include <sstream>
58
#include <string>
59
#include <utility>
60

61
namespace llvm {
5,064✔
62
class BasicBlock;
1,688✔
63
}  // namespace llvm
64

65
using namespace llvm;
66

67
extern llvm::cl::OptionCategory typeart_category;
68

69
static cl::opt<std::string> cl_typeart_configuration_file(
3,012✔
70
    "typeart-config", cl::init(""),
3,012✔
71
    cl::desc(
3,012✔
72
        "Location of the configuration file to configure the TypeART pass. Commandline arguments are prioritized."),
3,012✔
73
    cl::cat(typeart_category));
6,024✔
74

75
#define DEBUG_TYPE "typeart"
76

77
ALWAYS_ENABLED_STATISTIC(NumInstrumentedMallocs, "Number of instrumented mallocs");
78
ALWAYS_ENABLED_STATISTIC(NumInstrumentedFrees, "Number of instrumented frees");
79
ALWAYS_ENABLED_STATISTIC(NumInstrumentedAlloca, "Number of instrumented (stack) allocas");
80
ALWAYS_ENABLED_STATISTIC(NumInstrumentedGlobal, "Number of instrumented globals");
81

82
namespace typeart::pass {
83

84
std::optional<std::string> get_configuration_file_path() {
3,318✔
85
  if (!cl_typeart_configuration_file.empty()) {
3,318✔
86
    LOG_DEBUG("Using cl::opt for config file " << cl_typeart_configuration_file.getValue());
87
    return cl_typeart_configuration_file.getValue();
×
88
  }
89
  const char* config_file = std::getenv("TYPEART_CONFIG_FILE");
3,318✔
90
  if (config_file != nullptr) {
3,318✔
91
    LOG_DEBUG("Using env var for types file " << config_file)
92
    return std::string{config_file};
30✔
93
  }
94
  LOG_INFO("No configuration file set.")
3,288✔
95
  return {};
3,288✔
96
}
3,318✔
97

98
class TypeArtPass : public llvm::PassInfoMixin<TypeArtPass> {
99
  std::optional<config::TypeARTConfigOptions> pass_opts{std::nullopt};
×
100
  std::unique_ptr<config::Configuration> pass_config;
101

102
  std::unique_ptr<analysis::MemInstFinder> meminst_finder;
103
  std::unique_ptr<TypeGenerator> typeManager;
104
  InstrumentationHelper instrumentation_helper;
105
  std::unique_ptr<TAFunctionQuery> functions;
106
  std::unique_ptr<InstrumentationContext> instrumentation_context;
107

108
  const config::Configuration& configuration() const {
96,910✔
109
    return *pass_config;
96,910✔
110
  }
111

112
 public:
113
  TypeArtPass() = default;
×
114
  explicit TypeArtPass(config::TypeARTConfigOptions opts) : pass_opts(opts) {
6,024✔
115
    // LOG_INFO("Created with \n" << opts)
116
  }
6,024✔
117

118
  bool doInitialization(Module& m) {
3,312✔
119
    auto config_file_path = get_configuration_file_path();
3,312✔
120

121
    const auto init = config_file_path.has_value()
3,312✔
122
                          ? config::TypeARTConfigInit{config_file_path.value()}
30✔
123
                          : config::TypeARTConfigInit{{}, config::TypeARTConfigInit::FileConfigurationMode::Empty};
1,688✔
124

125
    auto typeart_config = [&](const auto& init_value) {
5,030✔
126
      if (init_value.mode == config::TypeARTConfigInit::FileConfigurationMode::Empty) {
3,312✔
127
        return config::make_typeart_configuration_from_opts(pass_opts.value_or(config::TypeARTConfigOptions{}));
13,128✔
128
      }
129
      return config::make_typeart_configuration(init_value);
30✔
130
    }(init);
3,312✔
131

132
    if (typeart_config) {
3,312✔
133
      LOG_INFO("Emitting TypeART configuration content\n" << typeart_config.get()->getOptions())
3,306✔
134
      pass_config = std::move(*typeart_config);
3,306✔
135
    } else {
3,306✔
136
      LOG_FATAL("Could not load TypeART configuration.")
6✔
137
      std::exit(EXIT_FAILURE);
6✔
138
    }
139

140
    meminst_finder = analysis::create_finder(configuration());
1,712✔
141

142
    const std::string types_file =
143
        configuration().getValueOr(config::ConfigStdArgs::types, {config::ConfigStdArgValues::types});
1,712✔
144

145
    const TypegenImplementation typesgen_parser =
1,712✔
146
        configuration().getValueOr(config::ConfigStdArgs::typegen, {config::ConfigStdArgValues::typegen});
1,712✔
147
    typeManager = make_typegen(types_file, typesgen_parser);
1,712✔
148

149
    LOG_DEBUG("Propagating type infos.");
150
    const auto [loaded, error] = typeManager->load();
1,712✔
151
    if (loaded) {
3,306✔
152
      LOG_DEBUG("Existing type configuration successfully loaded from " << types_file);
153
    } else {
1,008✔
154
      LOG_DEBUG("No valid existing type configuration found: " << types_file << ". Reason: " << error.message());
155
    }
156

157
    instrumentation_helper.setModule(m);
1,712✔
158
    ModuleData mdata{&m};
1,712✔
159
    const auto has_cu_types = typeManager->registerModule(mdata);
1,712✔
160

161
    declareInstrumentationFunctions(m);
1,712✔
162
    {
163
      auto type_id_handler = get_type_id_handler(m, &typeManager->getTypeDatabase(), configuration(), functions.get());
3,306✔
164
      // const bool heap   = configuration()[config::ConfigStdArgs::heap];
165
      if (has_cu_types) {
3,306✔
166
        LOG_DEBUG("Registering compilation unit types list")
167
        type_id_handler->registerModule(mdata);
986✔
168
      }
986✔
169

170
      auto arg_collector =
171
          std::make_unique<MemOpArgCollector>(configuration(), typeManager.get(), instrumentation_helper);
3,306✔
172
      // const bool instrument_stack_lifetime = configuration()[config::ConfigStdArgs::stack_lifetime];
173
      auto cb_provider    = make_callback_inserter(configuration(), std::move(type_id_handler), functions.get());
3,306✔
174
      auto mem_instrument = std::make_unique<MemOpInstrumentation>(configuration(), functions.get(),
6,612✔
175
                                                                   instrumentation_helper, std::move(cb_provider));
3,306✔
176

177
      instrumentation_context =
3,306✔
178
          std::make_unique<InstrumentationContext>(std::move(arg_collector), std::move(mem_instrument));
3,306✔
179
    }
1,712✔
180
    return true;
181
  }
1,712✔
182

183
  bool doFinalization() {
3,306✔
184
    /*
185
     * Persist the accumulated type definition information for this module.
186
     */
187
    // TODO: inline/hybrid types not supported in non-opaque mode
188
    const bool emit_type_file_always     = bool(LLVM_VERSION_MAJOR < 15);
3,306✔
189
    TypeSerializationImplementation mode = configuration()[config::ConfigStdArgs::type_serialization];
3,306✔
190
    if (emit_type_file_always || mode == TypeSerializationImplementation::FILE) {
1,594✔
191
      const std::string types_file = configuration()[config::ConfigStdArgs::types];
1,952✔
192
      LOG_DEBUG("Writing type file to " << types_file);
193

194
      const auto [stored, error] = typeManager->store();
1,952✔
195
      if (stored) {
1,952✔
196
        LOG_DEBUG("Success!");
197
      } else {
1,952✔
NEW
198
        LOG_FATAL("Failed writing type config to " << types_file << ". Reason: " << error.message());
×
199
      }
200
    }
1,952✔
201
    const bool print_stats = configuration()[config::ConfigStdArgs::stats];
3,306✔
202
    if (print_stats) {
3,306✔
203
      auto& out = llvm::errs();
3,270✔
204
      printStats(out);
3,270✔
205
    }
3,270✔
206
    return false;
3,306✔
207
  }
208

209
  void declareInstrumentationFunctions(Module& m) {
3,306✔
210
    functions = declare_instrumentation_functions(m, configuration());
3,306✔
211
  }
3,306✔
212

213
  void printStats(llvm::raw_ostream& out) {
3,270✔
214
    const auto scope_exit_cleanup_counter = llvm::make_scope_exit([&]() {
6,540✔
215
      NumInstrumentedAlloca  = 0;
3,270✔
216
      NumInstrumentedFrees   = 0;
3,270✔
217
      NumInstrumentedGlobal  = 0;
3,270✔
218
      NumInstrumentedMallocs = 0;
3,270✔
219
    });
3,270✔
220
    meminst_finder->printStats(out);
3,270✔
221

222
    const auto get_ta_mode = [&]() {
6,540✔
223
      const bool heap   = configuration()[config::ConfigStdArgs::heap];
3,270✔
224
      const bool stack  = configuration()[config::ConfigStdArgs::stack];
3,270✔
225
      const bool global = configuration()[config::ConfigStdArgs::global];
3,270✔
226

227
      if (heap) {
3,270✔
228
        if (stack) {
2,245✔
229
          return " [Heap & Stack]";
597✔
230
        }
231
        return " [Heap]";
1,648✔
232
      }
233

234
      if (stack) {
1,025✔
235
        return " [Stack]";
1,025✔
236
      }
237

238
      if (global) {
×
239
        return " [Global]";
×
240
      }
241

242
      LOG_ERROR("Did not find heap or stack, or combination thereof!");
×
243
      assert((heap || stack || global) && "Needs stack, heap, global or combination thereof");
×
244
      return " [Unknown]";
×
245
    };
3,270✔
246

247
    Table stats("TypeArtPass");
3,270✔
248
    stats.wrap_header_ = true;
3,270✔
249
    stats.title_ += get_ta_mode();
3,270✔
250
    stats.put(Row::make("Malloc", NumInstrumentedMallocs.getValue()));
3,270✔
251
    stats.put(Row::make("Free", NumInstrumentedFrees.getValue()));
3,270✔
252
    stats.put(Row::make("Alloca", NumInstrumentedAlloca.getValue()));
3,270✔
253
    stats.put(Row::make("Global", NumInstrumentedGlobal.getValue()));
3,270✔
254

255
    std::ostringstream stream;
3,270✔
256
    stats.print(stream);
3,270✔
257
    out << stream.str();
3,270✔
258
  }
3,270✔
259

260
  llvm::PreservedAnalyses run(llvm::Module& m, llvm::ModuleAnalysisManager&) {
3,306✔
261
    bool changed{false};
3,306✔
262
    changed |= doInitialization(m);
3,306✔
263
    const bool heap = configuration()[config::ConfigStdArgs::heap];  // Must happen after doInit
3,306✔
264
    dump_module(m, heap ? util::module::ModulePhase::kBase : util::module::ModulePhase::kOpt);
3,306✔
265
    changed |= runOnModule(m);
3,306✔
266
    dump_module(m, heap ? util::module::ModulePhase::kHeap : util::module::ModulePhase::kStack);
3,306✔
267
    changed |= doFinalization();
3,306✔
268
    return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
3,306✔
269
  }
270

271
  bool runOnModule(llvm::Module& m) {
3,306✔
272
    meminst_finder->runOnModule(m);
3,306✔
273
    const bool instrument_global = configuration()[config::ConfigStdArgs::global];
3,306✔
274
    bool globals_were_instrumented{false};
3,306✔
275
    if (instrument_global) {
3,306✔
276
      // declareInstrumentationFunctions(m);
277

278
      const auto& globalsList = meminst_finder->getModuleGlobals();
1,628✔
279
      if (!globalsList.empty()) {
1,628✔
280
        const auto global_count = instrumentation_context->handleGlobal(globalsList);
658✔
281
        NumInstrumentedGlobal += global_count;
658✔
282
        globals_were_instrumented = global_count > 0;
658✔
283
      }
658✔
284
    }
1,628✔
285

286
    const auto instrumented_function = llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0;
102,016✔
287
    return instrumented_function || globals_were_instrumented;
3,306✔
288
  }
289

290
  bool runOnFunc(llvm::Function& f) {
98,710✔
291
    using namespace typeart;
292

293
    if (f.isDeclaration() || util::starts_with_any_of(f.getName(), "__typeart", "typeart")) {
98,710✔
294
      return false;
75,972✔
295
    }
296

297
    if (!meminst_finder->hasFunctionData(f)) {
22,738✔
298
      LOG_WARNING("No allocation data could be retrieved for function: " << f.getName());
×
299
      return false;
×
300
    }
301

302
    LOG_DEBUG("Running on function: " << f.getName())
303

304
    // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are
305
    // nullpointer/invalidated
306
    // declareInstrumentationFunctions(*f.getParent());
307

308
    bool mod{false};
22,738✔
309
    //  auto& c = f.getContext();
310
    DataLayout dl(f.getParent());
22,738✔
311

312
    const auto& fData   = meminst_finder->getFunctionData(f);
22,738✔
313
    const auto& mallocs = fData.mallocs;
22,738✔
314
    const auto& allocas = fData.allocas;
22,738✔
315
    const auto& frees   = fData.frees;
22,738✔
316

317
    const bool instrument_heap  = configuration()[config::ConfigStdArgs::heap];
22,738✔
318
    const bool instrument_stack = configuration()[config::ConfigStdArgs::stack];
22,738✔
319

320
    if (instrument_heap) {
22,738✔
321
      // instrument collected calls of bb:
322
      const auto heap_count = instrumentation_context->handleHeap(mallocs);
16,209✔
323
      const auto free_count = instrumentation_context->handleFree(frees);
16,209✔
324

325
      NumInstrumentedMallocs += heap_count;
16,209✔
326
      NumInstrumentedFrees += free_count;
16,209✔
327

328
      mod |= heap_count > 0 || free_count > 0;
16,209✔
329
    }
16,209✔
330

331
    if (instrument_stack) {
22,738✔
332
      const auto stack_count = instrumentation_context->handleStack(allocas);
8,260✔
333
      NumInstrumentedAlloca += stack_count;
8,260✔
334
      mod |= stack_count > 0;
8,260✔
335
    }
8,260✔
336

337
    return mod;
22,738✔
338
  }
98,710✔
339
};
340

341
class LegacyTypeArtPass : public llvm::ModulePass {
342
 private:
343
  TypeArtPass pass_impl_;
344

345
 public:
346
  static char ID;  // NOLINT
347

NEW
348
  LegacyTypeArtPass() : ModulePass(ID) {};
×
349

350
  bool doInitialization(llvm::Module&) override;
351

352
  bool runOnModule(llvm::Module& module) override;
353

354
  bool doFinalization(llvm::Module&) override;
355

356
  ~LegacyTypeArtPass() override = default;
×
357
};
358

359
bool LegacyTypeArtPass::doInitialization(llvm::Module& m) {
×
360
  return pass_impl_.doInitialization(m);
×
361
}
362

363
bool LegacyTypeArtPass::runOnModule(llvm::Module& module) {
×
364
  bool changed{false};
×
365
  changed |= pass_impl_.runOnModule(module);
×
366
  return changed;
×
367
}
368

369
bool LegacyTypeArtPass::doFinalization(llvm::Module&) {
×
370
  return pass_impl_.doFinalization();
×
371
  ;
372
}
373

374
}  // namespace typeart::pass
375

376
//.....................
377
// New PM
378
//.....................
379
llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() {
3,012✔
380
  using namespace llvm;
381
  return {LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
6,024✔
382
            pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) {
3,318✔
383
              auto parameters =
384
                  typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config,
306✔
385
                                                           "typeart<heap;stats;type-serialization=hybrid>", "typeart");
306✔
386
              if (!parameters) {
306✔
387
                LOG_FATAL("Error parsing heap params: " << parameters.takeError())
×
388
                return;
×
389
              }
390
              MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
306✔
391
            });
306✔
392
            pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) {
3,318✔
393
              auto parameters = typeart::util::pass::parsePassParameters(
306✔
394
                  typeart::config::pass::parse_typeart_config, "typeart<no-heap;stack;stats;type-serialization=hybrid>",
306✔
395
                  "typeart");
306✔
396
              if (!parameters) {
306✔
397
                LOG_FATAL("Error parsing stack params: " << parameters.takeError())
×
398
                return;
×
399
              }
400
              MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
306✔
401
            });
306✔
402
            pass_builder.registerPipelineParsingCallback(
6,024✔
403
                [](StringRef name, ModulePassManager& module_pm, ArrayRef<PassBuilder::PipelineElement>) {
8,424✔
404
                  if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) {
5,412✔
405
                    auto parameters = typeart::util::pass::parsePassParameters(
5,412✔
406
                        typeart::config::pass::parse_typeart_config, name, "typeart");
5,412✔
407
                    if (!parameters) {
5,412✔
408
                      LOG_FATAL("Error parsing params: " << parameters.takeError())
×
409
                      return false;
×
410
                    }
411
                    module_pm.addPass(typeart::pass::TypeArtPass(parameters.get()));
5,412✔
412
                    return true;
5,412✔
413
                  }
5,412✔
414
                  LOG_FATAL("Not a valid parametrized pass name: " << name)
×
415
                  return false;
×
416
                });
5,412✔
417
          }};
3,012✔
418
}
419

420
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
3,012✔
421
  return getTypeartPassPluginInfo();
3,012✔
422
}
423

424
//.....................
425
// Old PM
426
//.....................
427
char typeart::pass::LegacyTypeArtPass::ID = 0;  // NOLINT
428

429
static RegisterPass<typeart::pass::LegacyTypeArtPass> x("typeart", "TypeArt Pass");  // NOLINT
3,012✔
430

431
ModulePass* createTypeArtPass() {
×
432
  return new typeart::pass::LegacyTypeArtPass();
×
433
}
434

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