brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.3 KiB · fad2b67 Raw
212 lines · cpp
1//===-- SPIRVCombinerHelper.cpp -------------------------------------------===//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#include "SPIRVCombinerHelper.h"10#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"11#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"12#include "llvm/IR/IntrinsicsSPIRV.h"13#include "llvm/Target/TargetMachine.h"14 15using namespace llvm;16using namespace MIPatternMatch;17 18SPIRVCombinerHelper::SPIRVCombinerHelper(19    GISelChangeObserver &Observer, MachineIRBuilder &B, bool IsPreLegalize,20    GISelValueTracking *VT, MachineDominatorTree *MDT, const LegalizerInfo *LI,21    const SPIRVSubtarget &STI)22    : CombinerHelper(Observer, B, IsPreLegalize, VT, MDT, LI), STI(STI) {}23 24/// This match is part of a combine that25/// rewrites length(X - Y) to distance(X, Y)26///   (f32 (g_intrinsic length27///           (g_fsub (vXf32 X) (vXf32 Y))))28/// ->29///   (f32 (g_intrinsic distance30///           (vXf32 X) (vXf32 Y)))31///32bool SPIRVCombinerHelper::matchLengthToDistance(MachineInstr &MI) const {33  if (MI.getOpcode() != TargetOpcode::G_INTRINSIC ||34      cast<GIntrinsic>(MI).getIntrinsicID() != Intrinsic::spv_length)35    return false;36 37  // First operand of MI is `G_INTRINSIC` so start at operand 2.38  Register SubReg = MI.getOperand(2).getReg();39  MachineInstr *SubInstr = MRI.getVRegDef(SubReg);40  if (SubInstr->getOpcode() != TargetOpcode::G_FSUB)41    return false;42 43  return true;44}45 46void SPIRVCombinerHelper::applySPIRVDistance(MachineInstr &MI) const {47  // Extract the operands for X and Y from the match criteria.48  Register SubDestReg = MI.getOperand(2).getReg();49  MachineInstr *SubInstr = MRI.getVRegDef(SubDestReg);50  Register SubOperand1 = SubInstr->getOperand(1).getReg();51  Register SubOperand2 = SubInstr->getOperand(2).getReg();52  Register ResultReg = MI.getOperand(0).getReg();53 54  Builder.setInstrAndDebugLoc(MI);55  Builder.buildIntrinsic(Intrinsic::spv_distance, ResultReg)56      .addUse(SubOperand1)57      .addUse(SubOperand2);58 59  MI.eraseFromParent();60}61 62/// This match is part of a combine that63/// rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N, I, Ng)64///   (vXf32 (g_select65///             (g_fcmp66///                (g_intrinsic dot(vXf32 I) (vXf32 Ng)67///                 0)68///             (vXf32 N)69///             (vXf32 g_fneg (vXf32 N))))70/// ->71///   (vXf32 (g_intrinsic faceforward72///             (vXf32 N) (vXf32 I) (vXf32 Ng)))73///74/// This only works for Vulkan shader targets.75///76bool SPIRVCombinerHelper::matchSelectToFaceForward(MachineInstr &MI) const {77  if (!STI.isShader())78    return false;79 80  // Match overall select pattern.81  Register CondReg, TrueReg, FalseReg;82  if (!mi_match(MI.getOperand(0).getReg(), MRI,83                m_GISelect(m_Reg(CondReg), m_Reg(TrueReg), m_Reg(FalseReg))))84    return false;85 86  // Match the FCMP condition.87  Register DotReg, CondZeroReg;88  CmpInst::Predicate Pred;89  if (!mi_match(CondReg, MRI,90                m_GFCmp(m_Pred(Pred), m_Reg(DotReg), m_Reg(CondZeroReg))) ||91      !(Pred == CmpInst::FCMP_OLT || Pred == CmpInst::FCMP_ULT)) {92    if (!(Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT))93      return false;94    std::swap(DotReg, CondZeroReg);95  }96 97  // Check if FCMP is a comparison between a dot product and 0.98  MachineInstr *DotInstr = MRI.getVRegDef(DotReg);99  if (DotInstr->getOpcode() != TargetOpcode::G_INTRINSIC ||100      cast<GIntrinsic>(DotInstr)->getIntrinsicID() != Intrinsic::spv_fdot) {101    Register DotOperand1, DotOperand2;102    // Check for scalar dot product.103    if (!mi_match(DotReg, MRI,104                  m_GFMul(m_Reg(DotOperand1), m_Reg(DotOperand2))) ||105        !MRI.getType(DotOperand1).isScalar() ||106        !MRI.getType(DotOperand2).isScalar())107      return false;108  }109 110  const ConstantFP *ZeroVal;111  if (!mi_match(CondZeroReg, MRI, m_GFCst(ZeroVal)) || !ZeroVal->isZero())112    return false;113 114  // Check if select's false operand is the negation of the true operand.115  auto AreNegatedConstantsOrSplats = [&](Register TrueReg, Register FalseReg) {116    std::optional<FPValueAndVReg> TrueVal, FalseVal;117    if (!mi_match(TrueReg, MRI, m_GFCstOrSplat(TrueVal)) ||118        !mi_match(FalseReg, MRI, m_GFCstOrSplat(FalseVal)))119      return false;120    APFloat TrueValNegated = TrueVal->Value;121    TrueValNegated.changeSign();122    return FalseVal->Value.compare(TrueValNegated) == APFloat::cmpEqual;123  };124 125  if (!mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg))) &&126      !mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg)))) {127    std::optional<FPValueAndVReg> MulConstant;128    MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);129    MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);130    if (TrueInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&131        FalseInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&132        TrueInstr->getNumOperands() == FalseInstr->getNumOperands()) {133      for (unsigned I = 1; I < TrueInstr->getNumOperands(); ++I)134        if (!AreNegatedConstantsOrSplats(TrueInstr->getOperand(I).getReg(),135                                         FalseInstr->getOperand(I).getReg()))136          return false;137    } else if (mi_match(TrueReg, MRI,138                        m_GFMul(m_SpecificReg(FalseReg),139                                m_GFCstOrSplat(MulConstant))) ||140               mi_match(FalseReg, MRI,141                        m_GFMul(m_SpecificReg(TrueReg),142                                m_GFCstOrSplat(MulConstant))) ||143               mi_match(TrueReg, MRI,144                        m_GFMul(m_GFCstOrSplat(MulConstant),145                                m_SpecificReg(FalseReg))) ||146               mi_match(FalseReg, MRI,147                        m_GFMul(m_GFCstOrSplat(MulConstant),148                                m_SpecificReg(TrueReg)))) {149      if (!MulConstant || !MulConstant->Value.isExactlyValue(-1.0))150        return false;151    } else if (!AreNegatedConstantsOrSplats(TrueReg, FalseReg))152      return false;153  }154 155  return true;156}157 158void SPIRVCombinerHelper::applySPIRVFaceForward(MachineInstr &MI) const {159  // Extract the operands for N, I, and Ng from the match criteria.160  Register CondReg = MI.getOperand(1).getReg();161  MachineInstr *CondInstr = MRI.getVRegDef(CondReg);162  Register DotReg = CondInstr->getOperand(2).getReg();163  CmpInst::Predicate Pred = cast<GFCmp>(CondInstr)->getCond();164  if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)165    DotReg = CondInstr->getOperand(3).getReg();166  MachineInstr *DotInstr = MRI.getVRegDef(DotReg);167  Register DotOperand1, DotOperand2;168  if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) {169    DotOperand1 = DotInstr->getOperand(1).getReg();170    DotOperand2 = DotInstr->getOperand(2).getReg();171  } else {172    DotOperand1 = DotInstr->getOperand(2).getReg();173    DotOperand2 = DotInstr->getOperand(3).getReg();174  }175  Register TrueReg = MI.getOperand(2).getReg();176  Register FalseReg = MI.getOperand(3).getReg();177  MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);178  if (TrueInstr->getOpcode() == TargetOpcode::G_FNEG ||179      TrueInstr->getOpcode() == TargetOpcode::G_FMUL)180    std::swap(TrueReg, FalseReg);181  MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);182 183  Register ResultReg = MI.getOperand(0).getReg();184  Builder.setInstrAndDebugLoc(MI);185  Builder.buildIntrinsic(Intrinsic::spv_faceforward, ResultReg)186      .addUse(TrueReg)      // N187      .addUse(DotOperand1)  // I188      .addUse(DotOperand2); // Ng189 190  SPIRVGlobalRegistry *GR =191      MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();192  auto RemoveAllUses = [&](Register Reg) {193    SmallVector<MachineInstr *, 4> UsesToErase;194    for (auto &UseMI : MRI.use_instructions(Reg))195      UsesToErase.push_back(&UseMI);196 197    // calling eraseFromParent to early invalidates the iterator.198    for (auto *MIToErase : UsesToErase)199      MIToErase->eraseFromParent();200  };201 202  RemoveAllUses(CondReg); // remove all uses of FCMP Result203  GR->invalidateMachineInstr(CondInstr);204  CondInstr->eraseFromParent(); // remove FCMP instruction205  RemoveAllUses(DotReg);        // remove all uses of spv_fdot/G_FMUL Result206  GR->invalidateMachineInstr(DotInstr);207  DotInstr->eraseFromParent(); // remove spv_fdot/G_FMUL instruction208  RemoveAllUses(FalseReg);209  GR->invalidateMachineInstr(FalseInstr);210  FalseInstr->eraseFromParent();211}212