llvm-project/llvm/lib/Target/Mips/MipsLegalizerInfo.cpp
Evgenii Kudriashov d365a45cb3
[GlobalISel] Introduce G_TRAP, G_DEBUGTRAP, G_UBSANTRAP (#84941)
Here we introduce three new GMIR instructions to cover a set of trap
intrinsics. The idea behind it is that generic intrinsics shouldn't be
used with G_INTRINSIC opcode.

These new instructions can match perfectly with existing trap ISD nodes.
It allows X86, AArch64, RISCV and Mips to reuse SelectionDAG patterns for
selection and avoid manual selection. However AMDGPU is an exception. It
selects traps during legalization regardless SelectionDAG or GlobalISel.

Since there are not many places where traps are used, this change
attempts to clean up all the usages of G_INTRINSIC with trap intrinsics. So,
there is no stage when both G_TRAP and
G_INTRINSIC_W_SIDE_EFFECTS(@llvm.trap) are allowed.
2024-03-23 13:12:44 +01:00

602 lines
21 KiB
C++

//===- MipsLegalizerInfo.cpp ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements the targeting of the Machinelegalizer class for Mips.
/// \todo This should be generated by TableGen.
//===----------------------------------------------------------------------===//
#include "MipsLegalizerInfo.h"
#include "MipsTargetMachine.h"
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/IR/IntrinsicsMips.h"
using namespace llvm;
struct TypesAndMemOps {
LLT ValTy;
LLT PtrTy;
unsigned MemSize;
bool SystemSupportsUnalignedAccess;
};
// Assumes power of 2 memory size. Subtargets that have only naturally-aligned
// memory access need to perform additional legalization here.
static bool isUnalignedMemmoryAccess(uint64_t MemSize, uint64_t AlignInBits) {
assert(isPowerOf2_64(MemSize) && "Expected power of 2 memory size");
assert(isPowerOf2_64(AlignInBits) && "Expected power of 2 align");
if (MemSize > AlignInBits)
return true;
return false;
}
static bool
CheckTy0Ty1MemSizeAlign(const LegalityQuery &Query,
std::initializer_list<TypesAndMemOps> SupportedValues) {
unsigned QueryMemSize = Query.MMODescrs[0].MemoryTy.getSizeInBits();
// Non power of two memory access is never legal.
if (!isPowerOf2_64(QueryMemSize))
return false;
for (auto &Val : SupportedValues) {
if (Val.ValTy != Query.Types[0])
continue;
if (Val.PtrTy != Query.Types[1])
continue;
if (Val.MemSize != QueryMemSize)
continue;
if (!Val.SystemSupportsUnalignedAccess &&
isUnalignedMemmoryAccess(QueryMemSize, Query.MMODescrs[0].AlignInBits))
return false;
return true;
}
return false;
}
static bool CheckTyN(unsigned N, const LegalityQuery &Query,
std::initializer_list<LLT> SupportedValues) {
return llvm::is_contained(SupportedValues, Query.Types[N]);
}
MipsLegalizerInfo::MipsLegalizerInfo(const MipsSubtarget &ST) {
using namespace TargetOpcode;
const LLT s1 = LLT::scalar(1);
const LLT s8 = LLT::scalar(8);
const LLT s16 = LLT::scalar(16);
const LLT s32 = LLT::scalar(32);
const LLT s64 = LLT::scalar(64);
const LLT v16s8 = LLT::fixed_vector(16, 8);
const LLT v8s16 = LLT::fixed_vector(8, 16);
const LLT v4s32 = LLT::fixed_vector(4, 32);
const LLT v2s64 = LLT::fixed_vector(2, 64);
const LLT p0 = LLT::pointer(0, 32);
getActionDefinitionsBuilder({G_ADD, G_SUB, G_MUL})
.legalIf([=, &ST](const LegalityQuery &Query) {
if (CheckTyN(0, Query, {s32}))
return true;
if (ST.hasMSA() && CheckTyN(0, Query, {v16s8, v8s16, v4s32, v2s64}))
return true;
return false;
})
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder({G_UADDO, G_UADDE, G_USUBO, G_USUBE, G_UMULO})
.lowerFor({{s32, s1}});
getActionDefinitionsBuilder(G_UMULH)
.legalFor({s32})
.maxScalar(0, s32);
// MIPS32r6 does not have alignment restrictions for memory access.
// For MIPS32r5 and older memory access must be naturally-aligned i.e. aligned
// to at least a multiple of its own size. There is however a two instruction
// combination that performs 4 byte unaligned access (lwr/lwl and swl/swr)
// therefore 4 byte load and store are legal and will use NoAlignRequirements.
bool NoAlignRequirements = true;
getActionDefinitionsBuilder({G_LOAD, G_STORE})
.legalIf([=, &ST](const LegalityQuery &Query) {
if (CheckTy0Ty1MemSizeAlign(
Query, {{s32, p0, 8, NoAlignRequirements},
{s32, p0, 16, ST.systemSupportsUnalignedAccess()},
{s32, p0, 32, NoAlignRequirements},
{p0, p0, 32, NoAlignRequirements},
{s64, p0, 64, ST.systemSupportsUnalignedAccess()}}))
return true;
if (ST.hasMSA() && CheckTy0Ty1MemSizeAlign(
Query, {{v16s8, p0, 128, NoAlignRequirements},
{v8s16, p0, 128, NoAlignRequirements},
{v4s32, p0, 128, NoAlignRequirements},
{v2s64, p0, 128, NoAlignRequirements}}))
return true;
return false;
})
// Custom lower scalar memory access, up to 8 bytes, for:
// - non-power-of-2 MemSizes
// - unaligned 2 or 8 byte MemSizes for MIPS32r5 and older
.customIf([=, &ST](const LegalityQuery &Query) {
if (!Query.Types[0].isScalar() || Query.Types[1] != p0 ||
Query.Types[0] == s1)
return false;
unsigned Size = Query.Types[0].getSizeInBits();
unsigned QueryMemSize = Query.MMODescrs[0].MemoryTy.getSizeInBits();
assert(QueryMemSize <= Size && "Scalar can't hold MemSize");
if (Size > 64 || QueryMemSize > 64)
return false;
if (!isPowerOf2_64(Query.MMODescrs[0].MemoryTy.getSizeInBits()))
return true;
if (!ST.systemSupportsUnalignedAccess() &&
isUnalignedMemmoryAccess(QueryMemSize,
Query.MMODescrs[0].AlignInBits)) {
assert(QueryMemSize != 32 && "4 byte load and store are legal");
return true;
}
return false;
})
.minScalar(0, s32)
.lower();
getActionDefinitionsBuilder(G_IMPLICIT_DEF)
.legalFor({s32, s64});
getActionDefinitionsBuilder(G_UNMERGE_VALUES)
.legalFor({{s32, s64}});
getActionDefinitionsBuilder(G_MERGE_VALUES)
.legalFor({{s64, s32}});
getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
.legalForTypesWithMemDesc({{s32, p0, s8, 8},
{s32, p0, s16, 8}})
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder({G_ZEXT, G_SEXT, G_ANYEXT})
.legalIf([](const LegalityQuery &Query) { return false; })
.maxScalar(0, s32);
getActionDefinitionsBuilder(G_TRUNC)
.legalIf([](const LegalityQuery &Query) { return false; })
.maxScalar(1, s32);
getActionDefinitionsBuilder(G_SELECT)
.legalForCartesianProduct({p0, s32, s64}, {s32})
.minScalar(0, s32)
.minScalar(1, s32);
getActionDefinitionsBuilder(G_BRCOND)
.legalFor({s32})
.minScalar(0, s32);
getActionDefinitionsBuilder(G_BRJT)
.legalFor({{p0, s32}});
getActionDefinitionsBuilder(G_BRINDIRECT)
.legalFor({p0});
getActionDefinitionsBuilder(G_PHI)
.legalFor({p0, s32, s64})
.minScalar(0, s32);
getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
.legalFor({s32})
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder({G_SDIV, G_SREM, G_UDIV, G_UREM})
.legalIf([=, &ST](const LegalityQuery &Query) {
if (CheckTyN(0, Query, {s32}))
return true;
if (ST.hasMSA() && CheckTyN(0, Query, {v16s8, v8s16, v4s32, v2s64}))
return true;
return false;
})
.minScalar(0, s32)
.libcallFor({s64});
getActionDefinitionsBuilder({G_SHL, G_ASHR, G_LSHR})
.legalFor({{s32, s32}})
.clampScalar(1, s32, s32)
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder(G_ICMP)
.legalForCartesianProduct({s32}, {s32, p0})
.clampScalar(1, s32, s32)
.minScalar(0, s32);
getActionDefinitionsBuilder(G_CONSTANT)
.legalFor({s32})
.clampScalar(0, s32, s32);
getActionDefinitionsBuilder({G_PTR_ADD, G_INTTOPTR})
.legalFor({{p0, s32}});
getActionDefinitionsBuilder(G_PTRTOINT)
.legalFor({{s32, p0}});
getActionDefinitionsBuilder(G_FRAME_INDEX)
.legalFor({p0});
getActionDefinitionsBuilder({G_GLOBAL_VALUE, G_JUMP_TABLE})
.legalFor({p0});
getActionDefinitionsBuilder(G_DYN_STACKALLOC)
.lowerFor({{p0, s32}});
getActionDefinitionsBuilder(G_VASTART)
.legalFor({p0});
getActionDefinitionsBuilder(G_BSWAP)
.legalIf([=, &ST](const LegalityQuery &Query) {
if (ST.hasMips32r2() && CheckTyN(0, Query, {s32}))
return true;
return false;
})
.lowerIf([=, &ST](const LegalityQuery &Query) {
if (!ST.hasMips32r2() && CheckTyN(0, Query, {s32}))
return true;
return false;
})
.maxScalar(0, s32);
getActionDefinitionsBuilder(G_BITREVERSE)
.lowerFor({s32})
.maxScalar(0, s32);
getActionDefinitionsBuilder(G_CTLZ)
.legalFor({{s32, s32}})
.maxScalar(0, s32)
.maxScalar(1, s32);
getActionDefinitionsBuilder(G_CTLZ_ZERO_UNDEF)
.lowerFor({{s32, s32}});
getActionDefinitionsBuilder(G_CTTZ)
.lowerFor({{s32, s32}})
.maxScalar(0, s32)
.maxScalar(1, s32);
getActionDefinitionsBuilder(G_CTTZ_ZERO_UNDEF)
.lowerFor({{s32, s32}, {s64, s64}});
getActionDefinitionsBuilder(G_CTPOP)
.lowerFor({{s32, s32}})
.clampScalar(0, s32, s32)
.clampScalar(1, s32, s32);
// FP instructions
getActionDefinitionsBuilder(G_FCONSTANT)
.legalFor({s32, s64});
getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FABS, G_FSQRT})
.legalIf([=, &ST](const LegalityQuery &Query) {
if (CheckTyN(0, Query, {s32, s64}))
return true;
if (ST.hasMSA() && CheckTyN(0, Query, {v16s8, v8s16, v4s32, v2s64}))
return true;
return false;
});
getActionDefinitionsBuilder(G_FCMP)
.legalFor({{s32, s32}, {s32, s64}})
.minScalar(0, s32);
getActionDefinitionsBuilder({G_FCEIL, G_FFLOOR})
.libcallFor({s32, s64});
getActionDefinitionsBuilder(G_FPEXT)
.legalFor({{s64, s32}});
getActionDefinitionsBuilder(G_FPTRUNC)
.legalFor({{s32, s64}});
// FP to int conversion instructions
getActionDefinitionsBuilder(G_FPTOSI)
.legalForCartesianProduct({s32}, {s64, s32})
.libcallForCartesianProduct({s64}, {s64, s32})
.minScalar(0, s32);
getActionDefinitionsBuilder(G_FPTOUI)
.libcallForCartesianProduct({s64}, {s64, s32})
.lowerForCartesianProduct({s32}, {s64, s32})
.minScalar(0, s32);
// Int to FP conversion instructions
getActionDefinitionsBuilder(G_SITOFP)
.legalForCartesianProduct({s64, s32}, {s32})
.libcallForCartesianProduct({s64, s32}, {s64})
.minScalar(1, s32);
getActionDefinitionsBuilder(G_UITOFP)
.libcallForCartesianProduct({s64, s32}, {s64})
.customForCartesianProduct({s64, s32}, {s32})
.minScalar(1, s32);
getActionDefinitionsBuilder(G_SEXT_INREG).lower();
getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE, G_MEMSET}).libcall();
getLegacyLegalizerInfo().computeTables();
verify(*ST.getInstrInfo());
}
bool MipsLegalizerInfo::legalizeCustom(
LegalizerHelper &Helper, MachineInstr &MI,
LostDebugLocObserver &LocObserver) const {
using namespace TargetOpcode;
MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
const LLT s32 = LLT::scalar(32);
const LLT s64 = LLT::scalar(64);
switch (MI.getOpcode()) {
case G_LOAD:
case G_STORE: {
unsigned MemSize = (**MI.memoperands_begin()).getSize().getValue();
Register Val = MI.getOperand(0).getReg();
unsigned Size = MRI.getType(Val).getSizeInBits();
MachineMemOperand *MMOBase = *MI.memoperands_begin();
assert(MemSize <= 8 && "MemSize is too large");
assert(Size <= 64 && "Scalar size is too large");
// Split MemSize into two, P2HalfMemSize is largest power of two smaller
// then MemSize. e.g. 8 = 4 + 4 , 6 = 4 + 2, 3 = 2 + 1.
unsigned P2HalfMemSize, RemMemSize;
if (isPowerOf2_64(MemSize)) {
P2HalfMemSize = RemMemSize = MemSize / 2;
} else {
P2HalfMemSize = 1 << Log2_32(MemSize);
RemMemSize = MemSize - P2HalfMemSize;
}
Register BaseAddr = MI.getOperand(1).getReg();
LLT PtrTy = MRI.getType(BaseAddr);
MachineFunction &MF = MIRBuilder.getMF();
auto P2HalfMemOp = MF.getMachineMemOperand(MMOBase, 0, P2HalfMemSize);
auto RemMemOp = MF.getMachineMemOperand(MMOBase, P2HalfMemSize, RemMemSize);
if (MI.getOpcode() == G_STORE) {
// Widen Val to s32 or s64 in order to create legal G_LSHR or G_UNMERGE.
if (Size < 32)
Val = MIRBuilder.buildAnyExt(s32, Val).getReg(0);
if (Size > 32 && Size < 64)
Val = MIRBuilder.buildAnyExt(s64, Val).getReg(0);
auto C_P2HalfMemSize = MIRBuilder.buildConstant(s32, P2HalfMemSize);
auto Addr = MIRBuilder.buildPtrAdd(PtrTy, BaseAddr, C_P2HalfMemSize);
if (MI.getOpcode() == G_STORE && MemSize <= 4) {
MIRBuilder.buildStore(Val, BaseAddr, *P2HalfMemOp);
auto C_P2Half_InBits = MIRBuilder.buildConstant(s32, P2HalfMemSize * 8);
auto Shift = MIRBuilder.buildLShr(s32, Val, C_P2Half_InBits);
MIRBuilder.buildStore(Shift, Addr, *RemMemOp);
} else {
auto Unmerge = MIRBuilder.buildUnmerge(s32, Val);
MIRBuilder.buildStore(Unmerge.getReg(0), BaseAddr, *P2HalfMemOp);
MIRBuilder.buildStore(Unmerge.getReg(1), Addr, *RemMemOp);
}
}
if (MI.getOpcode() == G_LOAD) {
if (MemSize <= 4) {
// This is anyextending load, use 4 byte lwr/lwl.
auto *Load4MMO = MF.getMachineMemOperand(MMOBase, 0, 4);
if (Size == 32)
MIRBuilder.buildLoad(Val, BaseAddr, *Load4MMO);
else {
auto Load = MIRBuilder.buildLoad(s32, BaseAddr, *Load4MMO);
MIRBuilder.buildTrunc(Val, Load.getReg(0));
}
} else {
auto C_P2HalfMemSize = MIRBuilder.buildConstant(s32, P2HalfMemSize);
auto Addr = MIRBuilder.buildPtrAdd(PtrTy, BaseAddr, C_P2HalfMemSize);
auto Load_P2Half = MIRBuilder.buildLoad(s32, BaseAddr, *P2HalfMemOp);
auto Load_Rem = MIRBuilder.buildLoad(s32, Addr, *RemMemOp);
if (Size == 64)
MIRBuilder.buildMergeLikeInstr(Val, {Load_P2Half, Load_Rem});
else {
auto Merge =
MIRBuilder.buildMergeLikeInstr(s64, {Load_P2Half, Load_Rem});
MIRBuilder.buildTrunc(Val, Merge);
}
}
}
MI.eraseFromParent();
break;
}
case G_UITOFP: {
Register Dst = MI.getOperand(0).getReg();
Register Src = MI.getOperand(1).getReg();
LLT DstTy = MRI.getType(Dst);
LLT SrcTy = MRI.getType(Src);
if (SrcTy != s32)
return false;
if (DstTy != s32 && DstTy != s64)
return false;
// Let 0xABCDEFGH be given unsigned in MI.getOperand(1). First let's convert
// unsigned to double. Mantissa has 52 bits so we use following trick:
// First make floating point bit mask 0x43300000ABCDEFGH.
// Mask represents 2^52 * 0x1.00000ABCDEFGH i.e. 0x100000ABCDEFGH.0 .
// Next, subtract 2^52 * 0x1.0000000000000 i.e. 0x10000000000000.0 from it.
// Done. Trunc double to float if needed.
auto C_HiMask = MIRBuilder.buildConstant(s32, UINT32_C(0x43300000));
auto Bitcast =
MIRBuilder.buildMergeLikeInstr(s64, {Src, C_HiMask.getReg(0)});
MachineInstrBuilder TwoP52FP = MIRBuilder.buildFConstant(
s64, llvm::bit_cast<double>(UINT64_C(0x4330000000000000)));
if (DstTy == s64)
MIRBuilder.buildFSub(Dst, Bitcast, TwoP52FP);
else {
MachineInstrBuilder ResF64 = MIRBuilder.buildFSub(s64, Bitcast, TwoP52FP);
MIRBuilder.buildFPTrunc(Dst, ResF64);
}
MI.eraseFromParent();
break;
}
default:
return false;
}
return true;
}
static bool SelectMSA3OpIntrinsic(MachineInstr &MI, unsigned Opcode,
MachineIRBuilder &MIRBuilder,
const MipsSubtarget &ST) {
assert(ST.hasMSA() && "MSA intrinsic not supported on target without MSA.");
if (!MIRBuilder.buildInstr(Opcode)
.add(MI.getOperand(0))
.add(MI.getOperand(2))
.add(MI.getOperand(3))
.constrainAllUses(MIRBuilder.getTII(), *ST.getRegisterInfo(),
*ST.getRegBankInfo()))
return false;
MI.eraseFromParent();
return true;
}
static bool MSA3OpIntrinsicToGeneric(MachineInstr &MI, unsigned Opcode,
MachineIRBuilder &MIRBuilder,
const MipsSubtarget &ST) {
assert(ST.hasMSA() && "MSA intrinsic not supported on target without MSA.");
MIRBuilder.buildInstr(Opcode)
.add(MI.getOperand(0))
.add(MI.getOperand(2))
.add(MI.getOperand(3));
MI.eraseFromParent();
return true;
}
static bool MSA2OpIntrinsicToGeneric(MachineInstr &MI, unsigned Opcode,
MachineIRBuilder &MIRBuilder,
const MipsSubtarget &ST) {
assert(ST.hasMSA() && "MSA intrinsic not supported on target without MSA.");
MIRBuilder.buildInstr(Opcode)
.add(MI.getOperand(0))
.add(MI.getOperand(2));
MI.eraseFromParent();
return true;
}
bool MipsLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
MachineInstr &MI) const {
MachineIRBuilder &MIRBuilder = Helper.MIRBuilder;
const MipsSubtarget &ST = MI.getMF()->getSubtarget<MipsSubtarget>();
switch (cast<GIntrinsic>(MI).getIntrinsicID()) {
case Intrinsic::vacopy: {
MachinePointerInfo MPO;
LLT PtrTy = LLT::pointer(0, 32);
auto Tmp =
MIRBuilder.buildLoad(PtrTy, MI.getOperand(2),
*MI.getMF()->getMachineMemOperand(
MPO, MachineMemOperand::MOLoad, PtrTy, Align(4)));
MIRBuilder.buildStore(Tmp, MI.getOperand(1),
*MI.getMF()->getMachineMemOperand(
MPO, MachineMemOperand::MOStore, PtrTy, Align(4)));
MI.eraseFromParent();
return true;
}
case Intrinsic::mips_addv_b:
case Intrinsic::mips_addv_h:
case Intrinsic::mips_addv_w:
case Intrinsic::mips_addv_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_ADD, MIRBuilder, ST);
case Intrinsic::mips_addvi_b:
return SelectMSA3OpIntrinsic(MI, Mips::ADDVI_B, MIRBuilder, ST);
case Intrinsic::mips_addvi_h:
return SelectMSA3OpIntrinsic(MI, Mips::ADDVI_H, MIRBuilder, ST);
case Intrinsic::mips_addvi_w:
return SelectMSA3OpIntrinsic(MI, Mips::ADDVI_W, MIRBuilder, ST);
case Intrinsic::mips_addvi_d:
return SelectMSA3OpIntrinsic(MI, Mips::ADDVI_D, MIRBuilder, ST);
case Intrinsic::mips_subv_b:
case Intrinsic::mips_subv_h:
case Intrinsic::mips_subv_w:
case Intrinsic::mips_subv_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_SUB, MIRBuilder, ST);
case Intrinsic::mips_subvi_b:
return SelectMSA3OpIntrinsic(MI, Mips::SUBVI_B, MIRBuilder, ST);
case Intrinsic::mips_subvi_h:
return SelectMSA3OpIntrinsic(MI, Mips::SUBVI_H, MIRBuilder, ST);
case Intrinsic::mips_subvi_w:
return SelectMSA3OpIntrinsic(MI, Mips::SUBVI_W, MIRBuilder, ST);
case Intrinsic::mips_subvi_d:
return SelectMSA3OpIntrinsic(MI, Mips::SUBVI_D, MIRBuilder, ST);
case Intrinsic::mips_mulv_b:
case Intrinsic::mips_mulv_h:
case Intrinsic::mips_mulv_w:
case Intrinsic::mips_mulv_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_MUL, MIRBuilder, ST);
case Intrinsic::mips_div_s_b:
case Intrinsic::mips_div_s_h:
case Intrinsic::mips_div_s_w:
case Intrinsic::mips_div_s_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_SDIV, MIRBuilder, ST);
case Intrinsic::mips_mod_s_b:
case Intrinsic::mips_mod_s_h:
case Intrinsic::mips_mod_s_w:
case Intrinsic::mips_mod_s_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_SREM, MIRBuilder, ST);
case Intrinsic::mips_div_u_b:
case Intrinsic::mips_div_u_h:
case Intrinsic::mips_div_u_w:
case Intrinsic::mips_div_u_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_UDIV, MIRBuilder, ST);
case Intrinsic::mips_mod_u_b:
case Intrinsic::mips_mod_u_h:
case Intrinsic::mips_mod_u_w:
case Intrinsic::mips_mod_u_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_UREM, MIRBuilder, ST);
case Intrinsic::mips_fadd_w:
case Intrinsic::mips_fadd_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_FADD, MIRBuilder, ST);
case Intrinsic::mips_fsub_w:
case Intrinsic::mips_fsub_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_FSUB, MIRBuilder, ST);
case Intrinsic::mips_fmul_w:
case Intrinsic::mips_fmul_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_FMUL, MIRBuilder, ST);
case Intrinsic::mips_fdiv_w:
case Intrinsic::mips_fdiv_d:
return MSA3OpIntrinsicToGeneric(MI, TargetOpcode::G_FDIV, MIRBuilder, ST);
case Intrinsic::mips_fmax_a_w:
return SelectMSA3OpIntrinsic(MI, Mips::FMAX_A_W, MIRBuilder, ST);
case Intrinsic::mips_fmax_a_d:
return SelectMSA3OpIntrinsic(MI, Mips::FMAX_A_D, MIRBuilder, ST);
case Intrinsic::mips_fsqrt_w:
return MSA2OpIntrinsicToGeneric(MI, TargetOpcode::G_FSQRT, MIRBuilder, ST);
case Intrinsic::mips_fsqrt_d:
return MSA2OpIntrinsicToGeneric(MI, TargetOpcode::G_FSQRT, MIRBuilder, ST);
default:
break;
}
return true;
}