2304 lines
91 KiB
TableGen
2304 lines
91 KiB
TableGen
|
//== LoongArchInstrInfo.td - Target Description for LoongArch -*- tablegen -*-//
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
// This file describes the LoongArch instructions in TableGen format.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// LoongArch specific DAG Nodes.
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
// Target-independent type requirements, but with target-specific formats.
|
||
|
def SDT_CallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>,
|
||
|
SDTCisVT<1, i32>]>;
|
||
|
def SDT_CallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>,
|
||
|
SDTCisVT<1, i32>]>;
|
||
|
|
||
|
// Target-dependent type requirements.
|
||
|
def SDT_LoongArchCall : SDTypeProfile<0, -1, [SDTCisVT<0, GRLenVT>]>;
|
||
|
def SDT_LoongArchIntBinOpW : SDTypeProfile<1, 2, [
|
||
|
SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisVT<0, i64>
|
||
|
]>;
|
||
|
|
||
|
def SDT_LoongArchBStrIns: SDTypeProfile<1, 4, [
|
||
|
SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisInt<3>,
|
||
|
SDTCisSameAs<3, 4>
|
||
|
]>;
|
||
|
|
||
|
def SDT_LoongArchBStrPick: SDTypeProfile<1, 3, [
|
||
|
SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisInt<2>, SDTCisSameAs<2, 3>
|
||
|
]>;
|
||
|
|
||
|
// "VI" means no output and an integer input.
|
||
|
def SDT_LoongArchVI : SDTypeProfile<0, 1, [SDTCisVT<0, GRLenVT>]>;
|
||
|
|
||
|
def SDT_LoongArchCsrrd : SDTypeProfile<1, 1, [SDTCisInt<0>,
|
||
|
SDTCisVT<1, GRLenVT>]>;
|
||
|
def SDT_LoongArchCsrwr : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>,
|
||
|
SDTCisVT<2, GRLenVT>]>;
|
||
|
def SDT_LoongArchCsrxchg : SDTypeProfile<1, 3, [SDTCisInt<0>,
|
||
|
SDTCisSameAs<0, 1>,
|
||
|
SDTCisSameAs<0, 2>,
|
||
|
SDTCisVT<3, GRLenVT>]>;
|
||
|
def SDT_LoongArchIocsrwr : SDTypeProfile<0, 2, [SDTCisInt<0>,
|
||
|
SDTCisSameAs<0, 1>]>;
|
||
|
def SDT_LoongArchMovgr2fcsr : SDTypeProfile<0, 2, [SDTCisVT<0, GRLenVT>,
|
||
|
SDTCisSameAs<0, 1>]>;
|
||
|
def SDT_LoongArchMovfcsr2gr : SDTypeProfile<1, 1, [SDTCisVT<0, GRLenVT>,
|
||
|
SDTCisSameAs<0, 1>]>;
|
||
|
|
||
|
// TODO: Add LoongArch specific DAG Nodes
|
||
|
// Target-independent nodes, but with target-specific formats.
|
||
|
def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_CallSeqStart,
|
||
|
[SDNPHasChain, SDNPOutGlue]>;
|
||
|
def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_CallSeqEnd,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
|
||
|
|
||
|
// Target-dependent nodes.
|
||
|
def loongarch_call : SDNode<"LoongArchISD::CALL", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_ret : SDNode<"LoongArchISD::RET", SDTNone,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
|
||
|
def loongarch_tail : SDNode<"LoongArchISD::TAIL", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_call_medium : SDNode<"LoongArchISD::CALL_MEDIUM", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_tail_medium : SDNode<"LoongArchISD::TAIL_MEDIUM", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_call_large : SDNode<"LoongArchISD::CALL_LARGE", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_tail_large : SDNode<"LoongArchISD::TAIL_LARGE", SDT_LoongArchCall,
|
||
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||
|
SDNPVariadic]>;
|
||
|
def loongarch_sll_w : SDNode<"LoongArchISD::SLL_W", SDT_LoongArchIntBinOpW>;
|
||
|
def loongarch_sra_w : SDNode<"LoongArchISD::SRA_W", SDT_LoongArchIntBinOpW>;
|
||
|
def loongarch_srl_w : SDNode<"LoongArchISD::SRL_W", SDT_LoongArchIntBinOpW>;
|
||
|
def loongarch_rotr_w : SDNode<"LoongArchISD::ROTR_W", SDT_LoongArchIntBinOpW>;
|
||
|
def loongarch_rotl_w : SDNode<"LoongArchISD::ROTL_W", SDT_LoongArchIntBinOpW>;
|
||
|
def loongarch_crc_w_b_w
|
||
|
: SDNode<"LoongArchISD::CRC_W_B_W", SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crc_w_h_w
|
||
|
: SDNode<"LoongArchISD::CRC_W_H_W", SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crc_w_w_w
|
||
|
: SDNode<"LoongArchISD::CRC_W_W_W", SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crc_w_d_w
|
||
|
: SDNode<"LoongArchISD::CRC_W_D_W", SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crcc_w_b_w : SDNode<"LoongArchISD::CRCC_W_B_W",
|
||
|
SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crcc_w_h_w : SDNode<"LoongArchISD::CRCC_W_H_W",
|
||
|
SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crcc_w_w_w : SDNode<"LoongArchISD::CRCC_W_W_W",
|
||
|
SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_crcc_w_d_w : SDNode<"LoongArchISD::CRCC_W_D_W",
|
||
|
SDT_LoongArchIntBinOpW, [SDNPHasChain]>;
|
||
|
def loongarch_bstrins
|
||
|
: SDNode<"LoongArchISD::BSTRINS", SDT_LoongArchBStrIns>;
|
||
|
def loongarch_bstrpick
|
||
|
: SDNode<"LoongArchISD::BSTRPICK", SDT_LoongArchBStrPick>;
|
||
|
def loongarch_revb_2h : SDNode<"LoongArchISD::REVB_2H", SDTUnaryOp>;
|
||
|
def loongarch_revb_2w : SDNode<"LoongArchISD::REVB_2W", SDTUnaryOp>;
|
||
|
def loongarch_bitrev_4b : SDNode<"LoongArchISD::BITREV_4B", SDTUnaryOp>;
|
||
|
def loongarch_bitrev_w : SDNode<"LoongArchISD::BITREV_W", SDTUnaryOp>;
|
||
|
def loongarch_clzw : SDNode<"LoongArchISD::CLZ_W", SDTIntBitCountUnaryOp>;
|
||
|
def loongarch_ctzw : SDNode<"LoongArchISD::CTZ_W", SDTIntBitCountUnaryOp>;
|
||
|
def loongarch_dbar : SDNode<"LoongArchISD::DBAR", SDT_LoongArchVI,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_ibar : SDNode<"LoongArchISD::IBAR", SDT_LoongArchVI,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_break : SDNode<"LoongArchISD::BREAK", SDT_LoongArchVI,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_movfcsr2gr : SDNode<"LoongArchISD::MOVFCSR2GR",
|
||
|
SDT_LoongArchMovfcsr2gr, [SDNPHasChain]>;
|
||
|
def loongarch_movgr2fcsr : SDNode<"LoongArchISD::MOVGR2FCSR",
|
||
|
SDT_LoongArchMovgr2fcsr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_syscall : SDNode<"LoongArchISD::SYSCALL", SDT_LoongArchVI,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_csrrd : SDNode<"LoongArchISD::CSRRD", SDT_LoongArchCsrrd,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_csrwr : SDNode<"LoongArchISD::CSRWR", SDT_LoongArchCsrwr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_csrxchg : SDNode<"LoongArchISD::CSRXCHG",
|
||
|
SDT_LoongArchCsrxchg,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrrd_b : SDNode<"LoongArchISD::IOCSRRD_B", SDTUnaryOp,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrrd_h : SDNode<"LoongArchISD::IOCSRRD_H", SDTUnaryOp,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrrd_w : SDNode<"LoongArchISD::IOCSRRD_W", SDTUnaryOp,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrrd_d : SDNode<"LoongArchISD::IOCSRRD_D", SDTUnaryOp,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrwr_b : SDNode<"LoongArchISD::IOCSRWR_B",
|
||
|
SDT_LoongArchIocsrwr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrwr_h : SDNode<"LoongArchISD::IOCSRWR_H",
|
||
|
SDT_LoongArchIocsrwr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrwr_w : SDNode<"LoongArchISD::IOCSRWR_W",
|
||
|
SDT_LoongArchIocsrwr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_iocsrwr_d : SDNode<"LoongArchISD::IOCSRWR_D",
|
||
|
SDT_LoongArchIocsrwr,
|
||
|
[SDNPHasChain, SDNPSideEffect]>;
|
||
|
def loongarch_cpucfg : SDNode<"LoongArchISD::CPUCFG", SDTUnaryOp,
|
||
|
[SDNPHasChain]>;
|
||
|
|
||
|
def to_fclass_mask: SDNodeXForm<timm, [{
|
||
|
uint64_t Check = N->getZExtValue();
|
||
|
unsigned Mask = 0;
|
||
|
if (Check & fcSNan)
|
||
|
Mask |= LoongArch::FClassMaskSignalingNaN;
|
||
|
if (Check & fcQNan)
|
||
|
Mask |= LoongArch::FClassMaskQuietNaN;
|
||
|
if (Check & fcPosInf)
|
||
|
Mask |= LoongArch::FClassMaskPositiveInfinity;
|
||
|
if (Check & fcNegInf)
|
||
|
Mask |= LoongArch::FClassMaskNegativeInfinity;
|
||
|
if (Check & fcPosNormal)
|
||
|
Mask |= LoongArch::FClassMaskPositiveNormal;
|
||
|
if (Check & fcNegNormal)
|
||
|
Mask |= LoongArch::FClassMaskNegativeNormal;
|
||
|
if (Check & fcPosSubnormal)
|
||
|
Mask |= LoongArch::FClassMaskPositiveSubnormal;
|
||
|
if (Check & fcNegSubnormal)
|
||
|
Mask |= LoongArch::FClassMaskNegativeSubnormal;
|
||
|
if (Check & fcPosZero)
|
||
|
Mask |= LoongArch::FClassMaskPositiveZero;
|
||
|
if (Check & fcNegZero)
|
||
|
Mask |= LoongArch::FClassMaskNegativeZero;
|
||
|
return CurDAG->getTargetConstant(Mask, SDLoc(N), Subtarget->getGRLenVT());
|
||
|
}]>;
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Operand and SDNode transformation definitions.
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
class ImmAsmOperand<string prefix, int width, string suffix>
|
||
|
: AsmOperandClass {
|
||
|
let Name = prefix # "Imm" # width # suffix;
|
||
|
let DiagnosticType = !strconcat("Invalid", Name);
|
||
|
let RenderMethod = "addImmOperands";
|
||
|
}
|
||
|
|
||
|
class SImmAsmOperand<int width, string suffix = "">
|
||
|
: ImmAsmOperand<"S", width, suffix> {
|
||
|
}
|
||
|
|
||
|
class UImmAsmOperand<int width, string suffix = "">
|
||
|
: ImmAsmOperand<"U", width, suffix> {
|
||
|
}
|
||
|
|
||
|
// A parse method for "$r*" or "$r*, 0", where the 0 is be silently ignored.
|
||
|
// Only used for "AM*" instructions, in order to be compatible with GAS.
|
||
|
def AtomicMemAsmOperand : AsmOperandClass {
|
||
|
let Name = "AtomicMemAsmOperand";
|
||
|
let RenderMethod = "addRegOperands";
|
||
|
let PredicateMethod = "isGPR";
|
||
|
let ParserMethod = "parseAtomicMemOp";
|
||
|
}
|
||
|
|
||
|
def GPRMemAtomic : RegisterOperand<GPR> {
|
||
|
let ParserMatchClass = AtomicMemAsmOperand;
|
||
|
let PrintMethod = "printAtomicMemOp";
|
||
|
}
|
||
|
|
||
|
// A parameterized register class alternative to i32imm/i64imm from Target.td.
|
||
|
def grlenimm : Operand<GRLenVT>;
|
||
|
def imm32 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = ImmAsmOperand<"", 32, "">;
|
||
|
}
|
||
|
|
||
|
def uimm1 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<1>(Imm);}]>{
|
||
|
let ParserMatchClass = UImmAsmOperand<1>;
|
||
|
}
|
||
|
|
||
|
def uimm2 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<2>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<2>;
|
||
|
}
|
||
|
|
||
|
def uimm2_plus1 : Operand<GRLenVT>,
|
||
|
ImmLeaf<GRLenVT, [{return isUInt<2>(Imm - 1);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<2, "plus1">;
|
||
|
let EncoderMethod = "getImmOpValueSub1";
|
||
|
let DecoderMethod = "decodeUImmOperand<2, 1>";
|
||
|
}
|
||
|
|
||
|
def uimm3 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<3>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<3>;
|
||
|
}
|
||
|
|
||
|
def uimm4 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<4>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<4>;
|
||
|
}
|
||
|
|
||
|
def uimm5 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<5>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<5>;
|
||
|
}
|
||
|
|
||
|
def uimm6 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<6>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<6>;
|
||
|
}
|
||
|
|
||
|
def uimm7 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = UImmAsmOperand<7>;
|
||
|
}
|
||
|
|
||
|
def uimm8 : Operand<GRLenVT>, ImmLeaf<GRLenVT, [{return isUInt<8>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<8>;
|
||
|
}
|
||
|
|
||
|
class UImm12Operand : Operand<GRLenVT>,
|
||
|
ImmLeaf <GRLenVT, [{return isUInt<12>(Imm);}]> {
|
||
|
let DecoderMethod = "decodeUImmOperand<12>";
|
||
|
}
|
||
|
|
||
|
def uimm12 : UImm12Operand {
|
||
|
let ParserMatchClass = UImmAsmOperand<12>;
|
||
|
}
|
||
|
|
||
|
def uimm12_ori : UImm12Operand {
|
||
|
let ParserMatchClass = UImmAsmOperand<12, "ori">;
|
||
|
}
|
||
|
|
||
|
def uimm14 : Operand<GRLenVT>,
|
||
|
ImmLeaf <GRLenVT, [{return isUInt<14>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<14>;
|
||
|
}
|
||
|
|
||
|
def uimm15 : Operand<GRLenVT>,
|
||
|
ImmLeaf <GRLenVT, [{return isUInt<15>(Imm);}]> {
|
||
|
let ParserMatchClass = UImmAsmOperand<15>;
|
||
|
}
|
||
|
|
||
|
def simm5 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<5>;
|
||
|
let DecoderMethod = "decodeSImmOperand<5>";
|
||
|
}
|
||
|
|
||
|
def simm8 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<8>;
|
||
|
let DecoderMethod = "decodeSImmOperand<8>";
|
||
|
}
|
||
|
|
||
|
foreach I = [1, 2, 3] in {
|
||
|
def simm8_lsl # I : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<8, "lsl" # I>;
|
||
|
let EncoderMethod = "getImmOpValueAsr<" # I # ">";
|
||
|
let DecoderMethod = "decodeSImmOperand<8," # I # ">";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
def simm9_lsl3 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<9, "lsl3">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<3>";
|
||
|
let DecoderMethod = "decodeSImmOperand<9, 3>";
|
||
|
}
|
||
|
|
||
|
def simm10 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<10>;
|
||
|
}
|
||
|
|
||
|
def simm10_lsl2 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<10, "lsl2">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<10, 2>";
|
||
|
}
|
||
|
|
||
|
def simm11_lsl1 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<11, "lsl1">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<1>";
|
||
|
let DecoderMethod = "decodeSImmOperand<11, 1>";
|
||
|
}
|
||
|
|
||
|
class SImm12Operand : Operand<GRLenVT>,
|
||
|
ImmLeaf <GRLenVT, [{return isInt<12>(Imm);}]> {
|
||
|
let DecoderMethod = "decodeSImmOperand<12>";
|
||
|
}
|
||
|
|
||
|
def simm12 : SImm12Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<12>;
|
||
|
}
|
||
|
|
||
|
def simm12_addlike : SImm12Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<12, "addlike">;
|
||
|
}
|
||
|
|
||
|
def simm12_lu52id : SImm12Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<12, "lu52id">;
|
||
|
}
|
||
|
|
||
|
def simm13 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<13>;
|
||
|
let DecoderMethod = "decodeSImmOperand<13>";
|
||
|
}
|
||
|
|
||
|
def simm14_lsl2 : Operand<GRLenVT>,
|
||
|
ImmLeaf<GRLenVT, [{return isShiftedInt<14,2>(Imm);}]> {
|
||
|
let ParserMatchClass = SImmAsmOperand<14, "lsl2">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<14, 2>";
|
||
|
}
|
||
|
|
||
|
def simm16 : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<16>;
|
||
|
let DecoderMethod = "decodeSImmOperand<16>";
|
||
|
}
|
||
|
|
||
|
def simm16_lsl2 : Operand<GRLenVT>,
|
||
|
ImmLeaf<GRLenVT, [{return isInt<16>(Imm>>2);}]> {
|
||
|
let ParserMatchClass = SImmAsmOperand<16, "lsl2">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<16, 2>";
|
||
|
}
|
||
|
|
||
|
def simm16_lsl2_br : Operand<OtherVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<16, "lsl2">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<16, 2>";
|
||
|
}
|
||
|
|
||
|
class SImm20Operand : Operand<GRLenVT> {
|
||
|
let DecoderMethod = "decodeSImmOperand<20>";
|
||
|
}
|
||
|
|
||
|
def simm20 : SImm20Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<20>;
|
||
|
}
|
||
|
|
||
|
def simm20_pcalau12i : SImm20Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<20, "pcalau12i">;
|
||
|
}
|
||
|
|
||
|
def simm20_lu12iw : SImm20Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<20, "lu12iw">;
|
||
|
}
|
||
|
|
||
|
def simm20_lu32id : SImm20Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<20, "lu32id">;
|
||
|
}
|
||
|
|
||
|
def simm20_pcaddu18i : SImm20Operand {
|
||
|
let ParserMatchClass = SImmAsmOperand<20, "pcaddu18i">;
|
||
|
}
|
||
|
|
||
|
def simm21_lsl2 : Operand<OtherVT> {
|
||
|
let ParserMatchClass = SImmAsmOperand<21, "lsl2">;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<21, 2>";
|
||
|
}
|
||
|
|
||
|
def SImm26OperandB: AsmOperandClass {
|
||
|
let Name = "SImm26OperandB";
|
||
|
let PredicateMethod = "isSImm26Operand";
|
||
|
let RenderMethod = "addImmOperands";
|
||
|
let DiagnosticType = "InvalidSImm26Operand";
|
||
|
let ParserMethod = "parseImmediate";
|
||
|
}
|
||
|
|
||
|
// A symbol or an imm used in B/PseudoBR.
|
||
|
def simm26_b : Operand<OtherVT> {
|
||
|
let ParserMatchClass = SImm26OperandB;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<26, 2>";
|
||
|
}
|
||
|
|
||
|
def SImm26OperandBL: AsmOperandClass {
|
||
|
let Name = "SImm26OperandBL";
|
||
|
let PredicateMethod = "isSImm26Operand";
|
||
|
let RenderMethod = "addImmOperands";
|
||
|
let DiagnosticType = "InvalidSImm26Operand";
|
||
|
let ParserMethod = "parseSImm26Operand";
|
||
|
}
|
||
|
|
||
|
// A symbol or an imm used in BL/PseudoCALL/PseudoTAIL.
|
||
|
def simm26_symbol : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = SImm26OperandBL;
|
||
|
let EncoderMethod = "getImmOpValueAsr<2>";
|
||
|
let DecoderMethod = "decodeSImmOperand<26, 2>";
|
||
|
}
|
||
|
|
||
|
// A 32-bit signed immediate with the lowest 16 bits zeroed, suitable for
|
||
|
// direct use with `addu16i.d`.
|
||
|
def simm16_lsl16 : Operand<GRLenVT>,
|
||
|
ImmLeaf<GRLenVT, [{return isShiftedInt<16, 16>(Imm);}]>;
|
||
|
|
||
|
// A 32-bit signed immediate expressible with a pair of `addu16i.d + addi` for
|
||
|
// use in additions.
|
||
|
def simm32_hi16_lo12: Operand<GRLenVT>, ImmLeaf<GRLenVT, [{
|
||
|
return isShiftedInt<16, 16>(Imm - SignExtend64<12>(Imm));
|
||
|
}]>;
|
||
|
|
||
|
def BareSymbol : AsmOperandClass {
|
||
|
let Name = "BareSymbol";
|
||
|
let RenderMethod = "addImmOperands";
|
||
|
let DiagnosticType = "InvalidBareSymbol";
|
||
|
let ParserMethod = "parseImmediate";
|
||
|
}
|
||
|
|
||
|
// A bare symbol used in "PseudoLA_*" instructions.
|
||
|
def bare_symbol : Operand<GRLenVT> {
|
||
|
let ParserMatchClass = BareSymbol;
|
||
|
}
|
||
|
|
||
|
// Standalone (codegen-only) immleaf patterns.
|
||
|
|
||
|
// A 12-bit signed immediate plus one where the imm range will be [-2047, 2048].
|
||
|
def simm12_plus1 : ImmLeaf<GRLenVT,
|
||
|
[{return (isInt<12>(Imm) && Imm != -2048) || Imm == 2048;}]>;
|
||
|
|
||
|
// Return the negation of an immediate value.
|
||
|
def NegImm : SDNodeXForm<imm, [{
|
||
|
return CurDAG->getTargetConstant(-N->getSExtValue(), SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// FP immediate patterns.
|
||
|
def fpimm0 : PatLeaf<(fpimm), [{return N->isExactlyValue(+0.0);}]>;
|
||
|
def fpimm0neg : PatLeaf<(fpimm), [{return N->isExactlyValue(-0.0);}]>;
|
||
|
def fpimm1 : PatLeaf<(fpimm), [{return N->isExactlyValue(+1.0);}]>;
|
||
|
|
||
|
// Return an immediate subtracted from 32.
|
||
|
def ImmSubFrom32 : SDNodeXForm<imm, [{
|
||
|
return CurDAG->getTargetConstant(32 - N->getZExtValue(), SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Return the lowest 12 bits of the signed immediate.
|
||
|
def LO12: SDNodeXForm<imm, [{
|
||
|
return CurDAG->getTargetConstant(SignExtend64<12>(N->getSExtValue()),
|
||
|
SDLoc(N), N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Return the higher 16 bits of the signed immediate.
|
||
|
def HI16 : SDNodeXForm<imm, [{
|
||
|
return CurDAG->getTargetConstant(N->getSExtValue() >> 16, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Return the higher 16 bits of the signed immediate, adjusted for use within an
|
||
|
// `addu16i.d + addi` pair.
|
||
|
def HI16ForAddu16idAddiPair: SDNodeXForm<imm, [{
|
||
|
auto Imm = N->getSExtValue();
|
||
|
return CurDAG->getTargetConstant((Imm - SignExtend64<12>(Imm)) >> 16,
|
||
|
SDLoc(N), N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
def BaseAddr : ComplexPattern<iPTR, 1, "SelectBaseAddr">;
|
||
|
def AddrConstant : ComplexPattern<iPTR, 2, "SelectAddrConstant">;
|
||
|
def NonFIBaseAddr : ComplexPattern<iPTR, 1, "selectNonFIBaseAddr">;
|
||
|
|
||
|
def fma_nsz : PatFrag<(ops node:$fj, node:$fk, node:$fa),
|
||
|
(fma node:$fj, node:$fk, node:$fa), [{
|
||
|
return N->getFlags().hasNoSignedZeros();
|
||
|
}]>;
|
||
|
|
||
|
// Check if (add r, imm) can be optimized to (ADDI (ADDI r, imm0), imm1),
|
||
|
// in which imm = imm0 + imm1, and both imm0 & imm1 are simm12.
|
||
|
def AddiPair : PatLeaf<(imm), [{
|
||
|
if (!N->hasOneUse())
|
||
|
return false;
|
||
|
// The immediate operand must be in range [-4096,-2049] or [2048,4094].
|
||
|
int64_t Imm = N->getSExtValue();
|
||
|
return (-4096 <= Imm && Imm <= -2049) || (2048 <= Imm && Imm <= 4094);
|
||
|
}]>;
|
||
|
|
||
|
// Return -2048 if immediate is negative or 2047 if positive.
|
||
|
def AddiPairImmLarge : SDNodeXForm<imm, [{
|
||
|
int64_t Imm = N->getSExtValue() < 0 ? -2048 : 2047;
|
||
|
return CurDAG->getTargetConstant(Imm, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Return imm - (imm < 0 ? -2048 : 2047).
|
||
|
def AddiPairImmSmall : SDNodeXForm<imm, [{
|
||
|
int64_t Imm = N->getSExtValue();
|
||
|
int64_t Adj = Imm < 0 ? -2048 : 2047;
|
||
|
return CurDAG->getTargetConstant(Imm - Adj, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Check if (mul r, imm) can be optimized to (SLLI (ALSL r, r, i0), i1),
|
||
|
// in which imm = (1 + (1 << i0)) << i1.
|
||
|
def AlslSlliImm : PatLeaf<(imm), [{
|
||
|
if (!N->hasOneUse())
|
||
|
return false;
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
unsigned I1 = llvm::countr_zero(Imm);
|
||
|
uint64_t Rem = Imm >> I1;
|
||
|
return Rem == 3 || Rem == 5 || Rem == 9 || Rem == 17;
|
||
|
}]>;
|
||
|
|
||
|
def AlslSlliImmI1 : SDNodeXForm<imm, [{
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
unsigned I1 = llvm::countr_zero(Imm);
|
||
|
return CurDAG->getTargetConstant(I1, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
def AlslSlliImmI0 : SDNodeXForm<imm, [{
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
unsigned I1 = llvm::countr_zero(Imm);
|
||
|
uint64_t I0;
|
||
|
switch (Imm >> I1) {
|
||
|
case 3: I0 = 1; break;
|
||
|
case 5: I0 = 2; break;
|
||
|
case 9: I0 = 3; break;
|
||
|
default: I0 = 4; break;
|
||
|
}
|
||
|
return CurDAG->getTargetConstant(I0, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
// Check if (and r, imm) can be optimized to (BSTRINS r, R0, msb, lsb),
|
||
|
// in which imm = ~((2^^(msb-lsb+1) - 1) << lsb).
|
||
|
def BstrinsImm : PatLeaf<(imm), [{
|
||
|
if (!N->hasOneUse())
|
||
|
return false;
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
// andi can be used instead if Imm <= 0xfff.
|
||
|
if (Imm <= 0xfff)
|
||
|
return false;
|
||
|
unsigned MaskIdx, MaskLen;
|
||
|
return N->getValueType(0).getSizeInBits() == 32
|
||
|
? llvm::isShiftedMask_32(~Imm, MaskIdx, MaskLen)
|
||
|
: llvm::isShiftedMask_64(~Imm, MaskIdx, MaskLen);
|
||
|
}]>;
|
||
|
|
||
|
def BstrinsMsb: SDNodeXForm<imm, [{
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
unsigned MaskIdx, MaskLen;
|
||
|
N->getValueType(0).getSizeInBits() == 32
|
||
|
? llvm::isShiftedMask_32(~Imm, MaskIdx, MaskLen)
|
||
|
: llvm::isShiftedMask_64(~Imm, MaskIdx, MaskLen);
|
||
|
return CurDAG->getTargetConstant(MaskIdx + MaskLen - 1, SDLoc(N),
|
||
|
N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
def BstrinsLsb: SDNodeXForm<imm, [{
|
||
|
uint64_t Imm = N->getZExtValue();
|
||
|
unsigned MaskIdx, MaskLen;
|
||
|
N->getValueType(0).getSizeInBits() == 32
|
||
|
? llvm::isShiftedMask_32(~Imm, MaskIdx, MaskLen)
|
||
|
: llvm::isShiftedMask_64(~Imm, MaskIdx, MaskLen);
|
||
|
return CurDAG->getTargetConstant(MaskIdx, SDLoc(N), N->getValueType(0));
|
||
|
}]>;
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Instruction Formats
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
include "LoongArchInstrFormats.td"
|
||
|
include "LoongArchFloatInstrFormats.td"
|
||
|
include "LoongArchLSXInstrFormats.td"
|
||
|
include "LoongArchLASXInstrFormats.td"
|
||
|
include "LoongArchLBTInstrFormats.td"
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Instruction Class Templates
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||
|
class ALU_3R<bits<32> op>
|
||
|
: Fmt3R<op, (outs GPR:$rd), (ins GPR:$rj, GPR:$rk), "$rd, $rj, $rk">;
|
||
|
class ALU_2R<bits<32> op>
|
||
|
: Fmt2R<op, (outs GPR:$rd), (ins GPR:$rj), "$rd, $rj">;
|
||
|
|
||
|
class ALU_3RI2<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt3RI2<op, (outs GPR:$rd), (ins GPR:$rj, GPR:$rk, ImmOpnd:$imm2),
|
||
|
"$rd, $rj, $rk, $imm2">;
|
||
|
class ALU_3RI3<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt3RI3<op, (outs GPR:$rd), (ins GPR:$rj, GPR:$rk, ImmOpnd:$imm3),
|
||
|
"$rd, $rj, $rk, $imm3">;
|
||
|
class ALU_2RI5<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt2RI5<op, (outs GPR:$rd), (ins GPR:$rj, ImmOpnd:$imm5),
|
||
|
"$rd, $rj, $imm5">;
|
||
|
class ALU_2RI6<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt2RI6<op, (outs GPR:$rd), (ins GPR:$rj, ImmOpnd:$imm6),
|
||
|
"$rd, $rj, $imm6">;
|
||
|
class ALU_2RI12<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt2RI12<op, (outs GPR:$rd), (ins GPR:$rj, ImmOpnd:$imm12),
|
||
|
"$rd, $rj, $imm12">;
|
||
|
class ALU_2RI16<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt2RI16<op, (outs GPR:$rd), (ins GPR:$rj, ImmOpnd:$imm16),
|
||
|
"$rd, $rj, $imm16">;
|
||
|
class ALU_1RI20<bits<32> op, Operand ImmOpnd>
|
||
|
: Fmt1RI20<op, (outs GPR:$rd), (ins ImmOpnd:$imm20), "$rd, $imm20">;
|
||
|
} // hasSideEffects = 0, mayLoad = 0, mayStore = 0
|
||
|
|
||
|
let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
|
||
|
class MISC_I15<bits<32> op>
|
||
|
: FmtI15<op, (outs), (ins uimm15:$imm15), "$imm15">;
|
||
|
|
||
|
let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
|
||
|
class RDTIME_2R<bits<32> op>
|
||
|
: Fmt2R<op, (outs GPR:$rd, GPR:$rj), (ins), "$rd, $rj">;
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||
|
class BrCC_2RI16<bits<32> op>
|
||
|
: Fmt2RI16<op, (outs), (ins GPR:$rj, GPR:$rd, simm16_lsl2_br:$imm16),
|
||
|
"$rj, $rd, $imm16"> {
|
||
|
let isBranch = 1;
|
||
|
let isTerminator = 1;
|
||
|
}
|
||
|
class BrCCZ_1RI21<bits<32> op>
|
||
|
: Fmt1RI21<op, (outs), (ins GPR:$rj, simm21_lsl2:$imm21),
|
||
|
"$rj, $imm21"> {
|
||
|
let isBranch = 1;
|
||
|
let isTerminator = 1;
|
||
|
}
|
||
|
class Br_I26<bits<32> op>
|
||
|
: FmtI26<op, (outs), (ins simm26_b:$imm26), "$imm26"> {
|
||
|
let isBranch = 1;
|
||
|
let isTerminator = 1;
|
||
|
let isBarrier = 1;
|
||
|
}
|
||
|
} // hasSideEffects = 0, mayLoad = 0, mayStore = 0
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
|
||
|
class LOAD_3R<bits<32> op>
|
||
|
: Fmt3R<op, (outs GPR:$rd), (ins GPR:$rj, GPR:$rk), "$rd, $rj, $rk">;
|
||
|
class LOAD_2RI12<bits<32> op>
|
||
|
: Fmt2RI12<op, (outs GPR:$rd), (ins GPR:$rj, simm12_addlike:$imm12),
|
||
|
"$rd, $rj, $imm12">;
|
||
|
class LOAD_2RI14<bits<32> op>
|
||
|
: Fmt2RI14<op, (outs GPR:$rd), (ins GPR:$rj, simm14_lsl2:$imm14),
|
||
|
"$rd, $rj, $imm14">;
|
||
|
} // hasSideEffects = 0, mayLoad = 1, mayStore = 0
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in {
|
||
|
class STORE_3R<bits<32> op>
|
||
|
: Fmt3R<op, (outs), (ins GPR:$rd, GPR:$rj, GPR:$rk),
|
||
|
"$rd, $rj, $rk">;
|
||
|
class STORE_2RI12<bits<32> op>
|
||
|
: Fmt2RI12<op, (outs), (ins GPR:$rd, GPR:$rj, simm12_addlike:$imm12),
|
||
|
"$rd, $rj, $imm12">;
|
||
|
class STORE_2RI14<bits<32> op>
|
||
|
: Fmt2RI14<op, (outs), (ins GPR:$rd, GPR:$rj, simm14_lsl2:$imm14),
|
||
|
"$rd, $rj, $imm14">;
|
||
|
} // hasSideEffects = 0, mayLoad = 0, mayStore = 1
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 1, Constraints = "@earlyclobber $rd" in
|
||
|
class AM_3R<bits<32> op>
|
||
|
: Fmt3R<op, (outs GPR:$rd), (ins GPR:$rk, GPRMemAtomic:$rj),
|
||
|
"$rd, $rk, $rj">;
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
|
||
|
class LLBase<bits<32> op>
|
||
|
: Fmt2RI14<op, (outs GPR:$rd), (ins GPR:$rj, simm14_lsl2:$imm14),
|
||
|
"$rd, $rj, $imm14">;
|
||
|
class LLBase_ACQ<bits<32> op>
|
||
|
: Fmt2R<op, (outs GPR:$rd), (ins GPR:$rj), "$rd, $rj">;
|
||
|
}
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 1, Constraints = "$rd = $dst" in {
|
||
|
class SCBase<bits<32> op>
|
||
|
: Fmt2RI14<op, (outs GPR:$dst), (ins GPR:$rd, GPR:$rj, simm14_lsl2:$imm14),
|
||
|
"$rd, $rj, $imm14">;
|
||
|
class SCBase_128<bits<32> op>
|
||
|
: Fmt3R<op, (outs GPR:$dst), (ins GPR:$rd, GPR:$rk, GPR:$rj),
|
||
|
"$rd, $rk, $rj">;
|
||
|
class SCBase_REL<bits<32> op>
|
||
|
: Fmt2R<op, (outs GPR:$dst), (ins GPR:$rd, GPR:$rj), "$rd, $rj">;
|
||
|
}
|
||
|
|
||
|
let hasSideEffects = 1 in
|
||
|
class IOCSRRD<bits<32> op>
|
||
|
: Fmt2R<op, (outs GPR:$rd), (ins GPR:$rj), "$rd, $rj">;
|
||
|
|
||
|
let hasSideEffects = 1 in
|
||
|
class IOCSRWR<bits<32> op>
|
||
|
: Fmt2R<op, (outs), (ins GPR:$rd, GPR:$rj), "$rd, $rj">;
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Basic Integer Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
// Arithmetic Operation Instructions
|
||
|
def ADD_W : ALU_3R<0x00100000>;
|
||
|
def SUB_W : ALU_3R<0x00110000>;
|
||
|
def ADDI_W : ALU_2RI12<0x02800000, simm12_addlike>;
|
||
|
def ALSL_W : ALU_3RI2<0x00040000, uimm2_plus1>;
|
||
|
def LU12I_W : ALU_1RI20<0x14000000, simm20_lu12iw>;
|
||
|
def SLT : ALU_3R<0x00120000>;
|
||
|
def SLTU : ALU_3R<0x00128000>;
|
||
|
def SLTI : ALU_2RI12<0x02000000, simm12>;
|
||
|
def SLTUI : ALU_2RI12<0x02400000, simm12>;
|
||
|
def PCADDI : ALU_1RI20<0x18000000, simm20>;
|
||
|
def PCADDU12I : ALU_1RI20<0x1c000000, simm20>;
|
||
|
def PCALAU12I : ALU_1RI20<0x1a000000, simm20_pcalau12i>;
|
||
|
def AND : ALU_3R<0x00148000>;
|
||
|
def OR : ALU_3R<0x00150000>;
|
||
|
def NOR : ALU_3R<0x00140000>;
|
||
|
def XOR : ALU_3R<0x00158000>;
|
||
|
def ANDN : ALU_3R<0x00168000>;
|
||
|
def ORN : ALU_3R<0x00160000>;
|
||
|
def ANDI : ALU_2RI12<0x03400000, uimm12>;
|
||
|
def ORI : ALU_2RI12<0x03800000, uimm12_ori>;
|
||
|
def XORI : ALU_2RI12<0x03c00000, uimm12>;
|
||
|
def MUL_W : ALU_3R<0x001c0000>;
|
||
|
def MULH_W : ALU_3R<0x001c8000>;
|
||
|
def MULH_WU : ALU_3R<0x001d0000>;
|
||
|
let usesCustomInserter = true in {
|
||
|
def DIV_W : ALU_3R<0x00200000>;
|
||
|
def MOD_W : ALU_3R<0x00208000>;
|
||
|
def DIV_WU : ALU_3R<0x00210000>;
|
||
|
def MOD_WU : ALU_3R<0x00218000>;
|
||
|
} // usesCustomInserter = true
|
||
|
|
||
|
// Bit-shift Instructions
|
||
|
def SLL_W : ALU_3R<0x00170000>;
|
||
|
def SRL_W : ALU_3R<0x00178000>;
|
||
|
def SRA_W : ALU_3R<0x00180000>;
|
||
|
def ROTR_W : ALU_3R<0x001b0000>;
|
||
|
|
||
|
def SLLI_W : ALU_2RI5<0x00408000, uimm5>;
|
||
|
def SRLI_W : ALU_2RI5<0x00448000, uimm5>;
|
||
|
def SRAI_W : ALU_2RI5<0x00488000, uimm5>;
|
||
|
def ROTRI_W : ALU_2RI5<0x004c8000, uimm5>;
|
||
|
|
||
|
// Bit-manipulation Instructions
|
||
|
def EXT_W_B : ALU_2R<0x00005c00>;
|
||
|
def EXT_W_H : ALU_2R<0x00005800>;
|
||
|
def CLO_W : ALU_2R<0x00001000>;
|
||
|
def CLZ_W : ALU_2R<0x00001400>;
|
||
|
def CTO_W : ALU_2R<0x00001800>;
|
||
|
def CTZ_W : ALU_2R<0x00001c00>;
|
||
|
def BYTEPICK_W : ALU_3RI2<0x00080000, uimm2>;
|
||
|
def REVB_2H : ALU_2R<0x00003000>;
|
||
|
def BITREV_4B : ALU_2R<0x00004800>;
|
||
|
def BITREV_W : ALU_2R<0x00005000>;
|
||
|
let Constraints = "$rd = $dst" in {
|
||
|
def BSTRINS_W : FmtBSTR_W<0x00600000, (outs GPR:$dst),
|
||
|
(ins GPR:$rd, GPR:$rj, uimm5:$msbw, uimm5:$lsbw),
|
||
|
"$rd, $rj, $msbw, $lsbw">;
|
||
|
}
|
||
|
def BSTRPICK_W : FmtBSTR_W<0x00608000, (outs GPR:$rd),
|
||
|
(ins GPR:$rj, uimm5:$msbw, uimm5:$lsbw),
|
||
|
"$rd, $rj, $msbw, $lsbw">;
|
||
|
def MASKEQZ : ALU_3R<0x00130000>;
|
||
|
def MASKNEZ : ALU_3R<0x00138000>;
|
||
|
|
||
|
// Branch Instructions
|
||
|
def BEQ : BrCC_2RI16<0x58000000>;
|
||
|
def BNE : BrCC_2RI16<0x5c000000>;
|
||
|
def BLT : BrCC_2RI16<0x60000000>;
|
||
|
def BGE : BrCC_2RI16<0x64000000>;
|
||
|
def BLTU : BrCC_2RI16<0x68000000>;
|
||
|
def BGEU : BrCC_2RI16<0x6c000000>;
|
||
|
def BEQZ : BrCCZ_1RI21<0x40000000>;
|
||
|
def BNEZ : BrCCZ_1RI21<0x44000000>;
|
||
|
def B : Br_I26<0x50000000>;
|
||
|
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCall = 1, Defs=[R1] in
|
||
|
def BL : FmtI26<0x54000000, (outs), (ins simm26_symbol:$imm26), "$imm26">;
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
|
||
|
def JIRL : Fmt2RI16<0x4c000000, (outs GPR:$rd),
|
||
|
(ins GPR:$rj, simm16_lsl2:$imm16), "$rd, $rj, $imm16">;
|
||
|
|
||
|
// Common Memory Access Instructions
|
||
|
def LD_B : LOAD_2RI12<0x28000000>;
|
||
|
def LD_H : LOAD_2RI12<0x28400000>;
|
||
|
def LD_W : LOAD_2RI12<0x28800000>;
|
||
|
def LD_BU : LOAD_2RI12<0x2a000000>;
|
||
|
def LD_HU : LOAD_2RI12<0x2a400000>;
|
||
|
def ST_B : STORE_2RI12<0x29000000>;
|
||
|
def ST_H : STORE_2RI12<0x29400000>;
|
||
|
def ST_W : STORE_2RI12<0x29800000>;
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 1 in
|
||
|
def PRELD : FmtPRELD<(outs), (ins uimm5:$imm5, GPR:$rj, simm12:$imm12),
|
||
|
"$imm5, $rj, $imm12">;
|
||
|
|
||
|
// Atomic Memory Access Instructions
|
||
|
def LL_W : LLBase<0x20000000>;
|
||
|
def SC_W : SCBase<0x21000000>;
|
||
|
def LLACQ_W : LLBase_ACQ<0x38578000>;
|
||
|
def SCREL_W : SCBase_REL<0x38578400>;
|
||
|
|
||
|
// Barrier Instructions
|
||
|
def DBAR : MISC_I15<0x38720000>;
|
||
|
def IBAR : MISC_I15<0x38728000>;
|
||
|
|
||
|
// Other Miscellaneous Instructions
|
||
|
def SYSCALL : MISC_I15<0x002b0000>;
|
||
|
def BREAK : MISC_I15<0x002a0000>;
|
||
|
def RDTIMEL_W : RDTIME_2R<0x00006000>;
|
||
|
def RDTIMEH_W : RDTIME_2R<0x00006400>;
|
||
|
def CPUCFG : ALU_2R<0x00006c00>;
|
||
|
|
||
|
// Cache Maintenance Instructions
|
||
|
def CACOP : FmtCACOP<(outs), (ins uimm5:$op, GPR:$rj, simm12:$imm12),
|
||
|
"$op, $rj, $imm12">;
|
||
|
|
||
|
/// LA64 instructions
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
|
||
|
// Arithmetic Operation Instructions for 64-bits
|
||
|
def ADD_D : ALU_3R<0x00108000>;
|
||
|
def SUB_D : ALU_3R<0x00118000>;
|
||
|
def ADDI_D : ALU_2RI12<0x02c00000, simm12_addlike>;
|
||
|
def ADDU16I_D : ALU_2RI16<0x10000000, simm16>;
|
||
|
def ALSL_WU : ALU_3RI2<0x00060000, uimm2_plus1>;
|
||
|
def ALSL_D : ALU_3RI2<0x002c0000, uimm2_plus1>;
|
||
|
let Constraints = "$rd = $dst" in {
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
|
||
|
def LU32I_D : Fmt1RI20<0x16000000, (outs GPR:$dst),
|
||
|
(ins GPR:$rd, simm20_lu32id:$imm20),
|
||
|
"$rd, $imm20">;
|
||
|
}
|
||
|
def LU52I_D : ALU_2RI12<0x03000000, simm12_lu52id>;
|
||
|
def PCADDU18I : ALU_1RI20<0x1e000000, simm20_pcaddu18i>;
|
||
|
def MUL_D : ALU_3R<0x001d8000>;
|
||
|
def MULH_D : ALU_3R<0x001e0000>;
|
||
|
def MULH_DU : ALU_3R<0x001e8000>;
|
||
|
def MULW_D_W : ALU_3R<0x001f0000>;
|
||
|
def MULW_D_WU : ALU_3R<0x001f8000>;
|
||
|
let usesCustomInserter = true in {
|
||
|
def DIV_D : ALU_3R<0x00220000>;
|
||
|
def MOD_D : ALU_3R<0x00228000>;
|
||
|
def DIV_DU : ALU_3R<0x00230000>;
|
||
|
def MOD_DU : ALU_3R<0x00238000>;
|
||
|
} // usesCustomInserter = true
|
||
|
|
||
|
// Bit-shift Instructions for 64-bits
|
||
|
def SLL_D : ALU_3R<0x00188000>;
|
||
|
def SRL_D : ALU_3R<0x00190000>;
|
||
|
def SRA_D : ALU_3R<0x00198000>;
|
||
|
def ROTR_D : ALU_3R<0x001b8000>;
|
||
|
def SLLI_D : ALU_2RI6<0x00410000, uimm6>;
|
||
|
def SRLI_D : ALU_2RI6<0x00450000, uimm6>;
|
||
|
def SRAI_D : ALU_2RI6<0x00490000, uimm6>;
|
||
|
def ROTRI_D : ALU_2RI6<0x004d0000, uimm6>;
|
||
|
|
||
|
// Bit-manipulation Instructions for 64-bits
|
||
|
def CLO_D : ALU_2R<0x00002000>;
|
||
|
def CLZ_D : ALU_2R<0x00002400>;
|
||
|
def CTO_D : ALU_2R<0x00002800>;
|
||
|
def CTZ_D : ALU_2R<0x00002c00>;
|
||
|
def BYTEPICK_D : ALU_3RI3<0x000c0000, uimm3>;
|
||
|
def REVB_4H : ALU_2R<0x00003400>;
|
||
|
def REVB_2W : ALU_2R<0x00003800>;
|
||
|
def REVB_D : ALU_2R<0x00003c00>;
|
||
|
def REVH_2W : ALU_2R<0x00004000>;
|
||
|
def REVH_D : ALU_2R<0x00004400>;
|
||
|
def BITREV_8B : ALU_2R<0x00004c00>;
|
||
|
def BITREV_D : ALU_2R<0x00005400>;
|
||
|
let Constraints = "$rd = $dst" in {
|
||
|
def BSTRINS_D : FmtBSTR_D<0x00800000, (outs GPR:$dst),
|
||
|
(ins GPR:$rd, GPR:$rj, uimm6:$msbd, uimm6:$lsbd),
|
||
|
"$rd, $rj, $msbd, $lsbd">;
|
||
|
}
|
||
|
def BSTRPICK_D : FmtBSTR_D<0x00c00000, (outs GPR:$rd),
|
||
|
(ins GPR:$rj, uimm6:$msbd, uimm6:$lsbd),
|
||
|
"$rd, $rj, $msbd, $lsbd">;
|
||
|
|
||
|
// Common Memory Access Instructions for 64-bits
|
||
|
def LD_WU : LOAD_2RI12<0x2a800000>;
|
||
|
def LD_D : LOAD_2RI12<0x28c00000>;
|
||
|
def ST_D : STORE_2RI12<0x29c00000>;
|
||
|
def LDX_B : LOAD_3R<0x38000000>;
|
||
|
def LDX_H : LOAD_3R<0x38040000>;
|
||
|
def LDX_W : LOAD_3R<0x38080000>;
|
||
|
def LDX_D : LOAD_3R<0x380c0000>;
|
||
|
def LDX_BU : LOAD_3R<0x38200000>;
|
||
|
def LDX_HU : LOAD_3R<0x38240000>;
|
||
|
def LDX_WU : LOAD_3R<0x38280000>;
|
||
|
def STX_B : STORE_3R<0x38100000>;
|
||
|
def STX_H : STORE_3R<0x38140000>;
|
||
|
def STX_W : STORE_3R<0x38180000>;
|
||
|
def STX_D : STORE_3R<0x381c0000>;
|
||
|
def LDPTR_W : LOAD_2RI14<0x24000000>;
|
||
|
def LDPTR_D : LOAD_2RI14<0x26000000>;
|
||
|
def STPTR_W : STORE_2RI14<0x25000000>;
|
||
|
def STPTR_D : STORE_2RI14<0x27000000>;
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 1 in
|
||
|
def PRELDX : FmtPRELDX<(outs), (ins uimm5:$imm5, GPR:$rj, GPR:$rk),
|
||
|
"$imm5, $rj, $rk">;
|
||
|
|
||
|
// Bound Check Memory Access Instructions
|
||
|
def LDGT_B : LOAD_3R<0x38780000>;
|
||
|
def LDGT_H : LOAD_3R<0x38788000>;
|
||
|
def LDGT_W : LOAD_3R<0x38790000>;
|
||
|
def LDGT_D : LOAD_3R<0x38798000>;
|
||
|
def LDLE_B : LOAD_3R<0x387a0000>;
|
||
|
def LDLE_H : LOAD_3R<0x387a8000>;
|
||
|
def LDLE_W : LOAD_3R<0x387b0000>;
|
||
|
def LDLE_D : LOAD_3R<0x387b8000>;
|
||
|
def STGT_B : STORE_3R<0x387c0000>;
|
||
|
def STGT_H : STORE_3R<0x387c8000>;
|
||
|
def STGT_W : STORE_3R<0x387d0000>;
|
||
|
def STGT_D : STORE_3R<0x387d8000>;
|
||
|
def STLE_B : STORE_3R<0x387e0000>;
|
||
|
def STLE_H : STORE_3R<0x387e8000>;
|
||
|
def STLE_W : STORE_3R<0x387f0000>;
|
||
|
def STLE_D : STORE_3R<0x387f8000>;
|
||
|
|
||
|
// Atomic Memory Access Instructions for 64-bits
|
||
|
def AMSWAP_B : AM_3R<0x385c0000>;
|
||
|
def AMSWAP_H : AM_3R<0x385c8000>;
|
||
|
def AMSWAP_W : AM_3R<0x38600000>;
|
||
|
def AMSWAP_D : AM_3R<0x38608000>;
|
||
|
def AMADD_B : AM_3R<0x385d0000>;
|
||
|
def AMADD_H : AM_3R<0x385d8000>;
|
||
|
def AMADD_W : AM_3R<0x38610000>;
|
||
|
def AMADD_D : AM_3R<0x38618000>;
|
||
|
def AMAND_W : AM_3R<0x38620000>;
|
||
|
def AMAND_D : AM_3R<0x38628000>;
|
||
|
def AMOR_W : AM_3R<0x38630000>;
|
||
|
def AMOR_D : AM_3R<0x38638000>;
|
||
|
def AMXOR_W : AM_3R<0x38640000>;
|
||
|
def AMXOR_D : AM_3R<0x38648000>;
|
||
|
def AMMAX_W : AM_3R<0x38650000>;
|
||
|
def AMMAX_D : AM_3R<0x38658000>;
|
||
|
def AMMIN_W : AM_3R<0x38660000>;
|
||
|
def AMMIN_D : AM_3R<0x38668000>;
|
||
|
def AMMAX_WU : AM_3R<0x38670000>;
|
||
|
def AMMAX_DU : AM_3R<0x38678000>;
|
||
|
def AMMIN_WU : AM_3R<0x38680000>;
|
||
|
def AMMIN_DU : AM_3R<0x38688000>;
|
||
|
def AMSWAP__DB_B : AM_3R<0x385e0000>;
|
||
|
def AMSWAP__DB_H : AM_3R<0x385e8000>;
|
||
|
def AMSWAP__DB_W : AM_3R<0x38690000>;
|
||
|
def AMSWAP__DB_D : AM_3R<0x38698000>;
|
||
|
def AMADD__DB_B : AM_3R<0x385f0000>;
|
||
|
def AMADD__DB_H : AM_3R<0x385f8000>;
|
||
|
def AMADD__DB_W : AM_3R<0x386a0000>;
|
||
|
def AMADD__DB_D : AM_3R<0x386a8000>;
|
||
|
def AMAND__DB_W : AM_3R<0x386b0000>;
|
||
|
def AMAND__DB_D : AM_3R<0x386b8000>;
|
||
|
def AMOR__DB_W : AM_3R<0x386c0000>;
|
||
|
def AMOR__DB_D : AM_3R<0x386c8000>;
|
||
|
def AMXOR__DB_W : AM_3R<0x386d0000>;
|
||
|
def AMXOR__DB_D : AM_3R<0x386d8000>;
|
||
|
def AMMAX__DB_W : AM_3R<0x386e0000>;
|
||
|
def AMMAX__DB_D : AM_3R<0x386e8000>;
|
||
|
def AMMIN__DB_W : AM_3R<0x386f0000>;
|
||
|
def AMMIN__DB_D : AM_3R<0x386f8000>;
|
||
|
def AMMAX__DB_WU : AM_3R<0x38700000>;
|
||
|
def AMMAX__DB_DU : AM_3R<0x38708000>;
|
||
|
def AMMIN__DB_WU : AM_3R<0x38710000>;
|
||
|
def AMMIN__DB_DU : AM_3R<0x38718000>;
|
||
|
def AMCAS_B : AM_3R<0x38580000>;
|
||
|
def AMCAS_H : AM_3R<0x38588000>;
|
||
|
def AMCAS_W : AM_3R<0x38590000>;
|
||
|
def AMCAS_D : AM_3R<0x38598000>;
|
||
|
def AMCAS__DB_B : AM_3R<0x385a0000>;
|
||
|
def AMCAS__DB_H : AM_3R<0x385a8000>;
|
||
|
def AMCAS__DB_W : AM_3R<0x385b0000>;
|
||
|
def AMCAS__DB_D : AM_3R<0x385b8000>;
|
||
|
def LL_D : LLBase<0x22000000>;
|
||
|
def SC_D : SCBase<0x23000000>;
|
||
|
def SC_Q : SCBase_128<0x38570000>;
|
||
|
def LLACQ_D : LLBase_ACQ<0x38578800>;
|
||
|
def SCREL_D : SCBase_REL<0x38578C00>;
|
||
|
|
||
|
// CRC Check Instructions
|
||
|
def CRC_W_B_W : ALU_3R<0x00240000>;
|
||
|
def CRC_W_H_W : ALU_3R<0x00248000>;
|
||
|
def CRC_W_W_W : ALU_3R<0x00250000>;
|
||
|
def CRC_W_D_W : ALU_3R<0x00258000>;
|
||
|
def CRCC_W_B_W : ALU_3R<0x00260000>;
|
||
|
def CRCC_W_H_W : ALU_3R<0x00268000>;
|
||
|
def CRCC_W_W_W : ALU_3R<0x00270000>;
|
||
|
def CRCC_W_D_W : ALU_3R<0x00278000>;
|
||
|
|
||
|
// Other Miscellaneous Instructions for 64-bits
|
||
|
def ASRTLE_D : FmtASRT<0x00010000, (outs), (ins GPR:$rj, GPR:$rk),
|
||
|
"$rj, $rk">;
|
||
|
def ASRTGT_D : FmtASRT<0x00018000, (outs), (ins GPR:$rj, GPR:$rk),
|
||
|
"$rj, $rk">;
|
||
|
def RDTIME_D : RDTIME_2R<0x00006800>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Pseudo-instructions and codegen patterns
|
||
|
//
|
||
|
// Naming convention: For 'generic' pattern classes, we use the naming
|
||
|
// convention PatTy1Ty2.
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
/// Generic pattern classes
|
||
|
|
||
|
class PatGprGpr<SDPatternOperator OpNode, LAInst Inst>
|
||
|
: Pat<(OpNode GPR:$rj, GPR:$rk), (Inst GPR:$rj, GPR:$rk)>;
|
||
|
class PatGprGpr_32<SDPatternOperator OpNode, LAInst Inst>
|
||
|
: Pat<(sext_inreg (OpNode GPR:$rj, GPR:$rk), i32), (Inst GPR:$rj, GPR:$rk)>;
|
||
|
class PatGpr<SDPatternOperator OpNode, LAInst Inst>
|
||
|
: Pat<(OpNode GPR:$rj), (Inst GPR:$rj)>;
|
||
|
|
||
|
class PatGprImm<SDPatternOperator OpNode, LAInst Inst, Operand ImmOpnd>
|
||
|
: Pat<(OpNode GPR:$rj, ImmOpnd:$imm),
|
||
|
(Inst GPR:$rj, ImmOpnd:$imm)>;
|
||
|
class PatGprImm_32<SDPatternOperator OpNode, LAInst Inst, Operand ImmOpnd>
|
||
|
: Pat<(sext_inreg (OpNode GPR:$rj, ImmOpnd:$imm), i32),
|
||
|
(Inst GPR:$rj, ImmOpnd:$imm)>;
|
||
|
|
||
|
/// Predicates
|
||
|
def AddLike: PatFrags<(ops node:$A, node:$B),
|
||
|
[(add node:$A, node:$B), (or node:$A, node:$B)], [{
|
||
|
return N->getOpcode() == ISD::ADD || isOrEquivalentToAdd(N);
|
||
|
}]>;
|
||
|
|
||
|
/// Simple arithmetic operations
|
||
|
|
||
|
// Match both a plain shift and one where the shift amount is masked (this is
|
||
|
// typically introduced when the legalizer promotes the shift amount and
|
||
|
// zero-extends it). For LoongArch, the mask is unnecessary as shifts in the
|
||
|
// base ISA only read the least significant 5 bits (LA32) or 6 bits (LA64).
|
||
|
def shiftMaskGRLen
|
||
|
: ComplexPattern<GRLenVT, 1, "selectShiftMaskGRLen", [], [], 0>;
|
||
|
def shiftMask32 : ComplexPattern<i64, 1, "selectShiftMask32", [], [], 0>;
|
||
|
|
||
|
def sexti32 : ComplexPattern<i64, 1, "selectSExti32">;
|
||
|
def zexti32 : ComplexPattern<i64, 1, "selectZExti32">;
|
||
|
|
||
|
class shiftop<SDPatternOperator operator>
|
||
|
: PatFrag<(ops node:$val, node:$count),
|
||
|
(operator node:$val, (GRLenVT (shiftMaskGRLen node:$count)))>;
|
||
|
class shiftopw<SDPatternOperator operator>
|
||
|
: PatFrag<(ops node:$val, node:$count),
|
||
|
(operator node:$val, (i64 (shiftMask32 node:$count)))>;
|
||
|
|
||
|
def mul_const_oneuse : PatFrag<(ops node:$A, node:$B),
|
||
|
(mul node:$A, node:$B), [{
|
||
|
if (auto *N1C = dyn_cast<ConstantSDNode>(N->getOperand(1)))
|
||
|
return N1C->hasOneUse();
|
||
|
return false;
|
||
|
}]>;
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : PatGprGpr<add, ADD_W>;
|
||
|
def : PatGprImm<add, ADDI_W, simm12>;
|
||
|
def : PatGprGpr<sub, SUB_W>;
|
||
|
def : PatGprGpr<sdiv, DIV_W>;
|
||
|
def : PatGprGpr<udiv, DIV_WU>;
|
||
|
def : PatGprGpr<srem, MOD_W>;
|
||
|
def : PatGprGpr<urem, MOD_WU>;
|
||
|
def : PatGprGpr<mul, MUL_W>;
|
||
|
def : PatGprGpr<mulhs, MULH_W>;
|
||
|
def : PatGprGpr<mulhu, MULH_WU>;
|
||
|
def : PatGprGpr<rotr, ROTR_W>;
|
||
|
def : PatGprImm<rotr, ROTRI_W, uimm5>;
|
||
|
|
||
|
foreach Idx = 1...3 in {
|
||
|
defvar ShamtA = !mul(8, Idx);
|
||
|
defvar ShamtB = !mul(8, !sub(4, Idx));
|
||
|
def : Pat<(or (shl GPR:$rk, (i32 ShamtA)), (srl GPR:$rj, (i32 ShamtB))),
|
||
|
(BYTEPICK_W GPR:$rj, GPR:$rk, Idx)>;
|
||
|
}
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : PatGprGpr<add, ADD_D>;
|
||
|
def : PatGprGpr_32<add, ADD_W>;
|
||
|
def : PatGprImm<add, ADDI_D, simm12>;
|
||
|
def : PatGprImm_32<add, ADDI_W, simm12>;
|
||
|
def : PatGprGpr<sub, SUB_D>;
|
||
|
def : PatGprGpr_32<sub, SUB_W>;
|
||
|
def : PatGprGpr<sdiv, DIV_D>;
|
||
|
def : PatGprGpr<udiv, DIV_DU>;
|
||
|
def : PatGprGpr<srem, MOD_D>;
|
||
|
def : PatGprGpr<urem, MOD_DU>;
|
||
|
def : PatGprGpr<rotr, ROTR_D>;
|
||
|
def : PatGprGpr<loongarch_rotr_w, ROTR_W>;
|
||
|
def : PatGprImm<rotr, ROTRI_D, uimm6>;
|
||
|
def : PatGprImm_32<rotr, ROTRI_W, uimm5>;
|
||
|
def : Pat<(loongarch_rotl_w GPR:$rj, uimm5:$imm),
|
||
|
(ROTRI_W GPR:$rj, (ImmSubFrom32 uimm5:$imm))>;
|
||
|
def : Pat<(sext_inreg (loongarch_rotl_w GPR:$rj, uimm5:$imm), i32),
|
||
|
(ROTRI_W GPR:$rj, (ImmSubFrom32 uimm5:$imm))>;
|
||
|
// TODO: Select "_W[U]" instructions for i32xi32 if only lower 32 bits of the
|
||
|
// product are used.
|
||
|
def : PatGprGpr<mul, MUL_D>;
|
||
|
def : PatGprGpr<mulhs, MULH_D>;
|
||
|
def : PatGprGpr<mulhu, MULH_DU>;
|
||
|
// Select MULW_D_W for calculating the full 64 bits product of i32xi32 signed
|
||
|
// multiplication.
|
||
|
def : Pat<(i64 (mul (sext_inreg GPR:$rj, i32), (sext_inreg GPR:$rk, i32))),
|
||
|
(MULW_D_W GPR:$rj, GPR:$rk)>;
|
||
|
// Select MULW_D_WU for calculating the full 64 bits product of i32xi32
|
||
|
// unsigned multiplication.
|
||
|
def : Pat<(i64 (mul (loongarch_bstrpick GPR:$rj, (i64 31), (i64 0)),
|
||
|
(loongarch_bstrpick GPR:$rk, (i64 31), (i64 0)))),
|
||
|
(MULW_D_WU GPR:$rj, GPR:$rk)>;
|
||
|
|
||
|
def : Pat<(add GPR:$rj, simm16_lsl16:$imm),
|
||
|
(ADDU16I_D GPR:$rj, (HI16 $imm))>;
|
||
|
def : Pat<(add GPR:$rj, simm32_hi16_lo12:$imm),
|
||
|
(ADDI_D (ADDU16I_D GPR:$rj, (HI16ForAddu16idAddiPair $imm)),
|
||
|
(LO12 $imm))>;
|
||
|
def : Pat<(sext_inreg (add GPR:$rj, simm32_hi16_lo12:$imm), i32),
|
||
|
(ADDI_W (ADDU16I_D GPR:$rj, (HI16ForAddu16idAddiPair $imm)),
|
||
|
(LO12 $imm))>;
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(add GPR:$rj, (AddiPair:$im)),
|
||
|
(ADDI_W (ADDI_W GPR:$rj, (AddiPairImmLarge AddiPair:$im)),
|
||
|
(AddiPairImmSmall AddiPair:$im))>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(add GPR:$rj, (AddiPair:$im)),
|
||
|
(ADDI_D (ADDI_D GPR:$rj, (AddiPairImmLarge AddiPair:$im)),
|
||
|
(AddiPairImmSmall AddiPair:$im))>;
|
||
|
def : Pat<(sext_inreg (add GPR:$rj, (AddiPair:$im)), i32),
|
||
|
(ADDI_W (ADDI_W GPR:$rj, (AddiPairImmLarge AddiPair:$im)),
|
||
|
(AddiPairImmSmall AddiPair:$im))>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
foreach Idx0 = 1...4 in {
|
||
|
foreach Idx1 = 1...4 in {
|
||
|
defvar CImm = !add(1, !shl(!add(1, !shl(1, Idx0)), Idx1));
|
||
|
def : Pat<(mul_const_oneuse GPR:$r, (i32 CImm)),
|
||
|
(ALSL_W (ALSL_W GPR:$r, GPR:$r, (i32 Idx0)),
|
||
|
GPR:$r, (i32 Idx1))>;
|
||
|
}
|
||
|
}
|
||
|
foreach Idx0 = 1...4 in {
|
||
|
foreach Idx1 = 1...4 in {
|
||
|
defvar Cb = !add(1, !shl(1, Idx0));
|
||
|
defvar CImm = !add(Cb, !shl(Cb, Idx1));
|
||
|
def : Pat<(mul_const_oneuse GPR:$r, (i32 CImm)),
|
||
|
(ALSL_W (ALSL_W GPR:$r, GPR:$r, (i32 Idx0)),
|
||
|
(ALSL_W GPR:$r, GPR:$r, (i32 Idx0)), (i32 Idx1))>;
|
||
|
}
|
||
|
}
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
foreach Idx0 = 1...4 in {
|
||
|
foreach Idx1 = 1...4 in {
|
||
|
defvar CImm = !add(1, !shl(!add(1, !shl(1, Idx0)), Idx1));
|
||
|
def : Pat<(sext_inreg (mul_const_oneuse GPR:$r, (i64 CImm)), i32),
|
||
|
(ALSL_W (ALSL_W GPR:$r, GPR:$r, (i64 Idx0)),
|
||
|
GPR:$r, (i64 Idx1))>;
|
||
|
def : Pat<(mul_const_oneuse GPR:$r, (i64 CImm)),
|
||
|
(ALSL_D (ALSL_D GPR:$r, GPR:$r, (i64 Idx0)),
|
||
|
GPR:$r, (i64 Idx1))>;
|
||
|
}
|
||
|
}
|
||
|
foreach Idx0 = 1...4 in {
|
||
|
foreach Idx1 = 1...4 in {
|
||
|
defvar Cb = !add(1, !shl(1, Idx0));
|
||
|
defvar CImm = !add(Cb, !shl(Cb, Idx1));
|
||
|
def : Pat<(sext_inreg (mul_const_oneuse GPR:$r, (i64 CImm)), i32),
|
||
|
(ALSL_W (ALSL_W GPR:$r, GPR:$r, (i64 Idx0)),
|
||
|
(ALSL_W GPR:$r, GPR:$r, (i64 Idx0)), (i64 Idx1))>;
|
||
|
def : Pat<(mul_const_oneuse GPR:$r, (i64 CImm)),
|
||
|
(ALSL_D (ALSL_D GPR:$r, GPR:$r, (i64 Idx0)),
|
||
|
(ALSL_D GPR:$r, GPR:$r, (i64 Idx0)), (i64 Idx1))>;
|
||
|
}
|
||
|
}
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(mul GPR:$rj, (AlslSlliImm:$im)),
|
||
|
(SLLI_W (ALSL_W GPR:$rj, GPR:$rj, (AlslSlliImmI0 AlslSlliImm:$im)),
|
||
|
(AlslSlliImmI1 AlslSlliImm:$im))>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(sext_inreg (mul GPR:$rj, (AlslSlliImm:$im)), i32),
|
||
|
(SLLI_W (ALSL_W GPR:$rj, GPR:$rj, (AlslSlliImmI0 AlslSlliImm:$im)),
|
||
|
(AlslSlliImmI1 AlslSlliImm:$im))>;
|
||
|
def : Pat<(mul GPR:$rj, (AlslSlliImm:$im)),
|
||
|
(SLLI_D (ALSL_D GPR:$rj, GPR:$rj, (AlslSlliImmI0 AlslSlliImm:$im)),
|
||
|
(AlslSlliImmI1 AlslSlliImm:$im))>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
foreach Idx = 1...7 in {
|
||
|
defvar ShamtA = !mul(8, Idx);
|
||
|
defvar ShamtB = !mul(8, !sub(8, Idx));
|
||
|
def : Pat<(or (shl GPR:$rk, (i64 ShamtA)), (srl GPR:$rj, (i64 ShamtB))),
|
||
|
(BYTEPICK_D GPR:$rj, GPR:$rk, Idx)>;
|
||
|
}
|
||
|
|
||
|
foreach Idx = 1...3 in {
|
||
|
defvar ShamtA = !mul(8, Idx);
|
||
|
defvar ShamtB = !mul(8, !sub(4, Idx));
|
||
|
// NOTE: the srl node would already be transformed into a loongarch_bstrpick
|
||
|
// by the time this pattern gets to execute, hence the weird construction.
|
||
|
def : Pat<(sext_inreg (or (shl GPR:$rk, (i64 ShamtA)),
|
||
|
(loongarch_bstrpick GPR:$rj, (i64 31),
|
||
|
(i64 ShamtB))), i32),
|
||
|
(BYTEPICK_W GPR:$rj, GPR:$rk, Idx)>;
|
||
|
}
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
def : PatGprGpr<and, AND>;
|
||
|
def : PatGprImm<and, ANDI, uimm12>;
|
||
|
def : PatGprGpr<or, OR>;
|
||
|
def : PatGprImm<or, ORI, uimm12>;
|
||
|
def : PatGprGpr<xor, XOR>;
|
||
|
def : PatGprImm<xor, XORI, uimm12>;
|
||
|
def : Pat<(not GPR:$rj), (NOR GPR:$rj, R0)>;
|
||
|
def : Pat<(not (or GPR:$rj, GPR:$rk)), (NOR GPR:$rj, GPR:$rk)>;
|
||
|
def : Pat<(or GPR:$rj, (not GPR:$rk)), (ORN GPR:$rj, GPR:$rk)>;
|
||
|
def : Pat<(and GPR:$rj, (not GPR:$rk)), (ANDN GPR:$rj, GPR:$rk)>;
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(and GPR:$rj, BstrinsImm:$imm),
|
||
|
(BSTRINS_W GPR:$rj, R0, (BstrinsMsb BstrinsImm:$imm),
|
||
|
(BstrinsLsb BstrinsImm:$imm))>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(and GPR:$rj, BstrinsImm:$imm),
|
||
|
(BSTRINS_D GPR:$rj, R0, (BstrinsMsb BstrinsImm:$imm),
|
||
|
(BstrinsLsb BstrinsImm:$imm))>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Traps
|
||
|
|
||
|
// We lower `trap` to `amswap.w rd:$r0, rk:$r1, rj:$r0`, as this is guaranteed
|
||
|
// to trap with an INE (non-existent on LA32, explicitly documented to INE on
|
||
|
// LA64). And the resulting signal is different from `debugtrap` like on some
|
||
|
// other existing ports so programs/porters might have an easier time.
|
||
|
def PseudoUNIMP : Pseudo<(outs), (ins), [(trap)]>,
|
||
|
PseudoInstExpansion<(AMSWAP_W R0, R1, R0)>;
|
||
|
|
||
|
// We lower `debugtrap` to `break 0`, as this is guaranteed to exist and work,
|
||
|
// even for LA32 Primary. Also, because so far the ISA does not provide a
|
||
|
// specific trap instruction/kind exclusively for alerting the debugger,
|
||
|
// every other project uses the generic immediate of 0 for this.
|
||
|
def : Pat<(debugtrap), (BREAK 0)>;
|
||
|
|
||
|
/// Bit counting operations
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : PatGpr<ctlz, CLZ_D>;
|
||
|
def : PatGpr<cttz, CTZ_D>;
|
||
|
def : Pat<(ctlz (not GPR:$rj)), (CLO_D GPR:$rj)>;
|
||
|
def : Pat<(cttz (not GPR:$rj)), (CTO_D GPR:$rj)>;
|
||
|
def : PatGpr<loongarch_clzw, CLZ_W>;
|
||
|
def : PatGpr<loongarch_ctzw, CTZ_W>;
|
||
|
def : Pat<(loongarch_clzw (not GPR:$rj)), (CLO_W GPR:$rj)>;
|
||
|
def : Pat<(loongarch_ctzw (not GPR:$rj)), (CTO_W GPR:$rj)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : PatGpr<ctlz, CLZ_W>;
|
||
|
def : PatGpr<cttz, CTZ_W>;
|
||
|
def : Pat<(ctlz (not GPR:$rj)), (CLO_W GPR:$rj)>;
|
||
|
def : Pat<(cttz (not GPR:$rj)), (CTO_W GPR:$rj)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
/// FrameIndex calculations
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(AddLike (i32 BaseAddr:$rj), simm12:$imm12),
|
||
|
(ADDI_W (i32 BaseAddr:$rj), simm12:$imm12)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(AddLike (i64 BaseAddr:$rj), simm12:$imm12),
|
||
|
(ADDI_D (i64 BaseAddr:$rj), simm12:$imm12)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Shifted addition
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(add GPR:$rk, (shl GPR:$rj, uimm2_plus1:$imm2)),
|
||
|
(ALSL_W GPR:$rj, GPR:$rk, uimm2_plus1:$imm2)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(add GPR:$rk, (shl GPR:$rj, uimm2_plus1:$imm2)),
|
||
|
(ALSL_D GPR:$rj, GPR:$rk, uimm2_plus1:$imm2)>;
|
||
|
def : Pat<(sext_inreg (add GPR:$rk, (shl GPR:$rj, uimm2_plus1:$imm2)), i32),
|
||
|
(ALSL_W GPR:$rj, GPR:$rk, uimm2_plus1:$imm2)>;
|
||
|
def : Pat<(loongarch_bstrpick (add GPR:$rk, (shl GPR:$rj, uimm2_plus1:$imm2)),
|
||
|
(i64 31), (i64 0)),
|
||
|
(ALSL_WU GPR:$rj, GPR:$rk, uimm2_plus1:$imm2)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Shift
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : PatGprGpr<shiftop<shl>, SLL_W>;
|
||
|
def : PatGprGpr<shiftop<sra>, SRA_W>;
|
||
|
def : PatGprGpr<shiftop<srl>, SRL_W>;
|
||
|
def : PatGprImm<shl, SLLI_W, uimm5>;
|
||
|
def : PatGprImm<sra, SRAI_W, uimm5>;
|
||
|
def : PatGprImm<srl, SRLI_W, uimm5>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : PatGprGpr<shiftopw<loongarch_sll_w>, SLL_W>;
|
||
|
def : PatGprGpr<shiftopw<loongarch_sra_w>, SRA_W>;
|
||
|
def : PatGprGpr<shiftopw<loongarch_srl_w>, SRL_W>;
|
||
|
def : PatGprGpr<shiftop<shl>, SLL_D>;
|
||
|
def : PatGprGpr<shiftop<sra>, SRA_D>;
|
||
|
def : PatGprGpr<shiftop<srl>, SRL_D>;
|
||
|
def : PatGprImm<shl, SLLI_D, uimm6>;
|
||
|
def : PatGprImm<sra, SRAI_D, uimm6>;
|
||
|
def : PatGprImm<srl, SRLI_D, uimm6>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// sext and zext
|
||
|
|
||
|
def : Pat<(sext_inreg GPR:$rj, i8), (EXT_W_B GPR:$rj)>;
|
||
|
def : Pat<(sext_inreg GPR:$rj, i16), (EXT_W_H GPR:$rj)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(sext_inreg GPR:$rj, i32), (ADDI_W GPR:$rj, 0)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Setcc
|
||
|
|
||
|
def : PatGprGpr<setlt, SLT>;
|
||
|
def : PatGprImm<setlt, SLTI, simm12>;
|
||
|
def : PatGprGpr<setult, SLTU>;
|
||
|
def : PatGprImm<setult, SLTUI, simm12>;
|
||
|
|
||
|
// Define pattern expansions for setcc operations that aren't directly
|
||
|
// handled by a LoongArch instruction.
|
||
|
def : Pat<(seteq GPR:$rj, 0), (SLTUI GPR:$rj, 1)>;
|
||
|
def : Pat<(seteq GPR:$rj, GPR:$rk), (SLTUI (XOR GPR:$rj, GPR:$rk), 1)>;
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(seteq GPR:$rj, simm12_plus1:$imm12),
|
||
|
(SLTUI (ADDI_W GPR:$rj, (NegImm simm12_plus1:$imm12)), 1)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(seteq GPR:$rj, simm12_plus1:$imm12),
|
||
|
(SLTUI (ADDI_D GPR:$rj, (NegImm simm12_plus1:$imm12)), 1)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
def : Pat<(setne GPR:$rj, 0), (SLTU R0, GPR:$rj)>;
|
||
|
def : Pat<(setne GPR:$rj, GPR:$rk), (SLTU R0, (XOR GPR:$rj, GPR:$rk))>;
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(setne GPR:$rj, simm12_plus1:$imm12),
|
||
|
(SLTU R0, (ADDI_W GPR:$rj, (NegImm simm12_plus1:$imm12)))>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(setne GPR:$rj, simm12_plus1:$imm12),
|
||
|
(SLTU R0, (ADDI_D GPR:$rj, (NegImm simm12_plus1:$imm12)))>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
def : Pat<(setugt GPR:$rj, GPR:$rk), (SLTU GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(setuge GPR:$rj, GPR:$rk), (XORI (SLTU GPR:$rj, GPR:$rk), 1)>;
|
||
|
def : Pat<(setule GPR:$rj, GPR:$rk), (XORI (SLTU GPR:$rk, GPR:$rj), 1)>;
|
||
|
def : Pat<(setgt GPR:$rj, GPR:$rk), (SLT GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(setge GPR:$rj, GPR:$rk), (XORI (SLT GPR:$rj, GPR:$rk), 1)>;
|
||
|
def : Pat<(setle GPR:$rj, GPR:$rk), (XORI (SLT GPR:$rk, GPR:$rj), 1)>;
|
||
|
|
||
|
/// Select
|
||
|
|
||
|
def : Pat<(select GPR:$cond, GPR:$t, 0), (MASKEQZ GPR:$t, GPR:$cond)>;
|
||
|
def : Pat<(select GPR:$cond, 0, GPR:$f), (MASKNEZ GPR:$f, GPR:$cond)>;
|
||
|
def : Pat<(select GPR:$cond, GPR:$t, GPR:$f),
|
||
|
(OR (MASKEQZ GPR:$t, GPR:$cond), (MASKNEZ GPR:$f, GPR:$cond))>;
|
||
|
|
||
|
/// Branches and jumps
|
||
|
|
||
|
class BccPat<PatFrag CondOp, LAInst Inst>
|
||
|
: Pat<(brcond (GRLenVT (CondOp GPR:$rj, GPR:$rd)), bb:$imm16),
|
||
|
(Inst GPR:$rj, GPR:$rd, bb:$imm16)>;
|
||
|
|
||
|
def : BccPat<seteq, BEQ>;
|
||
|
def : BccPat<setne, BNE>;
|
||
|
def : BccPat<setlt, BLT>;
|
||
|
def : BccPat<setge, BGE>;
|
||
|
def : BccPat<setult, BLTU>;
|
||
|
def : BccPat<setuge, BGEU>;
|
||
|
|
||
|
class BccSwapPat<PatFrag CondOp, LAInst InstBcc>
|
||
|
: Pat<(brcond (GRLenVT (CondOp GPR:$rd, GPR:$rj)), bb:$imm16),
|
||
|
(InstBcc GPR:$rj, GPR:$rd, bb:$imm16)>;
|
||
|
|
||
|
// Condition codes that don't have matching LoongArch branch instructions, but
|
||
|
// are trivially supported by swapping the two input operands.
|
||
|
def : BccSwapPat<setgt, BLT>;
|
||
|
def : BccSwapPat<setle, BGE>;
|
||
|
def : BccSwapPat<setugt, BLTU>;
|
||
|
def : BccSwapPat<setule, BGEU>;
|
||
|
|
||
|
// An extra pattern is needed for a brcond without a setcc (i.e. where the
|
||
|
// condition was calculated elsewhere).
|
||
|
def : Pat<(brcond GPR:$rj, bb:$imm21), (BNEZ GPR:$rj, bb:$imm21)>;
|
||
|
|
||
|
def : Pat<(brcond (GRLenVT (seteq GPR:$rj, 0)), bb:$imm21),
|
||
|
(BEQZ GPR:$rj, bb:$imm21)>;
|
||
|
def : Pat<(brcond (GRLenVT (setne GPR:$rj, 0)), bb:$imm21),
|
||
|
(BNEZ GPR:$rj, bb:$imm21)>;
|
||
|
|
||
|
let isBarrier = 1, isBranch = 1, isTerminator = 1 in
|
||
|
def PseudoBR : Pseudo<(outs), (ins simm26_b:$imm26), [(br bb:$imm26)]>,
|
||
|
PseudoInstExpansion<(B simm26_b:$imm26)>;
|
||
|
|
||
|
let isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in
|
||
|
def PseudoBRIND : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
|
||
|
PseudoInstExpansion<(JIRL R0, GPR:$rj, simm16_lsl2:$imm16)>;
|
||
|
|
||
|
def : Pat<(brind GPR:$rj), (PseudoBRIND GPR:$rj, 0)>;
|
||
|
def : Pat<(brind (add GPR:$rj, simm16_lsl2:$imm16)),
|
||
|
(PseudoBRIND GPR:$rj, simm16_lsl2:$imm16)>;
|
||
|
|
||
|
// Function call with 'Small' code model.
|
||
|
let isCall = 1, Defs = [R1] in
|
||
|
def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$func)>;
|
||
|
|
||
|
def : Pat<(loongarch_call tglobaladdr:$func), (PseudoCALL tglobaladdr:$func)>;
|
||
|
def : Pat<(loongarch_call texternalsym:$func), (PseudoCALL texternalsym:$func)>;
|
||
|
|
||
|
// Function call with 'Medium' code model.
|
||
|
let isCall = 1, Defs = [R1, R20], Size = 8 in
|
||
|
def PseudoCALL_MEDIUM : Pseudo<(outs), (ins bare_symbol:$func)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_call_medium tglobaladdr:$func),
|
||
|
(PseudoCALL_MEDIUM tglobaladdr:$func)>;
|
||
|
def : Pat<(loongarch_call_medium texternalsym:$func),
|
||
|
(PseudoCALL_MEDIUM texternalsym:$func)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
// Function call with 'Large' code model.
|
||
|
let isCall = 1, Defs = [R1, R20], Size = 24 in
|
||
|
def PseudoCALL_LARGE: Pseudo<(outs), (ins bare_symbol:$func)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_call_large tglobaladdr:$func),
|
||
|
(PseudoCALL_LARGE tglobaladdr:$func)>;
|
||
|
def : Pat<(loongarch_call_large texternalsym:$func),
|
||
|
(PseudoCALL_LARGE texternalsym:$func)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let isCall = 1, Defs = [R1] in
|
||
|
def PseudoCALLIndirect : Pseudo<(outs), (ins GPR:$rj),
|
||
|
[(loongarch_call GPR:$rj)]>,
|
||
|
PseudoInstExpansion<(JIRL R1, GPR:$rj, 0)>;
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_call_medium GPR:$rj), (PseudoCALLIndirect GPR:$rj)>;
|
||
|
def : Pat<(loongarch_call_large GPR:$rj), (PseudoCALLIndirect GPR:$rj)>;
|
||
|
}
|
||
|
|
||
|
let isCall = 1, hasSideEffects = 0, mayStore = 0, mayLoad = 0, Defs = [R1] in
|
||
|
def PseudoJIRL_CALL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
|
||
|
PseudoInstExpansion<(JIRL R1, GPR:$rj,
|
||
|
simm16_lsl2:$imm16)>;
|
||
|
|
||
|
let isBarrier = 1, isReturn = 1, isTerminator = 1 in
|
||
|
def PseudoRET : Pseudo<(outs), (ins), [(loongarch_ret)]>,
|
||
|
PseudoInstExpansion<(JIRL R0, R1, 0)>;
|
||
|
|
||
|
// Tail call with 'Small' code model.
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3] in
|
||
|
def PseudoTAIL : Pseudo<(outs), (ins bare_symbol:$dst)>;
|
||
|
|
||
|
def : Pat<(loongarch_tail (iPTR tglobaladdr:$dst)),
|
||
|
(PseudoTAIL tglobaladdr:$dst)>;
|
||
|
def : Pat<(loongarch_tail (iPTR texternalsym:$dst)),
|
||
|
(PseudoTAIL texternalsym:$dst)>;
|
||
|
|
||
|
// Tail call with 'Medium' code model.
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
|
||
|
Uses = [R3], Defs = [R20], Size = 8 in
|
||
|
def PseudoTAIL_MEDIUM : Pseudo<(outs), (ins bare_symbol:$dst)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_tail_medium (iPTR tglobaladdr:$dst)),
|
||
|
(PseudoTAIL_MEDIUM tglobaladdr:$dst)>;
|
||
|
def : Pat<(loongarch_tail_medium (iPTR texternalsym:$dst)),
|
||
|
(PseudoTAIL_MEDIUM texternalsym:$dst)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
// Tail call with 'Large' code model.
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
|
||
|
Uses = [R3], Defs = [R19, R20], Size = 24 in
|
||
|
def PseudoTAIL_LARGE : Pseudo<(outs), (ins bare_symbol:$dst)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_tail_large (iPTR tglobaladdr:$dst)),
|
||
|
(PseudoTAIL_LARGE tglobaladdr:$dst)>;
|
||
|
def : Pat<(loongarch_tail_large (iPTR texternalsym:$dst)),
|
||
|
(PseudoTAIL_LARGE texternalsym:$dst)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3] in
|
||
|
def PseudoTAILIndirect : Pseudo<(outs), (ins GPRT:$rj),
|
||
|
[(loongarch_tail GPRT:$rj)]>,
|
||
|
PseudoInstExpansion<(JIRL R0, GPR:$rj, 0)>;
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_tail_medium GPR:$rj), (PseudoTAILIndirect GPR:$rj)>;
|
||
|
def : Pat<(loongarch_tail_large GPR:$rj), (PseudoTAILIndirect GPR:$rj)>;
|
||
|
}
|
||
|
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
|
||
|
hasSideEffects = 0, mayStore = 0, mayLoad = 0, Uses = [R3] in
|
||
|
def PseudoB_TAIL : Pseudo<(outs), (ins simm26_b:$imm26)>,
|
||
|
PseudoInstExpansion<(B simm26_b:$imm26)>;
|
||
|
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
|
||
|
hasSideEffects = 0, mayStore = 0, mayLoad = 0, Uses = [R3] in
|
||
|
def PseudoJIRL_TAIL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
|
||
|
PseudoInstExpansion<(JIRL R0, GPR:$rj,
|
||
|
simm16_lsl2:$imm16)>;
|
||
|
|
||
|
/// call36/taill36 macro instructions
|
||
|
let isCall = 1, isBarrier = 1, isCodeGenOnly = 0, isAsmParserOnly = 1,
|
||
|
Defs = [R1], Size = 8, hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
|
||
|
def PseudoCALL36 : Pseudo<(outs), (ins bare_symbol:$dst), [],
|
||
|
"call36", "$dst">,
|
||
|
Requires<[IsLA64]>;
|
||
|
let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3],
|
||
|
isCodeGenOnly = 0, isAsmParserOnly = 1, Size = 8, hasSideEffects = 0,
|
||
|
mayStore = 0, mayLoad = 0 in
|
||
|
def PseudoTAIL36 : Pseudo<(outs), (ins GPR:$tmp, bare_symbol:$dst), [],
|
||
|
"tail36", "$tmp, $dst">,
|
||
|
Requires<[IsLA64]>;
|
||
|
|
||
|
/// Load address (la*) macro instructions.
|
||
|
|
||
|
// Define isCodeGenOnly = 0 to expose them to tablegened assembly parser.
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0,
|
||
|
isAsmParserOnly = 1 in {
|
||
|
def PseudoLA_ABS : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.abs", "$dst, $src">;
|
||
|
def PseudoLA_ABS_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.abs", "$dst, $src">;
|
||
|
def PseudoLA_PCREL : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.pcrel", "$dst, $src">;
|
||
|
let Defs = [R20], Size = 20 in
|
||
|
def PseudoLA_PCREL_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.pcrel", "$dst, $tmp, $src">,
|
||
|
Requires<[IsLA64]>;
|
||
|
def PseudoLA_TLS_LE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.tls.le", "$dst, $src">;
|
||
|
}
|
||
|
let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isCodeGenOnly = 0,
|
||
|
isAsmParserOnly = 1 in {
|
||
|
def PseudoLA_GOT : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.got", "$dst, $src">;
|
||
|
def PseudoLA_TLS_IE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.tls.ie", "$dst, $src">;
|
||
|
def PseudoLA_TLS_LD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.tls.ld", "$dst, $src">;
|
||
|
def PseudoLA_TLS_GD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||
|
"la.tls.gd", "$dst, $src">;
|
||
|
let Defs = [R20], Size = 20 in {
|
||
|
def PseudoLA_GOT_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.got", "$dst, $tmp, $src">,
|
||
|
Requires<[IsLA64]>;
|
||
|
def PseudoLA_TLS_IE_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.tls.ie", "$dst, $tmp, $src">,
|
||
|
Requires<[IsLA64]>;
|
||
|
def PseudoLA_TLS_LD_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.tls.ld", "$dst, $tmp, $src">,
|
||
|
Requires<[IsLA64]>;
|
||
|
def PseudoLA_TLS_GD_LARGE : Pseudo<(outs GPR:$dst),
|
||
|
(ins GPR:$tmp, bare_symbol:$src), [],
|
||
|
"la.tls.gd", "$dst, $tmp, $src">,
|
||
|
Requires<[IsLA64]>;
|
||
|
} // Defs = [R20], Size = 20
|
||
|
}
|
||
|
|
||
|
// Load address inst alias: "la", "la.global" and "la.local".
|
||
|
// Default:
|
||
|
// la = la.global = la.got
|
||
|
// la.local = la.pcrel
|
||
|
// With feature "+la-global-with-pcrel":
|
||
|
// la = la.global = la.pcrel
|
||
|
// With feature "+la-global-with-abs":
|
||
|
// la = la.global = la.abs
|
||
|
// With feature "+la-local-with-abs":
|
||
|
// la.local = la.abs
|
||
|
// With features "+la-global-with-pcrel,+la-global-with-abs"(disorder):
|
||
|
// la = la.global = la.pcrel
|
||
|
// Note: To keep consistent with gnu-as behavior, the "la" can only have one
|
||
|
// register operand.
|
||
|
def : InstAlias<"la $dst, $src", (PseudoLA_GOT GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $src",
|
||
|
(PseudoLA_GOT GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $tmp, $src",
|
||
|
(PseudoLA_GOT_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.local $dst, $src",
|
||
|
(PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.local $dst, $tmp, $src",
|
||
|
(PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
|
||
|
|
||
|
// Note: Keep HasLaGlobalWithPcrel before HasLaGlobalWithAbs to ensure
|
||
|
// "la-global-with-pcrel" takes effect when bose "la-global-with-pcrel" and
|
||
|
// "la-global-with-abs" are enabled.
|
||
|
let Predicates = [HasLaGlobalWithPcrel] in {
|
||
|
def : InstAlias<"la $dst, $src", (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $src",
|
||
|
(PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $tmp, $src",
|
||
|
(PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
|
||
|
} // Predicates = [HasLaGlobalWithPcrel]
|
||
|
|
||
|
let Predicates = [HasLaGlobalWithAbs] in {
|
||
|
def : InstAlias<"la $dst, $src", (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $src",
|
||
|
(PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.global $dst, $tmp, $src",
|
||
|
(PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
|
||
|
} // Predicates = [HasLaGlobalWithAbs]
|
||
|
|
||
|
let Predicates = [HasLaLocalWithAbs] in {
|
||
|
def : InstAlias<"la.local $dst, $src",
|
||
|
(PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
|
||
|
def : InstAlias<"la.local $dst, $tmp, $src",
|
||
|
(PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
|
||
|
} // Predicates = [HasLaLocalWithAbs]
|
||
|
|
||
|
/// BSTRINS and BSTRPICK
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(loongarch_bstrins GPR:$rd, GPR:$rj, uimm5:$msbd, uimm5:$lsbd),
|
||
|
(BSTRINS_W GPR:$rd, GPR:$rj, uimm5:$msbd, uimm5:$lsbd)>;
|
||
|
def : Pat<(loongarch_bstrpick GPR:$rj, uimm5:$msbd, uimm5:$lsbd),
|
||
|
(BSTRPICK_W GPR:$rj, uimm5:$msbd, uimm5:$lsbd)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_bstrins GPR:$rd, GPR:$rj, uimm6:$msbd, uimm6:$lsbd),
|
||
|
(BSTRINS_D GPR:$rd, GPR:$rj, uimm6:$msbd, uimm6:$lsbd)>;
|
||
|
def : Pat<(loongarch_bstrpick GPR:$rj, uimm6:$msbd, uimm6:$lsbd),
|
||
|
(BSTRPICK_D GPR:$rj, uimm6:$msbd, uimm6:$lsbd)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Byte-swapping and bit-reversal
|
||
|
|
||
|
def : Pat<(loongarch_revb_2h GPR:$rj), (REVB_2H GPR:$rj)>;
|
||
|
def : Pat<(loongarch_bitrev_4b GPR:$rj), (BITREV_4B GPR:$rj)>;
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : Pat<(bswap GPR:$rj), (ROTRI_W (REVB_2H GPR:$rj), 16)>;
|
||
|
def : Pat<(bitreverse GPR:$rj), (BITREV_W GPR:$rj)>;
|
||
|
def : Pat<(bswap (bitreverse GPR:$rj)), (BITREV_4B GPR:$rj)>;
|
||
|
def : Pat<(bitreverse (bswap GPR:$rj)), (BITREV_4B GPR:$rj)>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_revb_2w GPR:$rj), (REVB_2W GPR:$rj)>;
|
||
|
def : Pat<(bswap GPR:$rj), (REVB_D GPR:$rj)>;
|
||
|
def : Pat<(loongarch_bitrev_w GPR:$rj), (BITREV_W GPR:$rj)>;
|
||
|
def : Pat<(bitreverse GPR:$rj), (BITREV_D GPR:$rj)>;
|
||
|
def : Pat<(bswap (bitreverse GPR:$rj)), (BITREV_8B GPR:$rj)>;
|
||
|
def : Pat<(bitreverse (bswap GPR:$rj)), (BITREV_8B GPR:$rj)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Loads
|
||
|
|
||
|
multiclass LdPat<PatFrag LoadOp, LAInst Inst, ValueType vt = GRLenVT> {
|
||
|
def : Pat<(vt (LoadOp BaseAddr:$rj)), (Inst BaseAddr:$rj, 0)>;
|
||
|
def : Pat<(vt (LoadOp (AddrConstant GPR:$rj, simm12:$imm12))),
|
||
|
(Inst GPR:$rj, simm12:$imm12)>;
|
||
|
def : Pat<(vt (LoadOp (AddLike BaseAddr:$rj, simm12:$imm12))),
|
||
|
(Inst BaseAddr:$rj, simm12:$imm12)>;
|
||
|
}
|
||
|
|
||
|
defm : LdPat<sextloadi8, LD_B>;
|
||
|
defm : LdPat<extloadi8, LD_B>;
|
||
|
defm : LdPat<sextloadi16, LD_H>;
|
||
|
defm : LdPat<extloadi16, LD_H>;
|
||
|
defm : LdPat<load, LD_W>, Requires<[IsLA32]>;
|
||
|
defm : LdPat<zextloadi8, LD_BU>;
|
||
|
defm : LdPat<zextloadi16, LD_HU>;
|
||
|
let Predicates = [IsLA64] in {
|
||
|
defm : LdPat<sextloadi32, LD_W, i64>;
|
||
|
defm : LdPat<extloadi32, LD_W, i64>;
|
||
|
defm : LdPat<zextloadi32, LD_WU, i64>;
|
||
|
defm : LdPat<load, LD_D, i64>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
// LA64 register-register-addressed loads
|
||
|
let Predicates = [IsLA64] in {
|
||
|
class RegRegLdPat<PatFrag LoadOp, LAInst Inst, ValueType vt>
|
||
|
: Pat<(vt (LoadOp (add NonFIBaseAddr:$rj, GPR:$rk))),
|
||
|
(Inst NonFIBaseAddr:$rj, GPR:$rk)>;
|
||
|
|
||
|
def : RegRegLdPat<extloadi8, LDX_B, i64>;
|
||
|
def : RegRegLdPat<sextloadi8, LDX_B, i64>;
|
||
|
def : RegRegLdPat<zextloadi8, LDX_BU, i64>;
|
||
|
def : RegRegLdPat<extloadi16, LDX_H, i64>;
|
||
|
def : RegRegLdPat<sextloadi16, LDX_H, i64>;
|
||
|
def : RegRegLdPat<zextloadi16, LDX_HU, i64>;
|
||
|
def : RegRegLdPat<extloadi32, LDX_W, i64>;
|
||
|
def : RegRegLdPat<sextloadi32, LDX_W, i64>;
|
||
|
def : RegRegLdPat<zextloadi32, LDX_WU, i64>;
|
||
|
def : RegRegLdPat<load, LDX_D, i64>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Stores
|
||
|
|
||
|
multiclass StPat<PatFrag StoreOp, LAInst Inst, RegisterClass StTy,
|
||
|
ValueType vt> {
|
||
|
def : Pat<(StoreOp (vt StTy:$rd), BaseAddr:$rj),
|
||
|
(Inst StTy:$rd, BaseAddr:$rj, 0)>;
|
||
|
def : Pat<(StoreOp (vt StTy:$rs2), (AddrConstant GPR:$rj, simm12:$imm12)),
|
||
|
(Inst StTy:$rs2, GPR:$rj, simm12:$imm12)>;
|
||
|
def : Pat<(StoreOp (vt StTy:$rd), (AddLike BaseAddr:$rj, simm12:$imm12)),
|
||
|
(Inst StTy:$rd, BaseAddr:$rj, simm12:$imm12)>;
|
||
|
}
|
||
|
|
||
|
defm : StPat<truncstorei8, ST_B, GPR, GRLenVT>;
|
||
|
defm : StPat<truncstorei16, ST_H, GPR, GRLenVT>;
|
||
|
defm : StPat<store, ST_W, GPR, i32>, Requires<[IsLA32]>;
|
||
|
let Predicates = [IsLA64] in {
|
||
|
defm : StPat<truncstorei32, ST_W, GPR, i64>;
|
||
|
defm : StPat<store, ST_D, GPR, i64>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(i64 (sextloadi32 (AddLike BaseAddr:$rj, simm14_lsl2:$imm14))),
|
||
|
(LDPTR_W BaseAddr:$rj, simm14_lsl2:$imm14)>;
|
||
|
def : Pat<(i64 (load (AddLike BaseAddr:$rj, simm14_lsl2:$imm14))),
|
||
|
(LDPTR_D BaseAddr:$rj, simm14_lsl2:$imm14)>;
|
||
|
def : Pat<(truncstorei32 (i64 GPR:$rd),
|
||
|
(AddLike BaseAddr:$rj, simm14_lsl2:$imm14)),
|
||
|
(STPTR_W GPR:$rd, BaseAddr:$rj, simm14_lsl2:$imm14)>;
|
||
|
def : Pat<(store (i64 GPR:$rd), (AddLike BaseAddr:$rj, simm14_lsl2:$imm14)),
|
||
|
(STPTR_D GPR:$rd, BaseAddr:$rj, simm14_lsl2:$imm14)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
// LA64 register-register-addressed stores
|
||
|
let Predicates = [IsLA64] in {
|
||
|
class RegRegStPat<PatFrag StoreOp, LAInst Inst, RegisterClass StTy,
|
||
|
ValueType vt>
|
||
|
: Pat<(StoreOp (vt StTy:$rd), (add NonFIBaseAddr:$rj, GPR:$rk)),
|
||
|
(Inst StTy:$rd, NonFIBaseAddr:$rj, GPR:$rk)>;
|
||
|
|
||
|
def : RegRegStPat<truncstorei8, STX_B, GPR, i64>;
|
||
|
def : RegRegStPat<truncstorei16, STX_H, GPR, i64>;
|
||
|
def : RegRegStPat<truncstorei32, STX_W, GPR, i64>;
|
||
|
def : RegRegStPat<store, STX_D, GPR, i64>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Atomic loads and stores
|
||
|
|
||
|
// DBAR hint encoding for LA664 and later micro-architectures, paraphrased from
|
||
|
// the Linux patch revealing it [1]:
|
||
|
//
|
||
|
// - Bit 4: kind of constraint (0: completion, 1: ordering)
|
||
|
// - Bit 3: barrier for previous read (0: true, 1: false)
|
||
|
// - Bit 2: barrier for previous write (0: true, 1: false)
|
||
|
// - Bit 1: barrier for succeeding read (0: true, 1: false)
|
||
|
// - Bit 0: barrier for succeeding write (0: true, 1: false)
|
||
|
//
|
||
|
// Hint 0x700: barrier for "read after read" from the same address, which is
|
||
|
// e.g. needed by LL-SC loops on older models. (DBAR 0x700 behaves the same as
|
||
|
// nop if such reordering is disabled on supporting newer models.)
|
||
|
//
|
||
|
// [1]: https://lore.kernel.org/loongarch/20230516124536.535343-1-chenhuacai@loongson.cn/
|
||
|
//
|
||
|
// Implementations without support for the finer-granularity hints simply treat
|
||
|
// all as the full barrier (DBAR 0), so we can unconditionally start emiting the
|
||
|
// more precise hints right away.
|
||
|
|
||
|
def : Pat<(atomic_fence 4, timm), (DBAR 0b10100)>; // acquire
|
||
|
def : Pat<(atomic_fence 5, timm), (DBAR 0b10010)>; // release
|
||
|
def : Pat<(atomic_fence 6, timm), (DBAR 0b10000)>; // acqrel
|
||
|
def : Pat<(atomic_fence 7, timm), (DBAR 0b10000)>; // seqcst
|
||
|
|
||
|
defm : LdPat<atomic_load_8, LD_B>;
|
||
|
defm : LdPat<atomic_load_16, LD_H>;
|
||
|
defm : LdPat<atomic_load_32, LD_W>;
|
||
|
|
||
|
class release_seqcst_store<PatFrag base>
|
||
|
: PatFrag<(ops node:$val, node:$ptr), (base node:$val, node:$ptr), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getSuccessOrdering();
|
||
|
return isReleaseOrStronger(Ordering);
|
||
|
}]>;
|
||
|
|
||
|
class unordered_monotonic_store<PatFrag base>
|
||
|
: PatFrag<(ops node:$val, node:$ptr), (base node:$val, node:$ptr), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getSuccessOrdering();
|
||
|
return !isReleaseOrStronger(Ordering);
|
||
|
}]>;
|
||
|
|
||
|
def atomic_store_release_seqcst_32 : release_seqcst_store<atomic_store_32>;
|
||
|
def atomic_store_release_seqcst_64 : release_seqcst_store<atomic_store_64>;
|
||
|
def atomic_store_unordered_monotonic_32
|
||
|
: unordered_monotonic_store<atomic_store_32>;
|
||
|
def atomic_store_unordered_monotonic_64
|
||
|
: unordered_monotonic_store<atomic_store_64>;
|
||
|
|
||
|
defm : StPat<atomic_store_8, ST_B, GPR, GRLenVT>;
|
||
|
defm : StPat<atomic_store_16, ST_H, GPR, GRLenVT>;
|
||
|
defm : StPat<atomic_store_unordered_monotonic_32, ST_W, GPR, i32>,
|
||
|
Requires<[IsLA32]>;
|
||
|
|
||
|
def PseudoAtomicStoreW
|
||
|
: Pseudo<(outs GPR:$dst), (ins GPR:$rk, GPR:$rj)>,
|
||
|
PseudoInstExpansion<(AMSWAP__DB_W R0, GPR:$rk, GPRMemAtomic:$rj)>;
|
||
|
|
||
|
def : Pat<(atomic_store_release_seqcst_32 GPR:$rj, GPR:$rk),
|
||
|
(PseudoAtomicStoreW GPR:$rj, GPR:$rk)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def PseudoAtomicStoreD
|
||
|
: Pseudo<(outs GPR:$dst), (ins GPR:$rk, GPR:$rj)>,
|
||
|
PseudoInstExpansion<(AMSWAP__DB_D R0, GPR:$rk, GPRMemAtomic:$rj)>;
|
||
|
|
||
|
def : Pat<(atomic_store_release_seqcst_64 GPR:$rj, GPR:$rk),
|
||
|
(PseudoAtomicStoreD GPR:$rj, GPR:$rk)>;
|
||
|
|
||
|
defm : LdPat<atomic_load_64, LD_D>;
|
||
|
defm : StPat<atomic_store_unordered_monotonic_32, ST_W, GPR, i64>;
|
||
|
defm : StPat<atomic_store_unordered_monotonic_64, ST_D, GPR, i64>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Atomic Ops
|
||
|
|
||
|
class PseudoMaskedAM
|
||
|
: Pseudo<(outs GPR:$res, GPR:$scratch),
|
||
|
(ins GPR:$addr, GPR:$incr, GPR:$mask, grlenimm:$ordering)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 36;
|
||
|
}
|
||
|
|
||
|
def PseudoMaskedAtomicSwap32 : PseudoMaskedAM;
|
||
|
def PseudoMaskedAtomicLoadAdd32 : PseudoMaskedAM;
|
||
|
def PseudoMaskedAtomicLoadSub32 : PseudoMaskedAM;
|
||
|
def PseudoMaskedAtomicLoadNand32 : PseudoMaskedAM;
|
||
|
|
||
|
class PseudoAM : Pseudo<(outs GPR:$res, GPR:$scratch),
|
||
|
(ins GPR:$addr, GPR:$incr, grlenimm:$ordering)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 24;
|
||
|
}
|
||
|
|
||
|
def PseudoAtomicSwap32 : PseudoAM;
|
||
|
def PseudoAtomicLoadNand32 : PseudoAM;
|
||
|
def PseudoAtomicLoadNand64 : PseudoAM;
|
||
|
def PseudoAtomicLoadAdd32 : PseudoAM;
|
||
|
def PseudoAtomicLoadSub32 : PseudoAM;
|
||
|
def PseudoAtomicLoadAnd32 : PseudoAM;
|
||
|
def PseudoAtomicLoadOr32 : PseudoAM;
|
||
|
def PseudoAtomicLoadXor32 : PseudoAM;
|
||
|
|
||
|
multiclass PseudoBinPat<string Op, Pseudo BinInst> {
|
||
|
def : Pat<(!cast<PatFrag>(Op#"_monotonic") GPR:$addr, GPR:$incr),
|
||
|
(BinInst GPR:$addr, GPR:$incr, 2)>;
|
||
|
def : Pat<(!cast<PatFrag>(Op#"_acquire") GPR:$addr, GPR:$incr),
|
||
|
(BinInst GPR:$addr, GPR:$incr, 4)>;
|
||
|
def : Pat<(!cast<PatFrag>(Op#"_release") GPR:$addr, GPR:$incr),
|
||
|
(BinInst GPR:$addr, GPR:$incr, 5)>;
|
||
|
def : Pat<(!cast<PatFrag>(Op#"_acq_rel") GPR:$addr, GPR:$incr),
|
||
|
(BinInst GPR:$addr, GPR:$incr, 6)>;
|
||
|
def : Pat<(!cast<PatFrag>(Op#"_seq_cst") GPR:$addr, GPR:$incr),
|
||
|
(BinInst GPR:$addr, GPR:$incr, 7)>;
|
||
|
}
|
||
|
|
||
|
class PseudoMaskedAMUMinUMax
|
||
|
: Pseudo<(outs GPR:$res, GPR:$scratch1, GPR:$scratch2),
|
||
|
(ins GPR:$addr, GPR:$incr, GPR:$mask, grlenimm:$ordering)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch1,"
|
||
|
"@earlyclobber $scratch2";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 48;
|
||
|
}
|
||
|
|
||
|
def PseudoMaskedAtomicLoadUMax32 : PseudoMaskedAMUMinUMax;
|
||
|
def PseudoMaskedAtomicLoadUMin32 : PseudoMaskedAMUMinUMax;
|
||
|
|
||
|
class PseudoMaskedAMMinMax
|
||
|
: Pseudo<(outs GPR:$res, GPR:$scratch1, GPR:$scratch2),
|
||
|
(ins GPR:$addr, GPR:$incr, GPR:$mask, grlenimm:$sextshamt,
|
||
|
grlenimm:$ordering)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch1,"
|
||
|
"@earlyclobber $scratch2";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 56;
|
||
|
}
|
||
|
|
||
|
def PseudoMaskedAtomicLoadMax32 : PseudoMaskedAMMinMax;
|
||
|
def PseudoMaskedAtomicLoadMin32 : PseudoMaskedAMMinMax;
|
||
|
|
||
|
/// Compare and exchange
|
||
|
|
||
|
class PseudoCmpXchg
|
||
|
: Pseudo<(outs GPR:$res, GPR:$scratch),
|
||
|
(ins GPR:$addr, GPR:$cmpval, GPR:$newval, grlenimm:$fail_order)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 36;
|
||
|
}
|
||
|
|
||
|
def PseudoCmpXchg32 : PseudoCmpXchg;
|
||
|
def PseudoCmpXchg64 : PseudoCmpXchg;
|
||
|
|
||
|
def PseudoMaskedCmpXchg32
|
||
|
: Pseudo<(outs GPR:$res, GPR:$scratch),
|
||
|
(ins GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask,
|
||
|
grlenimm:$fail_order)> {
|
||
|
let Constraints = "@earlyclobber $res,@earlyclobber $scratch";
|
||
|
let mayLoad = 1;
|
||
|
let mayStore = 1;
|
||
|
let hasSideEffects = 0;
|
||
|
let Size = 44;
|
||
|
}
|
||
|
|
||
|
class PseudoMaskedAMMinMaxPat<Intrinsic intrin, Pseudo AMInst>
|
||
|
: Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt,
|
||
|
timm:$ordering),
|
||
|
(AMInst GPR:$addr, GPR:$incr, GPR:$mask, GPR:$shiftamt,
|
||
|
timm:$ordering)>;
|
||
|
|
||
|
class AtomicPat<Intrinsic intrin, Pseudo AMInst>
|
||
|
: Pat<(intrin GPR:$addr, GPR:$incr, GPR:$mask, timm:$ordering),
|
||
|
(AMInst GPR:$addr, GPR:$incr, GPR:$mask, timm:$ordering)>;
|
||
|
|
||
|
// These atomic cmpxchg PatFrags only care about the failure ordering.
|
||
|
// The PatFrags defined by multiclass `ternary_atomic_op_ord` in
|
||
|
// TargetSelectionDAG.td care about the merged memory ordering that is the
|
||
|
// stronger one between success and failure. But for LoongArch LL-SC we only
|
||
|
// need to care about the failure ordering as explained in PR #67391. So we
|
||
|
// define these PatFrags that will be used to define cmpxchg pats below.
|
||
|
multiclass ternary_atomic_op_failure_ord {
|
||
|
def NAME#_failure_monotonic : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
|
||
|
(!cast<SDPatternOperator>(NAME) node:$ptr, node:$cmp, node:$val), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getFailureOrdering();
|
||
|
return Ordering == AtomicOrdering::Monotonic;
|
||
|
}]>;
|
||
|
def NAME#_failure_acquire : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
|
||
|
(!cast<SDPatternOperator>(NAME) node:$ptr, node:$cmp, node:$val), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getFailureOrdering();
|
||
|
return Ordering == AtomicOrdering::Acquire;
|
||
|
}]>;
|
||
|
def NAME#_failure_release : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
|
||
|
(!cast<SDPatternOperator>(NAME) node:$ptr, node:$cmp, node:$val), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getFailureOrdering();
|
||
|
return Ordering == AtomicOrdering::Release;
|
||
|
}]>;
|
||
|
def NAME#_failure_acq_rel : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
|
||
|
(!cast<SDPatternOperator>(NAME) node:$ptr, node:$cmp, node:$val), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getFailureOrdering();
|
||
|
return Ordering == AtomicOrdering::AcquireRelease;
|
||
|
}]>;
|
||
|
def NAME#_failure_seq_cst : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
|
||
|
(!cast<SDPatternOperator>(NAME) node:$ptr, node:$cmp, node:$val), [{
|
||
|
AtomicOrdering Ordering = cast<AtomicSDNode>(N)->getFailureOrdering();
|
||
|
return Ordering == AtomicOrdering::SequentiallyConsistent;
|
||
|
}]>;
|
||
|
}
|
||
|
|
||
|
defm atomic_cmp_swap_32 : ternary_atomic_op_failure_ord;
|
||
|
defm atomic_cmp_swap_64 : ternary_atomic_op_failure_ord;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_xchg_i64,
|
||
|
PseudoMaskedAtomicSwap32>;
|
||
|
def : Pat<(atomic_swap_32 GPR:$addr, GPR:$incr),
|
||
|
(AMSWAP__DB_W GPR:$incr, GPR:$addr)>;
|
||
|
def : Pat<(atomic_swap_64 GPR:$addr, GPR:$incr),
|
||
|
(AMSWAP__DB_D GPR:$incr, GPR:$addr)>;
|
||
|
def : Pat<(atomic_load_add_64 GPR:$rj, GPR:$rk),
|
||
|
(AMADD__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_add_i64,
|
||
|
PseudoMaskedAtomicLoadAdd32>;
|
||
|
def : Pat<(atomic_load_sub_32 GPR:$rj, GPR:$rk),
|
||
|
(AMADD__DB_W (SUB_W R0, GPR:$rk), GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_sub_64 GPR:$rj, GPR:$rk),
|
||
|
(AMADD__DB_D (SUB_D R0, GPR:$rk), GPR:$rj)>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_sub_i64,
|
||
|
PseudoMaskedAtomicLoadSub32>;
|
||
|
defm : PseudoBinPat<"atomic_load_nand_64", PseudoAtomicLoadNand64>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_nand_i64,
|
||
|
PseudoMaskedAtomicLoadNand32>;
|
||
|
def : Pat<(atomic_load_add_32 GPR:$rj, GPR:$rk),
|
||
|
(AMADD__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_and_32 GPR:$rj, GPR:$rk),
|
||
|
(AMAND__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_and_64 GPR:$rj, GPR:$rk),
|
||
|
(AMAND__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_or_32 GPR:$rj, GPR:$rk),
|
||
|
(AMOR__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_or_64 GPR:$rj, GPR:$rk),
|
||
|
(AMOR__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_xor_32 GPR:$rj, GPR:$rk),
|
||
|
(AMXOR__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_xor_64 GPR:$rj, GPR:$rk),
|
||
|
(AMXOR__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
|
||
|
def : Pat<(atomic_load_umin_32 GPR:$rj, GPR:$rk),
|
||
|
(AMMIN__DB_WU GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_umin_64 GPR:$rj, GPR:$rk),
|
||
|
(AMMIN__DB_DU GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_umax_32 GPR:$rj, GPR:$rk),
|
||
|
(AMMAX__DB_WU GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_umax_64 GPR:$rj, GPR:$rk),
|
||
|
(AMMAX__DB_DU GPR:$rk, GPR:$rj)>;
|
||
|
|
||
|
def : Pat<(atomic_load_min_32 GPR:$rj, GPR:$rk),
|
||
|
(AMMIN__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_min_64 GPR:$rj, GPR:$rk),
|
||
|
(AMMIN__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_max_32 GPR:$rj, GPR:$rk),
|
||
|
(AMMAX__DB_W GPR:$rk, GPR:$rj)>;
|
||
|
def : Pat<(atomic_load_max_64 GPR:$rj, GPR:$rk),
|
||
|
(AMMAX__DB_D GPR:$rk, GPR:$rj)>;
|
||
|
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_umax_i64,
|
||
|
PseudoMaskedAtomicLoadUMax32>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_umin_i64,
|
||
|
PseudoMaskedAtomicLoadUMin32>;
|
||
|
|
||
|
// Ordering constants must be kept in sync with the AtomicOrdering enum in
|
||
|
// AtomicOrdering.h.
|
||
|
multiclass PseudoCmpXchgPat<string Op, Pseudo CmpXchgInst,
|
||
|
ValueType vt = GRLenVT> {
|
||
|
def : Pat<(vt (!cast<PatFrag>(Op#"_failure_monotonic") GPR:$addr, GPR:$cmp, GPR:$new)),
|
||
|
(CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 2)>;
|
||
|
def : Pat<(vt (!cast<PatFrag>(Op#"_failure_acquire") GPR:$addr, GPR:$cmp, GPR:$new)),
|
||
|
(CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 4)>;
|
||
|
def : Pat<(vt (!cast<PatFrag>(Op#"_failure_release") GPR:$addr, GPR:$cmp, GPR:$new)),
|
||
|
(CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 5)>;
|
||
|
def : Pat<(vt (!cast<PatFrag>(Op#"_failure_acq_rel") GPR:$addr, GPR:$cmp, GPR:$new)),
|
||
|
(CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 6)>;
|
||
|
def : Pat<(vt (!cast<PatFrag>(Op#"_failure_seq_cst") GPR:$addr, GPR:$cmp, GPR:$new)),
|
||
|
(CmpXchgInst GPR:$addr, GPR:$cmp, GPR:$new, 7)>;
|
||
|
}
|
||
|
|
||
|
defm : PseudoCmpXchgPat<"atomic_cmp_swap_32", PseudoCmpXchg32>;
|
||
|
defm : PseudoCmpXchgPat<"atomic_cmp_swap_64", PseudoCmpXchg64, i64>;
|
||
|
def : Pat<(int_loongarch_masked_cmpxchg_i64
|
||
|
GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$fail_order),
|
||
|
(PseudoMaskedCmpXchg32
|
||
|
GPR:$addr, GPR:$cmpval, GPR:$newval, GPR:$mask, timm:$fail_order)>;
|
||
|
|
||
|
def : PseudoMaskedAMMinMaxPat<int_loongarch_masked_atomicrmw_max_i64,
|
||
|
PseudoMaskedAtomicLoadMax32>;
|
||
|
def : PseudoMaskedAMMinMaxPat<int_loongarch_masked_atomicrmw_min_i64,
|
||
|
PseudoMaskedAtomicLoadMin32>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
defm : PseudoBinPat<"atomic_load_nand_32", PseudoAtomicLoadNand32>;
|
||
|
|
||
|
let Predicates = [IsLA32] in {
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_xchg_i32,
|
||
|
PseudoMaskedAtomicSwap32>;
|
||
|
defm : PseudoBinPat<"atomic_swap_32", PseudoAtomicSwap32>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_add_i32,
|
||
|
PseudoMaskedAtomicLoadAdd32>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_sub_i32,
|
||
|
PseudoMaskedAtomicLoadSub32>;
|
||
|
def : AtomicPat<int_loongarch_masked_atomicrmw_nand_i32,
|
||
|
PseudoMaskedAtomicLoadNand32>;
|
||
|
defm : PseudoBinPat<"atomic_load_add_32", PseudoAtomicLoadAdd32>;
|
||
|
defm : PseudoBinPat<"atomic_load_sub_32", PseudoAtomicLoadSub32>;
|
||
|
defm : PseudoBinPat<"atomic_load_and_32", PseudoAtomicLoadAnd32>;
|
||
|
defm : PseudoBinPat<"atomic_load_or_32", PseudoAtomicLoadOr32>;
|
||
|
defm : PseudoBinPat<"atomic_load_xor_32", PseudoAtomicLoadXor32>;
|
||
|
} // Predicates = [IsLA32]
|
||
|
|
||
|
/// Intrinsics
|
||
|
|
||
|
def : Pat<(int_loongarch_cacop_d timm:$op, i64:$rj, timm:$imm12),
|
||
|
(CACOP timm:$op, GPR:$rj, timm:$imm12)>;
|
||
|
def : Pat<(int_loongarch_cacop_w i32:$op, i32:$rj, i32:$imm12),
|
||
|
(CACOP timm:$op, GPR:$rj, timm:$imm12)>;
|
||
|
def : Pat<(loongarch_dbar uimm15:$imm15), (DBAR uimm15:$imm15)>;
|
||
|
def : Pat<(loongarch_ibar uimm15:$imm15), (IBAR uimm15:$imm15)>;
|
||
|
def : Pat<(loongarch_break uimm15:$imm15), (BREAK uimm15:$imm15)>;
|
||
|
def : Pat<(loongarch_syscall uimm15:$imm15), (SYSCALL uimm15:$imm15)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
// CRC Check Instructions
|
||
|
def : PatGprGpr<loongarch_crc_w_b_w, CRC_W_B_W>;
|
||
|
def : PatGprGpr<loongarch_crc_w_h_w, CRC_W_H_W>;
|
||
|
def : PatGprGpr<loongarch_crc_w_w_w, CRC_W_W_W>;
|
||
|
def : PatGprGpr<loongarch_crc_w_d_w, CRC_W_D_W>;
|
||
|
def : PatGprGpr<loongarch_crcc_w_b_w, CRCC_W_B_W>;
|
||
|
def : PatGprGpr<loongarch_crcc_w_h_w, CRCC_W_H_W>;
|
||
|
def : PatGprGpr<loongarch_crcc_w_w_w, CRCC_W_W_W>;
|
||
|
def : PatGprGpr<loongarch_crcc_w_d_w, CRCC_W_D_W>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
/// Other pseudo-instructions
|
||
|
|
||
|
// Pessimistically assume the stack pointer will be clobbered
|
||
|
let Defs = [R3], Uses = [R3] in {
|
||
|
def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
|
||
|
[(callseq_start timm:$amt1, timm:$amt2)]>;
|
||
|
def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
|
||
|
[(callseq_end timm:$amt1, timm:$amt2)]>;
|
||
|
} // Defs = [R3], Uses = [R3]
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Assembler Pseudo Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
def : InstAlias<"nop", (ANDI R0, R0, 0)>;
|
||
|
def : InstAlias<"move $dst, $src", (OR GPR:$dst, GPR:$src, R0)>;
|
||
|
// `ret` is supported since binutils commit 20f2e2686c79a5ac (version 2.40 and
|
||
|
// later).
|
||
|
def : InstAlias<"ret", (JIRL R0, R1, 0)>;
|
||
|
def : InstAlias<"jr $rj", (JIRL R0, GPR:$rj, 0)>;
|
||
|
|
||
|
// Branches implemented with alias.
|
||
|
// Always output the canonical mnemonic for the pseudo branch instructions.
|
||
|
// The GNU tools emit the canonical mnemonic for the branch pseudo instructions
|
||
|
// as well (e.g. "bgt" will be recognised by the assembler but never printed by
|
||
|
// objdump). Match this behaviour by setting a zero weight.
|
||
|
def : InstAlias<"bgt $rj, $rd, $imm16",
|
||
|
(BLT GPR:$rd, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"bgtu $rj, $rd, $imm16",
|
||
|
(BLTU GPR:$rd, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"ble $rj, $rd, $imm16",
|
||
|
(BGE GPR:$rd, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"bleu $rj, $rd, $imm16",
|
||
|
(BGEU GPR:$rd, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"bltz $rd, $imm16",
|
||
|
(BLT GPR:$rd, R0, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"bgtz $rj, $imm16",
|
||
|
(BLT R0, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"blez $rj, $imm16",
|
||
|
(BGE R0, GPR:$rj, simm16_lsl2_br:$imm16), 0>;
|
||
|
def : InstAlias<"bgez $rd, $imm16",
|
||
|
(BGE GPR:$rd, R0, simm16_lsl2_br:$imm16), 0>;
|
||
|
|
||
|
// Load immediate.
|
||
|
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0,
|
||
|
isAsmParserOnly = 1 in {
|
||
|
def PseudoLI_W : Pseudo<(outs GPR:$rd), (ins imm32:$imm), [],
|
||
|
"li.w", "$rd, $imm">;
|
||
|
def PseudoLI_D : Pseudo<(outs GPR:$rd), (ins grlenimm:$imm), [],
|
||
|
"li.d", "$rd, $imm">, Requires<[IsLA64]>;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Basic Floating-Point Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
include "LoongArchFloat32InstrInfo.td"
|
||
|
include "LoongArchFloat64InstrInfo.td"
|
||
|
|
||
|
let Predicates = [HasBasicF], usesCustomInserter = 1 in {
|
||
|
def WRFCSR : Pseudo<(outs), (ins uimm2:$fcsr, GPR:$src),
|
||
|
[(loongarch_movgr2fcsr uimm2:$fcsr, GRLenVT:$src)]>;
|
||
|
def RDFCSR : Pseudo<(outs GPR:$rd), (ins uimm2:$fcsr),
|
||
|
[(set GPR:$rd, (loongarch_movfcsr2gr uimm2:$fcsr))]>;
|
||
|
}
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Privilege Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
// CSR Access Instructions
|
||
|
let hasSideEffects = 1 in
|
||
|
def CSRRD : FmtCSR<0x04000000, (outs GPR:$rd), (ins uimm14:$csr_num),
|
||
|
"$rd, $csr_num">;
|
||
|
let hasSideEffects = 1, Constraints = "$rd = $dst" in {
|
||
|
def CSRWR : FmtCSR<0x04000020, (outs GPR:$dst),
|
||
|
(ins GPR:$rd, uimm14:$csr_num), "$rd, $csr_num">;
|
||
|
def CSRXCHG : FmtCSRXCHG<0x04000000, (outs GPR:$dst),
|
||
|
(ins GPR:$rd, GPR:$rj, uimm14:$csr_num),
|
||
|
"$rd, $rj, $csr_num">;
|
||
|
} // hasSideEffects = 1, Constraints = "$rd = $dst"
|
||
|
|
||
|
// IOCSR Access Instructions
|
||
|
def IOCSRRD_B : IOCSRRD<0x06480000>;
|
||
|
def IOCSRRD_H : IOCSRRD<0x06480400>;
|
||
|
def IOCSRRD_W : IOCSRRD<0x06480800>;
|
||
|
def IOCSRWR_B : IOCSRWR<0x06481000>;
|
||
|
def IOCSRWR_H : IOCSRWR<0x06481400>;
|
||
|
def IOCSRWR_W : IOCSRWR<0x06481800>;
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def IOCSRRD_D : IOCSRRD<0x06480c00>;
|
||
|
def IOCSRWR_D : IOCSRWR<0x06481c00>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
// TLB Maintenance Instructions
|
||
|
let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in {
|
||
|
def TLBSRCH : FmtI32<0x06482800>;
|
||
|
def TLBRD : FmtI32<0x06482c00>;
|
||
|
def TLBWR : FmtI32<0x06483000>;
|
||
|
def TLBFILL : FmtI32<0x06483400>;
|
||
|
def TLBCLR : FmtI32<0x06482000>;
|
||
|
def TLBFLUSH : FmtI32<0x06482400>;
|
||
|
def INVTLB : FmtINVTLB<(outs), (ins GPR:$rk, GPR:$rj, uimm5:$op),
|
||
|
"$op, $rj, $rk">;
|
||
|
} // hasSideEffects = 1, mayLoad = 0, mayStore = 0
|
||
|
|
||
|
// Software Page Walking Instructions
|
||
|
def LDDIR : Fmt2RI8<0x06400000, (outs GPR:$rd),
|
||
|
(ins GPR:$rj, uimm8:$imm8), "$rd, $rj, $imm8">;
|
||
|
def LDPTE : FmtLDPTE<(outs), (ins GPR:$rj, uimm8:$seq), "$rj, $seq">;
|
||
|
|
||
|
|
||
|
// Other Miscellaneous Instructions
|
||
|
let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
|
||
|
def ERTN : FmtI32<0x06483800>;
|
||
|
def DBCL : MISC_I15<0x002a8000>;
|
||
|
def IDLE : MISC_I15<0x06488000>;
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// Privilege Intrinsics
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
def : Pat<(loongarch_csrrd uimm14:$imm14), (CSRRD uimm14:$imm14)>;
|
||
|
def : Pat<(loongarch_csrwr GPR:$rd, uimm14:$imm14),
|
||
|
(CSRWR GPR:$rd, uimm14:$imm14)>;
|
||
|
def : Pat<(loongarch_csrxchg GPR:$rd, GPR:$rj, uimm14:$imm14),
|
||
|
(CSRXCHG GPR:$rd, GPR:$rj, uimm14:$imm14)>;
|
||
|
|
||
|
def : Pat<(loongarch_iocsrrd_b GPR:$rj), (IOCSRRD_B GPR:$rj)>;
|
||
|
def : Pat<(loongarch_iocsrrd_h GPR:$rj), (IOCSRRD_H GPR:$rj)>;
|
||
|
def : Pat<(loongarch_iocsrrd_w GPR:$rj), (IOCSRRD_W GPR:$rj)>;
|
||
|
|
||
|
def : Pat<(loongarch_iocsrwr_b GPR:$rd, GPR:$rj), (IOCSRWR_B GPR:$rd, GPR:$rj)>;
|
||
|
def : Pat<(loongarch_iocsrwr_h GPR:$rd, GPR:$rj), (IOCSRWR_H GPR:$rd, GPR:$rj)>;
|
||
|
def : Pat<(loongarch_iocsrwr_w GPR:$rd, GPR:$rj), (IOCSRWR_W GPR:$rd, GPR:$rj)>;
|
||
|
|
||
|
def : Pat<(loongarch_cpucfg GPR:$rj), (CPUCFG GPR:$rj)>;
|
||
|
|
||
|
let Predicates = [IsLA64] in {
|
||
|
def : Pat<(loongarch_iocsrrd_d GPR:$rj), (IOCSRRD_D GPR:$rj)>;
|
||
|
def : Pat<(loongarch_iocsrwr_d GPR:$rd, GPR:$rj), (IOCSRWR_D GPR:$rd, GPR:$rj)>;
|
||
|
def : Pat<(int_loongarch_asrtle_d GPR:$rj, GPR:$rk),
|
||
|
(ASRTLE_D GPR:$rj, GPR:$rk)>;
|
||
|
def : Pat<(int_loongarch_asrtgt_d GPR:$rj, GPR:$rk),
|
||
|
(ASRTGT_D GPR:$rj, GPR:$rk)>;
|
||
|
def : Pat<(int_loongarch_lddir_d GPR:$rj, timm:$imm8),
|
||
|
(LDDIR GPR:$rj, timm:$imm8)>;
|
||
|
def : Pat<(int_loongarch_ldpte_d GPR:$rj, timm:$imm8),
|
||
|
(LDPTE GPR:$rj, timm:$imm8)>;
|
||
|
} // Predicates = [IsLA64]
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// LSX Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
include "LoongArchLSXInstrInfo.td"
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// LASX Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
include "LoongArchLASXInstrInfo.td"
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// LVZ Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
include "LoongArchLVZInstrInfo.td"
|
||
|
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
// LBT Instructions
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
include "LoongArchLBTInstrInfo.td"
|