brintos

brintos / llvm-project-archived public Read only

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