1 //===- ReduceOpcodes.cpp - Specialized Delta Pass -------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Try to replace instructions that are likely to codegen to simpler or smaller
10 // sequences. This is a fuzzy and target specific concept.
12 //===----------------------------------------------------------------------===//
14 #include "ReduceOpcodes.h"
16 #include "llvm/IR/IRBuilder.h"
17 #include "llvm/IR/Instructions.h"
18 #include "llvm/IR/IntrinsicInst.h"
19 #include "llvm/IR/Intrinsics.h"
20 #include "llvm/IR/IntrinsicsAMDGPU.h"
24 // Assume outgoing undef arguments aren't relevant.
25 // TODO: Maybe skip any trivial constant arguments.
26 static bool shouldIgnoreArgument(const Value
*V
) {
27 return isa
<UndefValue
>(V
);
30 static Value
*replaceIntrinsic(Module
&M
, IntrinsicInst
*II
,
32 ArrayRef
<Type
*> Tys
= std::nullopt
) {
33 Function
*NewFunc
= Intrinsic::getDeclaration(&M
, NewIID
, Tys
);
34 II
->setCalledFunction(NewFunc
);
38 static Value
*reduceIntrinsic(Oracle
&O
, Module
&M
, IntrinsicInst
*II
) {
40 switch (II
->getIntrinsicID()) {
45 return B
.CreateFMul(II
->getArgOperand(0),
46 ConstantFP::get(II
->getType(), 2.0));
47 case Intrinsic::minnum
:
48 case Intrinsic::maxnum
:
49 case Intrinsic::minimum
:
50 case Intrinsic::maximum
:
51 case Intrinsic::amdgcn_fmul_legacy
:
54 return B
.CreateFMul(II
->getArgOperand(0), II
->getArgOperand(1));
55 case Intrinsic::amdgcn_workitem_id_y
:
56 case Intrinsic::amdgcn_workitem_id_z
:
59 return replaceIntrinsic(M
, II
, Intrinsic::amdgcn_workitem_id_x
);
60 case Intrinsic::amdgcn_workgroup_id_y
:
61 case Intrinsic::amdgcn_workgroup_id_z
:
64 return replaceIntrinsic(M
, II
, Intrinsic::amdgcn_workgroup_id_x
);
65 case Intrinsic::amdgcn_div_fixup
:
66 case Intrinsic::amdgcn_fma_legacy
:
69 return replaceIntrinsic(M
, II
, Intrinsic::fma
, {II
->getType()});
75 /// Look for calls that look like they could be replaced with a load or store.
76 static bool callLooksLikeLoadStore(CallBase
*CB
, Value
*&DataArg
,
78 const bool IsStore
= CB
->getType()->isVoidTy();
82 for (Value
*Arg
: CB
->args()) {
83 if (shouldIgnoreArgument(Arg
))
86 if (!Arg
->getType()->isSized())
89 if (!PtrArg
&& Arg
->getType()->isPointerTy()) {
94 if (!IsStore
|| DataArg
)
100 if (IsStore
&& !DataArg
) {
101 // FIXME: For typed pointers, use element type?
102 DataArg
= ConstantInt::get(IntegerType::getInt32Ty(CB
->getContext()), 0);
105 // If we didn't find any arguments, we can fill in the pointer.
107 unsigned AS
= CB
->getModule()->getDataLayout().getAllocaAddrSpace();
110 PointerType::get(DataArg
? DataArg
->getType()
111 : IntegerType::getInt32Ty(CB
->getContext()),
114 PtrArg
= ConstantPointerNull::get(PtrTy
);
117 // Make sure we don't emit an invalid store with typed pointers.
118 if (IsStore
&& DataArg
->getType()->getPointerTo(
119 cast
<PointerType
>(PtrArg
->getType())->getAddressSpace()) !=
126 // TODO: Replace 2 pointer argument calls with memcpy
127 static Value
*tryReplaceCallWithLoadStore(Oracle
&O
, Module
&M
, CallBase
*CB
) {
128 Value
*PtrArg
= nullptr;
129 Value
*DataArg
= nullptr;
130 if (!callLooksLikeLoadStore(CB
, DataArg
, PtrArg
) || O
.shouldKeep())
135 return B
.CreateStore(DataArg
, PtrArg
, true);
136 return B
.CreateLoad(CB
->getType(), PtrArg
, true);
139 static bool callLooksLikeOperator(CallBase
*CB
,
140 SmallVectorImpl
<Value
*> &OperatorArgs
) {
141 Type
*ReturnTy
= CB
->getType();
142 if (!ReturnTy
->isFirstClassType())
145 for (Value
*Arg
: CB
->args()) {
146 if (shouldIgnoreArgument(Arg
))
149 if (Arg
->getType() != ReturnTy
)
152 OperatorArgs
.push_back(Arg
);
158 static Value
*tryReplaceCallWithOperator(Oracle
&O
, Module
&M
, CallBase
*CB
) {
159 SmallVector
<Value
*, 4> Arguments
;
161 if (!callLooksLikeOperator(CB
, Arguments
) || Arguments
.size() > 3)
168 if (CB
->getType()->isFPOrFPVectorTy()) {
169 switch (Arguments
.size()) {
171 return B
.CreateFNeg(Arguments
[0]);
173 return B
.CreateFMul(Arguments
[0], Arguments
[1]);
175 return B
.CreateIntrinsic(Intrinsic::fma
, {CB
->getType()}, Arguments
);
180 llvm_unreachable("all argument sizes handled");
183 if (CB
->getType()->isIntOrIntVectorTy()) {
184 switch (Arguments
.size()) {
186 return B
.CreateUnaryIntrinsic(Intrinsic::bswap
, Arguments
[0]);
188 return B
.CreateAnd(Arguments
[0], Arguments
[1]);
190 return B
.CreateIntrinsic(Intrinsic::fshl
, {CB
->getType()}, Arguments
);
195 llvm_unreachable("all argument sizes handled");
201 static Value
*reduceInstruction(Oracle
&O
, Module
&M
, Instruction
&I
) {
204 // TODO: fp binary operator with constant to fneg
205 switch (I
.getOpcode()) {
206 case Instruction::FDiv
:
207 case Instruction::FRem
:
211 // Divisions tends to codegen into a long sequence or a library call.
212 return B
.CreateFMul(I
.getOperand(0), I
.getOperand(1));
213 case Instruction::UDiv
:
214 case Instruction::SDiv
:
215 case Instruction::URem
:
216 case Instruction::SRem
:
220 // Divisions tends to codegen into a long sequence or a library call.
221 return B
.CreateMul(I
.getOperand(0), I
.getOperand(1));
222 case Instruction::Add
:
223 case Instruction::Sub
: {
227 // Add/sub are more likely codegen to instructions with carry out side
229 return B
.CreateOr(I
.getOperand(0), I
.getOperand(1));
231 case Instruction::Call
: {
232 if (IntrinsicInst
*II
= dyn_cast
<IntrinsicInst
>(&I
))
233 return reduceIntrinsic(O
, M
, II
);
235 CallBase
*CB
= cast
<CallBase
>(&I
);
237 if (Value
*NewOp
= tryReplaceCallWithOperator(O
, M
, CB
))
240 if (Value
*NewOp
= tryReplaceCallWithLoadStore(O
, M
, CB
))
252 static void replaceOpcodesInModule(Oracle
&O
, ReducerWorkItem
&WorkItem
) {
253 Module
&Mod
= WorkItem
.getModule();
255 for (Function
&F
: Mod
) {
256 for (BasicBlock
&BB
: F
)
257 for (Instruction
&I
: make_early_inc_range(BB
)) {
258 Instruction
*Replacement
=
259 dyn_cast_or_null
<Instruction
>(reduceInstruction(O
, Mod
, I
));
260 if (Replacement
&& Replacement
!= &I
) {
261 if (isa
<FPMathOperator
>(Replacement
))
262 Replacement
->copyFastMathFlags(&I
);
264 Replacement
->copyIRFlags(&I
);
265 Replacement
->copyMetadata(I
);
266 Replacement
->takeName(&I
);
267 I
.replaceAllUsesWith(Replacement
);
274 void llvm::reduceOpcodesDeltaPass(TestRunner
&Test
) {
275 runDeltaPass(Test
, replaceOpcodesInModule
, "Reducing Opcodes");