Skip to content

Commit ffdaac4

Browse files
authored
Шульпин Илья. Лабораторная работа 2: LLVM. Вариант 3. (#147)
Этот пасс выполняет следующую задачу: - Проходит по всем функциям модуля (кроме самой @add). - Ищет бинарные операции add i32. - Если в модуле присутствует функция @add(i32, i32) (ровно два параметра одинакового типа), то каждую такую инструкцию add i32 заменяет на вызов call i32 @add(i32, i32), сохраняя семантику (операнды и имя результата). - Использует llvm::make_early_inc_range для безопасного удаления исходных инструкций во время итерации. - Если функции @add нет или её сигнатура не соответствует требуемой, инструкции add i32 остаются без изменений.
1 parent 735b466 commit ffdaac4

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "llvm/ADT/STLExtras.h"
2+
#include "llvm/IR/Function.h"
3+
#include "llvm/IR/IRBuilder.h"
4+
#include "llvm/IR/Instructions.h"
5+
#include "llvm/IR/Module.h"
6+
#include "llvm/Passes/PassBuilder.h"
7+
#include "llvm/Passes/PassPlugin.h"
8+
#include "llvm/Support/raw_ostream.h"
9+
10+
namespace {
11+
struct AddCallReplacerPass : llvm::PassInfoMixin<AddCallReplacerPass> {
12+
llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &) {
13+
llvm::Function *addFunc = M.getFunction("add");
14+
bool canReplace = false;
15+
llvm::Type *paramTy = nullptr;
16+
if (addFunc && addFunc->arg_size() == 2) {
17+
auto AI = addFunc->arg_begin();
18+
llvm::Type *t0 = AI->getType();
19+
++AI;
20+
llvm::Type *t1 = AI->getType();
21+
if (t0 == t1) {
22+
canReplace = true;
23+
paramTy = t0;
24+
}
25+
}
26+
27+
for (llvm::Function &F : M) {
28+
if (&F == addFunc)
29+
continue;
30+
for (llvm::BasicBlock &BB : F) {
31+
for (llvm::Instruction &I : llvm::make_early_inc_range(BB)) {
32+
if (!canReplace)
33+
break;
34+
if (auto *BI = llvm::dyn_cast<llvm::BinaryOperator>(&I)) {
35+
if (BI->getOpcode() == llvm::Instruction::Add &&
36+
BI->getType() == paramTy) {
37+
llvm::IRBuilder<> Builder(&I);
38+
llvm::Value *L = BI->getOperand(0);
39+
llvm::Value *R = BI->getOperand(1);
40+
llvm::CallInst *CI = Builder.CreateCall(addFunc, {L, R}, "sum");
41+
BI->replaceAllUsesWith(CI);
42+
BI->eraseFromParent();
43+
}
44+
}
45+
}
46+
}
47+
}
48+
49+
return llvm::PreservedAnalyses::all();
50+
}
51+
52+
static bool isRequired() { return true; }
53+
};
54+
} // namespace
55+
56+
extern "C" LLVM_ATTRIBUTE_WEAK::llvm::PassPluginLibraryInfo
57+
llvmGetPassPluginInfo() {
58+
return {LLVM_PLUGIN_API_VERSION, "AddCallReplacer", "0.1",
59+
[](llvm::PassBuilder &PB) {
60+
PB.registerPipelineParsingCallback(
61+
[](llvm::StringRef name, llvm::ModulePassManager &MPM,
62+
llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
63+
if (name == "ReplacerPass") {
64+
MPM.addPass(AddCallReplacerPass());
65+
return true;
66+
}
67+
return false;
68+
});
69+
}};
70+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
set(Title "ReplacerPass")
2+
set(Student "ShulpinIlya")
3+
set(Group "FIIT1")
4+
set(TARGET_NAME "${Title}_${Student}_${Group}_LLVM_IR")
5+
6+
if (NOT WIN32 AND NOT CYGWIN)
7+
file(GLOB_RECURSE SOURCES *.cpp *.h *.hpp)
8+
add_llvm_pass_plugin(${TARGET_NAME} ${SOURCES}
9+
DEPENDS
10+
intrinsics_gen
11+
BUILDTREE_ONLY
12+
)
13+
set(LLVM_TEST_DEPENDS ${TARGET_NAME} ${LLVM_TEST_DEPENDS} PARENT_SCOPE)
14+
endif()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; RUN: opt -load-pass-plugin %llvmshlibdir/ReplacerPass_ShulpinIlya_FIIT1_LLVM_IR%pluginext \
2+
; RUN: -passes="ReplacerPass" -S %s | FileCheck %s
3+
4+
;===------------------------------------------------------------------------===;
5+
; Test 8: @no_add_decl — No @add in module, i32 add instructions should remain
6+
; Verifies that when the add helper function is absent, the pass does not
7+
; replace `add i32` with a call to @add
8+
;===------------------------------------------------------------------------===;
9+
10+
; CHECK-LABEL: define i32 @no_add_decl(i32 %x, i32 %y)
11+
; CHECK: %sum = add i32 %x, %y
12+
; CHECK-NOT: call i32 @add
13+
14+
define i32 @no_add_decl(i32 %x, i32 %y) {
15+
%sum = add i32 %x, %y
16+
ret i32 %sum
17+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
; RUN: opt -load-pass-plugin %llvmshlibdir/ReplacerPass_ShulpinIlya_FIIT1_LLVM_IR%pluginext \
2+
; RUN: -passes="ReplacerPass" -S %s | FileCheck %s
3+
4+
;===------------------------------------------------------------------------===;
5+
; Test 1: @add - Reference implementation of add function (should not change)
6+
; Checks that function @add itself is not transformed by the pass
7+
;===------------------------------------------------------------------------===;
8+
9+
; CHECK: define i32 @add(i32 %a, i32 %b)
10+
; CHECK: %result = add i32 %a, %b
11+
; CHECK: ret i32 %result
12+
; CHECK-NOT: add i32
13+
14+
define i32 @add(i32 %a, i32 %b) {
15+
%result = add i32 %a, %b
16+
ret i32 %result
17+
}
18+
19+
;===------------------------------------------------------------------------===;
20+
; Test 2: @foo - Matching types, call to @add should replace the add instruction
21+
; This should be transformed to a call to @add because the operand types match
22+
;===------------------------------------------------------------------------===;
23+
24+
; CHECK-LABEL: define i32 @foo(i32 %x, i32 %y)
25+
; CHECK-NEXT: call i32 @add(i32 %x, i32 %y)
26+
; CHECK-NEXT: ret i32 %sum
27+
; CHECK-NOT: add i32
28+
29+
define i32 @foo(i32 %x, i32 %y) {
30+
%sum = add i32 %x, %y
31+
ret i32 %sum
32+
}
33+
34+
35+
;===------------------------------------------------------------------------===;
36+
; Test 3: @bar - Same as @foo, another usage of i32 add replaced with @add
37+
; Verifies that the pass replaces all i32 add instructions consistently
38+
;===------------------------------------------------------------------------===;
39+
40+
; CHECK-LABEL: define i32 @bar(i32 %m, i32 %n)
41+
; CHECK-NEXT: %sum1 = call i32 @add(i32 %m, i32 %n)
42+
; CHECK-NEXT: ret i32 %sum
43+
; CHECK-NOT: call i32 @add
44+
45+
define i32 @bar(i32 %m, i32 %n) {
46+
%sum = add i32 %m, %n
47+
ret i32 %sum
48+
}
49+
50+
;===------------------------------------------------------------------------===;
51+
; Test 4: @goo - i64 add should not be replaced, because @add only accepts i32
52+
; Ensures type mismatch prevents transformation
53+
;===------------------------------------------------------------------------===;
54+
55+
; CHECK-LABEL: define i64 @goo(i64 %x, i64 %y)
56+
; CHECK-NEXT: %sum = add i64 %x, %y
57+
; CHECK-NEXT: ret i64 %sum
58+
59+
define i64 @goo(i64 %x, i64 %y) {
60+
%sum = add i64 %x, %y
61+
ret i64 %sum
62+
}
63+
64+
;===------------------------------------------------------------------------===;
65+
; Test 5: @foo_alt - No add instruction here, should remain unchanged
66+
; Also checks that unrelated functions are not transformed
67+
;===------------------------------------------------------------------------===;
68+
69+
70+
; CHECK-NOT: define i64 @add(
71+
; CHECK-LABEL: define i64 @foo_alt(i64 %x)
72+
73+
define i64 @foo_alt(i64 %x) {
74+
ret i64 %x
75+
}
76+
77+
;===------------------------------------------------------------------------===;
78+
; Test 6: @baz - add of mismatched type (i1), should not be replaced
79+
; Verifies the pass skips non-i32 instructions (e.g. bitwise bools)
80+
;===------------------------------------------------------------------------===;
81+
82+
; CHECK-LABEL: define i1 @baz(i1 %a, i1 %b)
83+
; CHECK: %r = add i1 %a, %b
84+
; CHECK-NOT: call i32 @add
85+
86+
define i1 @baz(i1 %a, i1 %b) {
87+
%r = add i1 %a, %b
88+
ret i1 %r
89+
}
90+
91+
;===------------------------------------------------------------------------===;
92+
; Test 7: @multiple_adds - Only i32 adds should be replaced, others kept
93+
; This mixes types to ensure partial transformation
94+
;===------------------------------------------------------------------------===;
95+
96+
; CHECK-LABEL: define void @multiple_adds(i32 %a, i32 %b, i64 %x, i64 %y)
97+
; CHECK: call i32 @add(i32 %a, i32 %b)
98+
; CHECK: %b2 = add i64 %x, %y
99+
100+
define void @multiple_adds(i32 %a, i32 %b, i64 %x, i64 %y) {
101+
%b1 = add i32 %a, %b
102+
%b2 = add i64 %x, %y
103+
call void @use(i32 %b1, i64 %b2)
104+
ret void
105+
}
106+
107+
declare void @use(i32, i64)

0 commit comments

Comments
 (0)