[clang] 394f309 - [Clang][LoongArch] Add inline asm support for constraints f/l/I/K
Weining Lu via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 25 18:17:38 PDT 2022
Author: Weining Lu
Date: 2022-09-26T08:49:58+08:00
New Revision: 394f30919a029331ebdfe02c180bd1586c0d9ace
URL: https://github.com/llvm/llvm-project/commit/394f30919a029331ebdfe02c180bd1586c0d9ace
DIFF: https://github.com/llvm/llvm-project/commit/394f30919a029331ebdfe02c180bd1586c0d9ace.diff
LOG: [Clang][LoongArch] Add inline asm support for constraints f/l/I/K
This patch adds support for constraints `f`, `l`, `I`, `K` according
to [1]. The remain constraints (`k`, `m`, `ZB`, `ZC`) will be added
later as they are a little more complex than the others.
f: A floating-point register (if available).
l: A signed 16-bit constant.
I: A signed 12-bit constant (for arithmetic instructions).
K: An unsigned 12-bit constant (for logic instructions).
For now, no need to support register alias (e.g. `$a0`) in llvm as
clang will correctly decode the usage of register name aliases into
their official names. And AFAIK, the not yet upstreamed `rustc` for
LoongArch will always use official register names (e.g. `$r4`).
[1] https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html
Differential Revision: https://reviews.llvm.org/D134157
Added:
clang/test/CodeGen/LoongArch/inline-asm-constraints-error.c
clang/test/CodeGen/LoongArch/inline-asm-constraints.c
clang/test/CodeGen/LoongArch/inline-asm-gcc-regs-error.c
clang/test/CodeGen/LoongArch/inline-asm-gcc-regs.c
llvm/test/CodeGen/LoongArch/inline-asm-clobbers.ll
llvm/test/CodeGen/LoongArch/inline-asm-constraint-error.ll
llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
llvm/test/CodeGen/LoongArch/inline-asm-reg-names-error.ll
llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f-error.ll
llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f.ll
llvm/test/CodeGen/LoongArch/inline-asm-reg-names.ll
Modified:
clang/lib/Basic/Targets/LoongArch.cpp
llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
llvm/lib/Target/LoongArch/LoongArchISelLowering.h
Removed:
################################################################################
diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp
index d87d8e841c4b..cc93206dad68 100644
--- a/clang/lib/Basic/Targets/LoongArch.cpp
+++ b/clang/lib/Basic/Targets/LoongArch.cpp
@@ -21,20 +21,73 @@ using namespace clang;
using namespace clang::targets;
ArrayRef<const char *> LoongArchTargetInfo::getGCCRegNames() const {
- // TODO: To be implemented in future.
- return {};
+ static const char *const GCCRegNames[] = {
+ // General purpose registers.
+ "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9",
+ "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", "$r16", "$r17", "$r18",
+ "$r19", "$r20", "$r21", "$r22", "$r23", "$r24", "$r25", "$r26", "$r27",
+ "$r28", "$r29", "$r30", "$r31",
+ // Floating point registers.
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9",
+ "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18",
+ "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
+ "$f28", "$f29", "$f30", "$f31"};
+ return llvm::makeArrayRef(GCCRegNames);
}
ArrayRef<TargetInfo::GCCRegAlias>
LoongArchTargetInfo::getGCCRegAliases() const {
- // TODO: To be implemented in future.
- return {};
+ static const TargetInfo::GCCRegAlias GCCRegAliases[] = {
+ {{"$zero"}, "$r0"}, {{"$ra"}, "$r1"}, {{"$tp"}, "$r2"},
+ {{"$sp"}, "$r3"}, {{"$a0"}, "$r4"}, {{"$a1"}, "$r5"},
+ {{"$a2"}, "$r6"}, {{"$a3"}, "$r7"}, {{"$a4"}, "$r8"},
+ {{"$a5"}, "$r9"}, {{"$a6"}, "$r10"}, {{"$a7"}, "$r11"},
+ {{"$t0"}, "$r12"}, {{"$t1"}, "$r13"}, {{"$t2"}, "$r14"},
+ {{"$t3"}, "$r15"}, {{"$t4"}, "$r16"}, {{"$t5"}, "$r17"},
+ {{"$t6"}, "$r18"}, {{"$t7"}, "$r19"}, {{"$t8"}, "$r20"},
+ {{"$fp", "$s9"}, "$r22"}, {{"$s0"}, "$r23"}, {{"$s1"}, "$r24"},
+ {{"$s2"}, "$r25"}, {{"$s3"}, "$r26"}, {{"$s4"}, "$r27"},
+ {{"$s5"}, "$r28"}, {{"$s6"}, "$r29"}, {{"$s7"}, "$r30"},
+ {{"$s8"}, "$r31"}, {{"$fa0"}, "$f0"}, {{"$fa1"}, "$f1"},
+ {{"$fa2"}, "$f2"}, {{"$fa3"}, "$f3"}, {{"$fa4"}, "$f4"},
+ {{"$fa5"}, "$f5"}, {{"$fa6"}, "$f6"}, {{"$fa7"}, "$f7"},
+ {{"$ft0"}, "$f8"}, {{"$ft1"}, "$f9"}, {{"$ft2"}, "$f10"},
+ {{"$ft3"}, "$f11"}, {{"$ft4"}, "$f12"}, {{"$ft5"}, "$f13"},
+ {{"$ft6"}, "$f14"}, {{"$ft7"}, "$f15"}, {{"$ft8"}, "$f16"},
+ {{"$ft9"}, "$f17"}, {{"$ft10"}, "$f18"}, {{"$ft11"}, "$f19"},
+ {{"$ft12"}, "$f20"}, {{"$ft13"}, "$f21"}, {{"$ft14"}, "$f22"},
+ {{"$ft15"}, "$f23"}, {{"$fs0"}, "$f24"}, {{"$fs1"}, "$f25"},
+ {{"$fs2"}, "$f26"}, {{"$fs3"}, "$f27"}, {{"$fs4"}, "$f28"},
+ {{"$fs5"}, "$f29"}, {{"$fs6"}, "$f30"}, {{"$fs7"}, "$f31"},
+ };
+ return llvm::makeArrayRef(GCCRegAliases);
}
bool LoongArchTargetInfo::validateAsmConstraint(
const char *&Name, TargetInfo::ConstraintInfo &Info) const {
- // TODO: To be implemented in future.
- return false;
+ // See the GCC definitions here:
+ // https://gcc.gnu.org/onlinedocs/gccint/Machine-Constraints.html
+ switch (*Name) {
+ // TODO: handle 'k', 'm', "ZB", "ZC".
+ default:
+ return false;
+ case 'f':
+ // A floating-point register (if available).
+ Info.setAllowsRegister();
+ return true;
+ case 'l':
+ // A signed 16-bit constant.
+ Info.setRequiresImmediate(-32768, 32767);
+ return true;
+ case 'I':
+ // A signed 12-bit constant (for arithmetic instructions).
+ Info.setRequiresImmediate(-2048, 2047);
+ return true;
+ case 'K':
+ // An unsigned 12-bit constant (for logic instructions).
+ Info.setRequiresImmediate(0, 4095);
+ return true;
+ }
}
void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-constraints-error.c b/clang/test/CodeGen/LoongArch/inline-asm-constraints-error.c
new file mode 100644
index 000000000000..b4430cf40e62
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/inline-asm-constraints-error.c
@@ -0,0 +1,23 @@
+// RUN: not %clang_cc1 -triple loongarch32 -O2 -emit-llvm %s 2>&1 -o - | FileCheck %s
+// RUN: not %clang_cc1 -triple loongarch64 -O2 -emit-llvm %s 2>&1 -o - | FileCheck %s
+
+void test_l(void) {
+// CHECK: :[[#@LINE+1]]:27: error: value '32768' out of range for constraint 'l'
+ asm volatile ("" :: "l"(32768));
+// CHECK: :[[#@LINE+1]]:27: error: value '-32769' out of range for constraint 'l'
+ asm volatile ("" :: "l"(-32769));
+}
+
+void test_I(void) {
+// CHECK: :[[#@LINE+1]]:27: error: value '2048' out of range for constraint 'I'
+ asm volatile ("" :: "I"(2048));
+// CHECK: :[[#@LINE+1]]:27: error: value '-2049' out of range for constraint 'I'
+ asm volatile ("" :: "I"(-2049));
+}
+
+void test_K(void) {
+// CHECK: :[[#@LINE+1]]:27: error: value '4096' out of range for constraint 'K'
+ asm volatile ("" :: "K"(4096));
+// CHECK: :[[#@LINE+1]]:27: error: value '-1' out of range for constraint 'K'
+ asm volatile ("" :: "K"(-1));
+}
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
new file mode 100644
index 000000000000..9c9c60e48493
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -triple loongarch32 -O2 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple loongarch64 -O2 -emit-llvm %s -o - | FileCheck %s
+
+/// Test LoongArch specific inline assembly constraints.
+
+float f;
+double d;
+void test_f(void) {
+// CHECK-LABEL: define{{.*}} void @test_f()
+// CHECK: [[FLT_ARG:%[a-zA-Z_0-9]+]] = load float, ptr @f
+// CHECK: call void asm sideeffect "", "f"(float [[FLT_ARG]])
+ asm volatile ("" :: "f"(f));
+// CHECK: [[FLT_ARG:%[a-zA-Z_0-9]+]] = load double, ptr @d
+// CHECK: call void asm sideeffect "", "f"(double [[FLT_ARG]])
+ asm volatile ("" :: "f"(d));
+}
+
+void test_l(void) {
+// CHECK-LABEL: define{{.*}} void @test_l()
+// CHECK: call void asm sideeffect "", "l"(i32 32767)
+ asm volatile ("" :: "l"(32767));
+// CHECK: call void asm sideeffect "", "l"(i32 -32768)
+ asm volatile ("" :: "l"(-32768));
+}
+
+void test_I(void) {
+// CHECK-LABEL: define{{.*}} void @test_I()
+// CHECK: call void asm sideeffect "", "I"(i32 2047)
+ asm volatile ("" :: "I"(2047));
+// CHECK: call void asm sideeffect "", "I"(i32 -2048)
+ asm volatile ("" :: "I"(-2048));
+}
+
+void test_K(void) {
+// CHECK-LABEL: define{{.*}} void @test_K()
+// CHECK: call void asm sideeffect "", "K"(i32 4095)
+ asm volatile ("" :: "K"(4095));
+// CHECK: call void asm sideeffect "", "K"(i32 0)
+ asm volatile ("" :: "K"(0));
+}
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs-error.c b/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs-error.c
new file mode 100644
index 000000000000..39450c4657ae
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs-error.c
@@ -0,0 +1,22 @@
+// RUN: not %clang_cc1 -triple loongarch32 -emit-llvm %s 2>&1 -o - | FileCheck %s
+// RUN: not %clang_cc1 -triple loongarch64 -emit-llvm %s 2>&1 -o - | FileCheck %s
+
+void test(void) {
+// CHECK: :[[#@LINE+1]]:24: error: unknown register name '$r32' in asm
+ register int a0 asm ("$r32");
+// CHECK: :[[#@LINE+1]]:26: error: unknown register name '$f32' in asm
+ register float a1 asm ("$f32");
+// CHECK: :[[#@LINE+1]]:24: error: unknown register name '$foo' in asm
+ register int a2 asm ("$foo");
+
+/// Names not prefixed with '$' are invalid.
+
+// CHECK: :[[#@LINE+1]]:24: error: unknown register name 'r4' in asm
+ register int a3 asm ("r4");
+// CHECK: :[[#@LINE+1]]:24: error: unknown register name 'a0' in asm
+ register int a4 asm ("a0");
+// CHECK: :[[#@LINE+1]]:26: error: unknown register name 'f0' in asm
+ register float a5 asm ("f0");
+// CHECK: :[[#@LINE+1]]:26: error: unknown register name 'fa0' in asm
+ register float a6 asm ("fa0");
+}
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs.c b/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs.c
new file mode 100644
index 000000000000..067645366443
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/inline-asm-gcc-regs.c
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple loongarch32 -emit-llvm -O2 %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s -o - | FileCheck %s
+
+/// Check GCC register names and alias can be used in register variable definition.
+
+// CHECK-LABEL: @test_r0
+// CHECK: call void asm sideeffect "", "{$r0}"(i32 undef)
+void test_r0() {
+ register int a asm ("$r0");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_r12
+// CHECK: call void asm sideeffect "", "{$r12}"(i32 undef)
+void test_r12() {
+ register int a asm ("$r12");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_r31
+// CHECK: call void asm sideeffect "", "{$r31}"(i32 undef)
+void test_r31() {
+ register int a asm ("$r31");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_zero
+// CHECK: call void asm sideeffect "", "{$r0}"(i32 undef)
+void test_zero() {
+ register int a asm ("$zero");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_a0
+// CHECK: call void asm sideeffect "", "{$r4}"(i32 undef)
+void test_a0() {
+ register int a asm ("$a0");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_t1
+// CHECK: call void asm sideeffect "", "{$r13}"(i32 undef)
+void test_t1() {
+ register int a asm ("$t1");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_fp
+// CHECK: call void asm sideeffect "", "{$r22}"(i32 undef)
+void test_fp() {
+ register int a asm ("$fp");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_s2
+// CHECK: call void asm sideeffect "", "{$r25}"(i32 undef)
+void test_s2() {
+ register int a asm ("$s2");
+ asm ("" :: "r" (a));
+}
+
+// CHECK-LABEL: @test_f0
+// CHECK: call void asm sideeffect "", "{$f0}"(float undef)
+void test_f0() {
+ register float a asm ("$f0");
+ asm ("" :: "f" (a));
+}
+
+// CHECK-LABEL: @test_f14
+// CHECK: call void asm sideeffect "", "{$f14}"(float undef)
+void test_f14() {
+ register float a asm ("$f14");
+ asm ("" :: "f" (a));
+}
+
+// CHECK-LABEL: @test_f31
+// CHECK: call void asm sideeffect "", "{$f31}"(float undef)
+void test_f31() {
+ register float a asm ("$f31");
+ asm ("" :: "f" (a));
+}
+
+// CHECK-LABEL: @test_fa0
+// CHECK: call void asm sideeffect "", "{$f0}"(float undef)
+void test_fa0() {
+ register float a asm ("$fa0");
+ asm ("" :: "f" (a));
+}
+
+// CHECK-LABEL: @test_ft1
+// CHECK: call void asm sideeffect "", "{$f9}"(float undef)
+void test_ft1() {
+ register float a asm ("$ft1");
+ asm ("" :: "f" (a));
+}
+
+// CHECK-LABEL: @test_fs2
+// CHECK: call void asm sideeffect "", "{$f26}"(float undef)
+void test_fs2() {
+ register float a asm ("$fs2");
+ asm ("" :: "f" (a));
+}
diff --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
index 1467d1757ff0..9a7fb73b736c 100644
--- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
@@ -14,6 +14,7 @@
#include "LoongArchAsmPrinter.h"
#include "LoongArch.h"
#include "LoongArchTargetMachine.h"
+#include "MCTargetDesc/LoongArchInstPrinter.h"
#include "TargetInfo/LoongArchTargetInfo.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/MC/TargetRegistry.h"
@@ -39,6 +40,34 @@ void LoongArchAsmPrinter::emitInstruction(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, TmpInst);
}
+bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
+ const char *ExtraCode,
+ raw_ostream &OS) {
+ // First try the generic code, which knows about modifiers like 'c' and 'n'.
+ if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
+ return false;
+
+ // TODO: handle other extra codes if we have.
+ if (!ExtraCode) {
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ switch (MO.getType()) {
+ case MachineOperand::MO_Immediate:
+ OS << MO.getImm();
+ return false;
+ case MachineOperand::MO_Register:
+ OS << '$' << LoongArchInstPrinter::getRegisterName(MO.getReg());
+ return false;
+ case MachineOperand::MO_GlobalAddress:
+ PrintSymbolOperand(MO, OS);
+ return false;
+ default:
+ llvm_unreachable("not implemented");
+ }
+ }
+
+ return true;
+}
+
bool LoongArchAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
AsmPrinter::runOnMachineFunction(MF);
return true;
diff --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
index b51c19188051..941c86633fa1 100644
--- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
+++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
@@ -36,6 +36,9 @@ class LLVM_LIBRARY_VISIBILITY LoongArchAsmPrinter : public AsmPrinter {
void emitInstruction(const MachineInstr *MI) override;
+ bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
+ const char *ExtraCode, raw_ostream &OS) override;
+
// tblgen'erated function.
bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
const MachineInstr *MI);
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 800b9ddffbcf..d73f7f8da482 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -1907,3 +1907,139 @@ bool LoongArchTargetLowering::isFMAFasterThanFMulAndFAdd(
return false;
}
+
+//===----------------------------------------------------------------------===//
+// LoongArch Inline Assembly Support
+//===----------------------------------------------------------------------===//
+
+LoongArchTargetLowering::ConstraintType
+LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
+ // LoongArch specific constraints in GCC: config/loongarch/constraints.md
+ //
+ // 'f': A floating-point register (if available).
+ // 'k': A memory operand whose address is formed by a base register and
+ // (optionally scaled) index register.
+ // 'l': A signed 16-bit constant.
+ // 'm': 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 st.w and ld.w.
+ // 'I': A signed 12-bit constant (for arithmetic instructions).
+ // 'K': An unsigned 12-bit constant (for logic instructions).
+ // "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 (Constraint.size() == 1) {
+ switch (Constraint[0]) {
+ default:
+ break;
+ case 'f':
+ return C_RegisterClass;
+ case 'l':
+ case 'I':
+ case 'K':
+ return C_Immediate;
+ }
+ }
+
+ // TODO: handle 'k", "ZB" and "ZC".
+
+ return TargetLowering::getConstraintType(Constraint);
+}
+
+std::pair<unsigned, const TargetRegisterClass *>
+LoongArchTargetLowering::getRegForInlineAsmConstraint(
+ const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
+ // First, see if this is a constraint that directly corresponds to a LoongArch
+ // register class.
+ if (Constraint.size() == 1) {
+ switch (Constraint[0]) {
+ case 'r':
+ // TODO: Support fixed vectors up to GRLen?
+ if (VT.isVector())
+ break;
+ return std::make_pair(0U, &LoongArch::GPRRegClass);
+ case 'f':
+ if (Subtarget.hasBasicF() && VT == MVT::f32)
+ return std::make_pair(0U, &LoongArch::FPR32RegClass);
+ if (Subtarget.hasBasicD() && VT == MVT::f64)
+ return std::make_pair(0U, &LoongArch::FPR64RegClass);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // TargetLowering::getRegForInlineAsmConstraint uses the name of the TableGen
+ // record (e.g. the "R0" in `def R0`) to choose registers for InlineAsm
+ // constraints while the official register name is prefixed with a '$'. So we
+ // clip the '$' from the original constraint string (e.g. {$r0} to {r0}.)
+ // before it being parsed. And TargetLowering::getRegForInlineAsmConstraint is
+ // case insensitive, so no need to convert the constraint to upper case here.
+ //
+ // For now, no need to support ABI names (e.g. `$a0`) as clang will correctly
+ // decode the usage of register name aliases into their official names. And
+ // AFAIK, the not yet upstreamed `rustc` for LoongArch will always use
+ // official register names.
+ if (Constraint.startswith("{$r") || Constraint.startswith("{$f")) {
+ bool IsFP = Constraint[2] == 'f';
+ std::pair<StringRef, StringRef> Temp = Constraint.split('$');
+ std::pair<unsigned, const TargetRegisterClass *> R;
+ R = TargetLowering::getRegForInlineAsmConstraint(
+ TRI, join_items("", Temp.first, Temp.second), VT);
+ // Match those names to the widest floating point register type available.
+ if (IsFP) {
+ unsigned RegNo = R.first;
+ if (LoongArch::F0 <= RegNo && RegNo <= LoongArch::F31) {
+ if (Subtarget.hasBasicD() && (VT == MVT::f64 || VT == MVT::Other)) {
+ unsigned DReg = RegNo - LoongArch::F0 + LoongArch::F0_64;
+ return std::make_pair(DReg, &LoongArch::FPR64RegClass);
+ }
+ }
+ }
+ return R;
+ }
+
+ return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
+}
+
+void LoongArchTargetLowering::LowerAsmOperandForConstraint(
+ SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops,
+ SelectionDAG &DAG) const {
+ // Currently only support length 1 constraints.
+ if (Constraint.length() == 1) {
+ switch (Constraint[0]) {
+ case 'l':
+ // Validate & create a 16-bit signed immediate operand.
+ if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
+ uint64_t CVal = C->getSExtValue();
+ if (isInt<16>(CVal))
+ Ops.push_back(
+ DAG.getTargetConstant(CVal, SDLoc(Op), Subtarget.getGRLenVT()));
+ }
+ return;
+ case 'I':
+ // Validate & create a 12-bit signed immediate operand.
+ if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
+ uint64_t CVal = C->getSExtValue();
+ if (isInt<12>(CVal))
+ Ops.push_back(
+ DAG.getTargetConstant(CVal, SDLoc(Op), Subtarget.getGRLenVT()));
+ }
+ return;
+ case 'K':
+ // Validate & create a 12-bit unsigned immediate operand.
+ if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
+ uint64_t CVal = C->getZExtValue();
+ if (isUInt<12>(CVal))
+ Ops.push_back(
+ DAG.getTargetConstant(CVal, SDLoc(Op), Subtarget.getGRLenVT()));
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
+}
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index eb97622182e5..5e7dfee87217 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -148,6 +148,16 @@ class LoongArchTargetLowering : public TargetLowering {
bool ForCodeSize) const override;
bool shouldInsertFencesForAtomic(const Instruction *I) const override;
+
+ ConstraintType getConstraintType(StringRef Constraint) const override;
+
+ std::pair<unsigned, const TargetRegisterClass *>
+ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint, MVT VT) const override;
+
+ void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint,
+ std::vector<SDValue> &Ops,
+ SelectionDAG &DAG) const override;
};
} // end namespace llvm
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-clobbers.ll b/llvm/test/CodeGen/LoongArch/inline-asm-clobbers.ll
new file mode 100644
index 000000000000..f7e460e47545
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-clobbers.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA32 %s
+; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA64 %s
+
+;; Check that callee-saved registers clobbered by inlineasm are correctly saved.
+;;
+;; $r23: $s0 (callee-saved register under all ABIs)
+;; $r24: $s1 (callee-saved register under all ABIs)
+;; $f24: $fs0 (callee-saved register under *d/*f ABIs)
+;; $f25: $fs1 (callee-saved register under *d/*f ABIs)
+
+;; TODO: test other ABIs.
+
+define void @test() nounwind {
+; LA32-LABEL: test:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -32
+; LA32-NEXT: st.w $s0, $sp, 28 # 4-byte Folded Spill
+; LA32-NEXT: st.w $s1, $sp, 24 # 4-byte Folded Spill
+; LA32-NEXT: fst.d $fs0, $sp, 16 # 8-byte Folded Spill
+; LA32-NEXT: fst.d $fs1, $sp, 8 # 8-byte Folded Spill
+; LA32-NEXT: #APP
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: fld.d $fs1, $sp, 8 # 8-byte Folded Reload
+; LA32-NEXT: fld.d $fs0, $sp, 16 # 8-byte Folded Reload
+; LA32-NEXT: ld.w $s1, $sp, 24 # 4-byte Folded Reload
+; LA32-NEXT: ld.w $s0, $sp, 28 # 4-byte Folded Reload
+; LA32-NEXT: addi.w $sp, $sp, 32
+; LA32-NEXT: ret
+;
+; LA64-LABEL: test:
+; LA64: # %bb.0:
+; LA64-NEXT: addi.d $sp, $sp, -32
+; LA64-NEXT: st.d $s0, $sp, 24 # 8-byte Folded Spill
+; LA64-NEXT: st.d $s1, $sp, 16 # 8-byte Folded Spill
+; LA64-NEXT: fst.d $fs0, $sp, 8 # 8-byte Folded Spill
+; LA64-NEXT: fst.d $fs1, $sp, 0 # 8-byte Folded Spill
+; LA64-NEXT: #APP
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: fld.d $fs1, $sp, 0 # 8-byte Folded Reload
+; LA64-NEXT: fld.d $fs0, $sp, 8 # 8-byte Folded Reload
+; LA64-NEXT: ld.d $s1, $sp, 16 # 8-byte Folded Reload
+; LA64-NEXT: ld.d $s0, $sp, 24 # 8-byte Folded Reload
+; LA64-NEXT: addi.d $sp, $sp, 32
+; LA64-NEXT: ret
+ tail call void asm sideeffect "", "~{$f24},~{$f25},~{$r23},~{$r24}"()
+ ret void
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-error.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-error.ll
new file mode 100644
index 000000000000..470581d1faf9
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-error.ll
@@ -0,0 +1,40 @@
+; RUN: not llc --mtriple=loongarch32 < %s 2>&1 | FileCheck %s
+; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s
+
+define void @constraint_l() {
+; CHECK: error: value out of range for constraint 'l'
+ tail call void asm sideeffect "lu12i.w $$a0, $0", "l"(i32 32768)
+; CHECK: error: value out of range for constraint 'l'
+ tail call void asm sideeffect "lu12i.w $$a0, $0", "l"(i32 -32769)
+ ret void
+}
+
+define void @constraint_I() {
+; CHECK: error: value out of range for constraint 'I'
+ tail call void asm sideeffect "addi.w $$a0, $$a0, $0", "I"(i32 2048)
+; CHECK: error: value out of range for constraint 'I'
+ tail call void asm sideeffect "addi.w $$a0, $$a0, $0", "I"(i32 -2049)
+ ret void
+}
+
+define void @constraint_K() {
+; CHECK: error: value out of range for constraint 'K'
+ tail call void asm sideeffect "andi.w $$a0, $$a0, $0", "K"(i32 4096)
+; CHECK: error: value out of range for constraint 'K'
+ tail call void asm sideeffect "andi.w $$a0, $$a0, $0", "K"(i32 -1)
+ ret void
+}
+
+define void @constraint_f() nounwind {
+; CHECK: error: couldn't allocate input reg for constraint 'f'
+ tail call void asm "fadd.s $$fa0, $$fa0, $0", "f"(float 0.0)
+; CHECK: error: couldn't allocate input reg for constraint 'f'
+ tail call void asm "fadd.s $$fa0, $$fa0, $0", "f"(double 0.0)
+ ret void
+}
+
+define void @constraint_r_vec() nounwind {
+; CHECK: error: couldn't allocate input reg for constraint 'r'
+ tail call void asm "add.w $$a0, $$a0, $0", "r"(<4 x i32> zeroinitializer)
+ ret void
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
new file mode 100644
index 000000000000..a5569cdf9a5b
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
@@ -0,0 +1,62 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA32 %s
+; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA64 %s
+
+ at gd = external dso_local global double
+
+define double @constraint_f_double(double %a) nounwind {
+; LA32-LABEL: constraint_f_double:
+; LA32: # %bb.0:
+; LA32-NEXT: pcalau12i $a0, %pc_hi20(gd)
+; LA32-NEXT: addi.w $a0, $a0, %pc_lo12(gd)
+; LA32-NEXT: fld.d $fa1, $a0, 0
+; LA32-NEXT: #APP
+; LA32-NEXT: fadd.d $fa0, $fa0, $fa1
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ret
+;
+; LA64-LABEL: constraint_f_double:
+; LA64: # %bb.0:
+; LA64-NEXT: pcalau12i $a0, %pc_hi20(gd)
+; LA64-NEXT: addi.d $a0, $a0, %pc_lo12(gd)
+; LA64-NEXT: fld.d $fa1, $a0, 0
+; LA64-NEXT: #APP
+; LA64-NEXT: fadd.d $fa0, $fa0, $fa1
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ret
+ %1 = load double, double* @gd
+ %2 = tail call double asm "fadd.d $0, $1, $2", "=f,f,f"(double %a, double %1)
+ ret double %2
+}
+
+define double @constraint_gpr(double %a) {
+; LA32-LABEL: constraint_gpr:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -16
+; LA32-NEXT: .cfi_def_cfa_offset 16
+; LA32-NEXT: fst.d $fa0, $sp, 8
+; LA32-NEXT: ld.w $a7, $sp, 8
+; LA32-NEXT: ld.w $t0, $sp, 12
+; LA32-NEXT: #APP
+; LA32-NEXT: move $a6, $a7
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: st.w $a7, $sp, 4
+; LA32-NEXT: st.w $a6, $sp, 0
+; LA32-NEXT: fld.d $fa0, $sp, 0
+; LA32-NEXT: addi.w $sp, $sp, 16
+; LA32-NEXT: ret
+;
+; LA64-LABEL: constraint_gpr:
+; LA64: # %bb.0:
+; LA64-NEXT: .cfi_def_cfa_offset 0
+; LA64-NEXT: movfr2gr.d $a7, $fa0
+; LA64-NEXT: #APP
+; LA64-NEXT: move $a6, $a7
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: movgr2fr.d $fa0, $a6
+; LA64-NEXT: ret
+ %1 = tail call double asm sideeffect alignstack "move $0, $1", "={$r10},{$r11}"(double %a)
+ ret double %1
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
new file mode 100644
index 000000000000..ed2c621bf86a
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
@@ -0,0 +1,100 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --verify-machineinstrs --no-integrated-as < %s \
+; RUN: | FileCheck %s
+; RUN: llc --mtriple=loongarch64 --verify-machineinstrs --no-integrated-as < %s \
+; RUN: | FileCheck %s
+
+ at gi = external dso_local global i32, align 4
+
+define i32 @constraint_r(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: constraint_r:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: add.w $a0, $a0, $a1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ %1 = tail call i32 asm "add.w $0, $1, $2", "=r,r,r"(i32 %a, i32 %b)
+ ret i32 %1
+}
+
+define i32 @constraint_i(i32 %a) nounwind {
+; CHECK-LABEL: constraint_i:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: addi.w $a0, $a0, 113
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ %1 = tail call i32 asm "addi.w $0, $1, $2", "=r,r,i"(i32 %a, i32 113)
+ ret i32 %1
+}
+
+define void @constraint_l() nounwind {
+; CHECK-LABEL: constraint_l:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: lu12i.w $a0, 32767
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: #APP
+; CHECK-NEXT: lu12i.w $a0, -32768
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ tail call void asm sideeffect "lu12i.w $$a0, $0", "l"(i32 32767)
+ tail call void asm sideeffect "lu12i.w $$a0, $0", "l"(i32 -32768)
+ ret void
+}
+
+define void @constraint_I() nounwind {
+; CHECK-LABEL: constraint_I:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: addi.w $a0, $a0, 2047
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: #APP
+; CHECK-NEXT: addi.w $a0, $a0, -2048
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ tail call void asm sideeffect "addi.w $$a0, $$a0, $0", "I"(i32 2047)
+ tail call void asm sideeffect "addi.w $$a0, $$a0, $0", "I"(i32 -2048)
+ ret void
+}
+
+define void @constraint_K() nounwind {
+; CHECK-LABEL: constraint_K:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: andi $a0, $a0, 4095
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: #APP
+; CHECK-NEXT: andi $a0, $a0, 0
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ tail call void asm sideeffect "andi $$a0, $$a0, $0", "K"(i32 4095)
+ tail call void asm sideeffect "andi $$a0, $$a0, $0", "K"(i32 0)
+ ret void
+}
+
+define void @operand_global() nounwind {
+; CHECK-LABEL: operand_global:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: .8byte gi
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+ tail call void asm sideeffect ".8byte $0", "i"(ptr @gi)
+ ret void
+}
+
+define void @operand_block_address() nounwind {
+; CHECK-LABEL: operand_block_address:
+; CHECK: # %bb.0:
+; CHECK-NEXT: #APP
+; CHECK-NEXT: b .Ltmp0
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: .Ltmp0: # Block address taken
+; CHECK-NEXT: # %bb.1: # %bb
+; CHECK-NEXT: ret
+ call void asm sideeffect "b $0", "i"(i8* blockaddress(@operand_block_address, %bb))
+ br label %bb
+bb:
+ ret void
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-error.ll b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-error.ll
new file mode 100644
index 000000000000..56c335ffb3a6
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-error.ll
@@ -0,0 +1,14 @@
+; RUN: not llc --mtriple=loongarch32 2>&1 < %s | FileCheck %s
+; RUN: not llc --mtriple=loongarch64 2>&1 < %s | FileCheck %s
+
+define i32 @non_exit_r32(i32 %a) nounwind {
+; CHECK: error: couldn't allocate input reg for constraint '{$r32}'
+ %1 = tail call i32 asm "addi.w $0, $1, 1", "=r,{$r32}"(i32 %a)
+ ret i32 %1
+}
+
+define i32 @non_exit_foo(i32 %a) nounwind {
+; CHECK: error: couldn't allocate input reg for constraint '{$foo}'
+ %1 = tail call i32 asm "addi.w $0, $1, 1", "=r,{$foo}"(i32 %a)
+ ret i32 %1
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f-error.ll b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f-error.ll
new file mode 100644
index 000000000000..82d0d21e1cd5
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f-error.ll
@@ -0,0 +1,14 @@
+; RUN: not llc --mtriple=loongarch32 --mattr=+f,+d 2>&1 < %s | FileCheck %s
+; RUN: not llc --mtriple=loongarch64 --mattr=+f,+d 2>&1 < %s | FileCheck %s
+
+define double @non_exit_f32(double %a) nounwind {
+; CHECK: error: couldn't allocate input reg for constraint '{$f32}'
+ %1 = tail call double asm "fabs.d $0, $1", "=f,{$f32}"(double %a)
+ ret double %1
+}
+
+define double @non_exit_foo(double %a) nounwind {
+; CHECK: error: couldn't allocate input reg for constraint '{$foo}'
+ %1 = tail call double asm "fabs.d $0, $1", "=f,{$foo}"(double %a)
+ ret double %1
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f.ll b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f.ll
new file mode 100644
index 000000000000..8cf112223e25
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f.ll
@@ -0,0 +1,89 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --mattr=+f,+d --target-abi=ilp32d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA32 %s
+; RUN: llc --mtriple=loongarch64 --mattr=+f,+d --target-abi=lp64d --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA64 %s
+
+;; These test that we can use architectural names ($f[0-9]*) refer to registers in
+;; inline asm constraint lists. In each case, the named register should be used
+;; for the source register of the `fabs.d`. It is very likely that `$fa0` will
+;; be chosen as the designation register, but this is left to the compiler to
+;; choose.
+;;
+;; Parenthesised registers in comments are the other aliases for this register.
+
+define double @register_f0(double %a) nounwind {
+; LA32-LABEL: register_f0:
+; LA32: # %bb.0:
+; LA32-NEXT: #APP
+; LA32-NEXT: fabs.d $fa0, $fa0
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_f0:
+; LA64: # %bb.0:
+; LA64-NEXT: #APP
+; LA64-NEXT: fabs.d $fa0, $fa0
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ret
+ %1 = tail call double asm "fabs.d $0, $1", "=f,{$f0}"(double %a)
+ ret double %1
+}
+
+;; NOTE: This test uses `$f24` (`$fs0`) as an input, so it should be saved.
+define double @register_f24(double %a) nounwind {
+; LA32-LABEL: register_f24:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -16
+; LA32-NEXT: fst.d $fs0, $sp, 8 # 8-byte Folded Spill
+; LA32-NEXT: fmov.d $fs0, $fa0
+; LA32-NEXT: #APP
+; LA32-NEXT: fabs.d $fa0, $fs0
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: fld.d $fs0, $sp, 8 # 8-byte Folded Reload
+; LA32-NEXT: addi.w $sp, $sp, 16
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_f24:
+; LA64: # %bb.0:
+; LA64-NEXT: addi.d $sp, $sp, -16
+; LA64-NEXT: fst.d $fs0, $sp, 8 # 8-byte Folded Spill
+; LA64-NEXT: fmov.d $fs0, $fa0
+; LA64-NEXT: #APP
+; LA64-NEXT: fabs.d $fa0, $fs0
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: fld.d $fs0, $sp, 8 # 8-byte Folded Reload
+; LA64-NEXT: addi.d $sp, $sp, 16
+; LA64-NEXT: ret
+ %1 = tail call double asm "fabs.d $0, $1", "=f,{$f24}"(double %a)
+ ret double %1
+}
+
+;; NOTE: This test uses `$f31` (`$fs7`) as an input, so it should be saved.
+define double @register_f31(double %a) nounwind {
+; LA32-LABEL: register_f31:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -16
+; LA32-NEXT: fst.d $fs7, $sp, 8 # 8-byte Folded Spill
+; LA32-NEXT: fmov.d $fs7, $fa0
+; LA32-NEXT: #APP
+; LA32-NEXT: fabs.d $fa0, $fs7
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: fld.d $fs7, $sp, 8 # 8-byte Folded Reload
+; LA32-NEXT: addi.w $sp, $sp, 16
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_f31:
+; LA64: # %bb.0:
+; LA64-NEXT: addi.d $sp, $sp, -16
+; LA64-NEXT: fst.d $fs7, $sp, 8 # 8-byte Folded Spill
+; LA64-NEXT: fmov.d $fs7, $fa0
+; LA64-NEXT: #APP
+; LA64-NEXT: fabs.d $fa0, $fs7
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: fld.d $fs7, $sp, 8 # 8-byte Folded Reload
+; LA64-NEXT: addi.d $sp, $sp, 16
+; LA64-NEXT: ret
+ %1 = tail call double asm "fabs.d $0, $1", "=f,{$f31}"(double %a)
+ ret double %1
+}
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-reg-names.ll b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names.ll
new file mode 100644
index 000000000000..4bc16e6cc5fb
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-reg-names.ll
@@ -0,0 +1,109 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA32 %s
+; RUN: llc --mtriple=loongarch64 --verify-machineinstrs < %s \
+; RUN: | FileCheck --check-prefix=LA64 %s
+
+;; These test that we can use architectural names ($r*) refer to registers in
+;; inline asm constraint lists. In each case, the named register should be used
+;; for the source register of the `addi.w`. It is very likely that `$a0` will
+;; be chosen as the designation register, but this is left to the compiler to
+;; choose.
+;;
+;; Parenthesised registers in comments are the other aliases for this register.
+
+;; NOTE: This test has to pass in 0 to the inline asm, because that's the only
+;; value `$r0` (`$zero`) can take.
+define i32 @register_r0() nounwind {
+; LA32-LABEL: register_r0:
+; LA32: # %bb.0:
+; LA32-NEXT: #APP
+; LA32-NEXT: addi.w $a0, $zero, 0
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_r0:
+; LA64: # %bb.0:
+; LA64-NEXT: #APP
+; LA64-NEXT: addi.w $a0, $zero, 0
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ret
+ %1 = tail call i32 asm "addi.w $0, $1, 0", "=r,{$r0}"(i32 0)
+ ret i32 %1
+}
+
+define i32 @register_r4(i32 %a) nounwind {
+; LA32-LABEL: register_r4:
+; LA32: # %bb.0:
+; LA32-NEXT: #APP
+; LA32-NEXT: addi.w $a0, $a0, 1
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_r4:
+; LA64: # %bb.0:
+; LA64-NEXT: #APP
+; LA64-NEXT: addi.w $a0, $a0, 1
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ret
+ %1 = tail call i32 asm "addi.w $0, $1, 1", "=r,{$r4}"(i32 %a)
+ ret i32 %1
+}
+
+;; NOTE: This test uses `$r22` (`$s9`, `$fp`) as an input, so it should be saved.
+define i32 @register_r22(i32 %a) nounwind {
+; LA32-LABEL: register_r22:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -16
+; LA32-NEXT: st.w $fp, $sp, 12 # 4-byte Folded Spill
+; LA32-NEXT: move $fp, $a0
+; LA32-NEXT: #APP
+; LA32-NEXT: addi.w $a0, $fp, 1
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ld.w $fp, $sp, 12 # 4-byte Folded Reload
+; LA32-NEXT: addi.w $sp, $sp, 16
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_r22:
+; LA64: # %bb.0:
+; LA64-NEXT: addi.d $sp, $sp, -16
+; LA64-NEXT: st.d $fp, $sp, 8 # 8-byte Folded Spill
+; LA64-NEXT: move $fp, $a0
+; LA64-NEXT: #APP
+; LA64-NEXT: addi.w $a0, $fp, 1
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ld.d $fp, $sp, 8 # 8-byte Folded Reload
+; LA64-NEXT: addi.d $sp, $sp, 16
+; LA64-NEXT: ret
+ %1 = tail call i32 asm "addi.w $0, $1, 1", "=r,{$r22}"(i32 %a)
+ ret i32 %1
+}
+
+;; NOTE: This test uses `$r31` (`$s8`) as an input, so it should be saved.
+define i32 @register_r31(i32 %a) nounwind {
+; LA32-LABEL: register_r31:
+; LA32: # %bb.0:
+; LA32-NEXT: addi.w $sp, $sp, -16
+; LA32-NEXT: st.w $s8, $sp, 12 # 4-byte Folded Spill
+; LA32-NEXT: move $s8, $a0
+; LA32-NEXT: #APP
+; LA32-NEXT: addi.w $a0, $s8, 1
+; LA32-NEXT: #NO_APP
+; LA32-NEXT: ld.w $s8, $sp, 12 # 4-byte Folded Reload
+; LA32-NEXT: addi.w $sp, $sp, 16
+; LA32-NEXT: ret
+;
+; LA64-LABEL: register_r31:
+; LA64: # %bb.0:
+; LA64-NEXT: addi.d $sp, $sp, -16
+; LA64-NEXT: st.d $s8, $sp, 8 # 8-byte Folded Spill
+; LA64-NEXT: move $s8, $a0
+; LA64-NEXT: #APP
+; LA64-NEXT: addi.w $a0, $s8, 1
+; LA64-NEXT: #NO_APP
+; LA64-NEXT: ld.d $s8, $sp, 8 # 8-byte Folded Reload
+; LA64-NEXT: addi.d $sp, $sp, 16
+; LA64-NEXT: ret
+ %1 = tail call i32 asm "addi.w $0, $1, 1", "=r,{$r31}"(i32 %a)
+ ret i32 %1
+}
More information about the cfe-commits
mailing list