Skip to content

Commit 735b466

Browse files
authored
Маркин Иван. Лабораторная работа 2: LLVM. Вариант 3. (#158)
Пасс, который объединяет инструкции fadd и fmul, в fmuladd, для ускорения, повышения точности и уменьшения количества инструкций.
1 parent b0293f6 commit 735b466

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
set(Title "FmuladdMergePass")
2+
set(Student "MarkinIvan")
3+
set(Group "FIIT2")
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: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include "llvm/IR/Function.h"
2+
#include "llvm/IR/IRBuilder.h"
3+
#include "llvm/IR/Instructions.h"
4+
#include "llvm/Passes/PassBuilder.h"
5+
#include "llvm/Passes/PassPlugin.h"
6+
#include "llvm/Support/raw_ostream.h"
7+
8+
namespace {
9+
10+
struct FmulFaddMergePass final : llvm::PassInfoMixin<FmulFaddMergePass> {
11+
llvm::PreservedAnalyses run(llvm::Function &F,
12+
llvm::FunctionAnalysisManager &FAM) {
13+
bool codeChanged = false;
14+
15+
struct ReplaceCandidate {
16+
llvm::BinaryOperator *faddInst;
17+
llvm::BinaryOperator *fmulInst;
18+
llvm::Value *otherOperand;
19+
};
20+
21+
for (llvm::BasicBlock &block : F) {
22+
std::vector<ReplaceCandidate> candidates;
23+
24+
auto isFMulOperand = [](llvm::Value *op,
25+
llvm::Value *other) -> llvm::BinaryOperator * {
26+
if (auto *fmul = llvm::dyn_cast<llvm::BinaryOperator>(op)) {
27+
if (fmul->getOpcode() == llvm::Instruction::FMul) {
28+
return fmul;
29+
}
30+
}
31+
return nullptr;
32+
};
33+
34+
// Collect potential replacements
35+
for (llvm::Instruction &inst : block) {
36+
if (auto *fadd = llvm::dyn_cast<llvm::BinaryOperator>(&inst)) {
37+
if (fadd->getOpcode() == llvm::Instruction::FAdd) {
38+
llvm::Value *op0 = fadd->getOperand(0);
39+
llvm::Value *op1 = fadd->getOperand(1);
40+
41+
if (auto *fmul = isFMulOperand(op0, op1)) {
42+
if (fmul->hasAllowContract() && fadd->hasAllowContract()) {
43+
candidates.push_back({fadd, fmul, op1});
44+
}
45+
} else if (auto *fmul = isFMulOperand(op1, op0)) {
46+
if (fmul->hasAllowContract() && fadd->hasAllowContract()) {
47+
candidates.push_back({fadd, fmul, op0});
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
// Perform replacements
55+
for (const auto &candidate : candidates) {
56+
llvm::IRBuilder<> builder(candidate.faddInst);
57+
llvm::FastMathFlags flags = candidate.faddInst->getFastMathFlags();
58+
llvm::Value *fmuladd = builder.CreateIntrinsic(
59+
llvm::Intrinsic::fmuladd, candidate.fmulInst->getType(),
60+
{candidate.fmulInst->getOperand(0),
61+
candidate.fmulInst->getOperand(1), candidate.otherOperand});
62+
63+
if (llvm::isa<llvm::FPMathOperator>(fmuladd)) {
64+
llvm::FPMathOperator *fmuladdInst =
65+
llvm::cast<llvm::FPMathOperator>(fmuladd);
66+
}
67+
68+
candidate.faddInst->replaceAllUsesWith(fmuladd);
69+
candidate.faddInst->eraseFromParent();
70+
71+
if (candidate.fmulInst->use_empty()) {
72+
candidate.fmulInst->eraseFromParent();
73+
}
74+
codeChanged = true;
75+
}
76+
}
77+
78+
return codeChanged ? llvm::PreservedAnalyses::none()
79+
: llvm::PreservedAnalyses::all();
80+
}
81+
82+
static bool isRequired() { return true; }
83+
};
84+
85+
} // namespace
86+
87+
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
88+
llvmGetPassPluginInfo() {
89+
return {LLVM_PLUGIN_API_VERSION, "FmulFaddMergePass", "0.1",
90+
[](llvm::PassBuilder &PB) {
91+
PB.registerPipelineParsingCallback(
92+
[](llvm::StringRef passName, llvm::FunctionPassManager &FPM,
93+
llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) -> bool {
94+
if (passName == "FmulFaddMergePass") {
95+
FPM.addPass(FmulFaddMergePass{});
96+
return true;
97+
}
98+
return false;
99+
});
100+
}};
101+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
; RUN: opt -load-pass-plugin %llvmshlibdir/FmuladdMergePass_MarkinIvan_FIIT2_LLVM_IR%pluginext \
2+
; RUN: -passes="FmulFaddMergePass" -S %s | FileCheck %s
3+
4+
; CHECK-LABEL: @fmaDouble
5+
; CHECK: call double @llvm.fmuladd.f64(double %A, double %B, double %C)
6+
; CHECK-NOT: fmul
7+
; CHECK-NOT: fadd
8+
define dso_local noundef double @fmaDouble(double %A, double %B, double %C) {
9+
entry:
10+
%mul = fmul contract double %A, %B
11+
%add = fadd contract double %mul, %C
12+
ret double %add
13+
}
14+
15+
; CHECK-LABEL: @fmaFloat
16+
; CHECK: call float @llvm.fmuladd.f32(float %A, float %B, float %C)
17+
; CHECK-NOT: fmul
18+
; CHECK-NOT: fadd
19+
define dso_local noundef float @fmaFloat(float %A, float %B, float %C) {
20+
entry:
21+
%mul = fmul contract float %A, %B
22+
%add = fadd contract float %mul, %C
23+
ret float %add
24+
}
25+
26+
; CHECK-LABEL: @recursiveTest
27+
; CHECK: call float @llvm.fmuladd.f32(float %add1, float %B, float %add)
28+
; CHECK-NOT: fmul
29+
; CHECK-NOT: fadd
30+
define dso_local noundef float @recursiveTest(float %A, float %B, float %C) {
31+
entry:
32+
%add = fadd contract float %B, %C
33+
%add1 = fadd contract float %A, %B
34+
%mul = fmul contract float %add1, %B
35+
%add2 = fadd contract float %add, %mul
36+
ret float %add2
37+
}
38+
39+
; CHECK-LABEL: @foo
40+
; CHECK: %0 = call float @llvm.fmuladd.f32(float %A, float %B, float %C)
41+
; CHECK: %1 = call float @llvm.fmuladd.f32(float %A, float %B, float 1.000000e+00)
42+
; CHECK: %div = fdiv float %0, %1
43+
; CHECK-NOT: %mul = fmul contract float %A, %B
44+
; CHECK-NOT: fadd
45+
define float @foo(float %A, float %B, float %C) {
46+
entry:
47+
%mul = fmul contract float %A, %B
48+
%add = fadd contract float %mul, %C
49+
%add1 = fadd contract float %mul, 1.000000e+00
50+
%div = fdiv float %add, %add1
51+
ret float %div
52+
}
53+
; CHECK-LABEL: @noContract
54+
; CHECK: fmul float %A, %B
55+
; CHECK: fadd float %mul, %C
56+
define float @noContract(float %A, float %B, float %C) {
57+
entry:
58+
%mul = fmul float %A, %B
59+
%add = fadd float %mul, %C
60+
ret float %add
61+
}
62+
; CHECK-LABEL: @multipleFMA
63+
; CHECK: call float @llvm.fmuladd.f32(float %A, float %B, float %C)
64+
; CHECK: call float @llvm.fmuladd.f32(float %D, float %E, float %F)
65+
define float @multipleFMA(float %A, float %B, float %C, float %D, float %E, float %F) {
66+
entry:
67+
%mul1 = fmul contract float %A, %B
68+
%add1 = fadd contract float %mul1, %C
69+
%mul2 = fmul contract float %D, %E
70+
%add2 = fadd contract float %mul2, %F
71+
ret float %add2
72+
}
73+
; CHECK-LABEL: @differentArduments
74+
; CHECK: fmul contract float %A, %B
75+
; CHECK: fadd contract float %B, %D
76+
define float @differentArduments(float %A, float %B, float %D) {
77+
entry:
78+
%mul = fmul contract float %A, %B
79+
%add = fadd contract float %B, %D
80+
ret float %add
81+
}
82+
; CHECK-LABEL: @differentInstructions
83+
; CHECK: fadd contract float %A, %B
84+
; CHECK: fadd contract float %add1, %D
85+
define float @differentInstructions(float %A, float %B, float %D) {
86+
entry:
87+
%add1 = fadd contract float %A, %B
88+
%add = fadd contract float %add1, %D
89+
ret float %add
90+
}

0 commit comments

Comments
 (0)