From f561fec710ec5035d6ac143337a8981f67c987ef Mon Sep 17 00:00:00 2001 From: crn4tt Date: Sat, 18 Apr 2026 20:36:34 +0300 Subject: [PATCH 1/2] feat: lab2 --- .../llvm-ir/egashin_k_lab2/CMakeLists.txt | 12 +++ .../egashin_k_lab2/ReplaceIcmpGtGePass.cpp | 80 ++++++++++++++++ .../egashin_k_lab2_tests/test_llvm_ir.ll | 94 +++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 llvm/compiler-course/llvm-ir/egashin_k_lab2/CMakeLists.txt create mode 100644 llvm/compiler-course/llvm-ir/egashin_k_lab2/ReplaceIcmpGtGePass.cpp create mode 100644 llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll diff --git a/llvm/compiler-course/llvm-ir/egashin_k_lab2/CMakeLists.txt b/llvm/compiler-course/llvm-ir/egashin_k_lab2/CMakeLists.txt new file mode 100644 index 0000000000000..b16db884cc708 --- /dev/null +++ b/llvm/compiler-course/llvm-ir/egashin_k_lab2/CMakeLists.txt @@ -0,0 +1,12 @@ +get_filename_component(PASS_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) +set(PLUGIN_TARGET "${PASS_DIR_NAME}_LLVM_IR") + +if (NOT WIN32 AND NOT CYGWIN) + file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp) + add_llvm_pass_plugin(${PLUGIN_TARGET} ${SOURCES} + DEPENDS + intrinsics_gen + BUILDTREE_ONLY + ) + set(LLVM_TEST_DEPENDS ${PLUGIN_TARGET} ${LLVM_TEST_DEPENDS} PARENT_SCOPE) +endif() diff --git a/llvm/compiler-course/llvm-ir/egashin_k_lab2/ReplaceIcmpGtGePass.cpp b/llvm/compiler-course/llvm-ir/egashin_k_lab2/ReplaceIcmpGtGePass.cpp new file mode 100644 index 0000000000000..41cff7a3510ee --- /dev/null +++ b/llvm/compiler-course/llvm-ir/egashin_k_lab2/ReplaceIcmpGtGePass.cpp @@ -0,0 +1,80 @@ +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" + +#include + +using namespace llvm; + +namespace { + +std::optional +getReplacementPredicate(ICmpInst::Predicate Predicate) { + switch (Predicate) { + case ICmpInst::ICMP_SGT: + return ICmpInst::ICMP_SLE; + case ICmpInst::ICMP_SGE: + return ICmpInst::ICMP_SLT; + case ICmpInst::ICMP_UGT: + return ICmpInst::ICMP_ULE; + case ICmpInst::ICMP_UGE: + return ICmpInst::ICMP_ULT; + default: + return std::nullopt; + } +} + +struct ReplaceIcmpGtGePass : PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { + bool Changed = false; + + for (BasicBlock &BB : F) { + for (Instruction &I : make_early_inc_range(BB)) { + auto *Cmp = dyn_cast(&I); + if (!Cmp) + continue; + + const CmpPredicate OriginalPredicate = Cmp->getCmpPredicate(); + std::optional NewPredicate = + getReplacementPredicate(OriginalPredicate); + if (!NewPredicate) + continue; + + IRBuilder<> Builder(Cmp); + auto *OppositeCmp = cast( + Builder.CreateICmp(*NewPredicate, Cmp->getOperand(0), + Cmp->getOperand(1), Cmp->getName() + ".rev")); + OppositeCmp->setSameSign(Cmp->hasSameSign()); + Value *NegatedCmp = + Builder.CreateNot(OppositeCmp, Cmp->getName() + ".not"); + + Cmp->replaceAllUsesWith(NegatedCmp); + Cmp->eraseFromParent(); + Changed = true; + } + } + + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); + } + + static bool isRequired() { return true; } +}; + +} // namespace + +extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "ReplaceIcmpGtGePass", "0.1", + [](PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager &FPM, + ArrayRef) { + if (Name == "replace-icmp-gt-ge") { + FPM.addPass(ReplaceIcmpGtGePass()); + return true; + } + return false; + }); + }}; +} diff --git a/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll b/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll new file mode 100644 index 0000000000000..394a63dd2e0bc --- /dev/null +++ b/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll @@ -0,0 +1,94 @@ +; RUN: opt -load-pass-plugin %llvmshlibdir/egashin_k_lab2_LLVM_IR%pluginext \ +; RUN: -passes=replace-icmp-gt-ge -S %s | FileCheck %s + +; CHECK-LABEL: @test_sgt( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp sle i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: ret i1 %cmp.not +define i1 @test_sgt(i32 %a, i32 %b) { +entry: + %cmp = icmp sgt i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_sge( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp slt i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: ret i1 %cmp.not +define i1 @test_sge(i32 %a, i32 %b) { +entry: + %cmp = icmp sge i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_ugt( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp ule i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: ret i1 %cmp.not +define i1 @test_ugt(i32 %a, i32 %b) { +entry: + %cmp = icmp ugt i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_uge( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp ult i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: ret i1 %cmp.not +define i1 @test_uge(i32 %a, i32 %b) { +entry: + %cmp = icmp uge i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_samesign_sgt( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp samesign sle i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: ret i1 %cmp.not +define i1 @test_samesign_sgt(i32 %a, i32 %b) { +entry: + %cmp = icmp samesign sgt i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_slt( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp = icmp slt i32 %a, %b +; CHECK-NEXT: ret i1 %cmp +define i1 @test_slt(i32 %a, i32 %b) { +entry: + %cmp = icmp slt i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_eq( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp = icmp eq i32 %a, %b +; CHECK-NEXT: ret i1 %cmp +define i1 @test_eq(i32 %a, i32 %b) { +entry: + %cmp = icmp eq i32 %a, %b + ret i1 %cmp +} + +; CHECK-LABEL: @test_branch( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp.rev = icmp sle i32 %a, %b +; CHECK-NEXT: %cmp.not = xor i1 %cmp.rev, true +; CHECK-NEXT: br i1 %cmp.not, label %then, label %else +define void @test_branch(i32 %a, i32 %b) { +entry: + %cmp = icmp sgt i32 %a, %b + br i1 %cmp, label %then, label %else + +then: + ret void + +else: + ret void +} From d1fc0ae8f15b2efd30460fb8799e4e0b7a1850ff Mon Sep 17 00:00:00 2001 From: crn4tt Date: Sun, 19 Apr 2026 13:33:29 +0300 Subject: [PATCH 2/2] test: strengthen lab2 coverage --- .../egashin_k_lab2_tests/test_llvm_ir.ll | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll b/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll index 394a63dd2e0bc..b8b7a3fbb0685 100644 --- a/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll +++ b/llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll @@ -76,6 +76,16 @@ entry: ret i1 %cmp } +; CHECK-LABEL: @test_ne( +; CHECK-NEXT: entry: +; CHECK-NEXT: %cmp = icmp ne i32 %a, %b +; CHECK-NEXT: ret i1 %cmp +define i1 @test_ne(i32 %a, i32 %b) { +entry: + %cmp = icmp ne i32 %a, %b + ret i1 %cmp +} + ; CHECK-LABEL: @test_branch( ; CHECK-NEXT: entry: ; CHECK-NEXT: %cmp.rev = icmp sle i32 %a, %b @@ -92,3 +102,19 @@ then: else: ret void } + +; CHECK-LABEL: @test_two_cmps( +; CHECK-NEXT: entry: +; CHECK-NEXT: %first.rev = icmp sle i32 %a, %b +; CHECK-NEXT: %first.not = xor i1 %first.rev, true +; CHECK-NEXT: %second.rev = icmp slt i32 %b, %c +; CHECK-NEXT: %second.not = xor i1 %second.rev, true +; CHECK-NEXT: %result = and i1 %first.not, %second.not +; CHECK-NEXT: ret i1 %result +define i1 @test_two_cmps(i32 %a, i32 %b, i32 %c) { +entry: + %first = icmp sgt i32 %a, %b + %second = icmp sge i32 %b, %c + %result = and i1 %first, %second + ret i1 %result +}