SixWeining created this revision.
SixWeining added reviewers: xen0n, MaskRay, xry111, rengolin, myhsu, wangleiat, 
gonglingqin.
Herald added subscribers: StephenFan, JDevlieghere, hiraditya.
Herald added a project: All.
SixWeining requested review of this revision.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.

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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134157

Files:
  clang/lib/Basic/Targets/LoongArch.cpp
  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/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
  llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
  llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
  llvm/lib/Target/LoongArch/LoongArchISelLowering.h
  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

Index: llvm/test/CodeGen/LoongArch/inline-asm-reg-names.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-reg-names-f-error.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-reg-names-error.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/LoongArch/inline-asm-constraint.ll
@@ -0,0 +1,160 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 --verify-machineinstrs --no-integrated-as < %s \
+; RUN:   | FileCheck --check-prefix=LA32 %s
+; RUN: llc --mtriple=loongarch64 --verify-machineinstrs --no-integrated-as < %s \
+; RUN:   | FileCheck --check-prefix=LA64 %s
+
+@gi = external dso_local global i32, align 4
+
+define i32 @constraint_r(i32 %a, i32 %b) nounwind {
+; LA32-LABEL: constraint_r:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    add.w $a0, $a0, $a1
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: constraint_r:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    add.w $a0, $a0, $a1
+; LA64-NEXT:    #NO_APP
+; LA64-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 {
+; LA32-LABEL: constraint_i:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    addi.w $a0, $a0, 113
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: constraint_i:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    addi.w $a0, $a0, 113
+; LA64-NEXT:    #NO_APP
+; LA64-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 {
+; LA32-LABEL: constraint_l:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    lu12i.w $a0, 32767
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    #APP
+; LA32-NEXT:    lu12i.w $a0, -32768
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: constraint_l:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    lu12i.w $a0, 32767
+; LA64-NEXT:    #NO_APP
+; LA64-NEXT:    #APP
+; LA64-NEXT:    lu12i.w $a0, -32768
+; LA64-NEXT:    #NO_APP
+; LA64-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 {
+; LA32-LABEL: constraint_I:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    addi.w $a0, $a0, 2047
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    #APP
+; LA32-NEXT:    addi.w $a0, $a0, -2048
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: constraint_I:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    addi.w $a0, $a0, 2047
+; LA64-NEXT:    #NO_APP
+; LA64-NEXT:    #APP
+; LA64-NEXT:    addi.w $a0, $a0, -2048
+; LA64-NEXT:    #NO_APP
+; LA64-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 {
+; LA32-LABEL: constraint_K:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    andi $a0, $a0, 4095
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    #APP
+; LA32-NEXT:    andi $a0, $a0, 0
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: constraint_K:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    andi $a0, $a0, 4095
+; LA64-NEXT:    #NO_APP
+; LA64-NEXT:    #APP
+; LA64-NEXT:    andi $a0, $a0, 0
+; LA64-NEXT:    #NO_APP
+; LA64-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 {
+; LA32-LABEL: operand_global:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    .8byte gi
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: operand_global:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    .8byte gi
+; LA64-NEXT:    #NO_APP
+; LA64-NEXT:    ret
+  tail call void asm sideeffect ".8byte $0", "i"(ptr @gi)
+  ret void
+}
+
+define void @operand_block_address() nounwind {
+; LA32-LABEL: operand_block_address:
+; LA32:       # %bb.0:
+; LA32-NEXT:    #APP
+; LA32-NEXT:    b .Ltmp0
+; LA32-NEXT:    #NO_APP
+; LA32-NEXT:  .Ltmp0: # Block address taken
+; LA32-NEXT:  # %bb.1: # %bb
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: operand_block_address:
+; LA64:       # %bb.0:
+; LA64-NEXT:    #APP
+; LA64-NEXT:    b .Ltmp0
+; LA64-NEXT:    #NO_APP
+; LA64-NEXT:  .Ltmp0: # Block address taken
+; LA64-NEXT:  # %bb.1: # %bb
+; LA64-NEXT:    ret
+  call void asm sideeffect "b $0", "i"(i8* blockaddress(@operand_block_address, %bb))
+  br label %bb
+bb:
+  ret void
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
===================================================================
--- /dev/null
+++ 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
+
+@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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-constraint-error.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/test/CodeGen/LoongArch/inline-asm-clobbers.ll
===================================================================
--- /dev/null
+++ 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
+}
Index: llvm/lib/Target/LoongArch/LoongArchISelLowering.h
===================================================================
--- llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -148,6 +148,16 @@
                     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
Index: llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
===================================================================
--- llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -1888,3 +1888,205 @@
 
   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 manually select general purpose registers 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.
+  unsigned GPReg = StringSwitch<unsigned>(Constraint.lower())
+                       .Case("{$r0}", LoongArch::R0)
+                       .Case("{$r1}", LoongArch::R1)
+                       .Case("{$r2}", LoongArch::R2)
+                       .Case("{$r3}", LoongArch::R3)
+                       .Case("{$r4}", LoongArch::R4)
+                       .Case("{$r5}", LoongArch::R5)
+                       .Case("{$r6}", LoongArch::R6)
+                       .Case("{$r7}", LoongArch::R7)
+                       .Case("{$r8}", LoongArch::R8)
+                       .Case("{$r9}", LoongArch::R9)
+                       .Case("{$r10}", LoongArch::R10)
+                       .Case("{$r11}", LoongArch::R11)
+                       .Case("{$r12}", LoongArch::R12)
+                       .Case("{$r13}", LoongArch::R13)
+                       .Case("{$r14}", LoongArch::R14)
+                       .Case("{$r15}", LoongArch::R15)
+                       .Case("{$r16}", LoongArch::R16)
+                       .Case("{$r17}", LoongArch::R17)
+                       .Case("{$r18}", LoongArch::R18)
+                       .Case("{$r19}", LoongArch::R19)
+                       .Case("{$r20}", LoongArch::R20)
+                       .Case("{$r21}", LoongArch::R21)
+                       .Case("{$r22}", LoongArch::R22)
+                       .Case("{$r23}", LoongArch::R23)
+                       .Case("{$r24}", LoongArch::R24)
+                       .Case("{$r25}", LoongArch::R25)
+                       .Case("{$r26}", LoongArch::R26)
+                       .Case("{$r27}", LoongArch::R27)
+                       .Case("{$r28}", LoongArch::R28)
+                       .Case("{$r29}", LoongArch::R29)
+                       .Case("{$r30}", LoongArch::R30)
+                       .Case("{$r31}", LoongArch::R31)
+                       .Default(LoongArch::NoRegister);
+  if (GPReg != LoongArch::NoRegister)
+    return std::make_pair(GPReg, &LoongArch::GPRRegClass);
+
+  // As the same reason of GPR described above, plus we want to match those
+  // names to the widest floating point register type available, manually select
+  // floating point registers here.
+  if (Subtarget.hasBasicF()) {
+    unsigned FReg = StringSwitch<unsigned>(Constraint.lower())
+                        .Case("{$f0}", LoongArch::F0)
+                        .Case("{$f1}", LoongArch::F1)
+                        .Case("{$f2}", LoongArch::F2)
+                        .Case("{$f3}", LoongArch::F3)
+                        .Case("{$f4}", LoongArch::F4)
+                        .Case("{$f5}", LoongArch::F5)
+                        .Case("{$f6}", LoongArch::F6)
+                        .Case("{$f7}", LoongArch::F7)
+                        .Case("{$f8}", LoongArch::F8)
+                        .Case("{$f9}", LoongArch::F9)
+                        .Case("{$f10}", LoongArch::F10)
+                        .Case("{$f11}", LoongArch::F11)
+                        .Case("{$f12}", LoongArch::F12)
+                        .Case("{$f13}", LoongArch::F13)
+                        .Case("{$f14}", LoongArch::F14)
+                        .Case("{$f15}", LoongArch::F15)
+                        .Case("{$f16}", LoongArch::F16)
+                        .Case("{$f17}", LoongArch::F17)
+                        .Case("{$f18}", LoongArch::F18)
+                        .Case("{$f19}", LoongArch::F19)
+                        .Case("{$f20}", LoongArch::F20)
+                        .Case("{$f21}", LoongArch::F21)
+                        .Case("{$f22}", LoongArch::F22)
+                        .Case("{$f23}", LoongArch::F23)
+                        .Case("{$f24}", LoongArch::F24)
+                        .Case("{$f25}", LoongArch::F25)
+                        .Case("{$f26}", LoongArch::F26)
+                        .Case("{$f27}", LoongArch::F27)
+                        .Case("{$f28}", LoongArch::F28)
+                        .Case("{$f29}", LoongArch::F29)
+                        .Case("{$f30}", LoongArch::F30)
+                        .Case("{$f31}", LoongArch::F31)
+                        .Default(LoongArch::NoRegister);
+    if (FReg != LoongArch::NoRegister) {
+      assert(LoongArch::F0 <= FReg && FReg <= LoongArch::F31 &&
+             "Unknown fp-reg");
+      if (Subtarget.hasBasicD() && (VT == MVT::f64 || VT == MVT::Other)) {
+        unsigned RegNo = FReg - LoongArch::F0;
+        unsigned DReg = LoongArch::F0_64 + RegNo;
+        return std::make_pair(DReg, &LoongArch::FPR64RegClass);
+      }
+      if (VT == MVT::f32 || VT == MVT::Other)
+        return std::make_pair(FReg, &LoongArch::FPR32RegClass);
+    }
+  }
+
+  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);
+}
Index: llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
===================================================================
--- llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
+++ llvm/lib/Target/LoongArch/LoongArchAsmPrinter.h
@@ -36,6 +36,9 @@
 
   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);
Index: llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
===================================================================
--- llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
+++ 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 @@
     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;
Index: clang/test/CodeGen/LoongArch/inline-asm-gcc-regs.c
===================================================================
--- /dev/null
+++ 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));
+}
Index: clang/test/CodeGen/LoongArch/inline-asm-gcc-regs-error.c
===================================================================
--- /dev/null
+++ 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");
+}
Index: clang/test/CodeGen/LoongArch/inline-asm-constraints.c
===================================================================
--- /dev/null
+++ 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));
+}
Index: clang/test/CodeGen/LoongArch/inline-asm-constraints-error.c
===================================================================
--- /dev/null
+++ 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));
+}
Index: clang/lib/Basic/Targets/LoongArch.cpp
===================================================================
--- clang/lib/Basic/Targets/LoongArch.cpp
+++ clang/lib/Basic/Targets/LoongArch.cpp
@@ -22,20 +22,73 @@
 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,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to