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 @@ -31,5 +31,12 @@ TARGET_BUILTIN(__builtin_loongarch_crcc_w_w_w, "iii", "nc", "64bit") TARGET_BUILTIN(__builtin_loongarch_crcc_w_d_w, "iLii", "nc", "64bit") +TARGET_BUILTIN(__builtin_loongarch_csrrd_w, "UiIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_csrrd_d, "ULiIUi", "nc", "64bit") +TARGET_BUILTIN(__builtin_loongarch_csrwr_w, "UiUiIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_csrwr_d, "ULiULiIUi", "nc", "64bit") +TARGET_BUILTIN(__builtin_loongarch_csrxchg_w, "UiUiUiIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_csrxchg_d, "ULiULiULiIUi", "nc", "64bit") + #undef BUILTIN #undef TARGET_BUILTIN 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,24 @@ case LoongArch::BI__builtin_loongarch_crcc_w_d_w: ID = Intrinsic::loongarch_crcc_w_d_w; break; + case LoongArch::BI__builtin_loongarch_csrrd_w: + ID = Intrinsic::loongarch_csrrd_w; + break; + case LoongArch::BI__builtin_loongarch_csrwr_w: + ID = Intrinsic::loongarch_csrwr_w; + break; + case LoongArch::BI__builtin_loongarch_csrxchg_w: + ID = Intrinsic::loongarch_csrxchg_w; + break; + case LoongArch::BI__builtin_loongarch_csrrd_d: + ID = Intrinsic::loongarch_csrrd_d; + break; + case LoongArch::BI__builtin_loongarch_csrwr_d: + ID = Intrinsic::loongarch_csrwr_d; + break; + case LoongArch::BI__builtin_loongarch_csrxchg_d: + ID = Intrinsic::loongarch_csrxchg_d; + break; // TODO: Support more Intrinsics. } 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 @@ -72,6 +72,29 @@ #define __syscall(/*ui15*/ _1) __builtin_loongarch_syscall((_1)) +#define __csrrd_w(/*ui14*/ _1) ((unsigned int)__builtin_loongarch_csrrd_w((_1))) + +#define __csrwr_w(/*unsigned int*/ _1, /*ui14*/ _2) \ + ((unsigned int)__builtin_loongarch_csrwr_w((unsigned int)(_1), (_2))) + +#define __csrxchg_w(/*unsigned int*/ _1, /*unsigned int*/ _2, /*ui14*/ _3) \ + ((unsigned int)__builtin_loongarch_csrxchg_w((unsigned int)(_1), \ + (unsigned int)(_2), (_3))) + +#if __loongarch_grlen == 64 +#define __csrrd_d(/*ui14*/ _1) \ + ((unsigned long int)__builtin_loongarch_csrrd_d((_1))) + +#define __csrwr_d(/*unsigned long int*/ _1, /*ui14*/ _2) \ + ((unsigned long int)__builtin_loongarch_csrwr_d((unsigned long int)(_1), \ + (_2))) + +#define __csrxchg_d(/*unsigned long int*/ _1, /*unsigned long int*/ _2, \ + /*ui14*/ _3) \ + ((unsigned long int)__builtin_loongarch_csrxchg_d( \ + (unsigned long int)(_1), (unsigned long int)(_2), (_3))) +#endif + #ifdef __cplusplus } #endif 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 @@ -3723,6 +3723,30 @@ case LoongArch::BI__builtin_loongarch_syscall: // Check if immediate is in [0, 32767]. return SemaBuiltinConstantArgRange(TheCall, 0, 0, 32767); + case LoongArch::BI__builtin_loongarch_csrrd_w: + return SemaBuiltinConstantArgRange(TheCall, 0, 0, 16383); + case LoongArch::BI__builtin_loongarch_csrwr_w: + return SemaBuiltinConstantArgRange(TheCall, 1, 0, 16383); + case LoongArch::BI__builtin_loongarch_csrxchg_w: + return SemaBuiltinConstantArgRange(TheCall, 2, 0, 16383); + case LoongArch::BI__builtin_loongarch_csrrd_d: + if (!TI.hasFeature("64bit")) + return Diag(TheCall->getBeginLoc(), + diag::err_loongarch_builtin_requires_la64) + << TheCall->getSourceRange(); + return SemaBuiltinConstantArgRange(TheCall, 0, 0, 16383); + case LoongArch::BI__builtin_loongarch_csrwr_d: + if (!TI.hasFeature("64bit")) + return Diag(TheCall->getBeginLoc(), + diag::err_loongarch_builtin_requires_la64) + << TheCall->getSourceRange(); + return SemaBuiltinConstantArgRange(TheCall, 1, 0, 16383); + case LoongArch::BI__builtin_loongarch_csrxchg_d: + if (!TI.hasFeature("64bit")) + return Diag(TheCall->getBeginLoc(), + diag::err_loongarch_builtin_requires_la64) + << TheCall->getSourceRange(); + return SemaBuiltinConstantArgRange(TheCall, 2, 0, 16383); } return false; diff --git a/clang/test/CodeGen/LoongArch/intrinsic-error-la64.c b/clang/test/CodeGen/LoongArch/intrinsic-error-la64.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/LoongArch/intrinsic-error-la64.c @@ -0,0 +1,22 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -triple loongarch64 -emit-llvm -S -verify %s -o /dev/null + +#include + +void csrrd_d(int a) { + __builtin_loongarch_csrrd_d(16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrrd_d(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrrd_d(a); // expected-error {{argument to '__builtin_loongarch_csrrd_d' must be a constant integer}} +} + +void csrwr_d(unsigned long int a) { + __builtin_loongarch_csrwr_d(a, 16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrwr_d(a, -1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrwr_d(a, a); // expected-error {{argument to '__builtin_loongarch_csrwr_d' must be a constant integer}} +} + +void csrxchg_d(unsigned long int a, unsigned long int b) { + __builtin_loongarch_csrxchg_d(a, b, 16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrxchg_d(a, b, -1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrxchg_d(a, b, b); // expected-error {{argument to '__builtin_loongarch_csrxchg_d' must be a constant integer}} +} diff --git a/clang/test/CodeGen/LoongArch/intrinsic-error.c b/clang/test/CodeGen/LoongArch/intrinsic-error.c --- a/clang/test/CodeGen/LoongArch/intrinsic-error.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-error.c @@ -57,3 +57,33 @@ int crcc_w_d_w(long int a, int b) { return __builtin_loongarch_crcc_w_d_w(a, b); // expected-error {{this builtin requires target: loongarch64}} } + +unsigned long int csrrd_d() { + return __builtin_loongarch_csrrd_d(1); // expected-error {{this builtin requires target: loongarch64}} +} + +unsigned long int csrwr_d(unsigned long int a) { + return __builtin_loongarch_csrwr_d(a, 1); // expected-error {{this builtin requires target: loongarch64}} +} + +unsigned long int csrxchg_d(unsigned long int a, unsigned long int b) { + return __builtin_loongarch_csrxchg_d(a, b, 1); // expected-error {{this builtin requires target: loongarch64}} +} + +void csrrd_w(int a) { + __builtin_loongarch_csrrd_w(16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrrd_w(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrrd_w(a); // expected-error {{argument to '__builtin_loongarch_csrrd_w' must be a constant integer}} +} + +void csrwr_w(unsigned int a) { + __builtin_loongarch_csrwr_w(a, 16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrwr_w(a, -1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrwr_w(a, a); // expected-error {{argument to '__builtin_loongarch_csrwr_w' must be a constant integer}} +} + +void csrxchg_w(unsigned int a, unsigned int b) { + __builtin_loongarch_csrxchg_w(a, b, 16384); // expected-error {{argument value 16384 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrxchg_w(a, b, -1); // expected-error {{argument value 4294967295 is outside the valid range [0, 16383]}} + __builtin_loongarch_csrxchg_w(a, b, b); // expected-error {{argument to '__builtin_loongarch_csrxchg_w' must be a constant integer}} +} 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 @@ -74,3 +74,39 @@ int crcc_w_d_w(long int a, int b) { return __builtin_loongarch_crcc_w_d_w(a, b); } + +// CHECK-LABEL: @csrrd_d( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.loongarch.csrrd.d(i32 1) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.loongarch.csrrd.d(i32 1) +// CHECK-NEXT: ret i64 0 +// +unsigned long int csrrd_d() { + unsigned long int a = __csrrd_d(1); + unsigned long int b = __builtin_loongarch_csrrd_d(1); + return 0; +} + +// CHECK-LABEL: @csrwr_d( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.loongarch.csrwr.d(i64 [[A:%.*]], i32 1) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.loongarch.csrwr.d(i64 [[A]], i32 1) +// CHECK-NEXT: ret i64 0 +// +unsigned long int csrwr_d(unsigned long int a) { + unsigned long int b = __csrwr_d(a, 1); + unsigned long int c = __builtin_loongarch_csrwr_d(a, 1); + return 0; +} + +// CHECK-LABEL: @csrxchg_d( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.loongarch.csrxchg.d(i64 [[A:%.*]], i64 [[B:%.*]], i32 1) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.loongarch.csrxchg.d(i64 [[A]], i64 [[B]], i32 1) +// CHECK-NEXT: ret i64 0 +// +unsigned long int csrxchg_d(unsigned long int a, unsigned long int b) { + unsigned long int c = __csrxchg_d(a, b, 1); + unsigned long int d = __builtin_loongarch_csrxchg_d(a, b, 1); + return 0; +} diff --git a/clang/test/CodeGen/LoongArch/intrinsic.c b/clang/test/CodeGen/LoongArch/intrinsic.c --- a/clang/test/CodeGen/LoongArch/intrinsic.c +++ b/clang/test/CodeGen/LoongArch/intrinsic.c @@ -61,3 +61,105 @@ void syscall() { __builtin_loongarch_syscall(1); } + +// LA32-LABEL: @csrrd_w( +// LA32-NEXT: entry: +// LA32-NEXT: [[A:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[TMP0:%.*]] = call i32 @llvm.loongarch.csrrd.w(i32 1) +// LA32-NEXT: store i32 [[TMP0]], ptr [[A]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = call i32 @llvm.loongarch.csrrd.w(i32 1) +// LA32-NEXT: store i32 [[TMP1]], ptr [[B]], align 4 +// LA32-NEXT: ret i32 0 +// +// LA64-LABEL: @csrrd_w( +// LA64-NEXT: entry: +// LA64-NEXT: [[A:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[B:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[TMP0:%.*]] = call i32 @llvm.loongarch.csrrd.w(i32 1) +// LA64-NEXT: store i32 [[TMP0]], ptr [[A]], align 4 +// LA64-NEXT: [[TMP1:%.*]] = call i32 @llvm.loongarch.csrrd.w(i32 1) +// LA64-NEXT: store i32 [[TMP1]], ptr [[B]], align 4 +// LA64-NEXT: ret i32 0 +// +unsigned int csrrd_w() { + unsigned int a = __csrrd_w(1); + unsigned int b = __builtin_loongarch_csrrd_w(1); + return 0; +} + +// LA32-LABEL: @csrwr_w( +// LA32-NEXT: entry: +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[C:%.*]] = alloca i32, align 4 +// LA32-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = call i32 @llvm.loongarch.csrwr.w(i32 [[TMP0]], i32 1) +// LA32-NEXT: store i32 [[TMP1]], ptr [[B]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP3:%.*]] = call i32 @llvm.loongarch.csrwr.w(i32 [[TMP2]], i32 1) +// LA32-NEXT: store i32 [[TMP3]], ptr [[C]], align 4 +// LA32-NEXT: ret i32 0 +// +// LA64-LABEL: @csrwr_w( +// LA64-NEXT: entry: +// LA64-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[B:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[C:%.*]] = alloca i32, align 4 +// LA64-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA64-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA64-NEXT: [[TMP1:%.*]] = call i32 @llvm.loongarch.csrwr.w(i32 [[TMP0]], i32 1) +// LA64-NEXT: store i32 [[TMP1]], ptr [[B]], align 4 +// LA64-NEXT: [[TMP2:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA64-NEXT: [[TMP3:%.*]] = call i32 @llvm.loongarch.csrwr.w(i32 [[TMP2]], i32 1) +// LA64-NEXT: store i32 [[TMP3]], ptr [[C]], align 4 +// LA64-NEXT: ret i32 0 +// +unsigned int csrwr_w(unsigned int a) { + unsigned int b = __csrwr_w(a, 1); + unsigned int c = __builtin_loongarch_csrwr_w(a, 1); + return 0; +} + +// LA32-LABEL: @csrxchg_w( +// LA32-NEXT: entry: +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[C:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[D:%.*]] = alloca i32, align 4 +// LA32-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA32-NEXT: store i32 [[B:%.*]], ptr [[B_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = call i32 @llvm.loongarch.csrxchg.w(i32 [[TMP0]], i32 [[TMP1]], i32 1) +// LA32-NEXT: store i32 [[TMP2]], ptr [[C]], align 4 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP4:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: [[TMP5:%.*]] = call i32 @llvm.loongarch.csrxchg.w(i32 [[TMP3]], i32 [[TMP4]], i32 1) +// LA32-NEXT: store i32 [[TMP5]], ptr [[D]], align 4 +// LA32-NEXT: ret i32 0 +// +// LA64-LABEL: @csrxchg_w( +// LA64-NEXT: entry: +// LA64-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[C:%.*]] = alloca i32, align 4 +// LA64-NEXT: [[D:%.*]] = alloca i32, align 4 +// LA64-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA64-NEXT: store i32 [[B:%.*]], ptr [[B_ADDR]], align 4 +// LA64-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA64-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA64-NEXT: [[TMP2:%.*]] = call i32 @llvm.loongarch.csrxchg.w(i32 [[TMP0]], i32 [[TMP1]], i32 1) +// LA64-NEXT: store i32 [[TMP2]], ptr [[C]], align 4 +// LA64-NEXT: [[TMP3:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA64-NEXT: [[TMP4:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA64-NEXT: [[TMP5:%.*]] = call i32 @llvm.loongarch.csrxchg.w(i32 [[TMP3]], i32 [[TMP4]], i32 1) +// LA64-NEXT: store i32 [[TMP5]], ptr [[D]], align 4 +// LA64-NEXT: ret i32 0 +// +unsigned int csrxchg_w(unsigned int a, unsigned int b) { + unsigned int c = __csrxchg_w(a, b, 1); + unsigned int d = __builtin_loongarch_csrxchg_w(a, b, 1); + return 0; +} 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 @@ -73,4 +73,23 @@ [llvm_i32_ty, llvm_i32_ty]>; def int_loongarch_crcc_w_d_w : Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty]>; + +def int_loongarch_csrrd_w : Intrinsic<[llvm_i32_ty], [llvm_i32_ty], + [ImmArg>]>; +def int_loongarch_csrrd_d : Intrinsic<[llvm_i64_ty], [llvm_i32_ty], + [ImmArg>]>; +def int_loongarch_csrwr_w : Intrinsic<[llvm_i32_ty], + [llvm_i32_ty, llvm_i32_ty], + [ImmArg>]>; +def int_loongarch_csrwr_d : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty], + [ImmArg>]>; +def int_loongarch_csrxchg_w : Intrinsic<[llvm_i32_ty], + [llvm_i32_ty, llvm_i32_ty, + llvm_i32_ty], + [ImmArg>]>; +def int_loongarch_csrxchg_d : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i64_ty, + llvm_i32_ty], + [ImmArg>]>; } // TargetPrefix = "loongarch" 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 @@ -73,7 +73,11 @@ CRCC_W_B_W, CRCC_W_H_W, CRCC_W_W_W, - CRCC_W_D_W + CRCC_W_D_W, + + CSRRD, + CSRWR, + CSRXCHG, }; } // 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 @@ -63,6 +63,7 @@ setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); setOperationAction(ISD::TRAP, MVT::Other, Legal); setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); setOperationAction({ISD::GlobalAddress, ISD::BlockAddress, ISD::ConstantPool, ISD::JumpTable}, @@ -591,9 +592,25 @@ } } +// Helper function that emits error message for intrinsics with chain. +static SDValue emitIntrinsicWithChainErrorMessage(SDValue Op, + StringRef ErrorMsg, + SelectionDAG &DAG) { + + DAG.getContext()->emitError("argument to '" + Op->getOperationName(0) + "' " + + ErrorMsg); + return DAG.getMergeValues({DAG.getUNDEF(Op.getValueType()), Op.getOperand(0)}, + SDLoc(Op)); +} + SDValue LoongArchTargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op, SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT GRLenVT = Subtarget.getGRLenVT(); + SDValue Op0 = Op.getOperand(0); + std::string Name = Op->getOperationName(0); + const StringRef ErrorMsgOOR = "out of range"; switch (Op.getConstantOperandVal(1)) { default: @@ -608,8 +625,40 @@ case Intrinsic::loongarch_crcc_w_d_w: { std::string Name = Op->getOperationName(0); DAG.getContext()->emitError(Name + " requires target: loongarch64"); + return DAG.getMergeValues({DAG.getUNDEF(Op.getValueType()), Op0}, DL); + } + case Intrinsic::loongarch_csrrd_w: + case Intrinsic::loongarch_csrrd_d: { + unsigned Imm = cast(Op.getOperand(2))->getZExtValue(); + if (!isUInt<14>(Imm)) + return emitIntrinsicWithChainErrorMessage(Op, ErrorMsgOOR, DAG); + return DAG.getMergeValues( + {DAG.getNode(LoongArchISD::CSRRD, DL, GRLenVT, Op0, + DAG.getConstant(Imm, DL, GRLenVT)), + Op0}, + DL); + } + case Intrinsic::loongarch_csrwr_w: + case Intrinsic::loongarch_csrwr_d: { + unsigned Imm = cast(Op.getOperand(3))->getZExtValue(); + if (!isUInt<14>(Imm)) + return emitIntrinsicWithChainErrorMessage(Op, ErrorMsgOOR, DAG); return DAG.getMergeValues( - {DAG.getUNDEF(Op.getValueType()), Op.getOperand(0)}, SDLoc(Op)); + {DAG.getNode(LoongArchISD::CSRWR, DL, GRLenVT, Op0, Op.getOperand(2), + DAG.getConstant(Imm, DL, GRLenVT)), + Op0}, + DL); + } + case Intrinsic::loongarch_csrxchg_w: + case Intrinsic::loongarch_csrxchg_d: { + unsigned Imm = cast(Op.getOperand(4))->getZExtValue(); + if (!isUInt<14>(Imm)) + return emitIntrinsicWithChainErrorMessage(Op, ErrorMsgOOR, DAG); + return DAG.getMergeValues( + {DAG.getNode(LoongArchISD::CSRXCHG, DL, GRLenVT, Op0, Op.getOperand(2), + Op.getOperand(3), DAG.getConstant(Imm, DL, GRLenVT)), + Op0}, + DL); } } } @@ -939,10 +988,10 @@ break; } case ISD::INTRINSIC_W_CHAIN: { - assert(VT == MVT::i32 && Subtarget.is64Bit() && - "Unexpected custom legalisation"); + SDValue Op0 = N->getOperand(0); SDValue Op2 = N->getOperand(2); - SDValue Op3 = N->getOperand(3); + MVT GRLenVT = Subtarget.getGRLenVT(); + std::string Name = N->getOperationName(0); switch (N->getConstantOperandVal(1)) { default: @@ -951,9 +1000,10 @@ case Intrinsic::loongarch_##NAME: { \ Results.push_back(DAG.getNode( \ ISD::TRUNCATE, DL, VT, \ - DAG.getNode(LoongArchISD::NODE, DL, MVT::i64, \ - DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2), \ - DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op3)))); \ + DAG.getNode( \ + LoongArchISD::NODE, DL, MVT::i64, \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2), \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(3))))); \ Results.push_back(N->getOperand(0)); \ break; \ } @@ -966,15 +1016,80 @@ #define CRC_CASE_EXT_UNARYOP(NAME, NODE) \ case Intrinsic::loongarch_##NAME: { \ - Results.push_back(DAG.getNode( \ - ISD::TRUNCATE, DL, VT, \ - DAG.getNode(LoongArchISD::NODE, DL, MVT::i64, Op2, \ - DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op3)))); \ + Results.push_back( \ + DAG.getNode(ISD::TRUNCATE, DL, VT, \ + DAG.getNode(LoongArchISD::NODE, DL, MVT::i64, Op2, \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, \ + N->getOperand(3))))); \ Results.push_back(N->getOperand(0)); \ break; \ } CRC_CASE_EXT_UNARYOP(crc_w_d_w, CRC_W_D_W) CRC_CASE_EXT_UNARYOP(crcc_w_d_w, CRCC_W_D_W) +#define CSR_CASE(ID) \ + case Intrinsic::loongarch_##ID: { \ + if (!Subtarget.is64Bit()) { \ + DAG.getContext()->emitError(Name + " requires target: loongarch64"); \ + Results.push_back(DAG.getUNDEF(VT)); \ + Results.push_back(N->getOperand(0)); \ + } \ + break; \ + } + CSR_CASE(csrrd_d); + CSR_CASE(csrwr_d); + CSR_CASE(csrxchg_d); + case Intrinsic::loongarch_csrrd_w: { + unsigned Imm = cast(Op2)->getZExtValue(); + if (!isUInt<14>(Imm)) { + DAG.getContext()->emitError("argument to '" + Name + "' out of range"); + Results.push_back(DAG.getUNDEF(VT)); + Results.push_back(N->getOperand(0)); + break; + } + + Results.push_back( + DAG.getNode(ISD::TRUNCATE, DL, VT, + DAG.getNode(LoongArchISD::CSRRD, DL, GRLenVT, Op0, + DAG.getConstant(Imm, DL, GRLenVT)))); + Results.push_back(N->getOperand(0)); + break; + } + case Intrinsic::loongarch_csrwr_w: { + unsigned Imm = cast(N->getOperand(3))->getZExtValue(); + if (!isUInt<14>(Imm)) { + DAG.getContext()->emitError("argument to '" + Name + "' out of range"); + Results.push_back(DAG.getUNDEF(VT)); + Results.push_back(N->getOperand(0)); + break; + } + + Results.push_back(DAG.getNode( + ISD::TRUNCATE, DL, VT, + DAG.getNode(LoongArchISD::CSRWR, DL, GRLenVT, Op0, + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2), + DAG.getConstant(Imm, DL, GRLenVT)))); + Results.push_back(N->getOperand(0)); + break; + } + case Intrinsic::loongarch_csrxchg_w: { + unsigned Imm = cast(N->getOperand(4))->getZExtValue(); + if (!isUInt<14>(Imm)) { + DAG.getContext()->emitError("argument to '" + Name + "' out of range"); + Results.push_back(DAG.getUNDEF(VT)); + Results.push_back(N->getOperand(0)); + break; + } + + Results.push_back(DAG.getNode( + ISD::TRUNCATE, DL, VT, + DAG.getNode( + LoongArchISD::CSRXCHG, DL, GRLenVT, Op0, + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2), + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(3)), + DAG.getConstant(Imm, DL, GRLenVT)))); + Results.push_back(N->getOperand(0)); + break; + } } break; } @@ -1456,6 +1571,9 @@ NODE_NAME_CASE(CRCC_W_H_W) NODE_NAME_CASE(CRCC_W_W_W) NODE_NAME_CASE(CRCC_W_D_W) + NODE_NAME_CASE(CSRRD) + NODE_NAME_CASE(CSRWR) + NODE_NAME_CASE(CSRXCHG) } #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 @@ -38,6 +38,15 @@ // "VI" means no output and an integer input. def SDT_LoongArchVI : SDTypeProfile<0, 1, [SDTCisVT<0, GRLenVT>]>; +def SDT_LoongArchCsrrd : SDTypeProfile<1, 1, [SDTCisInt<0>, + SDTCisVT<1, GRLenVT>]>; +def SDT_LoongArchCsrwr : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, GRLenVT>]>; +def SDT_LoongArchCsrxchg : SDTypeProfile<1, 3, [SDTCisInt<0>, + SDTCisSameAs<0, 1>, + SDTCisSameAs<0, 2>, + SDTCisVT<3, GRLenVT>]>; + // TODO: Add LoongArch specific DAG Nodes // Target-independent nodes, but with target-specific formats. def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_CallSeqStart, @@ -93,6 +102,13 @@ [SDNPHasChain, SDNPSideEffect]>; def loongarch_syscall : SDNode<"LoongArchISD::SYSCALL", SDT_LoongArchVI, [SDNPHasChain, SDNPSideEffect]>; +def loongarch_csrrd : SDNode<"LoongArchISD::CSRRD", SDT_LoongArchCsrrd, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_csrwr : SDNode<"LoongArchISD::CSRWR", SDT_LoongArchCsrwr, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_csrxchg : SDNode<"LoongArchISD::CSRXCHG", + SDT_LoongArchCsrxchg, + [SDNPHasChain, SDNPSideEffect]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. @@ -159,7 +175,8 @@ let ParserMatchClass = UImmAsmOperand<12, "ori">; } -def uimm14 : Operand { +def uimm14 : Operand, + ImmLeaf (Imm);}]> { let ParserMatchClass = UImmAsmOperand<14>; } @@ -1621,3 +1638,13 @@ def ERTN : FmtI32<0b00000110010010000011100000000000, "ertn">; def DBCL : MISC_I15<0b00000000001010101, "dbcl">; def IDLE : MISC_I15<0b00000110010010001, "idle">; + +//===----------------------------------------------------------------------===// +// Privilege Intrinsics +//===----------------------------------------------------------------------===// + +def : Pat<(loongarch_csrrd uimm14:$imm14), (CSRRD uimm14:$imm14)>; +def : Pat<(loongarch_csrwr GPR:$rd, uimm14:$imm14), + (CSRWR GPR:$rd, uimm14:$imm14)>; +def : Pat<(loongarch_csrxchg GPR:$rd, GPR:$rj, uimm14:$imm14), + (CSRXCHG GPR:$rd, GPR:$rj, uimm14:$imm14)>; diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-error.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic-error.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-error.ll @@ -6,6 +6,9 @@ declare void @llvm.loongarch.ibar(i32) declare void @llvm.loongarch.break(i32) declare void @llvm.loongarch.syscall(i32) +declare i32 @llvm.loongarch.csrrd.w(i32 immarg) +declare i32 @llvm.loongarch.csrwr.w(i32, i32 immarg) +declare i32 @llvm.loongarch.csrxchg.w(i32, i32, i32 immarg) define void @dbar_imm_out_of_hi_range() nounwind { ; CHECK: argument to 'llvm.loongarch.dbar' out of range @@ -62,3 +65,45 @@ call void @llvm.loongarch.syscall(i32 -1) ret void } + +define i32 @csrrd_w_imm_out_of_hi_range() nounwind { +; CHECK: argument to 'llvm.loongarch.csrrd.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrrd.w(i32 16384) + ret i32 %0 +} + +define i32 @csrrd_w_imm_out_of_lo_range() nounwind { +; CHECK: argument to 'llvm.loongarch.csrrd.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrrd.w(i32 -1) + ret i32 %0 +} + +define i32 @csrwr_w_imm_out_of_hi_range(i32 %a) nounwind { +; CHECK: argument to 'llvm.loongarch.csrwr.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrwr.w(i32 %a, i32 16384) + ret i32 %0 +} + +define i32 @csrwr_w_imm_out_of_lo_range(i32 %a) nounwind { +; CHECK: argument to 'llvm.loongarch.csrwr.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrwr.w(i32 %a, i32 -1) + ret i32 %0 +} + +define i32 @csrxchg_w_imm_out_of_hi_range(i32 %a, i32 %b) nounwind { +; CHECK: argument to 'llvm.loongarch.csrxchg.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrxchg.w(i32 %a, i32 %b, i32 16384) + ret i32 %0 +} + +define i32 @csrxchg_w_imm_out_of_lo_range(i32 %a, i32 %b) nounwind { +; CHECK: argument to 'llvm.loongarch.csrxchg.w' out of range +entry: + %0 = call i32 @llvm.loongarch.csrxchg.w(i32 %a, i32 %b, i32 -1) + ret i32 %0 +} 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 @@ -8,6 +8,9 @@ declare i32 @llvm.loongarch.crcc.w.h.w(i32, i32) declare i32 @llvm.loongarch.crcc.w.w.w(i32, i32) declare i32 @llvm.loongarch.crcc.w.d.w(i64, i32) +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) define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind { ; CHECK: llvm.loongarch.crc.w.b.w requires target: loongarch64 @@ -64,3 +67,24 @@ %res = call i32 @llvm.loongarch.crcc.w.d.w(i64 %a, i32 %b) ret i32 %res } + +define i64 @csrrd_d() { +; CHECK: llvm.loongarch.csrrd.d requires target: loongarch64 +entry: + %0 = tail call i64 @llvm.loongarch.csrrd.d(i32 1) + ret i64 %0 +} + +define i64 @csrwr_d(i64 %a) { +; CHECK: llvm.loongarch.csrwr.d requires target: loongarch64 +entry: + %0 = tail call i64 @llvm.loongarch.csrwr.d(i64 %a, i32 1) + ret i64 %0 +} + +define i64 @csrxchg_d(i64 %a, i64 %b) { +; CHECK: llvm.loongarch.csrxchg.d requires target: loongarch64 +entry: + %0 = tail call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 1) + ret i64 %0 +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la64-error.ll @@ -0,0 +1,48 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s + +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) + +define i64 @csrrd_d_imm_out_of_hi_range() nounwind { +; CHECK: argument to 'llvm.loongarch.csrrd.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrrd.d(i32 16384) + ret i64 %0 +} + +define i64 @csrrd_d_imm_out_of_lo_range() nounwind { +; CHECK: argument to 'llvm.loongarch.csrrd.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrrd.d(i32 -1) + ret i64 %0 +} + +define i64 @csrwr_d_imm_out_of_hi_range(i64 %a) nounwind { +; CHECK: argument to 'llvm.loongarch.csrwr.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrwr.d(i64 %a, i32 16384) + ret i64 %0 +} + +define i64 @csrwr_d_imm_out_of_lo_range(i64 %a) nounwind { +; CHECK: argument to 'llvm.loongarch.csrwr.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrwr.d(i64 %a, i32 -1) + ret i64 %0 +} + +define i64 @csrxchg_d_imm_out_of_hi_range(i64 %a, i64 %b) nounwind { +; CHECK: argument to 'llvm.loongarch.csrxchg.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 16384) + ret i64 %0 +} + +define i64 @csrxchg_d_imm_out_of_lo_range(i64 %a, i64 %b) nounwind { +; CHECK: argument to 'llvm.loongarch.csrxchg.d' out of range +entry: + %0 = call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 -1) + ret i64 %0 +} 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 @@ -9,6 +9,9 @@ declare i32 @llvm.loongarch.crcc.w.h.w(i32, i32) declare i32 @llvm.loongarch.crcc.w.w.w(i32, i32) declare i32 @llvm.loongarch.crcc.w.d.w(i64, i32) +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) define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind { ; CHECK-LABEL: crc_w_b_w: @@ -81,3 +84,33 @@ %res = call i32 @llvm.loongarch.crcc.w.d.w(i64 %a, i32 %b) ret i32 %res } + +define i64 @csrrd_d() { +; CHECK-LABEL: csrrd_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrrd $a0, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i64 @llvm.loongarch.csrrd.d(i32 1) + ret i64 %0 +} + +define i64 @csrwr_d(i64 %a) { +; CHECK-LABEL: csrwr_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrwr $a0, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i64 @llvm.loongarch.csrwr.d(i64 %a, i32 1) + ret i64 %0 +} + +define i64 @csrxchg_d(i64 %a, i64 %b) { +; CHECK-LABEL: csrxchg_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrxchg $a0, $a1, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 1) + ret i64 %0 +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic.ll b/llvm/test/CodeGen/LoongArch/intrinsic.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic.ll @@ -6,6 +6,9 @@ declare void @llvm.loongarch.ibar(i32) declare void @llvm.loongarch.break(i32) declare void @llvm.loongarch.syscall(i32) +declare i32 @llvm.loongarch.csrrd.w(i32 immarg) +declare i32 @llvm.loongarch.csrwr.w(i32, i32 immarg) +declare i32 @llvm.loongarch.csrxchg.w(i32, i32, i32 immarg) define void @foo() nounwind { ; CHECK-LABEL: foo: @@ -46,3 +49,33 @@ call void @llvm.loongarch.syscall(i32 1) ret void } + +define i32 @csrrd_w() { +; CHECK-LABEL: csrrd_w: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrrd $a0, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.csrrd.w(i32 1) + ret i32 %0 +} + +define i32 @csrwr_w(i32 signext %a) { +; CHECK-LABEL: csrwr_w: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrwr $a0, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.csrwr.w(i32 %a, i32 1) + ret i32 %0 +} + +define i32 @csrxchg_w(i32 signext %a, i32 signext %b) { +; CHECK-LABEL: csrxchg_w: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: csrxchg $a0, $a1, 1 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.csrxchg.w(i32 %a, i32 %b, i32 1) + ret i32 %0 +}