diff --git a/clang/include/clang/Basic/BuiltinsLoongArch.def b/clang/include/clang/Basic/BuiltinsLoongArch.def --- a/clang/include/clang/Basic/BuiltinsLoongArch.def +++ b/clang/include/clang/Basic/BuiltinsLoongArch.def @@ -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") diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11764,4 +11764,6 @@ def warn_unsafe_buffer_variable : Warning< "variable %0 participates in unchecked buffer operations">, InGroup, DefaultIgnore; +def err_loongarch_builtin_requires_la32 : Error< + "this builtin requires target: loongarch32">; } // end of sema component. diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp --- a/clang/lib/Basic/Targets/LoongArch.cpp +++ b/clang/lib/Basic/Targets/LoongArch.cpp @@ -179,6 +179,8 @@ const std::vector &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); } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19694,6 +19694,12 @@ 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; diff --git a/clang/lib/Headers/larchintrin.h b/clang/lib/Headers/larchintrin.h --- a/clang/lib/Headers/larchintrin.h +++ b/clang/lib/Headers/larchintrin.h @@ -106,6 +106,16 @@ #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)) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3705,6 +3705,23 @@ 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: diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c b/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c --- a/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c @@ -3,6 +3,14 @@ #include +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]}} diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la32.c b/clang/test/CodeGen/LoongArch/intrinsic-la32.c --- a/clang/test/CodeGen/LoongArch/intrinsic-la32.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la32.c @@ -200,3 +200,14 @@ __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); +} diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la64.c b/clang/test/CodeGen/LoongArch/intrinsic-la64.c --- a/clang/test/CodeGen/LoongArch/intrinsic-la64.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la64.c @@ -123,6 +123,17 @@ 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:%.*]]) diff --git a/clang/test/Driver/loongarch-default-features.c b/clang/test/Driver/loongarch-default-features.c --- a/clang/test/Driver/loongarch-default-features.c +++ b/clang/test/Driver/loongarch-default-features.c @@ -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) { diff --git a/llvm/include/llvm/IR/IntrinsicsLoongArch.td b/llvm/include/llvm/IR/IntrinsicsLoongArch.td --- a/llvm/include/llvm/IR/IntrinsicsLoongArch.td +++ b/llvm/include/llvm/IR/IntrinsicsLoongArch.td @@ -52,6 +52,10 @@ // LoongArch BASE def int_loongarch_break : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; +def int_loongarch_cacop_d : Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_i64_ty], + [ImmArg>, ImmArg>]>; +def int_loongarch_cacop_w : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; def int_loongarch_dbar : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; def int_loongarch_ibar : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; def int_loongarch_movfcsr2gr : Intrinsic<[llvm_i32_ty], [llvm_i32_ty], diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h @@ -61,8 +61,10 @@ BITREV_4B, BITREV_W, - // Intrinsic operations + // Intrinsic operations start ============================================ BREAK, + CACOP_D, + CACOP_W, DBAR, IBAR, SYSCALL, @@ -93,6 +95,7 @@ // Read CPU configuration information operation CPUCFG, + // Intrinsic operations end ============================================= }; } // end namespace LoongArchISD diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -746,13 +746,38 @@ 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(Op2)->getZExtValue(); + if (!isUInt<5>(Imm1)) + return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG); + SDValue Op4 = Op.getOperand(4); + int Imm2 = cast(Op4)->getSExtValue(); + if (!isInt<12>(Imm2)) + return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG); + + return Op; + } + case Intrinsic::loongarch_dbar: { unsigned Imm = cast(Op2)->getZExtValue(); if (!isUInt<15>(Imm)) @@ -1778,6 +1803,8 @@ 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; diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -589,6 +589,10 @@ 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 @@ /// 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 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">; diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll @@ -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 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 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la32.ll @@ -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 +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll @@ -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 @@ %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 +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll @@ -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 @@ 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: