[LoongArch] Add intrinsics for CACOP instruction
authorXiaodong Liu <liuxiaodong@loongson.cn>
Fri, 6 Jan 2023 01:32:20 +0000 (09:32 +0800)
committerXiaodong Liu <liuxiaodong@loongson.cn>
Fri, 6 Jan 2023 03:41:35 +0000 (11:41 +0800)
The CACOP instruction is mainly used for cache initialization
and cache-consistency maintenance.

Depends on D140872

Reviewed By: SixWeining

Differential Revision: https://reviews.llvm.org/D140527

18 files changed:
clang/include/clang/Basic/BuiltinsLoongArch.def
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Basic/Targets/LoongArch.cpp
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/Headers/larchintrin.h
clang/lib/Sema/SemaChecking.cpp
clang/test/CodeGen/LoongArch/intrinsic-la32-error.c
clang/test/CodeGen/LoongArch/intrinsic-la32.c
clang/test/CodeGen/LoongArch/intrinsic-la64.c
clang/test/Driver/loongarch-default-features.c
llvm/include/llvm/IR/IntrinsicsLoongArch.td
llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
llvm/lib/Target/LoongArch/LoongArchISelLowering.h
llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll
llvm/test/CodeGen/LoongArch/intrinsic-la32.ll [new file with mode: 0644]
llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll
llvm/test/CodeGen/LoongArch/intrinsic-la64.ll

index cc466cf2703ca0f5ccbffbcd5b766b853a823952..7f2c8403410dd3ef491148c89c70c082ffc6b4df 100644 (file)
@@ -17,6 +17,8 @@
 
 // TODO: Support more builtins.
 // TODO: Added feature constraints.
+TARGET_BUILTIN(__builtin_loongarch_cacop_d, "vLiULiLi", "nc", "64bit")
+TARGET_BUILTIN(__builtin_loongarch_cacop_w, "viUii", "nc", "32bit")
 TARGET_BUILTIN(__builtin_loongarch_dbar, "vIUi", "nc", "")
 TARGET_BUILTIN(__builtin_loongarch_ibar, "vIUi", "nc", "")
 TARGET_BUILTIN(__builtin_loongarch_movfcsr2gr, "UiIUi", "nc", "f")
index 16ab35fcb7a674f06ba7e8f253710f8d720bb28e..c684d3b4a781073f4dde401301a105b052e507a0 100644 (file)
@@ -11764,4 +11764,6 @@ def warn_unsafe_buffer_expression : Warning<
 def warn_unsafe_buffer_variable : Warning<
   "variable %0 participates in unchecked buffer operations">,
   InGroup<UnsafeBufferUsage>, DefaultIgnore;
+def err_loongarch_builtin_requires_la32 : Error<
+  "this builtin requires target: loongarch32">;
 } // end of sema component.
index 5ac94864cebb25c06ec046e3926ca70cf8496e8c..4ce2be8c8074257ca65d469feb2efa41a1d183ec 100644 (file)
@@ -179,6 +179,8 @@ bool LoongArchTargetInfo::initFeatureMap(
     const std::vector<std::string> &FeaturesVec) const {
   if (getTriple().getArch() == llvm::Triple::loongarch64)
     Features["64bit"] = true;
+  if (getTriple().getArch() == llvm::Triple::loongarch32)
+    Features["32bit"] = true;
 
   return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
 }
index 0afa25da7aee30b38355c14b4c43cf575d1e68a2..fa6128b2a6441bd125e558b147d52a438eb26d31 100644 (file)
@@ -19694,6 +19694,12 @@ Value *CodeGenFunction::EmitLoongArchBuiltinExpr(unsigned BuiltinID,
   switch (BuiltinID) {
   default:
     llvm_unreachable("unexpected builtin ID.");
+  case LoongArch::BI__builtin_loongarch_cacop_d:
+    ID = Intrinsic::loongarch_cacop_d;
+    break;
+  case LoongArch::BI__builtin_loongarch_cacop_w:
+    ID = Intrinsic::loongarch_cacop_w;
+    break;
   case LoongArch::BI__builtin_loongarch_dbar:
     ID = Intrinsic::loongarch_dbar;
     break;
index 5edcf4c02a441bf242b55e467580db2d3ed0bb72..c5c533ee0b8c1d6e2372244fd8fc186e55a0409a 100644 (file)
@@ -106,6 +106,16 @@ extern __inline int
 
 #define __break(/*ui15*/ _1) __builtin_loongarch_break((_1))
 
+#if __loongarch_grlen == 32
+#define __cacop_w(/*uimm5*/ _1, /*unsigned int*/ _2, /*simm12*/ _3)            \
+  ((void)__builtin_loongarch_cacop_w((_1), (unsigned int)(_2), (_3)))
+#endif
+
+#if __loongarch_grlen == 64
+#define __cacop_d(/*uimm5*/ _1, /*unsigned long int*/ _2, /*simm12*/ _3)       \
+  ((void)__builtin_loongarch_cacop_d((_1), (unsigned long int)(_2), (_3)))
+#endif
+
 #define __dbar(/*ui15*/ _1) __builtin_loongarch_dbar((_1))
 
 #define __ibar(/*ui15*/ _1) __builtin_loongarch_ibar((_1))
index 05bf498737ec4830fcdc1db9ee0b31d16583bb79..f787d6c2255f570ed6ca171d6a429c6d666cd99e 100644 (file)
@@ -3705,6 +3705,23 @@ bool Sema::CheckLoongArchBuiltinFunctionCall(const TargetInfo &TI,
   switch (BuiltinID) {
   default:
     break;
+  case LoongArch::BI__builtin_loongarch_cacop_d:
+    if (!TI.hasFeature("64bit"))
+      return Diag(TheCall->getBeginLoc(),
+                  diag::err_loongarch_builtin_requires_la64)
+             << TheCall->getSourceRange();
+    LLVM_FALLTHROUGH;
+  case LoongArch::BI__builtin_loongarch_cacop_w: {
+    if (BuiltinID == LoongArch::BI__builtin_loongarch_cacop_w &&
+        !TI.hasFeature("32bit"))
+      return Diag(TheCall->getBeginLoc(),
+                  diag::err_loongarch_builtin_requires_la32)
+             << TheCall->getSourceRange();
+    SemaBuiltinConstantArgRange(TheCall, 0, 0, llvm::maxUIntN(5));
+    SemaBuiltinConstantArgRange(TheCall, 2, llvm::minIntN(12),
+                                llvm::maxIntN(12));
+    break;
+  }
   case LoongArch::BI__builtin_loongarch_crc_w_b_w:
   case LoongArch::BI__builtin_loongarch_crc_w_h_w:
   case LoongArch::BI__builtin_loongarch_crc_w_w_w:
index 9941a6f874cbacfd5661eadca0b3734ff6365e4b..2c3c249c54b137ff6319c2a479f35bbabeae5e43 100644 (file)
@@ -3,6 +3,14 @@
 
 #include <larchintrin.h>
 
+void cacop_d(unsigned long int a) {
+  __builtin_loongarch_cacop_d(1, a, 1024); // expected-error {{this builtin requires target: loongarch64}}
+  __builtin_loongarch_cacop_w(-1, a, 1024); // expected-error {{argument value -1 is outside the valid range [0, 31]}}
+  __builtin_loongarch_cacop_w(32, a, 1024); // expected-error {{argument value 32 is outside the valid range [0, 31]}}
+  __builtin_loongarch_cacop_w(1, a, -4096); // expected-error {{argument value -4096 is outside the valid range [-2048, 2047]}}
+  __builtin_loongarch_cacop_w(1, a, 4096); // expected-error {{argument value 4096 is outside the valid range [-2048, 2047]}}
+}
+
 void dbar(int a) {
   __builtin_loongarch_dbar(32768); // expected-error {{argument value 32768 is outside the valid range [0, 32767]}}
   __builtin_loongarch_dbar(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}}
index 060692a79ef4b80ddd82cbc117c9451acec2bd01..93d54f511a9cd271695ec066c483ff87f0a03c09 100644 (file)
@@ -200,3 +200,14 @@ void loongarch_movgr2fcsr(int a) {
   __movgr2fcsr(1, a);
   __builtin_loongarch_movgr2fcsr(1, a);
 }
+
+// CHECK-LABEL: @cacop_w(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    tail call void @llvm.loongarch.cacop.w(i32 1, i32 [[A:%.*]], i32 1024)
+// CHECK-NEXT:    tail call void @llvm.loongarch.cacop.w(i32 1, i32 [[A]], i32 1024)
+// CHECK-NEXT:    ret void
+//
+void cacop_w(unsigned long int a) {
+  __cacop_w(1, a, 1024);
+  __builtin_loongarch_cacop_w(1, a, 1024);
+}
index a8e8f7dddade01e2681f1d3bc8fd4b1cc32e5011..a740882eef5411cbb1940ce1538cbea12a672b2e 100644 (file)
@@ -123,6 +123,17 @@ int crc_w_w_w(int a, int b) {
   return 0;
 }
 
+// CHECK-LABEL: @cacop_d(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    tail call void @llvm.loongarch.cacop.d(i64 1, i64 [[A:%.*]], i64 1024)
+// CHECK-NEXT:    tail call void @llvm.loongarch.cacop.d(i64 1, i64 [[A]], i64 1024)
+// CHECK-NEXT:    ret void
+//
+void cacop_d(unsigned long int a) {
+  __cacop_d(1, a, 1024);
+  __builtin_loongarch_cacop_d(1, a, 1024);
+}
+
 // CHECK-LABEL: @crc_w_d_w(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[TMP0:%.*]] = tail call i32 @llvm.loongarch.crc.w.d.w(i64 [[A:%.*]], i32 [[B:%.*]])
index 8abc68b9ee7443f3a3c6de9d7cbddc2f62b2a65b..027d8cdb70cf42b575ecad44da1eaa25feb889e9 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clang --target=loongarch32 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=LA32
 // RUN: %clang --target=loongarch64 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=LA64
 
-// LA32-NOT: "target-features"=
+// LA32: "target-features"="+32bit"
 // LA64: "target-features"="+64bit,+d,+f"
 
 int foo(void) {
index 995e357d3186ff9631d9852cf5a1fba6a465201c..5edce3c529e1217514b67f6ef3f1d4a9d8a8fc61 100644 (file)
@@ -52,6 +52,10 @@ defm int_loongarch_masked_cmpxchg : MaskedAtomicRMWFiveOpIntrinsics;
 // LoongArch BASE
 
 def int_loongarch_break : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
+def int_loongarch_cacop_d : Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_i64_ty],
+    [ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<2>>]>;
+def int_loongarch_cacop_w : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+    [ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<2>>]>;
 def int_loongarch_dbar : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
 def int_loongarch_ibar : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
 def int_loongarch_movfcsr2gr : Intrinsic<[llvm_i32_ty], [llvm_i32_ty],
index 55d5af82587c146197f924b10944c6e33d909265..b5e5374a5e76b6a596907b1175fa6bdc3b5f6baa 100644 (file)
@@ -746,13 +746,38 @@ SDValue LoongArchTargetLowering::lowerINTRINSIC_VOID(SDValue Op,
   SDLoc DL(Op);
   MVT GRLenVT = Subtarget.getGRLenVT();
   SDValue Op0 = Op.getOperand(0);
+  uint64_t IntrinsicEnum = Op.getConstantOperandVal(1);
   SDValue Op2 = Op.getOperand(2);
   const StringRef ErrorMsgOOR = "out of range";
 
-  switch (Op.getConstantOperandVal(1)) {
+  switch (IntrinsicEnum) {
   default:
     // TODO: Add more Intrinsics.
     return SDValue();
+  case Intrinsic::loongarch_cacop_d:
+  case Intrinsic::loongarch_cacop_w: {
+    if (IntrinsicEnum == Intrinsic::loongarch_cacop_d && !Subtarget.is64Bit()) {
+      DAG.getContext()->emitError(
+          "llvm.loongarch.cacop.d requires target: loongarch64");
+      return Op.getOperand(0);
+    }
+    if (IntrinsicEnum == Intrinsic::loongarch_cacop_w && Subtarget.is64Bit()) {
+      DAG.getContext()->emitError(
+          "llvm.loongarch.cacop.w requires target: loongarch32");
+      return Op.getOperand(0);
+    }
+    // call void @llvm.loongarch.cacop.[d/w](uimm5, rj, simm12)
+    unsigned Imm1 = cast<ConstantSDNode>(Op2)->getZExtValue();
+    if (!isUInt<5>(Imm1))
+      return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG);
+    SDValue Op4 = Op.getOperand(4);
+    int Imm2 = cast<ConstantSDNode>(Op4)->getSExtValue();
+    if (!isInt<12>(Imm2))
+      return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG);
+
+    return Op;
+  }
+
   case Intrinsic::loongarch_dbar: {
     unsigned Imm = cast<ConstantSDNode>(Op2)->getZExtValue();
     if (!isUInt<15>(Imm))
@@ -1778,6 +1803,8 @@ const char *LoongArchTargetLowering::getTargetNodeName(unsigned Opcode) const {
     NODE_NAME_CASE(CPUCFG)
     NODE_NAME_CASE(MOVGR2FCSR)
     NODE_NAME_CASE(MOVFCSR2GR)
+    NODE_NAME_CASE(CACOP_D)
+    NODE_NAME_CASE(CACOP_W)
   }
 #undef NODE_NAME_CASE
   return nullptr;
index 9227131461d399ef34026f276b6d0e4f8529443e..976aae3bb6ceedebfe7992c6c0fd8e8b5f61c8aa 100644 (file)
@@ -61,8 +61,10 @@ enum NodeType : unsigned {
   BITREV_4B,
   BITREV_W,
 
-  // Intrinsic operations
+  // Intrinsic operations start ============================================
   BREAK,
+  CACOP_D,
+  CACOP_W,
   DBAR,
   IBAR,
   SYSCALL,
@@ -93,6 +95,7 @@ enum NodeType : unsigned {
 
   // Read CPU configuration information operation
   CPUCFG,
+  // Intrinsic operations end =============================================
 };
 } // end namespace LoongArchISD
 
index b4a8ad8991757b538e4997eeb3bda72313cc9e99..8e552b23b65d7ff1bef1371db7c7bf0062b4d71e 100644 (file)
@@ -589,6 +589,10 @@ def RDTIMEL_W : RDTIME_2R<0b0000000000000000011000, "rdtimel.w">;
 def RDTIMEH_W : RDTIME_2R<0b0000000000000000011001, "rdtimeh.w">;
 def CPUCFG : ALU_2R<0b0000000000000000011011, "cpucfg">;
 
+// Cache Maintenance Instructions
+def CACOP : FmtCACOP<(outs), (ins uimm5:$op, GPR:$rj, simm12:$imm12), "cacop",
+                     "$op, $rj, $imm12">;
+
 /// LA64 instructions
 
 let Predicates = [IsLA64] in {
@@ -1563,6 +1567,10 @@ defm : PseudoBinPat<"atomic_load_xor_32", PseudoAtomicLoadXor32>;
 
 /// Intrinsics
 
+def : Pat<(int_loongarch_cacop_d timm:$op, i64:$rj, timm:$imm12),
+          (CACOP uimm5:$op, GPR:$rj, simm12:$imm12)>;
+def : Pat<(int_loongarch_cacop_w i32:$op, i32:$rj, i32:$imm12),
+          (CACOP uimm5:$op, GPR:$rj, simm12:$imm12)>;
 def : Pat<(loongarch_dbar uimm15:$imm15), (DBAR uimm15:$imm15)>;
 def : Pat<(loongarch_ibar uimm15:$imm15), (IBAR uimm15:$imm15)>;
 def : Pat<(loongarch_break uimm15:$imm15), (BREAK uimm15:$imm15)>;
@@ -1673,10 +1681,6 @@ def IOCSRRD_D : IOCSRRD<0b0000011001001000000011, "iocsrrd.d">;
 def IOCSRWR_D : IOCSRWR<0b0000011001001000000111, "iocsrwr.d">;
 } // Predicates = [IsLA64]
 
-// Cache Maintenance Instructions
-def CACOP : FmtCACOP<(outs), (ins uimm5:$op, GPR:$rj, simm12:$imm12), "cacop",
-                     "$op, $rj, $imm12">;
-
 // TLB Maintenance Instructions
 def TLBSRCH  : FmtI32<0b00000110010010000010100000000000, "tlbsrch">;
 def TLBRD    : FmtI32<0b00000110010010000010110000000000, "tlbrd">;
index c4910ccad4d56319770382f1f65172c574f6a835..4b38e6ca10906eea8e3ac03b0b17065b7ee939a9 100644 (file)
@@ -1,5 +1,6 @@
 ; RUN: not llc --mtriple=loongarch32 --disable-verify < %s 2>&1 | FileCheck %s
 
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
 declare i32 @llvm.loongarch.crc.w.b.w(i32, i32)
 declare i32 @llvm.loongarch.crc.w.h.w(i32, i32)
 declare i32 @llvm.loongarch.crc.w.w.w(i32, i32)
@@ -18,6 +19,34 @@ declare void @llvm.loongarch.asrtgt.d(i64, i64)
 declare i64 @llvm.loongarch.lddir.d(i64, i32)
 declare void @llvm.loongarch.ldpte.d(i64, i32)
 
+define void @cacop_arg0_out_of_hi_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+  call void @llvm.loongarch.cacop.w(i32 32, i32 %a, i32 1024)
+  ret void
+}
+
+define void @cacop_arg0_out_of_lo_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+  call void @llvm.loongarch.cacop.w(i32 -1, i32 %a, i32 1024)
+  ret void
+}
+
+define void @cacop_arg2_out_of_hi_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+  call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4096)
+  ret void
+}
+
+define void @cacop_arg2_out_of_lo_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+  call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 -4096)
+  ret void
+}
+
 define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind {
 ; CHECK: llvm.loongarch.crc.w.b.w requires target: loongarch64
 entry:
diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la32.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la32.ll
new file mode 100644 (file)
index 0000000..37e0902
--- /dev/null
@@ -0,0 +1,13 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 < %s | FileCheck %s
+
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
+
+define void @cacop_w(i32 %a) nounwind {
+; CHECK-LABEL: cacop_w:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cacop 1, $a0, 4
+; CHECK-NEXT:    ret
+  call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4)
+  ret void
+}
index 05eb9f313e24ffa9e02f9119e9f786a63dc44537..51f6c445309ab794c1f74ad602efe7495b7390fe 100644 (file)
@@ -1,6 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s
 
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
+declare void @llvm.loongarch.cacop.d(i64, i64, i64)
 declare i64 @llvm.loongarch.csrrd.d(i32 immarg)
 declare i64 @llvm.loongarch.csrwr.d(i64, i32 immarg)
 declare i64 @llvm.loongarch.csrxchg.d(i64, i64, i32 immarg)
@@ -46,3 +48,37 @@ entry:
   %0 = call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 -1)
   ret i64 %0
 }
+
+define void @cacop_w(i32 %a) nounwind {
+; CHECK: llvm.loongarch.cacop.w requires target: loongarch32
+  call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4)
+  ret void
+}
+
+define void @cacop_arg0_out_of_hi_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+  call void @llvm.loongarch.cacop.d(i64 32, i64 %a, i64 1024)
+  ret void
+}
+
+define void @cacop_arg0_out_of_lo_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+  call void @llvm.loongarch.cacop.d(i64 -1, i64 %a, i64 1024)
+  ret void
+}
+
+define void @cacop_arg2_out_of_hi_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+  call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 4096)
+  ret void
+}
+
+define void @cacop_arg2_out_of_lo_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+  call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 -4096)
+  ret void
+}
index 1ec55f341629b6754cb50fdc914ff1b2c30c7b22..7b28682b5aa970c3627e9bd753a76922c7b626f6 100644 (file)
@@ -1,6 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s
 
+declare void @llvm.loongarch.cacop.d(i64, i64, i64)
 declare i32 @llvm.loongarch.crc.w.b.w(i32, i32)
 declare i32 @llvm.loongarch.crc.w.h.w(i32, i32)
 declare i32 @llvm.loongarch.crc.w.w.w(i32, i32)
@@ -46,6 +47,15 @@ define i32 @crc_w_w_w(i32 %a, i32 %b) nounwind {
   ret i32 %res
 }
 
+define void @cacop_d(i64 %a) nounwind {
+; CHECK-LABEL: cacop_d:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    cacop 1, $a0, 4
+; CHECK-NEXT:    ret
+  call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 4)
+  ret void
+}
+
 define i32 @crc_w_d_w(i64 %a, i32 %b) nounwind {
 ; CHECK-LABEL: crc_w_d_w:
 ; CHECK:       # %bb.0: