"$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
"$f28", "$f29", "$f30", "$f31",
// Condition flag registers.
- "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7"};
+ "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", "$fcc6", "$fcc7",
+ // 128-bit vector registers.
+ "$vr0", "$vr1", "$vr2", "$vr3", "$vr4", "$vr5", "$vr6", "$vr7", "$vr8",
+ "$vr9", "$vr10", "$vr11", "$vr12", "$vr13", "$vr14", "$vr15", "$vr16",
+ "$vr17", "$vr18", "$vr19", "$vr20", "$vr21", "$vr22", "$vr23", "$vr24",
+ "$vr25", "$vr26", "$vr27", "$vr28", "$vr29", "$vr30", "$vr31",
+ // 256-bit vector registers.
+ "$xr0", "$xr1", "$xr2", "$xr3", "$xr4", "$xr5", "$xr6", "$xr7", "$xr8",
+ "$xr9", "$xr10", "$xr11", "$xr12", "$xr13", "$xr14", "$xr15", "$xr16",
+ "$xr17", "$xr18", "$xr19", "$xr20", "$xr21", "$xr22", "$xr23", "$xr24",
+ "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31"};
return llvm::ArrayRef(GCCRegNames);
}
--- /dev/null
+// RUN: not %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s 2>&1 -o - | FileCheck %s
+
+typedef signed char v32i8 __attribute__((vector_size(32), aligned(32)));
+
+void test() {
+// CHECK: :[[#@LINE+1]]:28: error: unknown register name 'xr0' in asm
+ register v32i8 p0 asm ("xr0");
+// CHECK: :[[#@LINE+1]]:29: error: unknown register name '$xr32' in asm
+ register v32i8 p32 asm ("$xr32");
+}
--- /dev/null
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --filter "^define |tail call"
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s -o - | FileCheck %s
+
+typedef signed char v32i8 __attribute__((vector_size(32), aligned(32)));
+
+// CHECK-LABEL: @test_xr0(
+// CHECK: tail call void asm sideeffect "", "{$xr0}"(<32 x i8> undef) #[[ATTR1:[0-9]+]], !srcloc !2
+//
+void test_xr0() {
+ register v32i8 a asm ("$xr0");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_xr7(
+// CHECK: tail call void asm sideeffect "", "{$xr7}"(<32 x i8> undef) #[[ATTR1]], !srcloc !3
+//
+void test_xr7() {
+ register v32i8 a asm ("$xr7");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_xr15(
+// CHECK: tail call void asm sideeffect "", "{$xr15}"(<32 x i8> undef) #[[ATTR1]], !srcloc !4
+//
+void test_xr15() {
+ register v32i8 a asm ("$xr15");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_xr31(
+// CHECK: tail call void asm sideeffect "", "{$xr31}"(<32 x i8> undef) #[[ATTR1]], !srcloc !5
+//
+void test_xr31() {
+ register v32i8 a asm ("$xr31");
+ asm ("" :: "f"(a));
+}
--- /dev/null
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s -o - | FileCheck %s
+
+typedef long long v4i64 __attribute__ ((vector_size(32), aligned(32)));
+
+// CHECK-LABEL: define dso_local void @test_u
+// CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "=f"() #[[ATTR1:[0-9]+]], !srcloc !2
+// CHECK-NEXT: ret void
+//
+void test_u() {
+ v4i64 v4i64_r;
+ asm volatile ("xvldi %u0, 1" : "=f" (v4i64_r));
+}
--- /dev/null
+// RUN: not %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s 2>&1 -o - | FileCheck %s
+
+typedef signed char v16i8 __attribute__((vector_size(16), aligned(16)));
+
+void test() {
+// CHECK: :[[#@LINE+1]]:28: error: unknown register name 'vr0' in asm
+ register v16i8 p0 asm ("vr0");
+// CHECK: :[[#@LINE+1]]:29: error: unknown register name '$vr32' in asm
+ register v16i8 p32 asm ("$vr32");
+}
--- /dev/null
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --filter "^define |tail call"
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s -o - | FileCheck %s
+
+typedef signed char v16i8 __attribute__((vector_size(16), aligned(16)));
+
+// CHECK-LABEL: @test_vr0(
+// CHECK: tail call void asm sideeffect "", "{$vr0}"(<16 x i8> undef) #[[ATTR1:[0-9]+]], !srcloc !2
+//
+void test_vr0() {
+ register v16i8 a asm ("$vr0");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_vr7(
+// CHECK: tail call void asm sideeffect "", "{$vr7}"(<16 x i8> undef) #[[ATTR1]], !srcloc !3
+//
+void test_vr7() {
+ register v16i8 a asm ("$vr7");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_vr15(
+// CHECK: tail call void asm sideeffect "", "{$vr15}"(<16 x i8> undef) #[[ATTR1]], !srcloc !4
+//
+void test_vr15() {
+ register v16i8 a asm ("$vr15");
+ asm ("" :: "f"(a));
+}
+
+// CHECK-LABEL: @test_vr31(
+// CHECK: tail call void asm sideeffect "", "{$vr31}"(<16 x i8> undef) #[[ATTR1]], !srcloc !5
+//
+void test_vr31() {
+ register v16i8 a asm ("$vr31");
+ asm ("" :: "f"(a));
+}
--- /dev/null
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -O2 %s -o - | FileCheck %s
+
+typedef long long v2i64 __attribute__ ((vector_size(16), aligned(16)));
+
+// CHECK-LABEL: define dso_local void @test_w
+// CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[TMP0:%.*]] = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "=f"() #[[ATTR1:[0-9]+]], !srcloc !2
+// CHECK-NEXT: ret void
+//
+void test_w() {
+ v2i64 v2i64_r;
+ asm volatile ("vldi %w0, 1" : "=f" (v2i64_r));
+}
return false;
}
break;
+ case 'w': // Print LSX registers.
+ if (MO.getReg().id() >= LoongArch::VR0 &&
+ MO.getReg().id() <= LoongArch::VR31)
+ break;
+ // The modifier is 'w' but the operand is not an LSX register; Report an
+ // unknown operand error.
+ return true;
+ case 'u': // Print LASX registers.
+ if (MO.getReg().id() >= LoongArch::XR0 &&
+ MO.getReg().id() <= LoongArch::XR31)
+ break;
+ // The modifier is 'u' but the operand is not an LASX register; Report an
+ // unknown operand error.
+ return true;
// TODO: handle other extra codes if any.
}
}
addRegisterClass(MVT::f32, &LoongArch::FPR32RegClass);
if (Subtarget.hasBasicD())
addRegisterClass(MVT::f64, &LoongArch::FPR64RegClass);
+ if (Subtarget.hasExtLSX())
+ for (auto VT : {MVT::v4f32, MVT::v2f64, MVT::v16i8, MVT::v8i16, MVT::v4i32,
+ MVT::v2i64})
+ addRegisterClass(VT, &LoongArch::LSX128RegClass);
+ if (Subtarget.hasExtLASX())
+ for (auto VT : {MVT::v8f32, MVT::v4f64, MVT::v32i8, MVT::v16i16, MVT::v8i32,
+ MVT::v4i64})
+ addRegisterClass(VT, &LoongArch::LASX256RegClass);
setLoadExtAction({ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}, GRLenVT,
MVT::i1, Promote);
return std::make_pair(0U, &LoongArch::FPR32RegClass);
if (Subtarget.hasBasicD() && VT == MVT::f64)
return std::make_pair(0U, &LoongArch::FPR64RegClass);
+ if (Subtarget.hasExtLSX() &&
+ TRI->isTypeLegalForClass(LoongArch::LSX128RegClass, VT))
+ return std::make_pair(0U, &LoongArch::LSX128RegClass);
+ if (Subtarget.hasExtLASX() &&
+ TRI->isTypeLegalForClass(LoongArch::LASX256RegClass, VT))
+ return std::make_pair(0U, &LoongArch::LASX256RegClass);
break;
default:
break;
// 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")) {
+ if (Constraint.startswith("{$r") || Constraint.startswith("{$f") ||
+ Constraint.startswith("{$vr") || Constraint.startswith("{$xr")) {
bool IsFP = Constraint[2] == 'f';
std::pair<StringRef, StringRef> Temp = Constraint.split('$');
std::pair<unsigned, const TargetRegisterClass *> R;
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
+; RUN: llc --mtriple=loongarch64 --mattr=+lasx < %s | FileCheck %s
+
+define void @test_u() nounwind {
+; CHECK-LABEL: test_u:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: xvldi $xr0, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "=f"()
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
+; RUN: llc --mtriple=loongarch64 --mattr=+lasx < %s | FileCheck %s
+
+define void @register_xr1() nounwind {
+; CHECK-LABEL: register_xr1:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: xvldi $xr1, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "={$xr1}"()
+ ret void
+}
+
+define void @register_xr7() nounwind {
+; CHECK-LABEL: register_xr7:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: xvldi $xr7, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "={$xr7}"()
+ ret void
+}
+
+define void @register_xr23() nounwind {
+; CHECK-LABEL: register_xr23:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: xvldi $xr23, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "={$xr23}"()
+ ret void
+}
+
+;; The lower 64-bit of the vector register '$xr31' is overlapped with
+;; the floating-point register '$f31' ('$fs7'). And '$f31' ('$fs7')
+;; is a callee-saved register which is preserved across calls.
+;; That's why the fst.d and fld.d instructions are emitted.
+define void @register_xr31() nounwind {
+; CHECK-LABEL: register_xr31:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: addi.d $sp, $sp, -16
+; CHECK-NEXT: fst.d $fs7, $sp, 8 # 8-byte Folded Spill
+; CHECK-NEXT: #APP
+; CHECK-NEXT: xvldi $xr31, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: fld.d $fs7, $sp, 8 # 8-byte Folded Reload
+; CHECK-NEXT: addi.d $sp, $sp, 16
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <4 x i64> asm sideeffect "xvldi ${0:u}, 1", "={$xr31}"()
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
+; RUN: llc --mtriple=loongarch64 --mattr=+lsx < %s | FileCheck %s
+
+define void @test_w() nounwind {
+; CHECK-LABEL: test_w:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: vldi $vr0, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "=f"()
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
+; RUN: llc --mtriple=loongarch64 --mattr=+lsx < %s | FileCheck %s
+
+define void @register_vr1() nounwind {
+; CHECK-LABEL: register_vr1:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: vldi $vr1, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "={$vr1}"()
+ ret void
+}
+
+define void @register_vr7() nounwind {
+; CHECK-LABEL: register_vr7:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: vldi $vr7, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "={$vr7}"()
+ ret void
+}
+
+define void @register_vr23() nounwind {
+; CHECK-LABEL: register_vr23:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: vldi $vr23, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "={$vr23}"()
+ ret void
+}
+
+;; The lower half of the vector register '$vr31' is overlapped with
+;; the floating-point register '$f31'. And '$f31' is a callee-saved
+;; register which is preserved across calls. That's why the
+;; fst.d and fld.d instructions are emitted.
+define void @register_vr31() nounwind {
+; CHECK-LABEL: register_vr31:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: addi.d $sp, $sp, -16
+; CHECK-NEXT: fst.d $fs7, $sp, 8 # 8-byte Folded Spill
+; CHECK-NEXT: #APP
+; CHECK-NEXT: vldi $vr31, 1
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: fld.d $fs7, $sp, 8 # 8-byte Folded Reload
+; CHECK-NEXT: addi.d $sp, $sp, 16
+; CHECK-NEXT: ret
+entry:
+ %0 = tail call <2 x i64> asm sideeffect "vldi ${0:w}, 1", "={$vr31}"()
+ ret void
+}