diff --git a/clang/lib/Basic/Targets/LoongArch.h b/clang/lib/Basic/Targets/LoongArch.h --- a/clang/lib/Basic/Targets/LoongArch.h +++ b/clang/lib/Basic/Targets/LoongArch.h @@ -55,6 +55,7 @@ bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &Info) const override; + std::string convertConstraint(const char *&Constraint) const override; bool hasBitIntType() const override { return true; } }; diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp --- a/clang/lib/Basic/Targets/LoongArch.cpp +++ b/clang/lib/Basic/Targets/LoongArch.cpp @@ -67,14 +67,19 @@ const char *&Name, TargetInfo::ConstraintInfo &Info) const { // See the GCC definitions here: // https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html + // Note that the 'm' constraint is handled in TargetInfo. switch (*Name) { - // TODO: handle 'k', 'm', "ZB", "ZC". default: return false; case 'f': // A floating-point register (if available). Info.setAllowsRegister(); return true; + case 'k': + // A memory operand whose address is formed by a base register and + // (optionally scaled) index register. + Info.setAllowsMemory(); + return true; case 'l': // A signed 16-bit constant. Info.setRequiresImmediate(-32768, 32767); @@ -87,7 +92,36 @@ // An unsigned 12-bit constant (for logic instructions). Info.setRequiresImmediate(0, 4095); return true; + case 'Z': + // ZB: An address that is held in a general-purpose register. The offset is + // zero. + // ZC: A memory operand whose address is formed by a base register + // and offset that is suitable for use in instructions with the same + // addressing mode as ll.w and sc.w. + if (Name[1] == 'C' || Name[1] == 'B') { + Info.setAllowsMemory(); + ++Name; // Skip over 'Z'. + return true; + } + return false; + } +} + +std::string +LoongArchTargetInfo::convertConstraint(const char *&Constraint) const { + std::string R; + switch (*Constraint) { + case 'Z': + // "ZC"/"ZB" are two-character constraints; add "^" hint for later + // parsing. + R = "^" + std::string(Constraint, 2); + ++Constraint; + break; + default: + R = TargetInfo::convertConstraint(Constraint); + break; } + return R; } void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts, diff --git a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c --- a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c +++ b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c @@ -15,6 +15,12 @@ asm volatile ("" :: "f"(d)); } +void test_k(int *p, int idx) { +// CHECK-LABEL: define{{.*}} void @test_k(ptr noundef %p, i32 noundef{{.*}} %idx) +// CHECK: call void asm sideeffect "", "*k"(ptr elementtype(i32) %{{.*}}) + asm volatile("" :: "k"(*(p+idx))); +} + void test_l(void) { // CHECK-LABEL: define{{.*}} void @test_l() // CHECK: call void asm sideeffect "", "l"(i32 32767) @@ -23,6 +29,12 @@ asm volatile ("" :: "l"(-32768)); } +void test_m(int *p) { +// CHECK-LABEL: define{{.*}} void @test_m(ptr noundef %p) +// CHECK: call void asm sideeffect "", "*m"(ptr nonnull elementtype(i32) %{{.*}}) + asm volatile("" :: "m"(*(p+4))); +} + void test_I(void) { // CHECK-LABEL: define{{.*}} void @test_I() // CHECK: call void asm sideeffect "", "I"(i32 2047) @@ -38,3 +50,15 @@ // CHECK: call void asm sideeffect "", "K"(i32 0) asm volatile ("" :: "K"(0)); } + +void test_ZB(int *p) { +// CHECK-LABEL: define{{.*}} void @test_ZB(ptr noundef %p) +// CHECK: call void asm sideeffect "", "*^ZB"(ptr elementtype(i32) %p) + asm volatile ("" :: "ZB"(*p)); +} + +void test_ZC(int *p) { +// CHECK-LABEL: define{{.*}} void @test_ZC(ptr noundef %p) +// CHECK: call void asm sideeffect "", "*^ZC"(ptr elementtype(i32) %p) + asm volatile ("" :: "ZC"(*p)); +} diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h --- a/llvm/include/llvm/IR/InlineAsm.h +++ b/llvm/include/llvm/IR/InlineAsm.h @@ -252,6 +252,7 @@ Constraint_Unknown = 0, Constraint_es, Constraint_i, + Constraint_k, Constraint_m, Constraint_o, Constraint_v, @@ -269,6 +270,7 @@ Constraint_Uy, Constraint_X, Constraint_Z, + Constraint_ZB, Constraint_ZC, Constraint_Zy, diff --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h --- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h +++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h @@ -38,6 +38,8 @@ bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &OS) override; // tblgen'erated function. bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, diff --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp --- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp @@ -68,6 +68,35 @@ return true; } +bool LoongArchAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNo, + const char *ExtraCode, + raw_ostream &OS) { + // TODO: handle extra code. + if (ExtraCode) + return true; + + const MachineOperand &BaseMO = MI->getOperand(OpNo); + // Base address must be a register. + if (!BaseMO.isReg()) + return true; + // Print the base address register. + OS << "$" << LoongArchInstPrinter::getRegisterName(BaseMO.getReg()); + // Print the offset register or immediate if has. + if (OpNo + 1 < MI->getNumOperands()) { + const MachineOperand &OffsetMO = MI->getOperand(OpNo + 1); + if (OffsetMO.isReg()) + OS << ", $" << LoongArchInstPrinter::getRegisterName(OffsetMO.getReg()); + else if (OffsetMO.isImm()) + OS << ", " << OffsetMO.getImm(); + else + return true; + } + return false; + + return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS); +} + bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) { AsmPrinter::runOnMachineFunction(MF); return true; diff --git a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.h b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.h --- a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.h @@ -38,6 +38,9 @@ void Select(SDNode *Node) override; + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) override; + bool SelectBaseAddr(SDValue Addr, SDValue &Base); bool selectShiftMask(SDValue N, unsigned ShiftWidth, SDValue &ShAmt); diff --git a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelDAGToDAG.cpp @@ -77,6 +77,59 @@ SelectCode(Node); } +bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + switch (ConstraintID) { + default: + llvm_unreachable("unexpected asm memory constraint"); + // Reg+Reg addressing. + case InlineAsm::Constraint_k: + OutOps.push_back(Op.getOperand(0)); + OutOps.push_back(Op.getOperand(1)); + return false; + // Reg+simm12 addressing. + case InlineAsm::Constraint_m: { + SDValue Base = Op; + SDValue Offset = + CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT()); + if (CurDAG->isBaseWithConstantOffset(Op)) { + ConstantSDNode *CN = dyn_cast(Op.getOperand(1)); + if (isIntN(12, CN->getSExtValue())) { + Base = Op.getOperand(0); + Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op), + Op.getValueType()); + } + } + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + case InlineAsm::Constraint_ZB: + OutOps.push_back(Op); + // No offset. + return false; + // Reg+(simm14<<2) addressing. + case InlineAsm::Constraint_ZC: { + SDValue Base = Op; + SDValue Offset = + CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT()); + if (CurDAG->isBaseWithConstantOffset(Op)) { + ConstantSDNode *CN = dyn_cast(Op.getOperand(1)); + if (isIntN(16, CN->getSExtValue()) && + isAligned(Align(4ULL), CN->getZExtValue())) { + Base = Op.getOperand(0); + Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op), + Op.getValueType()); + } + } + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + } + return true; +} + bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) { // If this is FrameIndex, select it directly. Otherwise just let it get // selected to a register independently. diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h @@ -156,6 +156,8 @@ ConstraintType getConstraintType(StringRef Constraint) const override; + unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override; + std::pair getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const override; diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -1987,14 +1987,27 @@ case 'I': case 'K': return C_Immediate; + case 'k': + return C_Memory; } } - // TODO: handle 'k", "ZB" and "ZC". + if (Constraint == "ZC" || Constraint == "ZB") + return C_Memory; + // 'm' is handled here. return TargetLowering::getConstraintType(Constraint); } +unsigned LoongArchTargetLowering::getInlineAsmMemConstraint( + StringRef ConstraintCode) const { + return StringSwitch(ConstraintCode) + .Case("k", InlineAsm::Constraint_k) + .Case("ZB", InlineAsm::Constraint_ZB) + .Case("ZC", InlineAsm::Constraint_ZC) + .Default(TargetLowering::getInlineAsmMemConstraint(ConstraintCode)); +} + std::pair LoongArchTargetLowering::getRegForInlineAsmConstraint( const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZB.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s | FileCheck %s + +;; Note amswap.w is not available on loongarch32. + +define void @ZB(ptr %p) nounwind { +; CHECK-LABEL: ZB: +; CHECK: # %bb.0: +; CHECK-NEXT: #APP +; CHECK-NEXT: amswap.w $t0, $t1, $a0 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: ret + call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %p) + ret void +} + +define void @ZB_constant_offset(ptr %p) nounwind { +; CHECK-LABEL: ZB_constant_offset: +; CHECK: # %bb.0: +; CHECK-NEXT: addi.d $a0, $a0, 1 +; CHECK-NEXT: #APP +; CHECK-NEXT: amswap.w $t0, $t1, $a0 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 1 + call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %1) + ret void +} + +define void @ZB_variable_offset(ptr %p, i32 signext %idx) nounwind { +; CHECK-LABEL: ZB_variable_offset: +; CHECK: # %bb.0: +; CHECK-NEXT: add.d $a0, $a0, $a1 +; CHECK-NEXT: #APP +; CHECK-NEXT: amswap.w $t0, $t1, $a0 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 %idx + call void asm "amswap.w $$r12, $$r13, $0", "*^ZB"(ptr elementtype(i32) %1) + ret void +} diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZC.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZC.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-ZC.ll @@ -0,0 +1,170 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc --mtriple=loongarch32 --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA32 +; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA64 + +define i32 @ZC_offset_neg_32769(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_neg_32769: +; LA32: # %bb.0: +; LA32-NEXT: lu12i.w $a1, -9 +; LA32-NEXT: ori $a1, $a1, 4095 +; LA32-NEXT: add.w $a0, $a0, $a1 +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_neg_32769: +; LA64: # %bb.0: +; LA64-NEXT: lu12i.w $a1, -9 +; LA64-NEXT: ori $a1, $a1, 4095 +; LA64-NEXT: add.d $a0, $a0, $a1 +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -32769 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_neg_32768(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_neg_32768: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, -32768 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_neg_32768: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, -32768 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -32768 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_neg_4(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_neg_4: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, -4 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_neg_4: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, -4 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -4 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_neg_1(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_neg_1: +; LA32: # %bb.0: +; LA32-NEXT: addi.w $a0, $a0, -1 +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_neg_1: +; LA64: # %bb.0: +; LA64-NEXT: addi.d $a0, $a0, -1 +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -1 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_0(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_0: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_0: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %p) + ret i32 %1 +} + +define i32 @ZC_offset_1(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_1: +; LA32: # %bb.0: +; LA32-NEXT: addi.w $a0, $a0, 1 +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_1: +; LA64: # %bb.0: +; LA64-NEXT: addi.d $a0, $a0, 1 +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 1 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_32764(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_32764: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 32764 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_32764: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 32764 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 32764 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @ZC_offset_32767(ptr %p) nounwind { +; LA32-LABEL: ZC_offset_32767: +; LA32: # %bb.0: +; LA32-NEXT: lu12i.w $a1, 7 +; LA32-NEXT: ori $a1, $a1, 4095 +; LA32-NEXT: add.w $a0, $a0, $a1 +; LA32-NEXT: #APP +; LA32-NEXT: ll.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: ZC_offset_32767: +; LA64: # %bb.0: +; LA64-NEXT: lu12i.w $a1, 7 +; LA64-NEXT: ori $a1, $a1, 4095 +; LA64-NEXT: add.d $a0, $a0, $a1 +; LA64-NEXT: #APP +; LA64-NEXT: ll.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 32767 + %2 = call i32 asm "ll.w $0, $1", "=r,*^ZC"(ptr elementtype(i32) %1) + ret i32 %2 +} diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-k.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-k.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-k.ll @@ -0,0 +1,27 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s | FileCheck %s + +define i64 @k_variable_offset(ptr %p, i64 %idx) nounwind { +; CHECK-LABEL: k_variable_offset: +; CHECK: # %bb.0: +; CHECK-NEXT: #APP +; CHECK-NEXT: ldx.d $a0, $a0, $a1 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i64 %idx + %2 = call i64 asm "ldx.d $0, $1", "=r,*k"(ptr elementtype(i64) %1) + ret i64 %2 +} + +define i64 @k_constant_offset(ptr %p) nounwind { +; CHECK-LABEL: k_constant_offset: +; CHECK: # %bb.0: +; CHECK-NEXT: ori $a1, $zero, 5 +; CHECK-NEXT: #APP +; CHECK-NEXT: ldx.d $a0, $a0, $a1 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i64 5 + %2 = call i64 asm "ldx.d $0, $1", "=r,*k"(ptr elementtype(i64) %1) + ret i64 %2 +} diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll @@ -0,0 +1,145 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc --mtriple=loongarch32 --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA32 +; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s | FileCheck %s --check-prefix=LA64 + +define i32 @m_offset_neg_2049(ptr %p) nounwind { +; LA32-LABEL: m_offset_neg_2049: +; LA32: # %bb.0: +; LA32-NEXT: lu12i.w $a1, -1 +; LA32-NEXT: ori $a1, $a1, 2047 +; LA32-NEXT: add.w $a0, $a0, $a1 +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_neg_2049: +; LA64: # %bb.0: +; LA64-NEXT: lu12i.w $a1, -1 +; LA64-NEXT: ori $a1, $a1, 2047 +; LA64-NEXT: add.d $a0, $a0, $a1 +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -2049 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @m_offset_neg_2048(ptr %p) nounwind { +; LA32-LABEL: m_offset_neg_2048: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, -2048 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_neg_2048: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, -2048 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -2048 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @m_offset_neg_1(ptr %p) nounwind { +; LA32-LABEL: m_offset_neg_1: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, -1 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_neg_1: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, -1 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 -1 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @m_offset_0(ptr %p) nounwind { +; LA32-LABEL: m_offset_0: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_0: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %p) + ret i32 %1 +} + +define i32 @m_offset_1(ptr %p) nounwind { +; LA32-LABEL: m_offset_1: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, 1 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_1: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, 1 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 1 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @m_offset_2047(ptr %p) nounwind { +; LA32-LABEL: m_offset_2047: +; LA32: # %bb.0: +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, 2047 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_2047: +; LA64: # %bb.0: +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, 2047 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 2047 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +} + +define i32 @m_offset_2048(ptr %p) nounwind { +; LA32-LABEL: m_offset_2048: +; LA32: # %bb.0: +; LA32-NEXT: ori $a1, $zero, 2048 +; LA32-NEXT: add.w $a0, $a0, $a1 +; LA32-NEXT: #APP +; LA32-NEXT: ld.w $a0, $a0, 0 +; LA32-NEXT: #NO_APP +; LA32-NEXT: ret +; +; LA64-LABEL: m_offset_2048: +; LA64: # %bb.0: +; LA64-NEXT: ori $a1, $zero, 2048 +; LA64-NEXT: add.d $a0, $a0, $a1 +; LA64-NEXT: #APP +; LA64-NEXT: ld.w $a0, $a0, 0 +; LA64-NEXT: #NO_APP +; LA64-NEXT: ret + %1 = getelementptr inbounds i8, ptr %p, i32 2048 + %2 = call i32 asm "ld.w $0, $1", "=r,*m"(ptr elementtype(i32) %1) + ret i32 %2 +}