• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
You are now the owner of this repo.

tudasc / TypeART / 25217596666

01 May 2026 02:13PM UTC coverage: 89.815% (-0.4%) from 90.246%
25217596666

Pull #188

github

web-flow
Merge 54cee4245 into 278119205
Pull Request #188: GPU memory allocation support

238 of 298 new or added lines in 20 files covered. (79.87%)

1 existing line in 1 file now uncovered.

5106 of 5685 relevant lines covered (89.82%)

45206.0 hits per line

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

85.25
/lib/passes/TypeARTPass.cpp
1
// TypeART library
2
//
3
// Copyright (c) 2017-2026 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/CudaUtil.h"
29
#include "support/GpuUtil.h"
30
#include "support/Logger.h"
31
#include "support/ModuleDumper.h"
32
#include "support/Table.h"
33
#include "support/Util.h"
34
#include "typegen/TypeGenerator.h"
1,971✔
35

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

57
#include <cassert>
58
#include <cstddef>
59
#include <llvm/ADT/DenseSet.h>
60
#include <llvm/Config/llvm-config.h>
61
#include <llvm/IR/Constant.h>
62
#include <llvm/Support/Error.h>
5,913✔
63
#include <memory>
1,971✔
64
#include <optional>
65
#include <sstream>
66
#include <string>
67
#include <utility>
68

69
namespace llvm {
70
class BasicBlock;
71
}  // namespace llvm
72

73
using namespace llvm;
74

75
extern llvm::cl::OptionCategory typeart_category;
76

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

83
#define DEBUG_TYPE "typeart"
84

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

90
namespace typeart::pass {
91

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

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

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

116
  const config::Configuration& configuration() const {
186,486✔
117
    return *pass_config;
186,486✔
118
  }
119

120
 public:
121
  TypeArtPass() = default;
×
122
  explicit TypeArtPass(config::TypeARTConfigOptions opts) : pass_opts(opts) {
11,858✔
123
    // LOG_INFO("Created with \n" << opts)
124
  }
11,858✔
125

126
  bool doInitialization(Module& m) {
6,610✔
127
    auto config_file_path = get_configuration_file_path();
6,610✔
128

129
    const auto init = config_file_path.has_value()
6,610✔
130
                          ? config::TypeARTConfigInit{config_file_path.value()}
35✔
131
                          : config::TypeARTConfigInit{{}, config::TypeARTConfigInit::FileConfigurationMode::Empty};
3,885✔
132

133
    auto typeart_config = [&](const auto& init_value) {
10,530✔
134
      if (init_value.mode == config::TypeARTConfigInit::FileConfigurationMode::Empty) {
6,610✔
135
        return config::make_typeart_configuration_from_opts(pass_opts.value_or(config::TypeARTConfigOptions{}));
26,300✔
136
      }
137
      return config::make_typeart_configuration(init_value);
35✔
138
    }(init);
6,610✔
139

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

148
    meminst_finder = analysis::create_finder(configuration());
3,913✔
149

150
    const std::string types_file =
151
        configuration().getValueOr(config::ConfigStdArgs::types, {config::ConfigStdArgValues::types});
3,913✔
152

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

157
    LOG_DEBUG("Propagating type infos.");
158
    const auto [loaded, error] = typeManager->load();
3,913✔
159
    if (loaded) {
6,603✔
160
      LOG_DEBUG("Existing type configuration successfully loaded from " << types_file);
161
    } else {
1,518✔
162
      LOG_DEBUG("No valid existing type configuration found: " << types_file << ". Reason: " << error.message());
163
    }
164

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

169
    declareInstrumentationFunctions(m);
3,913✔
170
    {
171
      auto type_id_handler = get_type_id_handler(m, &typeManager->getTypeDatabase(), configuration(), functions.get());
6,603✔
172
      // const bool heap   = configuration()[config::ConfigStdArgs::heap];
173
      if (has_cu_types) {
6,603✔
174
        LOG_DEBUG("Registering compilation unit types list")
175
        type_id_handler->registerModule(mdata);
2,328✔
176
      }
2,328✔
177

178
      auto arg_collector =
179
          std::make_unique<MemOpArgCollector>(configuration(), typeManager.get(), instrumentation_helper);
6,603✔
180
      // const bool instrument_stack_lifetime = configuration()[config::ConfigStdArgs::stack_lifetime];
181
      auto cb_provider    = make_callback_inserter(configuration(), std::move(type_id_handler), functions.get());
6,603✔
182
      auto mem_instrument = std::make_unique<MemOpInstrumentation>(configuration(), functions.get(),
13,206✔
183
                                                                   instrumentation_helper, std::move(cb_provider));
6,603✔
184

185
      instrumentation_context =
6,603✔
186
          std::make_unique<InstrumentationContext>(std::move(arg_collector), std::move(mem_instrument));
6,603✔
187
    }
3,913✔
188
    return true;
189
  }
3,913✔
190

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

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

217
  void declareInstrumentationFunctions(Module& m) {
6,603✔
218
    functions = declare_instrumentation_functions(m, configuration());
6,603✔
219
  }
6,603✔
220

221
  void printStats(llvm::raw_ostream& out){
6,531✔
222
#if LLVM_VERSION_MAJOR < 22
223
      const auto scope_exit_cleanup_counter = llvm::make_scope_exit([&]() {
10,882✔
224
#else
225
      llvm::scope_exit scope_exit_cleanup_counter([&]() {
2,180✔
226
#endif
227
        NumInstrumentedAlloca  = 0;
6,531✔
228
        NumInstrumentedFrees   = 0;
6,531✔
229
        NumInstrumentedGlobal  = 0;
6,531✔
230
        NumInstrumentedMallocs = 0;
6,531✔
231
      });
6,531✔
232
  meminst_finder->printStats(out);
6,531✔
233

234
  const auto get_ta_mode = [&]() {
13,062✔
235
    const bool heap   = configuration()[config::ConfigStdArgs::heap];
6,531✔
236
    const bool stack  = configuration()[config::ConfigStdArgs::stack];
6,531✔
237
    const bool global = configuration()[config::ConfigStdArgs::global];
6,531✔
238

239
    if (heap) {
6,531✔
240
      if (stack) {
4,471✔
241
        return " [Heap & Stack]";
1,104✔
242
      }
243
      return " [Heap]";
3,367✔
244
    }
245

246
    if (stack) {
2,060✔
247
      return " [Stack]";
2,060✔
248
    }
249

250
    if (global) {
×
251
      return " [Global]";
×
252
    }
253

254
    LOG_ERROR("Did not find heap or stack, or combination thereof!");
×
255
    assert((heap || stack || global) && "Needs stack, heap, global or combination thereof");
×
256
    return " [Unknown]";
×
257
  };
6,531✔
258

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

267
  std::ostringstream stream;
6,531✔
268
  stats.print(stream);
6,531✔
269
  out << stream.str();
6,531✔
270
}
6,531✔
271

272
llvm::PreservedAnalyses
273
run(llvm::Module& m, llvm::ModuleAnalysisManager&) {
6,603✔
274
  if (gpu::is_device_module(m)) {
6,603✔
275
    LOG_DEBUG("Skipping GPU device module: " << m.getName());
NEW
276
    return llvm::PreservedAnalyses::all();
×
277
  }
278
  bool changed{false};
6,603✔
279
  changed |= doInitialization(m);
6,603✔
280
  const bool heap = configuration()[config::ConfigStdArgs::heap];  // Must happen after doInit
6,603✔
281
  dump_module(m, heap ? util::module::ModulePhase::kBase : util::module::ModulePhase::kOpt);
6,603✔
282
  changed |= runOnModule(m);
6,603✔
283
  dump_module(m, heap ? util::module::ModulePhase::kHeap : util::module::ModulePhase::kStack);
6,603✔
284
  changed |= doFinalization();
6,603✔
285
  return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
6,603✔
286
}
6,603✔
287

288
bool runOnModule(llvm::Module& m) {
6,603✔
289
  meminst_finder->runOnModule(m);
6,603✔
290
  const bool instrument_global = configuration()[config::ConfigStdArgs::global];
6,603✔
291
  bool globals_were_instrumented{false};
6,603✔
292
  if (instrument_global) {
6,603✔
293
    // declareInstrumentationFunctions(m);
294

295
    const auto& globalsList = meminst_finder->getModuleGlobals();
3,160✔
296
    if (!globalsList.empty()) {
3,160✔
297
      const auto global_count = instrumentation_context->handleGlobal(globalsList);
1,240✔
298
      NumInstrumentedGlobal += global_count;
1,240✔
299
      globals_were_instrumented = global_count > 0;
1,240✔
300
    }
1,240✔
301
  }
3,160✔
302

303
  llvm::DenseSet<const llvm::Constant*> tor_funcs;
6,603✔
304
  {
305
    const auto collect_funcs = [&tor_funcs](const auto* constant) -> bool {
8,327✔
306
      if (llvm::isa<llvm::Function>(constant)) {
1,724✔
307
        tor_funcs.insert(constant);
1,724✔
308
      }
1,724✔
309
      return false;
1,724✔
310
    };
311

312
    util::for_each_cdtor("llvm.global_ctors", m, collect_funcs);
6,603✔
313
    util::for_each_cdtor("llvm.global_dtors", m, collect_funcs);
6,603✔
314
  }
315

316
  const auto instrumented_function = llvm::count_if(m.functions(), [&](auto& f) {
224,429✔
317
                                       if (tor_funcs.contains(&f)) {
211,223✔
318
                                         LOG_DEBUG("Function is in LLVM global ctor or dtor " << f.getName())
319
                                         return false;
1,724✔
320
                                       }
321
                                       return runOnFunc(f);
209,499✔
322
                                     }) > 0;
217,826✔
323
  return instrumented_function || globals_were_instrumented;
6,603✔
324
}
6,603✔
325

326
bool runOnFunc(llvm::Function& f) {
209,499✔
327
  using namespace typeart;
328

329
  if (f.isDeclaration() || util::starts_with_any_of(f.getName(), "__typeart", "typeart", "__sanitizer", "__tysan")) {
209,499✔
330
    return false;
166,994✔
331
  }
332

333
  if (cuda::is_cuda_helper_function(f)) {
42,505✔
NEW
334
    return false;
×
335
  }
336

337
  if (!meminst_finder->hasFunctionData(f)) {
42,505✔
338
    LOG_WARNING("No allocation data could be retrieved for function: " << f.getName());
×
339
    return false;
×
340
  }
341

342
  LOG_DEBUG("Running on function: " << f.getName())
343

344
  // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are
345
  // nullpointer/invalidated
346
  // declareInstrumentationFunctions(*f.getParent());
347

348
  bool mod{false};
42,505✔
349
//  auto& c = f.getContext();
350
#if LLVM_VERSION_MAJOR > 19
351
  DataLayout dl(f.getParent()->getDataLayout());
17,201✔
352
#else
353
  DataLayout dl(f.getParent());
25,304✔
354
#endif
355

356
  const auto& fData   = meminst_finder->getFunctionData(f);
42,505✔
357
  const auto& mallocs = fData.mallocs;
42,505✔
358
  const auto& allocas = fData.allocas;
42,505✔
359
  const auto& frees   = fData.frees;
42,505✔
360

361
  const bool instrument_heap  = configuration()[config::ConfigStdArgs::heap];
42,505✔
362
  const bool instrument_stack = configuration()[config::ConfigStdArgs::stack];
42,505✔
363

364
  if (instrument_heap) {
42,505✔
365
    // instrument collected calls of bb:
366
    const auto heap_count = instrumentation_context->handleHeap(mallocs);
29,457✔
367
    const auto free_count = instrumentation_context->handleFree(frees);
29,457✔
368

369
    NumInstrumentedMallocs += heap_count;
29,457✔
370
    NumInstrumentedFrees += free_count;
29,457✔
371

372
    mod |= heap_count > 0 || free_count > 0;
29,457✔
373
  }
29,457✔
374

375
  if (instrument_stack) {
42,505✔
376
    const auto stack_count = instrumentation_context->handleStack(allocas);
16,042✔
377
    NumInstrumentedAlloca += stack_count;
16,042✔
378
    mod |= stack_count > 0;
16,042✔
379
  }
16,042✔
380

381
  return mod;
42,505✔
382
}
209,499✔
383
};  // namespace typeart::pass
384

385
class LegacyTypeArtPass : public llvm::ModulePass {
386
 private:
387
  TypeArtPass pass_impl_;
388

389
 public:
390
  static char ID;  // NOLINT
391

392
  LegacyTypeArtPass() : ModulePass(ID) {};
×
393

394
  bool doInitialization(llvm::Module&) override;
395

396
  bool runOnModule(llvm::Module& module) override;
397

398
  bool doFinalization(llvm::Module&) override;
399

400
  ~LegacyTypeArtPass() override = default;
×
401
};
402

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

407
bool LegacyTypeArtPass::runOnModule(llvm::Module& module) {
×
408
  bool changed{false};
×
409
  changed |= pass_impl_.runOnModule(module);
×
410
  return changed;
×
411
}
412

413
bool LegacyTypeArtPass::doFinalization(llvm::Module&) {
×
414
  return pass_impl_.doFinalization();
×
415
  ;
416
}
417

418
}  // namespace typeart::pass
419

420
//.....................
421
// New PM
422
//.....................
423
llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() {
5,929✔
424
  using namespace llvm;
425
  return {
11,858✔
426
    LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
11,858✔
427
      pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) {
6,617✔
428
        auto parameters = typeart::util::pass::parsePassParameters(
688✔
429
            typeart::config::pass::parse_typeart_config, "typeart<heap;stats;type-serialization=hybrid>", "typeart");
688✔
430
        if (!parameters) {
688✔
431
          LOG_FATAL("Error parsing heap params: " << parameters.takeError())
×
432
          return;
×
433
        }
434
        MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
688✔
435
      });
688✔
436
#if LLVM_VERSION_MAJOR > 19
437
      pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel, ThinOrFullLTOPhase) {
2,726✔
438
#else
439
      pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) {
3,891✔
440
#endif
441
        auto parameters = typeart::util::pass::parsePassParameters(
688✔
442
            typeart::config::pass::parse_typeart_config, "typeart<no-heap;stack;stats;type-serialization=hybrid>",
688✔
443
            "typeart");
688✔
444
        if (!parameters) {
688✔
445
          LOG_FATAL("Error parsing stack params: " << parameters.takeError())
×
446
          return;
×
447
        }
448
        MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
688✔
449
      });
688✔
450
      pass_builder.registerPipelineParsingCallback([](StringRef name, ModulePassManager& module_pm,
16,411✔
451
                                                      ArrayRef<PassBuilder::PipelineElement>) {
452
        if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) {
10,482✔
453
          auto parameters =
454
              typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config, name, "typeart");
10,482✔
455
          if (!parameters) {
10,482✔
456
            LOG_FATAL("Error parsing params: " << parameters.takeError())
×
457
            return false;
×
458
          }
459
          module_pm.addPass(typeart::pass::TypeArtPass(parameters.get()));
10,482✔
460
          return true;
10,482✔
461
        }
10,482✔
462
        LOG_FATAL("Not a valid parametrized pass name: " << name)
×
463
        return false;
×
464
      });
10,482✔
465
    }
5,929✔
466
  };
467
}
468

469
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
5,929✔
470
  return getTypeartPassPluginInfo();
5,929✔
471
}
472

473
//.....................
474
// Old PM
475
//.....................
476
char typeart::pass::LegacyTypeArtPass::ID = 0;  // NOLINT
477

478
static RegisterPass<typeart::pass::LegacyTypeArtPass> x("typeart", "TypeArt Pass");  // NOLINT
5,929✔
479

480
ModulePass* createTypeArtPass() {
×
481
  return new typeart::pass::LegacyTypeArtPass();
×
482
}
483

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