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

ahueck / llvm-dimeta / 15096846644

18 May 2025 02:18PM UTC coverage: 83.355% (+0.06%) from 83.296%
15096846644

push

github

web-flow
Merge PR #44 from ahueck/devel

1153 of 1664 branches covered (69.29%)

Branch coverage included in aggregate %.

200 of 218 new or added lines in 14 files covered. (91.74%)

1 existing line in 1 file now uncovered.

2112 of 2253 relevant lines covered (93.74%)

4268.46 hits per line

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

88.16
/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
#include "llvm/Passes/PassPlugin.h"
32
#include "llvm/Support/Casting.h"
33
#include "llvm/Support/CommandLine.h"
34
#include "llvm/Support/ErrorHandling.h"
35
#include "llvm/Support/raw_ostream.h"
36

37
#include <iomanip>
38
#include <iterator>
39
#include <llvm/IR/Instruction.h>
40
#include <llvm/Support/YAMLTraits.h>
41
#include <optional>
42
#include <sstream>
43
#include <string>
44
#include <string_view>
45

46
namespace llvm {
47
class PointerType;
48
}  // namespace llvm
49

50
using namespace llvm;
51

52
static cl::opt<bool> cl_dimeta_test_print_yaml("yaml", cl::init(true));
1,079✔
53
static cl::opt<bool> cl_dimeta_test_print_yaml_retained("yaml-retained", cl::init(false));
1,079✔
54
static cl::opt<bool> cl_dimeta_test_print_tree("dump-tree", cl::init(false));
1,079✔
55
static cl::opt<bool> cl_dimeta_test_stack_pointer("stack-pointer-skip", cl::init(false));
1,079✔
56
static cl::opt<bool> cl_dimeta_test_print("dump", cl::init(false));
1,079✔
57
static cl::opt<bool> cl_dimeta_test_kernel_call("kernel-call", cl::init(false));
1,079✔
58

59
namespace dimeta::test {
60

61
template <class... Ts>
62
struct overload : Ts... {
63
  using Ts::operator()...;
64
};
65
template <class... Ts>
66
overload(Ts...) -> overload<Ts...>;
67

68
template <typename String>
69
inline std::string demangle(String&& s) {
1,738✔
70
  std::string name = std::string{s};
1,738✔
71
#if LLVM_VERSION_MAJOR >= 17
72
  auto demangle = llvm::itaniumDemangle(name.data());
704✔
73
#else
74
  auto demangle = llvm::itaniumDemangle(name.data(), nullptr, nullptr, nullptr);
1,034✔
75
#endif
76
  if (demangle && !std::string(demangle).empty()) {
1,738✔
77
    return {demangle};
876✔
78
  }
79
  return name;
862✔
80
}
1,738✔
81

82
template <typename T>
83
inline std::string try_demangle(const T& site) {
1,738✔
84
  if constexpr (std::is_same_v<T, llvm::CallBase>) {
85
    if (site.isIndirectCall()) {
166!
UNCOV
86
      return "";
×
87
    }
88
    return demangle(site.getCalledFunction()->getName());
166✔
89
  } else {
90
    if constexpr (std::is_same_v<T, llvm::Function>) {
91
      return demangle(site.getName());
1,572✔
92
    } else {
93
      return demangle(site);
94
    }
95
  }
96
}
166✔
97

98
namespace util {
99

100
std::string rep_string(const std::string& input, int rep) {
2,946✔
101
  std::ostringstream os;
2,946✔
102
  std::fill_n(std::ostream_iterator<std::string>(os), rep, input);
2,946✔
103
  return os.str();
2,946✔
104
}
2,946✔
105

106
auto to_string(dimeta::DimetaData& data, bool stack = false) {
2,946✔
107
  const std::string prefix = [&]() {
5,892✔
108
    switch (data.memory_location) {
2,946✔
109
      case DimetaData::MemLoc::kGlobal:
110
        return " Global";
163✔
111
      case DimetaData::MemLoc::kStack:
112
        return " Stack";
1,905✔
113
      default:
114
        return "";
878✔
115
    }
116
  }();
2,946✔
117

118
  std::string logging_message;
2,946✔
119
  llvm::raw_string_ostream rso(logging_message);
2,946✔
120
  rso << "Extracted Type" << prefix << ": " << log::ditype_str(data.entry_type.value_or(nullptr)) << "\n";
2,946✔
121
  rso << "Final Type" << prefix << ": " << log::ditype_str(data.base_type.value_or(nullptr)) << "\n";
2,946✔
122
  rso << "Pointer level: " << data.pointer_level << " (T" << rep_string("*", data.pointer_level) << ")\n";
2,946✔
123
  return rso.str();
2,946✔
124
}
2,946✔
125

126
auto print_loc(std::optional<location::SourceLocation> loc) {
2,938✔
127
  std::string logging_message;
2,938✔
128
  llvm::raw_string_ostream rso(logging_message);
2,938✔
129
  rso << "Location: ";
2,938✔
130
  if (loc) {
2,938!
131
    rso << "\"" << loc->file << "\":\"" << loc->function << "\":" << loc->line;
2,938✔
132
  } else {
2,938✔
133
    rso << "empty";
×
134
  }
135
  return rso.str();
2,938✔
136
}
2,938✔
137

138
template <typename T>
139
bool variable_is_toggled(const T& var, std::string_view env_name) {
11,442✔
140
  if (var.getNumOccurrences() > 0) {
11,442!
141
    return var.getValue();
×
142
  }
143
  const char* env_value = std::getenv(env_name.data());
11,442✔
144
  if (env_value != nullptr) {
11,442✔
145
    return std::string_view{env_value}.compare("1") == 0;
685✔
146
  }
147
  return var.getValue();
10,757✔
148
}
11,442✔
149

150
}  // namespace util
151

152
class TestPass : public llvm::PassInfoMixin<TestPass> {
2,158✔
153
 private:
154
  Module* current_module{nullptr};
2,158✔
155

156
  template <typename Type>
157
  bool serialization_roundtrip(Type compound, bool print_yaml = false) {
2,938✔
158
    std::string initial_oss_string;
2,938✔
159
    llvm::raw_string_ostream initial_oss(initial_oss_string);
2,938✔
160
    io::emit(initial_oss, compound);
2,938✔
161

162
    if (print_yaml) {
2,938!
163
      llvm::outs() << initial_oss.str();
2,938✔
164
    }
2,938✔
165

166
    Type cmp;
2,938✔
167
    dimeta::io::input(initial_oss.str(), cmp);
2,938✔
168

169
    std::string roundtrip_oss_string;
2,938✔
170
    llvm::raw_string_ostream roundtrip_oss(roundtrip_oss_string);
2,938✔
171
    io::emit(roundtrip_oss, cmp);
2,938✔
172

173
    llvm::StringRef const initial{initial_oss.str()};
2,938✔
174

175
    return initial.compare(llvm::StringRef{roundtrip_oss.str()}) == 0;
2,938✔
176
  }
2,938✔
177

178
 public:
179
  llvm::PreservedAnalyses run(llvm::Module& module, llvm::ModuleAnalysisManager&) {
1,054✔
180
    const auto changed = runOnModule(module);
1,054✔
181
    return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
1,054!
182
  }
1,054✔
183

184
  bool runOnModule(Module& module) {
1,079✔
185
    this->current_module = &module;
1,079✔
186
    log::LogContext::get().setModule(&module);
1,079✔
187

188
    const auto serialize_yaml = [&](const auto& located_type) {
1,242✔
189
      bool result{false};
163✔
190
      if (located_type) {
163!
191
        result = serialization_roundtrip(located_type.value(),
326✔
192
                                         util::variable_is_toggled(cl_dimeta_test_print_yaml, "DIMETA_TEST_YAML"));
163✔
193
      }
163✔
194
      LOG_MSG("Yaml Verifier Global: " << static_cast<int>(result));
163✔
195
    };
163✔
196

197
    for (auto& global : module.globals()) {
1,455✔
198
      auto global_meta = type_for(&global);
376✔
199
      if (global_meta) {
376✔
200
        LOG_DEBUG("Type for global: " << global)
163✔
201
        LOG_DEBUG(util::to_string(global_meta.value()));
163✔
202
        auto located_type = located_type_for(&global);
163✔
203
        if (located_type) {
163!
204
          LOG_DEBUG(util::print_loc(located_type->location));
163✔
205
          serialize_yaml(located_type);
163✔
206
        } else {
163✔
207
          LOG_ERROR("No located dimeta type for global")
×
208
        }
209
      }
163✔
210
    }
376✔
211

212
    auto compile_unit_list = dimeta::compile_unit_types(&module).value_or(CompileUnitTypeList{});
1,079✔
213
    if (util::variable_is_toggled(cl_dimeta_test_print_yaml_retained, "DIMETA_TEST_YAML_RETAINED")) {
1,079✔
214
      std::string initial_oss_string;
4✔
215
      llvm::raw_string_ostream initial_oss(initial_oss_string);
4✔
216
      io::emit(initial_oss, compile_unit_list);
4✔
217
      llvm::outs() << initial_oss.str();
4✔
218
      // for (const auto& cu : compile_unit_list) {
219
      //   std::string initial_oss_string;
220
      //   llvm::raw_string_ostream initial_oss(initial_oss_string);
221
      //   io::emit(initial_oss, cu);
222
      //   llvm::outs() << initial_oss.str();
223
      // }
224
    }
4✔
225

226
    llvm::for_each(module.functions(), [&](auto& func) { return runOnFunc(func); });
5,565✔
227
    return false;
228
  }
1,079✔
229

230
  void runOnFunc(Function& func) {
4,461✔
231
    if (func.isDeclaration()) {
4,461✔
232
      return;
2,914✔
233
    }
234
    const auto f_name = try_demangle(func);
1,547✔
235
    // const auto f_name_ref = llvm::StringRef(f_name);
236

237
    LOG_MSG("\nFunction: " << f_name << ":");
1,547✔
238

239
    const auto get_located_type = [&](auto* call_inst) -> std::optional<LocatedType> {
8,903✔
240
      auto located_type = located_type_for(call_inst);
7,356✔
241
      if (located_type) {
7,356!
242
        LOG_DEBUG(util::print_loc(located_type->location));
2,775✔
243
        return located_type;
2,775✔
244
      }
245
      LOG_ERROR("No located dimeta type.")
4,581✔
246
      return {};
4,581✔
247
    };
7,356✔
248

249
    const auto serialize_yaml = [&](auto* inst) {
4,330✔
250
      auto located_type = get_located_type(inst);
2,783✔
251
      bool result{false};
2,783✔
252
      if (located_type) {
2,783!
253
        result = serialization_roundtrip(located_type.value(),
5,550✔
254
                                         util::variable_is_toggled(cl_dimeta_test_print_yaml, "DIMETA_TEST_YAML"));
2,775✔
255
      }
2,775✔
256
      LOG_MSG(*inst << ": Yaml Verifier: " << static_cast<int>(result));
2,783✔
257
    };
2,783✔
258

259
    const auto print_di_tree = [&](const DimetaData& di_var) {
3,452✔
260
      if (util::variable_is_toggled(cl_dimeta_test_print, "DIMETA_TEST_DUMP")) {
1,905✔
261
        di::util::print_dinode(std::get<DILocalVariable*>(di_var.di_variable.value()), outs(), current_module);
5✔
262
      }
5✔
263
    };
1,905✔
264

265
    const auto dump_di_tree = [&](const DimetaData& di_var) {
3,452✔
266
      if (util::variable_is_toggled(cl_dimeta_test_print_tree, "DIMETA_TEST_DUMP_TREE")) {
1,905!
267
        auto local_di_var = std::get<DILocalVariable*>(di_var.di_variable.value());
×
268
#if LLVM_VERSION_MAJOR < 14
269
        local_di_var->print(outs(), current_module);
270
#else
271
        local_di_var->dumpTree(current_module);
272
#endif
273
      }
×
274
    };
1,905✔
275

276
    const bool kernel_call_analysis = util::variable_is_toggled(cl_dimeta_test_kernel_call, "DIMETA_TEST_KERNEL_CALL");
1,547✔
277

278
    if (kernel_call_analysis) {
1,547✔
279
      check_kernel_call(func);
45✔
280
      return;
45✔
281
    }
282

283
    for (auto& inst : llvm::instructions(func)) {
21,462✔
284
      if (auto* call_inst = dyn_cast<CallBase>(&inst)) {
25,411✔
285
        auto ditype_meta = type_for(call_inst);
5,451✔
286
        if (ditype_meta) {
5,451✔
287
          LOG_DEBUG("Type for heap-like: " << *call_inst)
878✔
288
          LOG_DEBUG(util::to_string(ditype_meta.value()) << "\n");
878✔
289
          // auto result = located_type_for(ditype_meta.value());
290
          serialize_yaml(call_inst);
878✔
291
          continue;
878✔
292
        }
293
        get_located_type(call_inst);
4,573✔
294
      }
5,451✔
295

296
      if (auto* alloca_inst = dyn_cast<AllocaInst>(&inst)) {
21,150!
297
        if (util::variable_is_toggled(cl_dimeta_test_stack_pointer, "DIMETA_TEST_STACK_POINTER_SKIP") &&
2,068!
298
            isa<llvm::PointerType>(alloca_inst->getAllocatedType())) {
×
299
          LOG_DEBUG("Skip " << *alloca_inst << "\n");
×
300
          continue;
×
301
        }
302
        auto di_var = type_for(alloca_inst);
2,068✔
303
        if (di_var) {
2,068✔
304
          LOG_DEBUG("Type for alloca: " << *alloca_inst)
1,905✔
305
          LOG_DEBUG(util::to_string(di_var.value()) << "\n");
1,905✔
306
          dump_di_tree(di_var.value());
1,905✔
307
          print_di_tree(di_var.value());
1,905✔
308
          serialize_yaml(alloca_inst);
1,905✔
309
        }
1,905✔
310
      }
2,068✔
311
    }
19,960✔
312
  }
4,461✔
313

314
  void check_kernel_call(Function& func) {
45✔
315
    for (auto& inst : llvm::instructions(func)) {
528✔
316
      if (auto* call_inst = dyn_cast<CallBase>(&inst)) {
649✔
317
        auto name = try_demangle(*call_inst);
166✔
318
        LOG_DEBUG(name)
166✔
319
        if (name.find("kernel") == std::string::npos) {
166✔
320
          continue;
131✔
321
        }
322
        LOG_DEBUG("kernel found");
35✔
323
        for (auto& operand : call_inst->operands()) {
140✔
324
          if (llvm::isa<Instruction>(operand.get())) {
105✔
325
            LOG_DEBUG("Call operand: " << *operand.get());
70✔
326
            auto result = experimental::type_for(operand.get());
70✔
327
            if (result) {
70!
328
              std::string initial_oss_string;
70✔
329
              llvm::raw_string_ostream initial_oss(initial_oss_string);
70✔
330
              std::visit(
70✔
331
                  overload{
70✔
332
                      [&](auto&& f) -> void { io::emit(initial_oss, f); },
140✔
333
                  },
334
                  result.value());
70✔
335
              LOG_DEBUG("Final value type: " << *operand.get())
70✔
336
              LOG_DEBUG("\n" << initial_oss.str())
70✔
337
            }
70✔
338
          }
70✔
339
        }
105✔
340
      }
166✔
341
    }
483!
342
  }
45✔
343
};
344

345
class LegacyTestPass : public ModulePass {
346
 private:
347
  TestPass pass_impl_;
348

349
 public:
350
  static char ID;  // NOLINT
351
  LegacyTestPass() : ModulePass(ID) {
×
352
  }
×
353

354
  bool runOnModule(Module& module) override {
×
355
    const auto modified = pass_impl_.runOnModule(module);
×
356
    return modified;
×
357
  }
×
358

359
  ~LegacyTestPass() override = default;
×
360
};
361

362
}  // namespace dimeta::test
363

364
#define DEBUG_TYPE "dimeta-test-pass"
365

366
//.....................
367
// New PM
368
//.....................
369
llvm::PassPluginLibraryInfo getDimetaTestPassPluginInfo() {
1,079✔
370
  return {LLVM_PLUGIN_API_VERSION, "Dimeta Test Pass", LLVM_VERSION_STRING, [](PassBuilder& pass_builder) {
2,158✔
371
            pass_builder.registerPipelineParsingCallback(
2,158✔
372
                [](StringRef name, ModulePassManager& module_pm, ArrayRef<PassBuilder::PipelineElement>) {
3,237✔
373
                  if (name == "dimeta-test") {
2,158!
374
                    module_pm.addPass(dimeta::test::TestPass());
2,158✔
375
                    return true;
2,158✔
376
                  }
377
                  return false;
×
378
                });
2,158✔
379
          }};
1,079✔
380
}
381

382
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
1,079✔
383
  return getDimetaTestPassPluginInfo();
1,079✔
384
}
385

386
//.....................
387
// Old PM
388
//.....................
389
char dimeta::test::LegacyTestPass::ID = 0;  // NOLINT
390

391
static RegisterPass<dimeta::test::LegacyTestPass> x("dimeta-test", "Dimeta Data Type Test Pass");  // NOLINT
1,079✔
392

393
ModulePass* createTestPass() {
×
394
  return new dimeta::test::LegacyTestPass();
×
395
}
396

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