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

tudasc / TypeART / 13528988609

25 Feb 2025 07:06PM UTC coverage: 88.854% (-1.9%) from 90.735%
13528988609

Pull #163

github

web-flow
Merge e4a2d80f6 into d2e14acc5
Pull Request #163: LLVM 18 support

974 of 1122 new or added lines in 38 files covered. (86.81%)

30 existing lines in 6 files now uncovered.

4201 of 4728 relevant lines covered (88.85%)

190054.62 hits per line

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

83.61
/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/MemOpArgCollector.h"
23
#include "instrumentation/MemOpInstrumentation.h"
24
#include "instrumentation/TypeARTFunctions.h"
25
#include "support/ConfigurationBase.h"
26
#include "support/Logger.h"
27
#include "support/ModuleDumper.h"
28
#include "support/Table.h"
29
#include "support/Util.h"
30
#include "typegen/TypeGenerator.h"
31

32
#include "llvm/ADT/DenseMap.h"
33
#include "llvm/ADT/STLExtras.h"
1,654✔
34
#include "llvm/ADT/ScopeExit.h"
1,654✔
35
#include "llvm/ADT/SmallVector.h"
1,654✔
36
#include "llvm/ADT/Statistic.h"
37
#include "llvm/ADT/StringRef.h"
38
#include "llvm/IR/DataLayout.h"
39
#include "llvm/IR/Function.h"
40
#include "llvm/IR/LegacyPassManager.h"
41
#include "llvm/IR/Module.h"
42
#include "llvm/IR/PassManager.h"
43
#include "llvm/Pass.h"
44
#include "llvm/Passes/PassBuilder.h"
45
#include "llvm/Passes/PassPlugin.h"
46
#include "llvm/Support/CommandLine.h"
1,654✔
47
#include "llvm/Support/raw_ostream.h"
48

49
#include <cassert>
50
#include <cstddef>
51
#include <llvm/Support/Error.h>
52
#include <optional>
53
#include <sstream>
54
#include <string>
55
#include <utility>
4,962✔
56

1,654✔
57
namespace llvm {
58
class BasicBlock;
59
}  // namespace llvm
60

61
using namespace llvm;
62

63
extern llvm::cl::OptionCategory typeart_category;
64

65
static cl::opt<std::string> cl_typeart_configuration_file(
2,149✔
66
    "typeart-config", cl::init(""),
2,149✔
67
    cl::desc(
2,149✔
68
        "Location of the configuration file to configure the TypeART pass. Commandline arguments are prioritized."),
2,149✔
69
    cl::cat(typeart_category));
4,298✔
70

71
#define DEBUG_TYPE "typeart"
72

73
ALWAYS_ENABLED_STATISTIC(NumInstrumentedMallocs, "Number of instrumented mallocs");
74
ALWAYS_ENABLED_STATISTIC(NumInstrumentedFrees, "Number of instrumented frees");
75
ALWAYS_ENABLED_STATISTIC(NumInstrumentedAlloca, "Number of instrumented (stack) allocas");
76
ALWAYS_ENABLED_STATISTIC(NumInstrumentedGlobal, "Number of instrumented globals");
77

78
namespace typeart::pass {
79

80
std::optional<std::string> get_configuration_file_path() {
2,356✔
81
  if (!cl_typeart_configuration_file.empty()) {
2,356✔
82
    LOG_DEBUG("Using cl::opt for config file " << cl_typeart_configuration_file.getValue());
UNCOV
83
    return cl_typeart_configuration_file.getValue();
×
84
  }
85
  const char* config_file = std::getenv("TYPEART_CONFIG_FILE");
2,356✔
86
  if (config_file != nullptr) {
2,356✔
87
    LOG_DEBUG("Using env var for types file " << config_file)
88
    return std::string{config_file};
×
89
  }
90
  LOG_INFO("No configuration file set.")
2,356✔
91
  return {};
2,356✔
92
}
2,356✔
93

94
class TypeArtPass : public llvm::PassInfoMixin<TypeArtPass> {
NEW
95
  std::optional<config::TypeARTConfigOptions> pass_opts{std::nullopt};
×
96
  std::unique_ptr<config::Configuration> pass_config;
97

98
  struct TypeArtFunc {
99
    const std::string name;
100
    llvm::Value* f{nullptr};
101
  };
102

103
  TypeArtFunc typeart_alloc{"__typeart_alloc"};
4,298✔
104
  TypeArtFunc typeart_alloc_global{"__typeart_alloc_global"};
4,298✔
105
  TypeArtFunc typeart_alloc_stack{"__typeart_alloc_stack"};
4,298✔
106
  TypeArtFunc typeart_free{"__typeart_free"};
4,298✔
107
  TypeArtFunc typeart_leave_scope{"__typeart_leave_scope"};
4,298✔
108

109
  TypeArtFunc typeart_alloc_omp        = typeart_alloc;
4,298✔
110
  TypeArtFunc typeart_alloc_stacks_omp = typeart_alloc_stack;
4,298✔
111
  TypeArtFunc typeart_free_omp         = typeart_free;
4,298✔
112
  TypeArtFunc typeart_leave_scope_omp  = typeart_leave_scope;
4,298✔
113

114
  std::unique_ptr<analysis::MemInstFinder> meminst_finder;
115
  std::unique_ptr<TypeGenerator> typeManager;
116
  InstrumentationHelper instrumentation_helper;
117
  TAFunctions functions;
118
  std::unique_ptr<InstrumentationContext> instrumentation_context;
119

120
  const config::Configuration& configuration() const {
63,459✔
121
    return *pass_config;
63,459✔
122
  }
123

124
 public:
NEW
125
  TypeArtPass() = default;
×
126
  explicit TypeArtPass(config::TypeARTConfigOptions opts) : pass_opts(opts) {
8,596✔
127
    // LOG_INFO("Created with \n" << opts)
128
  }
4,298✔
129

130
  bool doInitialization(Module& m) {
2,350✔
131
    auto config_file_path = get_configuration_file_path();
2,350✔
132

133
    const auto init = config_file_path.has_value()
2,350✔
NEW
134
                          ? config::TypeARTConfigInit{config_file_path.value()}
×
135
                          : config::TypeARTConfigInit{{}, config::TypeARTConfigInit::FileConfigurationMode::Empty};
1,654✔
136

137
    auto typeart_config = [&](const auto& init_value) {
4,004✔
138
      if (init_value.mode == config::TypeARTConfigInit::FileConfigurationMode::Empty) {
2,350✔
139
        return config::make_typeart_configuration_from_opts(pass_opts.value_or(config::TypeARTConfigOptions{}));
9,400✔
140
      }
NEW
141
      return config::make_typeart_configuration(init_value);
×
142
    }(init);
2,350✔
143

144
    if (typeart_config) {
2,350✔
145
      LOG_INFO("Emitting TypeART configuration content\n" << typeart_config.get()->getOptions())
2,350✔
146
      pass_config = std::move(*typeart_config);
2,350✔
147
    } else {
2,350✔
NEW
148
      LOG_FATAL("Could not load TypeART configuration.")
×
UNCOV
149
      std::exit(EXIT_FAILURE);
×
150
    }
151

152
    meminst_finder = analysis::create_finder(configuration());
1,654✔
153

154
    const std::string types_file =
155
        configuration().getValueOr(config::ConfigStdArgs::types, {config::ConfigStdArgValues::types});
1,654✔
156

157
    const TypegenImplementation typesgen_parser =
1,654✔
158
        configuration().getValueOr(config::ConfigStdArgs::typegen, {config::ConfigStdArgValues::typegen});
1,654✔
159
    typeManager = make_typegen(types_file, typesgen_parser);
1,654✔
160

161
    LOG_DEBUG("Propagating type infos.");
162
    const auto [loaded, error] = typeManager->load();
1,654✔
163
    if (loaded) {
2,350✔
164
      LOG_DEBUG("Existing type configuration successfully loaded from " << types_file);
165
    } else {
1,093✔
166
      LOG_DEBUG("No valid existing type configuration found: " << types_file << ". Reason: " << error.message());
167
    }
168

169
    instrumentation_helper.setModule(m);
1,654✔
170
    ModuleData mdata{&m};
1,654✔
171
    typeManager->registerModule(mdata);
1,654✔
172

173
    auto arg_collector =
174
        std::make_unique<MemOpArgCollector>(configuration(), typeManager.get(), instrumentation_helper);
1,654✔
175
    // const bool instrument_stack_lifetime = configuration()[config::ConfigStdArgs::stack_lifetime];
176
    auto mem_instrument = std::make_unique<MemOpInstrumentation>(configuration(), functions, instrumentation_helper);
1,654✔
177
    instrumentation_context =
1,654✔
178
        std::make_unique<InstrumentationContext>(std::move(arg_collector), std::move(mem_instrument));
1,654✔
179

180
    return true;
181
  }
1,654✔
182

183
  bool doFinalization() {
2,350✔
184
    /*
185
     * Persist the accumulated type definition information for this module.
186
     */
187
    const std::string types_file = configuration()[config::ConfigStdArgs::types];
2,350✔
188
    LOG_DEBUG("Writing type file to " << types_file);
189

190
    const auto [stored, error] = typeManager->store();
2,350✔
191
    if (stored) {
2,350✔
192
      LOG_DEBUG("Success!");
193
    } else {
2,350✔
NEW
194
      LOG_FATAL("Failed writing type config to " << types_file << ". Reason: " << error.message());
×
195
    }
196

197
    const bool print_stats = configuration()[config::ConfigStdArgs::stats];
2,350✔
198
    if (print_stats) {
2,350✔
199
      auto& out = llvm::errs();
2,323✔
200
      printStats(out);
2,323✔
201
    }
2,323✔
202
    return false;
203
  }
2,350✔
204

205
  void declareInstrumentationFunctions(Module& m) {
18,841✔
206
    // Remove this return if problems come up during compilation
207
    if (typeart_alloc_global.f != nullptr && typeart_alloc_stack.f != nullptr && typeart_alloc.f != nullptr &&
35,332✔
208
        typeart_free.f != nullptr && typeart_leave_scope.f != nullptr) {
16,491✔
209
      return;
16,491✔
210
    }
211

212
    TAFunctionDeclarator decl(m, instrumentation_helper, functions);
2,350✔
213

214
    auto alloc_arg_types      = instrumentation_helper.make_parameters(IType::ptr, IType::type_id, IType::extent);
2,350✔
215
    auto free_arg_types       = instrumentation_helper.make_parameters(IType::ptr);
2,350✔
216
    auto leavescope_arg_types = instrumentation_helper.make_parameters(IType::stack_count);
2,350✔
217

218
    typeart_alloc.f        = decl.make_function(IFunc::heap, typeart_alloc.name, alloc_arg_types);
2,350✔
219
    typeart_alloc_stack.f  = decl.make_function(IFunc::stack, typeart_alloc_stack.name, alloc_arg_types);
2,350✔
220
    typeart_alloc_global.f = decl.make_function(IFunc::global, typeart_alloc_global.name, alloc_arg_types);
2,350✔
221
    typeart_free.f         = decl.make_function(IFunc::free, typeart_free.name, free_arg_types);
2,350✔
222
    typeart_leave_scope.f  = decl.make_function(IFunc::scope, typeart_leave_scope.name, leavescope_arg_types);
2,350✔
223

224
    typeart_alloc_omp.f = decl.make_function(IFunc::heap_omp, typeart_alloc_omp.name, alloc_arg_types, true);
2,350✔
225
    typeart_alloc_stacks_omp.f =
2,350✔
226
        decl.make_function(IFunc::stack_omp, typeart_alloc_stacks_omp.name, alloc_arg_types, true);
2,350✔
227
    typeart_free_omp.f = decl.make_function(IFunc::free_omp, typeart_free_omp.name, free_arg_types, true);
2,350✔
228
    typeart_leave_scope_omp.f =
2,350✔
229
        decl.make_function(IFunc::scope_omp, typeart_leave_scope_omp.name, leavescope_arg_types, true);
2,350✔
230
  }
18,841✔
231

232
  void printStats(llvm::raw_ostream& out) {
2,323✔
233
    const auto scope_exit_cleanup_counter = llvm::make_scope_exit([&]() {
4,646✔
234
      NumInstrumentedAlloca  = 0;
2,323✔
235
      NumInstrumentedFrees   = 0;
2,323✔
236
      NumInstrumentedGlobal  = 0;
2,323✔
237
      NumInstrumentedMallocs = 0;
2,323✔
238
    });
2,323✔
239
    meminst_finder->printStats(out);
2,323✔
240

241
    const auto get_ta_mode = [&]() {
4,646✔
242
      const bool heap   = configuration()[config::ConfigStdArgs::heap];
2,323✔
243
      const bool stack  = configuration()[config::ConfigStdArgs::stack];
2,323✔
244
      const bool global = configuration()[config::ConfigStdArgs::global];
2,323✔
245

246
      if (heap) {
2,323✔
247
        if (stack) {
1,593✔
248
          return " [Heap & Stack]";
414✔
249
        }
250
        return " [Heap]";
1,179✔
251
      }
252

253
      if (stack) {
730✔
254
        return " [Stack]";
730✔
255
      }
256

NEW
257
      if (global) {
×
NEW
258
        return " [Global]";
×
259
      }
260

NEW
261
      LOG_ERROR("Did not find heap or stack, or combination thereof!");
×
NEW
262
      assert((heap || stack || global) && "Needs stack, heap, global or combination thereof");
×
NEW
263
      return " [Unknown]";
×
264
    };
2,323✔
265

266
    Table stats("TypeArtPass");
2,323✔
267
    stats.wrap_header_ = true;
2,323✔
268
    stats.title_ += get_ta_mode();
2,323✔
269
    stats.put(Row::make("Malloc", NumInstrumentedMallocs.getValue()));
2,323✔
270
    stats.put(Row::make("Free", NumInstrumentedFrees.getValue()));
2,323✔
271
    stats.put(Row::make("Alloca", NumInstrumentedAlloca.getValue()));
2,323✔
272
    stats.put(Row::make("Global", NumInstrumentedGlobal.getValue()));
2,323✔
273

274
    std::ostringstream stream;
2,323✔
275
    stats.print(stream);
2,323✔
276
    out << stream.str();
2,323✔
277
  }
2,323✔
278

279
  llvm::PreservedAnalyses run(llvm::Module& m, llvm::ModuleAnalysisManager&) {
2,350✔
280
    bool changed{false};
2,350✔
281
    changed |= doInitialization(m);
2,350✔
282
    const bool heap = configuration()[config::ConfigStdArgs::heap];  // Must happen after doInit
2,350✔
283
    dump_module(m, heap ? util::module::ModulePhase::kBase : util::module::ModulePhase::kOpt);
2,350✔
284
    changed |= runOnModule(m);
2,350✔
285
    dump_module(m, heap ? util::module::ModulePhase::kHeap : util::module::ModulePhase::kStack);
2,350✔
286
    changed |= doFinalization();
2,350✔
287
    return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
2,350✔
288
  }
289

290
  bool runOnModule(llvm::Module& m) {
2,350✔
291
    meminst_finder->runOnModule(m);
2,350✔
292
    const bool instrument_global = configuration()[config::ConfigStdArgs::global];
2,350✔
293
    bool globals_were_instrumented{false};
2,350✔
294
    if (instrument_global) {
2,350✔
295
      declareInstrumentationFunctions(m);
1,171✔
296

297
      const auto& globalsList = meminst_finder->getModuleGlobals();
1,171✔
298
      if (!globalsList.empty()) {
1,171✔
299
        const auto global_count = instrumentation_context->handleGlobal(globalsList);
480✔
300
        NumInstrumentedGlobal += global_count;
480✔
301
        globals_were_instrumented = global_count > 0;
480✔
302
      }
480✔
303
    }
1,171✔
304

305
    const auto instrumented_function = llvm::count_if(m.functions(), [&](auto& f) { return runOnFunc(f); }) > 0;
60,735✔
306
    return instrumented_function || globals_were_instrumented;
2,350✔
307
  }
308

309
  bool runOnFunc(llvm::Function& f) {
58,385✔
310
    using namespace typeart;
311

312
    if (f.isDeclaration() || util::starts_with_any_of(f.getName(), "__typeart")) {
58,385✔
313
      return false;
40,715✔
314
    }
315

316
    if (!meminst_finder->hasFunctionData(f)) {
17,670✔
NEW
317
      LOG_WARNING("No allocation data could be retrieved for function: " << f.getName());
×
NEW
318
      return false;
×
319
    }
320

321
    LOG_DEBUG("Running on function: " << f.getName())
322

323
    // FIXME this is required when "PassManagerBuilder::EP_OptimizerLast" is used as the function (constant) pointer are
324
    // nullpointer/invalidated
325
    declareInstrumentationFunctions(*f.getParent());
17,670✔
326

327
    bool mod{false};
17,670✔
328
    //  auto& c = f.getContext();
329
    DataLayout dl(f.getParent());
17,670✔
330

331
    const auto& fData   = meminst_finder->getFunctionData(f);
17,670✔
332
    const auto& mallocs = fData.mallocs;
17,670✔
333
    const auto& allocas = fData.allocas;
17,670✔
334
    const auto& frees   = fData.frees;
17,670✔
335

336
    const bool instrument_heap  = configuration()[config::ConfigStdArgs::heap];
17,670✔
337
    const bool instrument_stack = configuration()[config::ConfigStdArgs::stack];
17,670✔
338

339
    if (instrument_heap) {
17,670✔
340
      // instrument collected calls of bb:
341
      const auto heap_count = instrumentation_context->handleHeap(mallocs);
12,465✔
342
      const auto free_count = instrumentation_context->handleFree(frees);
12,465✔
343

344
      NumInstrumentedMallocs += heap_count;
12,465✔
345
      NumInstrumentedFrees += free_count;
12,465✔
346

347
      mod |= heap_count > 0 || free_count > 0;
12,465✔
348
    }
12,465✔
349

350
    if (instrument_stack) {
17,670✔
351
      const auto stack_count = instrumentation_context->handleStack(allocas);
6,503✔
352
      NumInstrumentedAlloca += stack_count;
6,503✔
353
      mod |= stack_count > 0;
6,503✔
354
    }
6,503✔
355

356
    return mod;
17,670✔
357
  }
58,385✔
358
};
359

360
class LegacyTypeArtPass : public llvm::ModulePass {
361
 private:
362
  TypeArtPass pass_impl_;
363

364
 public:
365
  static char ID;  // NOLINT
366

NEW
367
  LegacyTypeArtPass() : ModulePass(ID){};
×
368

369
  bool doInitialization(llvm::Module&) override;
370

371
  bool runOnModule(llvm::Module& module) override;
372

373
  bool doFinalization(llvm::Module&) override;
374

NEW
375
  ~LegacyTypeArtPass() override = default;
×
376
};
377

NEW
378
bool LegacyTypeArtPass::doInitialization(llvm::Module& m) {
×
NEW
379
  return pass_impl_.doInitialization(m);
×
380
}
381

NEW
382
bool LegacyTypeArtPass::runOnModule(llvm::Module& module) {
×
NEW
383
  bool changed{false};
×
NEW
384
  changed |= pass_impl_.runOnModule(module);
×
NEW
385
  return changed;
×
386
}
387

NEW
388
bool LegacyTypeArtPass::doFinalization(llvm::Module&) {
×
NEW
389
  return pass_impl_.doFinalization();
×
390
  ;
391
}
392

393
}  // namespace typeart::pass
394

395
//.....................
396
// New PM
397
//.....................
398
llvm::PassPluginLibraryInfo getTypeartPassPluginInfo() {
2,149✔
399
  using namespace llvm;
400
  return {LLVM_PLUGIN_API_VERSION, "TypeART", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
4,298✔
401
            pass_builder.registerPipelineStartEPCallback([](auto& MPM, OptimizationLevel) {
2,356✔
402
              auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config,
207✔
403
                                                                         "typeart<heap;stats>", "typeart");
207✔
404
              if (!parameters) {
207✔
NEW
405
                LOG_FATAL("Error parsing heap params: " << parameters.takeError())
×
NEW
406
                return;
×
407
              }
408
              MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
207✔
409
            });
207✔
410
            pass_builder.registerOptimizerLastEPCallback([](auto& MPM, OptimizationLevel) {
2,356✔
411
              auto parameters = typeart::util::pass::parsePassParameters(typeart::config::pass::parse_typeart_config,
207✔
412
                                                                         "typeart<no-heap;stack;stats>", "typeart");
207✔
413
              if (!parameters) {
207✔
NEW
414
                LOG_FATAL("Error parsing stack params: " << parameters.takeError())
×
NEW
415
                return;
×
416
              }
417
              MPM.addPass(typeart::pass::TypeArtPass(typeart::pass::TypeArtPass(parameters.get())));
207✔
418
            });
207✔
419
            pass_builder.registerPipelineParsingCallback(
4,298✔
420
                [](StringRef name, ModulePassManager& module_pm, ArrayRef<PassBuilder::PipelineElement>) {
6,033✔
421
                  if (typeart::util::pass::checkParametrizedPassName(name, "typeart")) {
3,884✔
422
                    auto parameters = typeart::util::pass::parsePassParameters(
3,884✔
423
                        typeart::config::pass::parse_typeart_config, name, "typeart");
3,884✔
424
                    if (!parameters) {
3,884✔
NEW
425
                      LOG_FATAL("Error parsing params: " << parameters.takeError())
×
NEW
426
                      return false;
×
427
                    }
428
                    module_pm.addPass(typeart::pass::TypeArtPass(parameters.get()));
3,884✔
429
                    return true;
3,884✔
430
                  }
3,884✔
NEW
431
                  LOG_FATAL("Not a valid parametrized pass name: " << name)
×
NEW
432
                  return false;
×
433
                });
3,884✔
434
          }};
2,149✔
435
}
436

437
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
2,149✔
438
  return getTypeartPassPluginInfo();
2,149✔
439
}
440

441
//.....................
442
// Old PM
443
//.....................
444
char typeart::pass::LegacyTypeArtPass::ID = 0;  // NOLINT
445

446
static RegisterPass<typeart::pass::LegacyTypeArtPass> x("typeart", "TypeArt Pass");  // NOLINT
2,149✔
447

NEW
448
ModulePass* createTypeArtPass() {
×
NEW
449
  return new typeart::pass::LegacyTypeArtPass();
×
450
}
451

NEW
452
extern "C" void AddTypeArtPass(LLVMPassManagerRef pass_manager) {
×
NEW
453
  unwrap(pass_manager)->add(createTypeArtPass());
×
UNCOV
454
}
×
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