ReduceOpcodes.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //===- ReduceOpcodes.cpp - Specialized Delta Pass -------------------------===//
  2. //
  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
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Try to replace instructions that are likely to codegen to simpler or smaller
  10. // sequences. This is a fuzzy and target specific concept.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "ReduceOpcodes.h"
  14. #include "Delta.h"
  15. #include "llvm/IR/IRBuilder.h"
  16. #include "llvm/IR/Instructions.h"
  17. #include "llvm/IR/IntrinsicInst.h"
  18. #include "llvm/IR/Intrinsics.h"
  19. #include "llvm/IR/IntrinsicsAMDGPU.h"
  20. using namespace llvm;
  21. // Assume outgoing undef arguments aren't relevant.
  22. // TODO: Maybe skip any trivial constant arguments.
  23. static bool shouldIgnoreArgument(const Value *V) {
  24. return isa<UndefValue>(V);
  25. }
  26. static Value *replaceIntrinsic(Module &M, IntrinsicInst *II,
  27. Intrinsic::ID NewIID,
  28. ArrayRef<Type *> Tys = std::nullopt) {
  29. Function *NewFunc = Intrinsic::getDeclaration(&M, NewIID, Tys);
  30. II->setCalledFunction(NewFunc);
  31. return II;
  32. }
  33. static Value *reduceIntrinsic(Oracle &O, Module &M, IntrinsicInst *II) {
  34. IRBuilder<> B(II);
  35. switch (II->getIntrinsicID()) {
  36. case Intrinsic::sqrt:
  37. if (O.shouldKeep())
  38. return nullptr;
  39. return B.CreateFMul(II->getArgOperand(0),
  40. ConstantFP::get(II->getType(), 2.0));
  41. case Intrinsic::minnum:
  42. case Intrinsic::maxnum:
  43. case Intrinsic::minimum:
  44. case Intrinsic::maximum:
  45. case Intrinsic::amdgcn_fmul_legacy:
  46. if (O.shouldKeep())
  47. return nullptr;
  48. return B.CreateFMul(II->getArgOperand(0), II->getArgOperand(1));
  49. case Intrinsic::amdgcn_workitem_id_y:
  50. case Intrinsic::amdgcn_workitem_id_z:
  51. if (O.shouldKeep())
  52. return nullptr;
  53. return replaceIntrinsic(M, II, Intrinsic::amdgcn_workitem_id_x);
  54. case Intrinsic::amdgcn_workgroup_id_y:
  55. case Intrinsic::amdgcn_workgroup_id_z:
  56. if (O.shouldKeep())
  57. return nullptr;
  58. return replaceIntrinsic(M, II, Intrinsic::amdgcn_workgroup_id_x);
  59. case Intrinsic::amdgcn_div_fixup:
  60. case Intrinsic::amdgcn_fma_legacy:
  61. if (O.shouldKeep())
  62. return nullptr;
  63. return replaceIntrinsic(M, II, Intrinsic::fma, {II->getType()});
  64. default:
  65. return nullptr;
  66. }
  67. }
  68. /// Look for calls that look like they could be replaced with a load or store.
  69. static bool callLooksLikeLoadStore(CallBase *CB, Value *&DataArg,
  70. Value *&PtrArg) {
  71. const bool IsStore = CB->getType()->isVoidTy();
  72. PtrArg = nullptr;
  73. DataArg = nullptr;
  74. for (Value *Arg : CB->args()) {
  75. if (shouldIgnoreArgument(Arg))
  76. continue;
  77. if (!Arg->getType()->isSized())
  78. return false;
  79. PointerType *PT = dyn_cast<PointerType>(Arg->getType());
  80. if (!PtrArg && PT) {
  81. // FIXME: Could create bitcast for typed pointers, but roll back unused
  82. // replacement only erases one instruction.
  83. if (!IsStore && !PT->isOpaqueOrPointeeTypeMatches(CB->getType()))
  84. return false;
  85. PtrArg = Arg;
  86. continue;
  87. }
  88. if (!IsStore || DataArg)
  89. return false;
  90. DataArg = Arg;
  91. }
  92. if (IsStore && !DataArg) {
  93. // FIXME: For typed pointers, use element type?
  94. DataArg = ConstantInt::get(IntegerType::getInt32Ty(CB->getContext()), 0);
  95. }
  96. // If we didn't find any arguments, we can fill in the pointer.
  97. if (!PtrArg) {
  98. unsigned AS = CB->getModule()->getDataLayout().getAllocaAddrSpace();
  99. PointerType *PtrTy =
  100. PointerType::get(DataArg ? DataArg->getType()
  101. : IntegerType::getInt32Ty(CB->getContext()),
  102. AS);
  103. PtrArg = ConstantPointerNull::get(PtrTy);
  104. }
  105. // Make sure we don't emit an invalid store with typed pointers.
  106. if (IsStore && DataArg->getType()->getPointerTo(
  107. cast<PointerType>(PtrArg->getType())->getAddressSpace()) !=
  108. PtrArg->getType())
  109. return false;
  110. return true;
  111. }
  112. // TODO: Replace 2 pointer argument calls with memcpy
  113. static Value *tryReplaceCallWithLoadStore(Oracle &O, Module &M, CallBase *CB) {
  114. Value *PtrArg = nullptr;
  115. Value *DataArg = nullptr;
  116. if (!callLooksLikeLoadStore(CB, DataArg, PtrArg) || O.shouldKeep())
  117. return nullptr;
  118. IRBuilder<> B(CB);
  119. if (DataArg)
  120. return B.CreateStore(DataArg, PtrArg, true);
  121. return B.CreateLoad(CB->getType(), PtrArg, true);
  122. }
  123. static bool callLooksLikeOperator(CallBase *CB,
  124. SmallVectorImpl<Value *> &OperatorArgs) {
  125. Type *ReturnTy = CB->getType();
  126. if (!ReturnTy->isFirstClassType())
  127. return false;
  128. for (Value *Arg : CB->args()) {
  129. if (shouldIgnoreArgument(Arg))
  130. continue;
  131. if (Arg->getType() != ReturnTy)
  132. return false;
  133. OperatorArgs.push_back(Arg);
  134. }
  135. return true;
  136. }
  137. static Value *tryReplaceCallWithOperator(Oracle &O, Module &M, CallBase *CB) {
  138. SmallVector<Value *, 4> Arguments;
  139. if (!callLooksLikeOperator(CB, Arguments) || Arguments.size() > 3)
  140. return nullptr;
  141. if (O.shouldKeep())
  142. return nullptr;
  143. IRBuilder<> B(CB);
  144. if (CB->getType()->isFPOrFPVectorTy()) {
  145. switch (Arguments.size()) {
  146. case 1:
  147. return B.CreateFNeg(Arguments[0]);
  148. case 2:
  149. return B.CreateFMul(Arguments[0], Arguments[1]);
  150. case 3:
  151. return B.CreateIntrinsic(Intrinsic::fma, {CB->getType()}, Arguments);
  152. default:
  153. return nullptr;
  154. }
  155. llvm_unreachable("all argument sizes handled");
  156. }
  157. if (CB->getType()->isIntOrIntVectorTy()) {
  158. switch (Arguments.size()) {
  159. case 1:
  160. return B.CreateUnaryIntrinsic(Intrinsic::bswap, Arguments[0]);
  161. case 2:
  162. return B.CreateAnd(Arguments[0], Arguments[1]);
  163. case 3:
  164. return B.CreateIntrinsic(Intrinsic::fshl, {CB->getType()}, Arguments);
  165. default:
  166. return nullptr;
  167. }
  168. llvm_unreachable("all argument sizes handled");
  169. }
  170. return nullptr;
  171. }
  172. static Value *reduceInstruction(Oracle &O, Module &M, Instruction &I) {
  173. IRBuilder<> B(&I);
  174. // TODO: fp binary operator with constant to fneg
  175. switch (I.getOpcode()) {
  176. case Instruction::FDiv:
  177. case Instruction::FRem:
  178. if (O.shouldKeep())
  179. return nullptr;
  180. // Divisions tends to codegen into a long sequence or a library call.
  181. return B.CreateFMul(I.getOperand(0), I.getOperand(1));
  182. case Instruction::UDiv:
  183. case Instruction::SDiv:
  184. case Instruction::URem:
  185. case Instruction::SRem:
  186. if (O.shouldKeep())
  187. return nullptr;
  188. // Divisions tends to codegen into a long sequence or a library call.
  189. return B.CreateMul(I.getOperand(0), I.getOperand(1));
  190. case Instruction::Add:
  191. case Instruction::Sub: {
  192. if (O.shouldKeep())
  193. return nullptr;
  194. // Add/sub are more likely codegen to instructions with carry out side
  195. // effects.
  196. return B.CreateOr(I.getOperand(0), I.getOperand(1));
  197. }
  198. case Instruction::Call: {
  199. if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I))
  200. return reduceIntrinsic(O, M, II);
  201. CallBase *CB = cast<CallBase>(&I);
  202. if (Value *NewOp = tryReplaceCallWithOperator(O, M, CB))
  203. return NewOp;
  204. if (Value *NewOp = tryReplaceCallWithLoadStore(O, M, CB))
  205. return NewOp;
  206. return nullptr;
  207. }
  208. default:
  209. return nullptr;
  210. }
  211. return nullptr;
  212. }
  213. static void replaceOpcodesInModule(Oracle &O, ReducerWorkItem &WorkItem) {
  214. Module &Mod = WorkItem.getModule();
  215. for (Function &F : Mod) {
  216. for (BasicBlock &BB : F)
  217. for (Instruction &I : make_early_inc_range(BB)) {
  218. Instruction *Replacement =
  219. dyn_cast_or_null<Instruction>(reduceInstruction(O, Mod, I));
  220. if (Replacement && Replacement != &I) {
  221. if (isa<FPMathOperator>(Replacement))
  222. Replacement->copyFastMathFlags(&I);
  223. Replacement->copyIRFlags(&I);
  224. Replacement->copyMetadata(I);
  225. Replacement->takeName(&I);
  226. I.replaceAllUsesWith(Replacement);
  227. I.eraseFromParent();
  228. }
  229. }
  230. }
  231. }
  232. void llvm::reduceOpcodesDeltaPass(TestRunner &Test) {
  233. runDeltaPass(Test, replaceOpcodesInModule, "Reducing Opcodes");
  234. }