diff options
author | chenli <chenli@loongson.cn> | 2023-07-25 09:02:14 +0800 |
---|---|---|
committer | Weining Lu <luweining@loongson.cn> | 2023-07-25 09:02:29 +0800 |
commit | d25c79dc70008b835312e5cc7ef48b199fda3165 (patch) | |
tree | f62d8adb179cf33809dfd12c092419bd4f32512a /llvm | |
parent | c299efbf284fae92bb8d7d29110f76912f089daf (diff) | |
download | llvm-d25c79dc70008b835312e5cc7ef48b199fda3165.zip llvm-d25c79dc70008b835312e5cc7ef48b199fda3165.tar.gz llvm-d25c79dc70008b835312e5cc7ef48b199fda3165.tar.bz2 |
[LoongArch] Support InlineAsm for LSX and LASX
The author of the following files is licongtian <licongtian@loongson.cn>:
- clang/lib/Basic/Targets/LoongArch.cpp
- llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp
- llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
The files mentioned above implement InlineAsm for LSX and LASX as follows:
- Enable clang parsing LSX/LASX register name, such as $vr0.
- Support the case which operand type is 128bit or 256bit when the
constraints is 'f'.
- Support the way of specifying LSX/LASX register by using constraint,
such as "={$xr0}".
- Support the operand modifiers 'u' and 'w'.
- Support and legalize the data types and register classes involved in
LSX/LASX in the lowering process.
Reviewed By: xen0n, SixWeining
Differential Revision: https://reviews.llvm.org/D154931
Diffstat (limited to 'llvm')
6 files changed, 174 insertions, 1 deletions
diff --git a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp index 5cf0673..27979a8 100644 --- a/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchAsmPrinter.cpp @@ -75,6 +75,20 @@ bool LoongArchAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, 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. } } diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp index 9487166..b6e9210 100644 --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -53,6 +53,14 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM, 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); @@ -3048,6 +3056,12 @@ LoongArchTargetLowering::getRegForInlineAsmConstraint( 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; @@ -3065,7 +3079,8 @@ LoongArchTargetLowering::getRegForInlineAsmConstraint( // 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; diff --git a/llvm/test/CodeGen/LoongArch/lasx/inline-asm-operand-modifier.ll b/llvm/test/CodeGen/LoongArch/lasx/inline-asm-operand-modifier.ll new file mode 100644 index 0000000..201e34c --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/lasx/inline-asm-operand-modifier.ll @@ -0,0 +1,14 @@ +; 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 +} diff --git a/llvm/test/CodeGen/LoongArch/lasx/inline-asm-reg-names.ll b/llvm/test/CodeGen/LoongArch/lasx/inline-asm-reg-names.ll new file mode 100644 index 0000000..dd400ecf --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/lasx/inline-asm-reg-names.ll @@ -0,0 +1,58 @@ +; 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 +} diff --git a/llvm/test/CodeGen/LoongArch/lsx/inline-asm-operand-modifier.ll b/llvm/test/CodeGen/LoongArch/lsx/inline-asm-operand-modifier.ll new file mode 100644 index 0000000..c46e624 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/lsx/inline-asm-operand-modifier.ll @@ -0,0 +1,14 @@ +; 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 +} diff --git a/llvm/test/CodeGen/LoongArch/lsx/inline-asm-reg-names.ll b/llvm/test/CodeGen/LoongArch/lsx/inline-asm-reg-names.ll new file mode 100644 index 0000000..ceea362 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/lsx/inline-asm-reg-names.ll @@ -0,0 +1,58 @@ +; 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 +} |