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

ahueck / llvm-dimeta / 25333120092

04 May 2026 05:25PM UTC coverage: 73.596% (+0.3%) from 73.295%
25333120092

Pull #58

github

web-flow
Merge 9bc7df9fc into 98116ab4b
Pull Request #58: Enable custom allocators

2241 of 3751 branches covered (59.74%)

Branch coverage included in aggregate %.

125 of 139 new or added lines in 5 files covered. (89.93%)

2 existing lines in 2 files now uncovered.

2687 of 2945 relevant lines covered (91.24%)

20073.69 hits per line

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

86.88
/test/verifier/TestPass.cpp
1
//  Dimeta library
2
//  Copyright (c) 2022-2025 llvm-dimeta authors
3
//  Distributed under the BSD 3-Clause license.
4
//  (See accompanying file LICENSE)
5
//  SPDX-License-Identifier: BSD-3-Clause
6
//
7

8
#include "support/Logger.h"
9
#include "type/DIUtil.h"
10
#include "type/DIVisitor.h"
11
#include "type/Dimeta.h"
12
#include "type/DimetaData.h"
13
#include "type/DimetaIO.h"
14
#include "type/DimetaParse.h"
15

16
#include "llvm-c/Types.h"
17
#include "llvm/ADT/STLExtras.h"
18
#include "llvm/ADT/StringRef.h"
19
#include "llvm/ADT/ilist_iterator.h"
20
#include "llvm/Demangle/Demangle.h"
21
#include "llvm/IR/DebugInfoMetadata.h"
22
#include "llvm/IR/Function.h"
23
#include "llvm/IR/InstIterator.h"
24
#include "llvm/IR/InstrTypes.h"
25
#include "llvm/IR/Instructions.h"
26
#include "llvm/IR/LegacyPassManager.h"
27
#include "llvm/IR/Module.h"
28
#include "llvm/IR/Value.h"
29
#include "llvm/Pass.h"
30
#include "llvm/Passes/PassBuilder.h"
31
#if LLVM_VERSION_MAJOR < 22
32
#include "llvm/Passes/PassPlugin.h"
33
#else
34
#include "llvm/Plugins/PassPlugin.h"
35
#endif
36
#include "llvm/Support/Casting.h"
37
#include "llvm/Support/CommandLine.h"
38
#include "llvm/Support/ErrorHandling.h"
39
#include "llvm/Support/raw_ostream.h"
40

41
#include <iomanip>
42
#include <iterator>
43
#include <llvm/IR/Instruction.h>
44
#include <llvm/Support/YAMLTraits.h>
45
#include <cerrno>
46
#include <cstdlib>
47
#include <optional>
48
#include <sstream>
49
#include <string>
50
#include <string_view>
51
#include <type_traits>
52

53
namespace llvm {
54
class PointerType;
55
}  // namespace llvm
56

57
using namespace llvm;
58

59
static cl::opt<bool> cl_dimeta_test_print_yaml("yaml", cl::init(true));
4,346✔
60
static cl::opt<bool> cl_dimeta_test_print_yaml_retained("yaml-retained", cl::init(false));
4,346✔
61
static cl::opt<bool> cl_dimeta_test_print_tree("dump-tree", cl::init(false));
4,346✔
62
static cl::opt<bool> cl_dimeta_test_stack_pointer("stack-pointer-skip", cl::init(false));
4,346✔
63
static cl::opt<bool> cl_dimeta_test_print("dump", cl::init(false));
4,346✔
64
static cl::opt<bool> cl_dimeta_test_kernel_call("kernel-call", cl::init(false));
4,346✔
65
static cl::opt<std::string> cl_dimeta_test_callbase_mode("callbase-mode", cl::init("auto"));
4,346✔
66
static cl::opt<unsigned> cl_dimeta_test_callbase_arg("callbase-arg", cl::init(0));
4,346✔
67

68
namespace dimeta::test {
69

70
template <class... Ts>
71
struct overload : Ts... {
72
  using Ts::operator()...;
73
};
74
template <class... Ts>
75
overload(Ts...) -> overload<Ts...>;
76

77
template <typename String>
78
inline std::string demangle(String&& s) {
6,574✔
79
  std::string name = std::string{s};
6,574✔
80
#if LLVM_VERSION_MAJOR >= 17
81
  auto demangle = llvm::itaniumDemangle(name.data());
5,467✔
82
#else
83
  auto demangle = llvm::itaniumDemangle(name.data(), nullptr, nullptr, nullptr);
1,107✔
84
#endif
85
  if (demangle && !std::string(demangle).empty()) {
6,574✔
86
    return {demangle};
3,033✔
87
  }
88
  return name;
3,541✔
89
}
6,574✔
90

91
template <typename T>
92
inline std::string try_demangle(const T& site) {
6,574✔
93
  if constexpr (std::is_same_v<T, llvm::CallBase>) {
94
    if (site.isIndirectCall()) {
487!
95
      return "";
×
96
    }
97
    return demangle(site.getCalledFunction()->getName());
487✔
98
  } else {
99
    if constexpr (std::is_same_v<T, llvm::Function>) {
100
      return demangle(site.getName());
6,087✔
101
    } else {
102
      return demangle(site);
103
    }
104
  }
105
}
3,495✔
106

107
namespace util {
108

109
std::string rep_string(const std::string& input, int rep) {
12,455✔
110
  std::ostringstream os;
12,455✔
111
  std::fill_n(std::ostream_iterator<std::string>(os), rep, input);
12,455✔
112
  return os.str();
12,455✔
113
}
12,455✔
114

115
auto to_string(dimeta::DimetaData& data, bool stack = false) {
12,455✔
116
  const std::string prefix = [&]() {
24,910✔
117
    switch (data.memory_location) {
12,455✔
118
      case DimetaData::MemLoc::kGlobal:
119
        return " Global";
942✔
120
      case DimetaData::MemLoc::kStack:
121
        return " Stack";
7,266✔
122
      default:
123
        return "";
4,247✔
124
    }
125
  }();
12,455✔
126

127
  std::string logging_message;
12,455✔
128
  llvm::raw_string_ostream rso(logging_message);
12,455✔
129
  rso << "Extracted Type" << prefix << ": " << log::ditype_str(data.entry_type.value_or(nullptr)) << "\n";
12,455✔
130
  rso << "Final Type" << prefix << ": " << log::ditype_str(data.base_type.value_or(nullptr)) << "\n";
12,455✔
131
  rso << "Pointer level: " << data.pointer_level << " (T" << rep_string("*", data.pointer_level) << ")\n";
12,455✔
132
  return rso.str();
12,455✔
133
}
12,455✔
134

135
auto print_loc(std::optional<location::SourceLocation> loc) {
12,429✔
136
  std::string logging_message;
12,429✔
137
  llvm::raw_string_ostream rso(logging_message);
12,429✔
138
  rso << "Location: ";
12,429✔
139
  if (loc) {
12,429!
140
    rso << "\"" << loc->file << "\":\"" << loc->function << "\":" << loc->line;
12,429✔
141
  } else {
12,429✔
142
    rso << "empty";
×
143
  }
144
  return rso.str();
12,429✔
145
}
12,429✔
146

147
template <typename T>
148
bool variable_is_toggled(const T& var, std::string_view env_name) {
45,431✔
149
  if (var.getNumOccurrences() > 0) {
45,431!
150
    return var.getValue();
×
151
  }
152
  const char* env_value = std::getenv(env_name.data());
45,431✔
153
  if (env_value != nullptr) {
45,431✔
154
    return std::string_view{env_value}.compare("1") == 0;
2,329✔
155
  }
156
  return var.getValue();
43,102✔
157
}
45,431✔
158

159
inline std::string variable_or_env_string(const cl::opt<std::string>& var, std::string_view env_name) {
6,042✔
160
  if (var.getNumOccurrences() > 0) {
6,042!
NEW
161
    return var.getValue();
×
162
  }
163
  const char* env_value = std::getenv(env_name.data());
6,042✔
164
  if (env_value != nullptr) {
6,042✔
165
    return env_value;
102✔
166
  }
167
  return var.getValue();
5,940✔
168
}
6,042✔
169

170
inline unsigned variable_or_env_unsigned(const cl::opt<unsigned>& var, std::string_view env_name) {
6,042✔
171
  if (var.getNumOccurrences() > 0) {
6,042!
NEW
172
    return var.getValue();
×
173
  }
174
  const char* env_value = std::getenv(env_name.data());
6,042✔
175
  if (env_value != nullptr) {
6,042✔
176
    char* end_ptr = nullptr;
68✔
177
    errno         = 0;
68✔
178
    auto value    = std::strtoul(env_value, &end_ptr, 10);
68✔
179
    if (errno == 0 && end_ptr != env_value && *end_ptr == '\0') {
68!
180
      return static_cast<unsigned>(value);
68✔
181
    }
NEW
182
    return var.getValue();
×
183
  }
68✔
184
  return var.getValue();
5,974✔
185
}
6,042✔
186

187
}  // namespace util
188

189
class TestPass : public llvm::PassInfoMixin<TestPass> {
8,692✔
190
 private:
191
  Module* current_module{nullptr};
8,692✔
192

193
  template <typename Type>
194
  bool serialization_roundtrip(Type compound, bool print_yaml = false) {
12,429✔
195
    std::string initial_oss_string;
12,429✔
196
    llvm::raw_string_ostream initial_oss(initial_oss_string);
12,429✔
197
    io::emit(initial_oss, compound);
12,429✔
198

199
    if (print_yaml) {
12,429!
200
      llvm::outs() << initial_oss.str();
12,429✔
201
    }
12,429✔
202

203
    Type cmp;
12,429✔
204
    dimeta::io::input(initial_oss.str(), cmp);
12,429✔
205

206
    std::string roundtrip_oss_string;
12,429✔
207
    llvm::raw_string_ostream roundtrip_oss(roundtrip_oss_string);
12,429✔
208
    io::emit(roundtrip_oss, cmp);
12,429✔
209

210
    llvm::StringRef const initial{initial_oss.str()};
12,429✔
211

212
    return initial.compare(llvm::StringRef{roundtrip_oss.str()}) == 0;
12,429✔
213
  }
12,429✔
214

215
 public:
216
  llvm::PreservedAnalyses run(llvm::Module& module, llvm::ModuleAnalysisManager&) {
4,301✔
217
    const auto changed = runOnModule(module);
4,301✔
218
    return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
4,301!
219
  }
4,301✔
220

221
  bool runOnModule(Module& module) {
4,346✔
222
    this->current_module = &module;
4,346✔
223
    log::LogContext::get().setModule(&module);
4,346✔
224

225
    const auto serialize_yaml = [&](const auto& located_type) {
5,288✔
226
      bool result{false};
942✔
227
      if (located_type) {
942!
228
        result = serialization_roundtrip(located_type.value(),
1,884✔
229
                                         util::variable_is_toggled(cl_dimeta_test_print_yaml, "DIMETA_TEST_YAML"));
942✔
230
      }
942✔
231
      LOG_MSG("Yaml Verifier Global: " << static_cast<int>(result));
942✔
232
    };
942✔
233

234
    for (auto& global : module.globals()) {
8,013✔
235
      auto global_meta = type_for(&global);
3,667✔
236
      if (global_meta) {
3,667✔
237
        LOG_DEBUG("Type for global: " << global)
942✔
238
        LOG_DEBUG(util::to_string(global_meta.value()));
942✔
239
        auto located_type = located_type_for(&global);
942✔
240
        if (located_type) {
942!
241
          LOG_DEBUG(util::print_loc(located_type->location));
942✔
242
          serialize_yaml(located_type);
942✔
243
        } else {
942✔
244
          LOG_ERROR("No located dimeta type for global")
×
245
        }
246
      }
942✔
247
    }
3,667✔
248

249
    auto compile_unit_list = dimeta::compile_unit_types(&module).value_or(CompileUnitTypeList{});
4,346✔
250
    if (util::variable_is_toggled(cl_dimeta_test_print_yaml_retained, "DIMETA_TEST_YAML_RETAINED")) {
4,346✔
251
      std::string initial_oss_string;
16✔
252
      llvm::raw_string_ostream initial_oss(initial_oss_string);
16✔
253
      io::emit(initial_oss, compile_unit_list);
16✔
254
      llvm::outs() << initial_oss.str();
16✔
255
      // for (const auto& cu : compile_unit_list) {
256
      //   std::string initial_oss_string;
257
      //   llvm::raw_string_ostream initial_oss(initial_oss_string);
258
      //   io::emit(initial_oss, cu);
259
      //   llvm::outs() << initial_oss.str();
260
      // }
261
    }
16✔
262

263
    llvm::for_each(module.functions(), [&](auto& func) { return runOnFunc(func); });
20,091✔
264
    return false;
265
  }
4,346✔
266

267
  void runOnFunc(Function& func) {
15,700✔
268
    if (func.isDeclaration()) {
15,700✔
269
      return;
9,658✔
270
    }
271
    const auto f_name = try_demangle(func);
6,042✔
272
    // const auto f_name_ref = llvm::StringRef(f_name);
273

274
    LOG_MSG("\nFunction: " << f_name << ":");
6,042✔
275

276
    const auto callbase_mode = util::variable_or_env_string(cl_dimeta_test_callbase_mode, "DIMETA_TEST_CALLBASE_MODE");
6,042✔
277
    const auto callbase_arg  = util::variable_or_env_unsigned(cl_dimeta_test_callbase_arg, "DIMETA_TEST_CALLBASE_ARG");
6,042✔
278

279
    const auto make_callbase_config = [&]() -> std::optional<CallBaseTypeConfig> {
12,084✔
280
      if (callbase_mode == "auto") {
6,042✔
281
        return {};
5,940✔
282
      }
283

284
      CallBaseTypeConfig config;
102✔
285
      config.argument_index = callbase_arg;
102✔
286

287
      if (callbase_mode == "return-flow") {
102✔
288
        config.dataflow = CallBaseTypeConfig::Dataflow::kReturnValueForwardThenBackward;
34✔
289
        return config;
34✔
290
      }
291

292
      if (callbase_mode == "argument-backward") {
68!
293
        config.dataflow = CallBaseTypeConfig::Dataflow::kArgumentBackward;
68✔
294
        return config;
68✔
295
      }
296

NEW
297
      LOG_ERROR("Unsupported callbase mode: " << callbase_mode)
×
NEW
298
      return {};
×
299
    }();
6,042✔
300

301
    const auto get_located_type = [&](auto* call_inst) -> std::optional<LocatedType> {
30,508✔
302
      auto located_type = [&]() -> std::optional<LocatedType> {
48,932✔
303
        if constexpr (std::is_same_v<std::remove_pointer_t<decltype(call_inst)>, llvm::CallBase>) {
304
          if (callbase_mode == "auto") {
17,200✔
305
            return located_type_for(call_inst);
17,068✔
306
          }
307
          if (!make_callbase_config) {
132!
NEW
308
            return {};
×
309
          }
310
          return located_type_for(call_inst, make_callbase_config.value());
132✔
311
        }
312
        return located_type_for(call_inst);
7,266✔
313
      }();
20,696✔
314

315
      if (located_type) {
24,466!
316
        LOG_DEBUG(util::print_loc(located_type->location));
11,487✔
317
        return located_type;
11,487✔
318
      }
319
      LOG_ERROR("No located dimeta type.")
12,979✔
320
      return {};
12,979✔
321
    };
24,466✔
322

323
    const auto serialize_yaml = [&](auto* inst) {
17,555✔
324
      auto located_type = get_located_type(inst);
11,513✔
325
      bool result{false};
11,513✔
326
      if (located_type) {
11,513!
327
        result = serialization_roundtrip(located_type.value(),
22,974✔
328
                                         util::variable_is_toggled(cl_dimeta_test_print_yaml, "DIMETA_TEST_YAML"));
11,487✔
329
      }
11,487✔
330
      LOG_MSG(*inst << ": Yaml Verifier: " << static_cast<int>(result));
11,513✔
331
    };
11,513✔
332

333
    const auto print_di_tree = [&](const DimetaData& di_var) {
13,308✔
334
      if (util::variable_is_toggled(cl_dimeta_test_print, "DIMETA_TEST_DUMP")) {
7,266✔
335
        di::util::print_dinode(std::get<DILocalVariable*>(di_var.di_variable.value()), outs(), current_module);
17✔
336
      }
17✔
337
    };
7,266✔
338

339
    const auto dump_di_tree = [&](const DimetaData& di_var) {
13,308✔
340
      if (util::variable_is_toggled(cl_dimeta_test_print_tree, "DIMETA_TEST_DUMP_TREE")) {
7,266!
341
        auto local_di_var = std::get<DILocalVariable*>(di_var.di_variable.value());
×
342
#if LLVM_VERSION_MAJOR < 14
343
        local_di_var->print(outs(), current_module);
344
#else
345
        local_di_var->dumpTree(current_module);
346
#endif
347
      }
×
348
    };
7,266✔
349

350
    const bool kernel_call_analysis = util::variable_is_toggled(cl_dimeta_test_kernel_call, "DIMETA_TEST_KERNEL_CALL");
6,042✔
351

352
    if (kernel_call_analysis) {
6,042✔
353
      check_kernel_call(func);
153✔
354
      return;
153✔
355
    }
356

357
    for (auto& inst : llvm::instructions(func)) {
87,547✔
358
      if (auto* call_inst = dyn_cast<CallBase>(&inst)) {
98,858✔
359
        auto ditype_meta = [&]() -> std::optional<DimetaData> {
34,400✔
360
          if (callbase_mode == "auto") {
17,200✔
361
            return type_for(call_inst);
17,068✔
362
          }
363
          if (!make_callbase_config) {
132!
NEW
364
            return {};
×
365
          }
366
          return type_for(call_inst, make_callbase_config.value());
132✔
367
        }();
17,200✔
368

369
        if (ditype_meta && ditype_meta->entry_type) {
17,200✔
370
          LOG_DEBUG("Type for heap-like: " << *call_inst)
4,247✔
371
          LOG_DEBUG(util::to_string(ditype_meta.value()) << "\n");
4,247✔
372
          // auto result = located_type_for(ditype_meta.value());
373
          serialize_yaml(call_inst);
4,247✔
374
          continue;
4,247✔
375
        }
376
        get_located_type(call_inst);
12,953✔
377
      }
17,200✔
378

379
      if (auto* alloca_inst = dyn_cast<AllocaInst>(&inst)) {
85,493!
380
        if (util::variable_is_toggled(cl_dimeta_test_stack_pointer, "DIMETA_TEST_STACK_POINTER_SKIP") &&
8,082!
381
            isa<llvm::PointerType>(alloca_inst->getAllocatedType())) {
×
382
          LOG_DEBUG("Skip " << *alloca_inst << "\n");
×
383
          continue;
×
384
        }
385
        auto di_var = type_for(alloca_inst);
8,082✔
386
        if (di_var) {
8,082✔
387
          LOG_DEBUG("Type for alloca: " << *alloca_inst)
7,266✔
388
          LOG_DEBUG(util::to_string(di_var.value()) << "\n");
7,266✔
389
          dump_di_tree(di_var.value());
7,266✔
390
          print_di_tree(di_var.value());
7,266✔
391
          serialize_yaml(alloca_inst);
7,266✔
392
        }
7,266✔
393
      }
8,082✔
394
    }
81,658✔
395
  }
18,708✔
396

397
  void check_kernel_call(Function& func) {
153✔
398
    for (auto& inst : llvm::instructions(func)) {
1,641✔
399
      if (auto* call_inst = dyn_cast<CallBase>(&inst)) {
1,975✔
400
        auto name = try_demangle(*call_inst);
487✔
401
        LOG_DEBUG(name)
487✔
402
        if (name.find("kernel") == std::string::npos) {
487✔
403
          continue;
368✔
404
        }
405
        LOG_DEBUG("kernel found");
119✔
406
        for (auto& operand : call_inst->operands()) {
476✔
407
          if (llvm::isa<Instruction>(operand.get())) {
357✔
408
            LOG_DEBUG("Call operand: " << *operand.get());
238✔
409
            auto result = experimental::type_for(operand.get());
238✔
410
            if (result) {
238!
411
              std::string initial_oss_string;
238✔
412
              llvm::raw_string_ostream initial_oss(initial_oss_string);
238✔
413
              std::visit(
238✔
414
                  overload{
238✔
415
                      [&](auto&& f) -> void { io::emit(initial_oss, f); },
476✔
416
                  },
417
                  result.value());
238✔
418
              LOG_DEBUG("Final value type: " << *operand.get())
238✔
419
              LOG_DEBUG("\n" << initial_oss.str())
238✔
420
            }
238✔
421
          }
238✔
422
        }
357✔
423
      }
487✔
424
    }
1,488!
425
  }
153✔
426
};
427

428
class LegacyTestPass : public ModulePass {
429
 private:
430
  TestPass pass_impl_;
431

432
 public:
433
  static char ID;  // NOLINT
434
  LegacyTestPass() : ModulePass(ID) {
×
435
  }
×
436

437
  bool runOnModule(Module& module) override {
×
438
    const auto modified = pass_impl_.runOnModule(module);
×
439
    return modified;
×
440
  }
×
441

442
  ~LegacyTestPass() override = default;
×
443
};
444

445
}  // namespace dimeta::test
446

447
#define DEBUG_TYPE "dimeta-test-pass"
448

449
//.....................
450
// New PM
451
//.....................
452
llvm::PassPluginLibraryInfo getDimetaTestPassPluginInfo() {
4,346✔
453
  return {LLVM_PLUGIN_API_VERSION, "Dimeta Test Pass", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
8,692✔
454
            pass_builder.registerPipelineParsingCallback(
8,692✔
455
                [](StringRef name, ModulePassManager& module_pm, ArrayRef<PassBuilder::PipelineElement>) {
13,038✔
456
                  if (name == "dimeta-test") {
8,692!
457
                    module_pm.addPass(dimeta::test::TestPass());
8,692✔
458
                    return true;
8,692✔
459
                  }
460
                  return false;
×
461
                });
8,692✔
462
          }};
4,346✔
463
}
464

465
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
4,346✔
466
  return getDimetaTestPassPluginInfo();
4,346✔
467
}
468

469
//.....................
470
// Old PM
471
//.....................
472
char dimeta::test::LegacyTestPass::ID = 0;  // NOLINT
473

474
static RegisterPass<dimeta::test::LegacyTestPass> x("dimeta-test", "Dimeta Data Type Test Pass");  // NOLINT
4,346✔
475

476
ModulePass* createTestPass() {
×
477
  return new dimeta::test::LegacyTestPass();
×
478
}
479

480
extern "C" void AddTestPass(LLVMPassManagerRef pass_manager) {
×
481
  unwrap(pass_manager)->add(createTestPass());
×
482
}
×
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