xref: /llvm-project/llvm/tools/llvm-reduce/deltas/ReduceOpcodes.cpp (revision 416f1c465db62d829283f6902ef35e027e127aa7)
1573a5de7SMatt Arsenault //===- ReduceOpcodes.cpp - Specialized Delta Pass -------------------------===//
2573a5de7SMatt Arsenault //
3573a5de7SMatt Arsenault // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4573a5de7SMatt Arsenault // See https://llvm.org/LICENSE.txt for license information.
5573a5de7SMatt Arsenault // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6573a5de7SMatt Arsenault //
7573a5de7SMatt Arsenault //===----------------------------------------------------------------------===//
8573a5de7SMatt Arsenault //
9573a5de7SMatt Arsenault // Try to replace instructions that are likely to codegen to simpler or smaller
10573a5de7SMatt Arsenault // sequences. This is a fuzzy and target specific concept.
11573a5de7SMatt Arsenault //
12573a5de7SMatt Arsenault //===----------------------------------------------------------------------===//
13573a5de7SMatt Arsenault 
14573a5de7SMatt Arsenault #include "ReduceOpcodes.h"
15573a5de7SMatt Arsenault #include "Delta.h"
16573a5de7SMatt Arsenault #include "llvm/IR/IRBuilder.h"
17573a5de7SMatt Arsenault #include "llvm/IR/Instructions.h"
18573a5de7SMatt Arsenault #include "llvm/IR/IntrinsicInst.h"
19573a5de7SMatt Arsenault #include "llvm/IR/Intrinsics.h"
20573a5de7SMatt Arsenault #include "llvm/IR/IntrinsicsAMDGPU.h"
21573a5de7SMatt Arsenault 
22333ffafbSMatt Arsenault using namespace llvm;
23333ffafbSMatt Arsenault 
244638ba7bSMatt Arsenault // Assume outgoing undef arguments aren't relevant.
254638ba7bSMatt Arsenault // TODO: Maybe skip any trivial constant arguments.
264638ba7bSMatt Arsenault static bool shouldIgnoreArgument(const Value *V) {
274638ba7bSMatt Arsenault   return isa<UndefValue>(V);
284638ba7bSMatt Arsenault }
294638ba7bSMatt Arsenault 
30573a5de7SMatt Arsenault static Value *replaceIntrinsic(Module &M, IntrinsicInst *II,
31573a5de7SMatt Arsenault                                Intrinsic::ID NewIID,
32e03f4271SJay Foad                                ArrayRef<Type *> Tys = {}) {
33fa789dffSRahul Joshi   Function *NewFunc = Intrinsic::getOrInsertDeclaration(&M, NewIID, Tys);
34573a5de7SMatt Arsenault   II->setCalledFunction(NewFunc);
35573a5de7SMatt Arsenault   return II;
36573a5de7SMatt Arsenault }
37573a5de7SMatt Arsenault 
383e6f7ab8SMatt Arsenault static Value *reduceIntrinsic(Oracle &O, Module &M, IntrinsicInst *II) {
393e6f7ab8SMatt Arsenault   IRBuilder<> B(II);
403e6f7ab8SMatt Arsenault   switch (II->getIntrinsicID()) {
413e6f7ab8SMatt Arsenault   case Intrinsic::sqrt:
423e6f7ab8SMatt Arsenault     if (O.shouldKeep())
433e6f7ab8SMatt Arsenault       return nullptr;
443e6f7ab8SMatt Arsenault 
453e6f7ab8SMatt Arsenault     return B.CreateFMul(II->getArgOperand(0),
463e6f7ab8SMatt Arsenault                         ConstantFP::get(II->getType(), 2.0));
473e6f7ab8SMatt Arsenault   case Intrinsic::minnum:
483e6f7ab8SMatt Arsenault   case Intrinsic::maxnum:
493e6f7ab8SMatt Arsenault   case Intrinsic::minimum:
503e6f7ab8SMatt Arsenault   case Intrinsic::maximum:
513e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_fmul_legacy:
523e6f7ab8SMatt Arsenault     if (O.shouldKeep())
533e6f7ab8SMatt Arsenault       return nullptr;
543e6f7ab8SMatt Arsenault     return B.CreateFMul(II->getArgOperand(0), II->getArgOperand(1));
553e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_workitem_id_y:
563e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_workitem_id_z:
573e6f7ab8SMatt Arsenault     if (O.shouldKeep())
583e6f7ab8SMatt Arsenault       return nullptr;
593e6f7ab8SMatt Arsenault     return replaceIntrinsic(M, II, Intrinsic::amdgcn_workitem_id_x);
603e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_workgroup_id_y:
613e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_workgroup_id_z:
623e6f7ab8SMatt Arsenault     if (O.shouldKeep())
633e6f7ab8SMatt Arsenault       return nullptr;
643e6f7ab8SMatt Arsenault     return replaceIntrinsic(M, II, Intrinsic::amdgcn_workgroup_id_x);
653e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_div_fixup:
663e6f7ab8SMatt Arsenault   case Intrinsic::amdgcn_fma_legacy:
673e6f7ab8SMatt Arsenault     if (O.shouldKeep())
683e6f7ab8SMatt Arsenault       return nullptr;
693e6f7ab8SMatt Arsenault     return replaceIntrinsic(M, II, Intrinsic::fma, {II->getType()});
703e6f7ab8SMatt Arsenault   default:
713e6f7ab8SMatt Arsenault     return nullptr;
723e6f7ab8SMatt Arsenault   }
733e6f7ab8SMatt Arsenault }
743e6f7ab8SMatt Arsenault 
754638ba7bSMatt Arsenault /// Look for calls that look like they could be replaced with a load or store.
764638ba7bSMatt Arsenault static bool callLooksLikeLoadStore(CallBase *CB, Value *&DataArg,
774638ba7bSMatt Arsenault                                    Value *&PtrArg) {
784638ba7bSMatt Arsenault   const bool IsStore = CB->getType()->isVoidTy();
794638ba7bSMatt Arsenault 
804638ba7bSMatt Arsenault   PtrArg = nullptr;
814638ba7bSMatt Arsenault   DataArg = nullptr;
824638ba7bSMatt Arsenault   for (Value *Arg : CB->args()) {
834638ba7bSMatt Arsenault     if (shouldIgnoreArgument(Arg))
844638ba7bSMatt Arsenault       continue;
854638ba7bSMatt Arsenault 
864638ba7bSMatt Arsenault     if (!Arg->getType()->isSized())
874638ba7bSMatt Arsenault       return false;
884638ba7bSMatt Arsenault 
8935bdcb03SNikita Popov     if (!PtrArg && Arg->getType()->isPointerTy()) {
904638ba7bSMatt Arsenault       PtrArg = Arg;
914638ba7bSMatt Arsenault       continue;
924638ba7bSMatt Arsenault     }
934638ba7bSMatt Arsenault 
944638ba7bSMatt Arsenault     if (!IsStore || DataArg)
954638ba7bSMatt Arsenault       return false;
964638ba7bSMatt Arsenault 
974638ba7bSMatt Arsenault     DataArg = Arg;
984638ba7bSMatt Arsenault   }
994638ba7bSMatt Arsenault 
1004638ba7bSMatt Arsenault   if (IsStore && !DataArg) {
1014638ba7bSMatt Arsenault     // FIXME: For typed pointers, use element type?
1024638ba7bSMatt Arsenault     DataArg = ConstantInt::get(IntegerType::getInt32Ty(CB->getContext()), 0);
1034638ba7bSMatt Arsenault   }
1044638ba7bSMatt Arsenault 
1054638ba7bSMatt Arsenault   // If we didn't find any arguments, we can fill in the pointer.
1064638ba7bSMatt Arsenault   if (!PtrArg) {
1072d209d96SNikita Popov     unsigned AS = CB->getDataLayout().getAllocaAddrSpace();
1084638ba7bSMatt Arsenault 
109*416f1c46SMats Jun Larsen     PointerType *PtrTy = PointerType::get(CB->getContext(), AS);
1104638ba7bSMatt Arsenault 
1114638ba7bSMatt Arsenault     PtrArg = ConstantPointerNull::get(PtrTy);
1124638ba7bSMatt Arsenault   }
1134638ba7bSMatt Arsenault 
1144638ba7bSMatt Arsenault   return true;
1154638ba7bSMatt Arsenault }
1164638ba7bSMatt Arsenault 
1174638ba7bSMatt Arsenault // TODO: Replace 2 pointer argument calls with memcpy
1184638ba7bSMatt Arsenault static Value *tryReplaceCallWithLoadStore(Oracle &O, Module &M, CallBase *CB) {
1194638ba7bSMatt Arsenault   Value *PtrArg = nullptr;
1204638ba7bSMatt Arsenault   Value *DataArg = nullptr;
1214638ba7bSMatt Arsenault   if (!callLooksLikeLoadStore(CB, DataArg, PtrArg) || O.shouldKeep())
1224638ba7bSMatt Arsenault     return nullptr;
1234638ba7bSMatt Arsenault 
1244638ba7bSMatt Arsenault   IRBuilder<> B(CB);
1254638ba7bSMatt Arsenault   if (DataArg)
1264638ba7bSMatt Arsenault     return B.CreateStore(DataArg, PtrArg, true);
1274638ba7bSMatt Arsenault   return B.CreateLoad(CB->getType(), PtrArg, true);
1284638ba7bSMatt Arsenault }
1294638ba7bSMatt Arsenault 
1304638ba7bSMatt Arsenault static bool callLooksLikeOperator(CallBase *CB,
1314638ba7bSMatt Arsenault                                   SmallVectorImpl<Value *> &OperatorArgs) {
1324638ba7bSMatt Arsenault   Type *ReturnTy = CB->getType();
1334638ba7bSMatt Arsenault   if (!ReturnTy->isFirstClassType())
1344638ba7bSMatt Arsenault     return false;
1354638ba7bSMatt Arsenault 
1364638ba7bSMatt Arsenault   for (Value *Arg : CB->args()) {
1374638ba7bSMatt Arsenault     if (shouldIgnoreArgument(Arg))
1384638ba7bSMatt Arsenault       continue;
1394638ba7bSMatt Arsenault 
1404638ba7bSMatt Arsenault     if (Arg->getType() != ReturnTy)
1414638ba7bSMatt Arsenault       return false;
1424638ba7bSMatt Arsenault 
1434638ba7bSMatt Arsenault     OperatorArgs.push_back(Arg);
1444638ba7bSMatt Arsenault   }
1454638ba7bSMatt Arsenault 
1464638ba7bSMatt Arsenault   return true;
1474638ba7bSMatt Arsenault }
1484638ba7bSMatt Arsenault 
1494638ba7bSMatt Arsenault static Value *tryReplaceCallWithOperator(Oracle &O, Module &M, CallBase *CB) {
1504638ba7bSMatt Arsenault   SmallVector<Value *, 4> Arguments;
1514638ba7bSMatt Arsenault 
1524638ba7bSMatt Arsenault   if (!callLooksLikeOperator(CB, Arguments) || Arguments.size() > 3)
1534638ba7bSMatt Arsenault     return nullptr;
1544638ba7bSMatt Arsenault 
1554638ba7bSMatt Arsenault   if (O.shouldKeep())
1564638ba7bSMatt Arsenault     return nullptr;
1574638ba7bSMatt Arsenault 
1584638ba7bSMatt Arsenault   IRBuilder<> B(CB);
1594638ba7bSMatt Arsenault   if (CB->getType()->isFPOrFPVectorTy()) {
1604638ba7bSMatt Arsenault     switch (Arguments.size()) {
1614638ba7bSMatt Arsenault     case 1:
1624638ba7bSMatt Arsenault       return B.CreateFNeg(Arguments[0]);
1634638ba7bSMatt Arsenault     case 2:
1644638ba7bSMatt Arsenault       return B.CreateFMul(Arguments[0], Arguments[1]);
1654638ba7bSMatt Arsenault     case 3:
1664638ba7bSMatt Arsenault       return B.CreateIntrinsic(Intrinsic::fma, {CB->getType()}, Arguments);
1674638ba7bSMatt Arsenault     default:
1684638ba7bSMatt Arsenault       return nullptr;
1694638ba7bSMatt Arsenault     }
1704638ba7bSMatt Arsenault 
1714638ba7bSMatt Arsenault     llvm_unreachable("all argument sizes handled");
1724638ba7bSMatt Arsenault   }
1734638ba7bSMatt Arsenault 
1744638ba7bSMatt Arsenault   if (CB->getType()->isIntOrIntVectorTy()) {
1754638ba7bSMatt Arsenault     switch (Arguments.size()) {
1764638ba7bSMatt Arsenault     case 1:
1774638ba7bSMatt Arsenault       return B.CreateUnaryIntrinsic(Intrinsic::bswap, Arguments[0]);
1784638ba7bSMatt Arsenault     case 2:
1794638ba7bSMatt Arsenault       return B.CreateAnd(Arguments[0], Arguments[1]);
1804638ba7bSMatt Arsenault     case 3:
1814638ba7bSMatt Arsenault       return B.CreateIntrinsic(Intrinsic::fshl, {CB->getType()}, Arguments);
1824638ba7bSMatt Arsenault     default:
1834638ba7bSMatt Arsenault       return nullptr;
1844638ba7bSMatt Arsenault     }
1854638ba7bSMatt Arsenault 
1864638ba7bSMatt Arsenault     llvm_unreachable("all argument sizes handled");
1874638ba7bSMatt Arsenault   }
1884638ba7bSMatt Arsenault 
1894638ba7bSMatt Arsenault   return nullptr;
1904638ba7bSMatt Arsenault }
1914638ba7bSMatt Arsenault 
1923e6f7ab8SMatt Arsenault static Value *reduceInstruction(Oracle &O, Module &M, Instruction &I) {
193573a5de7SMatt Arsenault   IRBuilder<> B(&I);
1944638ba7bSMatt Arsenault 
1954638ba7bSMatt Arsenault   // TODO: fp binary operator with constant to fneg
196573a5de7SMatt Arsenault   switch (I.getOpcode()) {
197573a5de7SMatt Arsenault   case Instruction::FDiv:
198573a5de7SMatt Arsenault   case Instruction::FRem:
1993e6f7ab8SMatt Arsenault     if (O.shouldKeep())
2003e6f7ab8SMatt Arsenault       return nullptr;
2013e6f7ab8SMatt Arsenault 
202573a5de7SMatt Arsenault     // Divisions tends to codegen into a long sequence or a library call.
203573a5de7SMatt Arsenault     return B.CreateFMul(I.getOperand(0), I.getOperand(1));
204573a5de7SMatt Arsenault   case Instruction::UDiv:
205573a5de7SMatt Arsenault   case Instruction::SDiv:
206573a5de7SMatt Arsenault   case Instruction::URem:
207573a5de7SMatt Arsenault   case Instruction::SRem:
2083e6f7ab8SMatt Arsenault     if (O.shouldKeep())
2093e6f7ab8SMatt Arsenault       return nullptr;
2103e6f7ab8SMatt Arsenault 
211573a5de7SMatt Arsenault     // Divisions tends to codegen into a long sequence or a library call.
212573a5de7SMatt Arsenault     return B.CreateMul(I.getOperand(0), I.getOperand(1));
213573a5de7SMatt Arsenault   case Instruction::Add:
214573a5de7SMatt Arsenault   case Instruction::Sub: {
2153e6f7ab8SMatt Arsenault     if (O.shouldKeep())
2163e6f7ab8SMatt Arsenault       return nullptr;
2173e6f7ab8SMatt Arsenault 
218573a5de7SMatt Arsenault     // Add/sub are more likely codegen to instructions with carry out side
219573a5de7SMatt Arsenault     // effects.
220573a5de7SMatt Arsenault     return B.CreateOr(I.getOperand(0), I.getOperand(1));
221573a5de7SMatt Arsenault   }
222573a5de7SMatt Arsenault   case Instruction::Call: {
2233e6f7ab8SMatt Arsenault     if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I))
2243e6f7ab8SMatt Arsenault       return reduceIntrinsic(O, M, II);
225573a5de7SMatt Arsenault 
2264638ba7bSMatt Arsenault     CallBase *CB = cast<CallBase>(&I);
2274638ba7bSMatt Arsenault 
2284638ba7bSMatt Arsenault     if (Value *NewOp = tryReplaceCallWithOperator(O, M, CB))
2294638ba7bSMatt Arsenault       return NewOp;
2304638ba7bSMatt Arsenault 
2314638ba7bSMatt Arsenault     if (Value *NewOp = tryReplaceCallWithLoadStore(O, M, CB))
2324638ba7bSMatt Arsenault       return NewOp;
2334638ba7bSMatt Arsenault 
234573a5de7SMatt Arsenault     return nullptr;
235573a5de7SMatt Arsenault   }
236573a5de7SMatt Arsenault   default:
237573a5de7SMatt Arsenault     return nullptr;
238573a5de7SMatt Arsenault   }
239573a5de7SMatt Arsenault 
240573a5de7SMatt Arsenault   return nullptr;
241573a5de7SMatt Arsenault }
242573a5de7SMatt Arsenault 
24323cc36e4SMatt Arsenault static void replaceOpcodesInModule(Oracle &O, ReducerWorkItem &WorkItem) {
24423cc36e4SMatt Arsenault   Module &Mod = WorkItem.getModule();
24523cc36e4SMatt Arsenault 
246573a5de7SMatt Arsenault   for (Function &F : Mod) {
247573a5de7SMatt Arsenault     for (BasicBlock &BB : F)
248573a5de7SMatt Arsenault       for (Instruction &I : make_early_inc_range(BB)) {
249573a5de7SMatt Arsenault         Instruction *Replacement =
2503e6f7ab8SMatt Arsenault             dyn_cast_or_null<Instruction>(reduceInstruction(O, Mod, I));
251573a5de7SMatt Arsenault         if (Replacement && Replacement != &I) {
252988025adSKazu Hirata           if (isa<FPMathOperator>(Replacement))
253573a5de7SMatt Arsenault             Replacement->copyFastMathFlags(&I);
254573a5de7SMatt Arsenault 
255573a5de7SMatt Arsenault           Replacement->copyIRFlags(&I);
256573a5de7SMatt Arsenault           Replacement->copyMetadata(I);
257573a5de7SMatt Arsenault           Replacement->takeName(&I);
258573a5de7SMatt Arsenault           I.replaceAllUsesWith(Replacement);
259573a5de7SMatt Arsenault           I.eraseFromParent();
260573a5de7SMatt Arsenault         }
261573a5de7SMatt Arsenault       }
262573a5de7SMatt Arsenault   }
263573a5de7SMatt Arsenault }
264573a5de7SMatt Arsenault 
265573a5de7SMatt Arsenault void llvm::reduceOpcodesDeltaPass(TestRunner &Test) {
2662592ccdeSArthur Eubanks   runDeltaPass(Test, replaceOpcodesInModule, "Reducing Opcodes");
267573a5de7SMatt Arsenault }
268