Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions llvm/compiler-course/llvm-ir/egashin_k_lab2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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 <optional>

using namespace llvm;

namespace {

std::optional<ICmpInst::Predicate>
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<ReplaceIcmpGtGePass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
bool Changed = false;

for (BasicBlock &BB : F) {
for (Instruction &I : make_early_inc_range(BB)) {
auto *Cmp = dyn_cast<ICmpInst>(&I);
if (!Cmp)
continue;

const CmpPredicate OriginalPredicate = Cmp->getCmpPredicate();
std::optional<ICmpInst::Predicate> NewPredicate =
getReplacementPredicate(OriginalPredicate);
if (!NewPredicate)
continue;

IRBuilder<> Builder(Cmp);
auto *OppositeCmp = cast<ICmpInst>(
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<PassBuilder::PipelineElement>) {
if (Name == "replace-icmp-gt-ge") {
FPM.addPass(ReplaceIcmpGtGePass());
return true;
}
return false;
});
}};
}
120 changes: 120 additions & 0 deletions llvm/test/compiler-course/egashin_k_lab2_tests/test_llvm_ir.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
; 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_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
; 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
}

; 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
}
Loading