Skip to content

[Clang][LoongArch] Add inline asm support for the q constraint #141037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

heiher
Copy link
Member

@heiher heiher commented May 22, 2025

This patch adds support for the q constraint:
a general-purpose register except for $r0 and $r1 (for the csrxchg instruction)

Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/684339.html

@heiher heiher requested review from wangleiat and SixWeining May 22, 2025 10:53
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" backend:loongarch labels May 22, 2025
@llvmbot
Copy link
Member

llvmbot commented May 22, 2025

@llvm/pr-subscribers-clang

Author: hev (heiher)

Changes

This patch adds support for the q constraint:
a general-purpose register except for $r0 and $r1 (for the csrxchg instruction)

Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/684339.html


Full diff: https://github.com/llvm/llvm-project/pull/141037.diff

4 Files Affected:

  • (modified) clang/lib/Basic/Targets/LoongArch.cpp (+5)
  • (modified) clang/test/CodeGen/LoongArch/inline-asm-constraints.c (+6)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+5)
  • (modified) llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll (+11)
diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp
index ca742797d7a3b..f4bcb54bd470d 100644
--- a/clang/lib/Basic/Targets/LoongArch.cpp
+++ b/clang/lib/Basic/Targets/LoongArch.cpp
@@ -139,6 +139,11 @@ bool LoongArchTargetInfo::validateAsmConstraint(
     // A signed 16-bit constant.
     Info.setRequiresImmediate(-32768, 32767);
     return true;
+  case 'q':
+    // A general-purpose register except for $r0 and $r1 (for the csrxchg
+    // instruction)
+    Info.setAllowsRegister();
+    return true;
   case 'I':
     // A signed 12-bit constant (for arithmetic instructions).
     Info.setRequiresImmediate(-2048, 2047);
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
index b19494284bd99..ded21206d63bf 100644
--- a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
+++ b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
@@ -35,6 +35,12 @@ void test_m(int *p) {
   asm volatile("" :: "m"(*(p+4)));
 }
 
+void test_q(void) {
+// CHECK-LABEL: define{{.*}} void @test_q()
+// CHECK: call void asm sideeffect "", "q"(i32 0)
+  asm volatile ("" :: "q"(0));
+}
+
 void test_I(void) {
 // CHECK-LABEL: define{{.*}} void @test_I()
 // CHECK: call void asm sideeffect "", "I"(i32 2047)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 9774683e16291..50ec0b2e3ca78 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -7276,6 +7276,8 @@ LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
   // '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.
+  // 'q':  A general-purpose register except for $r0 and $r1 (for the csrxchg
+  //       instruction)
   // 'I':  A signed 12-bit constant (for arithmetic instructions).
   // 'J':  Integer zero.
   // 'K':  An unsigned 12-bit constant (for logic instructions).
@@ -7289,6 +7291,7 @@ LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
     default:
       break;
     case 'f':
+    case 'q':
       return C_RegisterClass;
     case 'l':
     case 'I':
@@ -7328,6 +7331,8 @@ LoongArchTargetLowering::getRegForInlineAsmConstraint(
       if (VT.isVector())
         break;
       return std::make_pair(0U, &LoongArch::GPRRegClass);
+    case 'q':
+      return std::make_pair(0U, &LoongArch::GPRNoR0R1RegClass);
     case 'f':
       if (Subtarget.hasBasicF() && VT == MVT::f32)
         return std::make_pair(0U, &LoongArch::FPR32RegClass);
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
index 4bcc88be97396..426082b2906d5 100644
--- a/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
@@ -17,6 +17,17 @@ define i32 @constraint_r(i32 %a, i32 %b) nounwind {
   ret i32 %1
 }
 
+define i32 @constraint_q(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: constraint_q:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    csrxchg $a0, $a0, $a1
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    ret
+  %1 = tail call i32 asm "csrxchg $0, $1, $2", "=r,r,q,i"(i32 %a, i32 %b, i32 0)
+  ret i32 %1
+}
+
 define i32 @constraint_i(i32 %a) nounwind {
 ; CHECK-LABEL: constraint_i:
 ; CHECK:       # %bb.0:

@llvmbot
Copy link
Member

llvmbot commented May 22, 2025

@llvm/pr-subscribers-backend-loongarch

Author: hev (heiher)

Changes

This patch adds support for the q constraint:
a general-purpose register except for $r0 and $r1 (for the csrxchg instruction)

Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/684339.html


Full diff: https://github.com/llvm/llvm-project/pull/141037.diff

4 Files Affected:

  • (modified) clang/lib/Basic/Targets/LoongArch.cpp (+5)
  • (modified) clang/test/CodeGen/LoongArch/inline-asm-constraints.c (+6)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+5)
  • (modified) llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll (+11)
diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp
index ca742797d7a3b..f4bcb54bd470d 100644
--- a/clang/lib/Basic/Targets/LoongArch.cpp
+++ b/clang/lib/Basic/Targets/LoongArch.cpp
@@ -139,6 +139,11 @@ bool LoongArchTargetInfo::validateAsmConstraint(
     // A signed 16-bit constant.
     Info.setRequiresImmediate(-32768, 32767);
     return true;
+  case 'q':
+    // A general-purpose register except for $r0 and $r1 (for the csrxchg
+    // instruction)
+    Info.setAllowsRegister();
+    return true;
   case 'I':
     // A signed 12-bit constant (for arithmetic instructions).
     Info.setRequiresImmediate(-2048, 2047);
diff --git a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
index b19494284bd99..ded21206d63bf 100644
--- a/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
+++ b/clang/test/CodeGen/LoongArch/inline-asm-constraints.c
@@ -35,6 +35,12 @@ void test_m(int *p) {
   asm volatile("" :: "m"(*(p+4)));
 }
 
+void test_q(void) {
+// CHECK-LABEL: define{{.*}} void @test_q()
+// CHECK: call void asm sideeffect "", "q"(i32 0)
+  asm volatile ("" :: "q"(0));
+}
+
 void test_I(void) {
 // CHECK-LABEL: define{{.*}} void @test_I()
 // CHECK: call void asm sideeffect "", "I"(i32 2047)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 9774683e16291..50ec0b2e3ca78 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -7276,6 +7276,8 @@ LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
   // '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.
+  // 'q':  A general-purpose register except for $r0 and $r1 (for the csrxchg
+  //       instruction)
   // 'I':  A signed 12-bit constant (for arithmetic instructions).
   // 'J':  Integer zero.
   // 'K':  An unsigned 12-bit constant (for logic instructions).
@@ -7289,6 +7291,7 @@ LoongArchTargetLowering::getConstraintType(StringRef Constraint) const {
     default:
       break;
     case 'f':
+    case 'q':
       return C_RegisterClass;
     case 'l':
     case 'I':
@@ -7328,6 +7331,8 @@ LoongArchTargetLowering::getRegForInlineAsmConstraint(
       if (VT.isVector())
         break;
       return std::make_pair(0U, &LoongArch::GPRRegClass);
+    case 'q':
+      return std::make_pair(0U, &LoongArch::GPRNoR0R1RegClass);
     case 'f':
       if (Subtarget.hasBasicF() && VT == MVT::f32)
         return std::make_pair(0U, &LoongArch::FPR32RegClass);
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
index 4bcc88be97396..426082b2906d5 100644
--- a/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
@@ -17,6 +17,17 @@ define i32 @constraint_r(i32 %a, i32 %b) nounwind {
   ret i32 %1
 }
 
+define i32 @constraint_q(i32 %a, i32 %b) nounwind {
+; CHECK-LABEL: constraint_q:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    #APP
+; CHECK-NEXT:    csrxchg $a0, $a0, $a1
+; CHECK-NEXT:    #NO_APP
+; CHECK-NEXT:    ret
+  %1 = tail call i32 asm "csrxchg $0, $1, $2", "=r,r,q,i"(i32 %a, i32 %b, i32 0)
+  ret i32 %1
+}
+
 define i32 @constraint_i(i32 %a) nounwind {
 ; CHECK-LABEL: constraint_i:
 ; CHECK:       # %bb.0:

@heiher
Copy link
Member Author

heiher commented May 22, 2025

cc @xry111 @xen0n

@heiher heiher marked this pull request as draft May 22, 2025 10:56
@heiher heiher marked this pull request as ready for review May 22, 2025 11:05
Copy link
Contributor

@xen0n xen0n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about making a test case that exerts great pressure on the register allocator to verify that $ra does not get accidentally handed out? Or if that's too fragile, if it's possible, make a variable pinned to $ra or $zero then pass it to the inline asm block with the newly added q constraint, to ensure a new temp gets allocated.

heiher added 2 commits May 22, 2025 19:05
This patch adds support for the `q` constraint:
a general-purpose register except for $r0 and $r1 (for the csrxchg
instruction)

Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/684339.html
@heiher
Copy link
Member Author

heiher commented May 22, 2025

What about making a test case that exerts great pressure on the register allocator to verify that $ra does not get accidentally handed out?

I've done that, similar to what was done in #140862

Or if that's too fragile, if it's possible, make a variable pinned to $ra or $zero then pass it to the inline asm block with the newly added q constraint, to ensure a new temp gets allocated.

It doesn't seem to work. A register variable appears to bypass the "q" constraint, which I believe is expected.

@xry111
Copy link
Contributor

xry111 commented May 22, 2025

I have a C test case:

unsigned long
test (unsigned long x, unsigned long y)
{
  register unsigned long ra asm ("ra");
  asm("" : "+r"(ra));

  unsigned long t = ra;
  asm ("csrxchg %0, %1, 0" : "+r" (x) : "r" (t));

  return x;
}

clang-20.1.4 errors out:

t.c:8:8: error: must not be $r0 or $r1
    8 |   asm ("csrxchg %0, %1, 0" : "+r" (x) : "r" (t));
      |        ^
<inline asm>:1:16: note: instantiated into assembly here
    1 |         csrxchg $a0, $ra, 0
      |                       ^
1 error generated.

And I suppose it should work after this PR and changing the second "r" to "q".

@heiher
Copy link
Member Author

heiher commented May 22, 2025

I have a C test case:

unsigned long
test (unsigned long x, unsigned long y)
{
  register unsigned long ra asm ("ra");
  asm("" : "+r"(ra));

  unsigned long t = ra;
  asm ("csrxchg %0, %1, 0" : "+r" (x) : "r" (t));

  return x;
}

clang-20.1.4 errors out:

t.c:8:8: error: must not be $r0 or $r1
    8 |   asm ("csrxchg %0, %1, 0" : "+r" (x) : "r" (t));
      |        ^
<inline asm>:1:16: note: instantiated into assembly here
    1 |         csrxchg $a0, $ra, 0
      |                       ^
1 error generated.

And I suppose it should work after this PR and changing the second "r" to "q".

Good idea!

Copy link

github-actions bot commented May 22, 2025

✅ With the latest revision this PR passed the undef deprecator.

heiher added a commit to heiher/llvm-project that referenced this pull request May 22, 2025
Took xry's idea [^1] to improve the csrxchg instrinsic test case.

[^1]: llvm#141037 (comment)
Copy link
Contributor

@xen0n xen0n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:loongarch clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants