From 048612050a2fc0a89b84632a48da0d86f0d13646 Mon Sep 17 00:00:00 2001 From: gonglingqin Date: Sat, 10 Dec 2022 11:45:18 +0800 Subject: [PATCH] [Clang][LoongArch] Add intrinsic for iocsrrd and iocsrwr These intrinsics are required by Linux [1]. [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/loongarch/include/asm/loongarch.h#n240 Differential Revision: https://reviews.llvm.org/D139612 --- .../include/clang/Basic/BuiltinsLoongArch.def | 9 + clang/lib/CodeGen/CGBuiltin.cpp | 24 +++ clang/lib/Headers/larchintrin.h | 52 ++++++ clang/lib/Sema/SemaChecking.cpp | 2 + .../CodeGen/LoongArch/intrinsic-la32-error.c | 8 + clang/test/CodeGen/LoongArch/intrinsic-la32.c | 154 ++++++++++++++++++ clang/test/CodeGen/LoongArch/intrinsic-la64.c | 94 +++++++++++ llvm/include/llvm/IR/IntrinsicsLoongArch.td | 10 ++ .../LoongArch/LoongArchISelLowering.cpp | 70 ++++++++ .../Target/LoongArch/LoongArchISelLowering.h | 10 ++ .../Target/LoongArch/LoongArchInstrInfo.td | 35 ++++ .../CodeGen/LoongArch/intrinsic-la32-error.ll | 16 ++ llvm/test/CodeGen/LoongArch/intrinsic-la64.ll | 22 +++ llvm/test/CodeGen/LoongArch/intrinsic.ll | 66 ++++++++ 14 files changed, 572 insertions(+) diff --git a/clang/include/clang/Basic/BuiltinsLoongArch.def b/clang/include/clang/Basic/BuiltinsLoongArch.def index fa4aaffa5ff7..5b0dd799a4bc 100644 --- a/clang/include/clang/Basic/BuiltinsLoongArch.def +++ b/clang/include/clang/Basic/BuiltinsLoongArch.def @@ -38,5 +38,14 @@ 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") +TARGET_BUILTIN(__builtin_loongarch_iocsrrd_b, "UiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrrd_h, "UiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrrd_w, "UiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrrd_d, "ULiUi", "nc", "64bit") +TARGET_BUILTIN(__builtin_loongarch_iocsrwr_b, "vUiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrwr_h, "vUiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrwr_w, "vUiUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_iocsrwr_d, "vULiUi", "nc", "64bit") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 9650b9ce978f..be1ce2796c8a 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19718,6 +19718,30 @@ Value *CodeGenFunction::EmitLoongArchBuiltinExpr(unsigned BuiltinID, case LoongArch::BI__builtin_loongarch_csrxchg_d: ID = Intrinsic::loongarch_csrxchg_d; break; + case LoongArch::BI__builtin_loongarch_iocsrrd_b: + ID = Intrinsic::loongarch_iocsrrd_b; + break; + case LoongArch::BI__builtin_loongarch_iocsrrd_h: + ID = Intrinsic::loongarch_iocsrrd_h; + break; + case LoongArch::BI__builtin_loongarch_iocsrrd_w: + ID = Intrinsic::loongarch_iocsrrd_w; + break; + case LoongArch::BI__builtin_loongarch_iocsrrd_d: + ID = Intrinsic::loongarch_iocsrrd_d; + break; + case LoongArch::BI__builtin_loongarch_iocsrwr_b: + ID = Intrinsic::loongarch_iocsrwr_b; + break; + case LoongArch::BI__builtin_loongarch_iocsrwr_h: + ID = Intrinsic::loongarch_iocsrwr_h; + break; + case LoongArch::BI__builtin_loongarch_iocsrwr_w: + ID = Intrinsic::loongarch_iocsrwr_w; + break; + case LoongArch::BI__builtin_loongarch_iocsrwr_d: + ID = Intrinsic::loongarch_iocsrwr_d; + break; // TODO: Support more Intrinsics. } diff --git a/clang/lib/Headers/larchintrin.h b/clang/lib/Headers/larchintrin.h index 02c31657a2bc..787b7b8deda2 100644 --- a/clang/lib/Headers/larchintrin.h +++ b/clang/lib/Headers/larchintrin.h @@ -95,6 +95,58 @@ extern __inline int (unsigned long int)(_1), (unsigned long int)(_2), (_3))) #endif +extern __inline unsigned char + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrrd_b(unsigned int _1) { + return (unsigned char)__builtin_loongarch_iocsrrd_b((unsigned int)_1); +} + +extern __inline unsigned char + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrrd_h(unsigned int _1) { + return (unsigned short)__builtin_loongarch_iocsrrd_h((unsigned int)_1); +} + +extern __inline unsigned int + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrrd_w(unsigned int _1) { + return (unsigned int)__builtin_loongarch_iocsrrd_w((unsigned int)_1); +} + +#if __loongarch_grlen == 64 +extern __inline unsigned long int + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrrd_d(unsigned int _1) { + return (unsigned long int)__builtin_loongarch_iocsrrd_d((unsigned int)_1); +} +#endif + +extern __inline void + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrwr_b(unsigned char _1, unsigned int _2) { + __builtin_loongarch_iocsrwr_b((unsigned char)_1, (unsigned int)_2); +} + +extern __inline void + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrwr_h(unsigned short _1, unsigned int _2) { + __builtin_loongarch_iocsrwr_h((unsigned short)_1, (unsigned int)_2); +} + +extern __inline void + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrwr_w(unsigned int _1, unsigned int _2) { + __builtin_loongarch_iocsrwr_w((unsigned int)_1, (unsigned int)_2); +} + +#if __loongarch_grlen == 64 +extern __inline void + __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + __iocsrwr_d(unsigned long int _1, unsigned int _2) { + __builtin_loongarch_iocsrwr_d((unsigned long int)_1, (unsigned int)_2); +} +#endif + #ifdef __cplusplus } #endif diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 4b3b52db5295..48a11b853ec2 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3712,6 +3712,8 @@ bool Sema::CheckLoongArchBuiltinFunctionCall(const TargetInfo &TI, case LoongArch::BI__builtin_loongarch_crcc_w_h_w: case LoongArch::BI__builtin_loongarch_crcc_w_w_w: case LoongArch::BI__builtin_loongarch_crcc_w_d_w: + case LoongArch::BI__builtin_loongarch_iocsrrd_d: + case LoongArch::BI__builtin_loongarch_iocsrwr_d: if (!TI.hasFeature("64bit")) return Diag(TheCall->getBeginLoc(), diag::err_loongarch_builtin_requires_la64) diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c b/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c index 633a32fc919d..be4f319e884d 100644 --- a/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la32-error.c @@ -87,3 +87,11 @@ void csrxchg_w(unsigned int a, unsigned int b) { __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}} } + +unsigned long int iocsrrd_d(unsigned int a) { + return __builtin_loongarch_iocsrrd_d(a); // expected-error {{this builtin requires target: loongarch64}} +} + +void iocsrwr_d(unsigned long int a, unsigned int b) { + __builtin_loongarch_iocsrwr_d(a, b); // expected-error {{this builtin requires target: loongarch64}} +} diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la32.c b/clang/test/CodeGen/LoongArch/intrinsic-la32.c index 4d973d4a6569..d9c201f05141 100644 --- a/clang/test/CodeGen/LoongArch/intrinsic-la32.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la32.c @@ -107,3 +107,157 @@ unsigned int csrxchg_w(unsigned int a, unsigned int b) { unsigned int d = __builtin_loongarch_csrxchg_w(a, b, 1); return 0; } + +// LA32-LABEL: @iocsrrd_b( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B:%.*]] = alloca i8, align 1 +// LA32-NEXT: [[C:%.*]] = alloca i8, align 1 +// LA32-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: store i32 [[TMP0]], ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = call i32 @llvm.loongarch.iocsrrd.b(i32 [[TMP1]]) +// LA32-NEXT: [[CONV_I:%.*]] = trunc i32 [[TMP2]] to i8 +// LA32-NEXT: store i8 [[CONV_I]], ptr [[B]], align 1 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP4:%.*]] = call i32 @llvm.loongarch.iocsrrd.b(i32 [[TMP3]]) +// LA32-NEXT: [[CONV:%.*]] = trunc i32 [[TMP4]] to i8 +// LA32-NEXT: store i8 [[CONV]], ptr [[C]], align 1 +// LA32-NEXT: ret i8 0 +// +unsigned char iocsrrd_b(unsigned int a) { + unsigned char b = __iocsrrd_b(a); + unsigned char c = __builtin_loongarch_iocsrrd_b(a); + return 0; +} + +// LA32-LABEL: @iocsrrd_h( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B:%.*]] = alloca i16, align 2 +// LA32-NEXT: [[C:%.*]] = alloca i16, align 2 +// LA32-NEXT: store i32 [[A:%.*]], ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: store i32 [[TMP0]], ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = call i32 @llvm.loongarch.iocsrrd.h(i32 [[TMP1]]) +// LA32-NEXT: [[CONV_I:%.*]] = trunc i32 [[TMP2]] to i16 +// LA32-NEXT: [[CONV1_I:%.*]] = trunc i16 [[CONV_I]] to i8 +// LA32-NEXT: [[CONV:%.*]] = zext i8 [[CONV1_I]] to i16 +// LA32-NEXT: store i16 [[CONV]], ptr [[B]], align 2 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP4:%.*]] = call i32 @llvm.loongarch.iocsrrd.h(i32 [[TMP3]]) +// LA32-NEXT: [[CONV1:%.*]] = trunc i32 [[TMP4]] to i16 +// LA32-NEXT: store i16 [[CONV1]], ptr [[C]], align 2 +// LA32-NEXT: ret i16 0 +// +unsigned short iocsrrd_h(unsigned int a) { + unsigned short b = __iocsrrd_h(a); + unsigned short c = __builtin_loongarch_iocsrrd_h(a); + return 0; +} + +// LA32-LABEL: @iocsrrd_w( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i32, align 4 +// 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: store i32 [[TMP0]], ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = call i32 @llvm.loongarch.iocsrrd.w(i32 [[TMP1]]) +// LA32-NEXT: store i32 [[TMP2]], ptr [[B]], align 4 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP4:%.*]] = call i32 @llvm.loongarch.iocsrrd.w(i32 [[TMP3]]) +// LA32-NEXT: store i32 [[TMP4]], ptr [[C]], align 4 +// LA32-NEXT: ret i32 0 +// +unsigned int iocsrrd_w(unsigned int a) { + unsigned int b = __iocsrrd_w(a); + unsigned int c = __builtin_loongarch_iocsrrd_w(a); + return 0; +} + +// LA32-LABEL: @iocsrwr_b( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i8, align 1 +// LA32-NEXT: [[_2_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i8, align 1 +// LA32-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: store i8 [[A:%.*]], ptr [[A_ADDR]], align 1 +// LA32-NEXT: store i32 [[B:%.*]], ptr [[B_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i8, ptr [[A_ADDR]], align 1 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: store i8 [[TMP0]], ptr [[_1_ADDR_I]], align 1 +// LA32-NEXT: store i32 [[TMP1]], ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = load i8, ptr [[_1_ADDR_I]], align 1 +// LA32-NEXT: [[CONV_I:%.*]] = zext i8 [[TMP2]] to i32 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.b(i32 [[CONV_I]], i32 [[TMP3]]) +// LA32-NEXT: [[TMP4:%.*]] = load i8, ptr [[A_ADDR]], align 1 +// LA32-NEXT: [[CONV:%.*]] = zext i8 [[TMP4]] to i32 +// LA32-NEXT: [[TMP5:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.b(i32 [[CONV]], i32 [[TMP5]]) +// LA32-NEXT: ret void +// +void iocsrwr_b(unsigned char a, unsigned int b) { + __iocsrwr_b(a, b); + __builtin_loongarch_iocsrwr_b(a, b); +} + +// LA32-LABEL: @iocsrwr_h( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i16, align 2 +// LA32-NEXT: [[_2_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i16, align 2 +// LA32-NEXT: [[B_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: store i16 [[A:%.*]], ptr [[A_ADDR]], align 2 +// LA32-NEXT: store i32 [[B:%.*]], ptr [[B_ADDR]], align 4 +// LA32-NEXT: [[TMP0:%.*]] = load i16, ptr [[A_ADDR]], align 2 +// LA32-NEXT: [[TMP1:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: store i16 [[TMP0]], ptr [[_1_ADDR_I]], align 2 +// LA32-NEXT: store i32 [[TMP1]], ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = load i16, ptr [[_1_ADDR_I]], align 2 +// LA32-NEXT: [[CONV_I:%.*]] = zext i16 [[TMP2]] to i32 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.h(i32 [[CONV_I]], i32 [[TMP3]]) +// LA32-NEXT: [[TMP4:%.*]] = load i16, ptr [[A_ADDR]], align 2 +// LA32-NEXT: [[CONV:%.*]] = zext i16 [[TMP4]] to i32 +// LA32-NEXT: [[TMP5:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.h(i32 [[CONV]], i32 [[TMP5]]) +// LA32-NEXT: ret void +// +void iocsrwr_h(unsigned short a, unsigned int b) { + __iocsrwr_h(a, b); + __builtin_loongarch_iocsrwr_h(a, b); +} + +// LA32-LABEL: @iocsrwr_w( +// LA32-NEXT: entry: +// LA32-NEXT: [[_1_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[_2_ADDR_I:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// LA32-NEXT: [[B_ADDR:%.*]] = 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: store i32 [[TMP0]], ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: store i32 [[TMP1]], ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: [[TMP2:%.*]] = load i32, ptr [[_1_ADDR_I]], align 4 +// LA32-NEXT: [[TMP3:%.*]] = load i32, ptr [[_2_ADDR_I]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.w(i32 [[TMP2]], i32 [[TMP3]]) +// LA32-NEXT: [[TMP4:%.*]] = load i32, ptr [[A_ADDR]], align 4 +// LA32-NEXT: [[TMP5:%.*]] = load i32, ptr [[B_ADDR]], align 4 +// LA32-NEXT: call void @llvm.loongarch.iocsrwr.w(i32 [[TMP4]], i32 [[TMP5]]) +// LA32-NEXT: ret void +// +void iocsrwr_w(unsigned int a, unsigned int b) { + __iocsrwr_w(a, b); + __builtin_loongarch_iocsrwr_w(a, b); +} diff --git a/clang/test/CodeGen/LoongArch/intrinsic-la64.c b/clang/test/CodeGen/LoongArch/intrinsic-la64.c index 97b93a6077c0..4497fae66a75 100644 --- a/clang/test/CodeGen/LoongArch/intrinsic-la64.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-la64.c @@ -222,3 +222,97 @@ unsigned long int csrxchg_d(unsigned long int a, unsigned long int b) { unsigned long int d = __builtin_loongarch_csrxchg_d(a, b, 1); return 0; } + +// CHECK-LABEL: @iocsrrd_b( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.b(i32 [[A:%.*]]) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.b(i32 [[A]]) +// CHECK-NEXT: ret i8 0 +// +unsigned char iocsrrd_b(unsigned int a) { + unsigned char b = __iocsrrd_b(a); + unsigned char c = __builtin_loongarch_iocsrrd_b(a); + return 0; +} + +// CHECK-LABEL: @iocsrrd_h( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.h(i32 [[A:%.*]]) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.h(i32 [[A]]) +// CHECK-NEXT: ret i16 0 +// +unsigned short iocsrrd_h(unsigned int a) { + unsigned short b = __iocsrrd_h(a); + unsigned short c = __builtin_loongarch_iocsrrd_h(a); + return 0; +} + +// CHECK-LABEL: @iocsrrd_w( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.w(i32 [[A:%.*]]) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.loongarch.iocsrrd.w(i32 [[A]]) +// CHECK-NEXT: ret i32 0 +// +unsigned int iocsrrd_w(unsigned int a) { + unsigned int b = __iocsrrd_w(a); + unsigned int c = __builtin_loongarch_iocsrrd_w(a); + return 0; +} + +// CHECK-LABEL: @iocsrwr_b( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV_I:%.*]] = zext i8 [[A:%.*]] to i32 +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.b(i32 [[CONV_I]], i32 [[B:%.*]]) +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.b(i32 [[CONV_I]], i32 [[B]]) +// CHECK-NEXT: ret void +// +void iocsrwr_b(unsigned char a, unsigned int b) { + __iocsrwr_b(a, b); + __builtin_loongarch_iocsrwr_b(a, b); +} + +// CHECK-LABEL: @iocsrwr_h( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CONV_I:%.*]] = zext i16 [[A:%.*]] to i32 +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.h(i32 [[CONV_I]], i32 [[B:%.*]]) +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.h(i32 [[CONV_I]], i32 [[B]]) +// CHECK-NEXT: ret void +// +void iocsrwr_h(unsigned short a, unsigned int b) { + __iocsrwr_h(a, b); + __builtin_loongarch_iocsrwr_h(a, b); +} + +// CHECK-LABEL: @iocsrwr_w( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.w(i32 [[A:%.*]], i32 [[B:%.*]]) +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.w(i32 [[A]], i32 [[B]]) +// CHECK-NEXT: ret void +// +void iocsrwr_w(unsigned int a, unsigned int b) { + __iocsrwr_w(a, b); + __builtin_loongarch_iocsrwr_w(a, b); +} + +// CHECK-LABEL: @iocsrrd_d( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.loongarch.iocsrrd.d(i32 [[A:%.*]]) +// CHECK-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.loongarch.iocsrrd.d(i32 [[A]]) +// CHECK-NEXT: ret i64 0 +// +unsigned long int iocsrrd_d(unsigned int a) { + unsigned long int b = __iocsrrd_d(a); + unsigned long int c = __builtin_loongarch_iocsrrd_d(a); + return 0; +} + +// CHECK-LABEL: @iocsrwr_d( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.d(i64 [[A:%.*]], i32 [[B:%.*]]) +// CHECK-NEXT: tail call void @llvm.loongarch.iocsrwr.d(i64 [[A]], i32 [[B]]) +// CHECK-NEXT: ret void +// +void iocsrwr_d(unsigned long int a, unsigned int b) { + __iocsrwr_d(a, b); + __builtin_loongarch_iocsrwr_d(a, b); +} diff --git a/llvm/include/llvm/IR/IntrinsicsLoongArch.td b/llvm/include/llvm/IR/IntrinsicsLoongArch.td index 599ed8360921..8fc20d98d484 100644 --- a/llvm/include/llvm/IR/IntrinsicsLoongArch.td +++ b/llvm/include/llvm/IR/IntrinsicsLoongArch.td @@ -92,4 +92,14 @@ def int_loongarch_csrxchg_d : Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty, llvm_i32_ty], [ImmArg>]>; + +def int_loongarch_iocsrrd_b : Intrinsic<[llvm_i32_ty], [llvm_i32_ty]>; +def int_loongarch_iocsrrd_h : Intrinsic<[llvm_i32_ty], [llvm_i32_ty]>; +def int_loongarch_iocsrrd_w : Intrinsic<[llvm_i32_ty], [llvm_i32_ty]>; +def int_loongarch_iocsrrd_d : Intrinsic<[llvm_i64_ty], [llvm_i32_ty]>; + +def int_loongarch_iocsrwr_b : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty]>; +def int_loongarch_iocsrwr_h : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty]>; +def int_loongarch_iocsrwr_w : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty]>; +def int_loongarch_iocsrwr_d : Intrinsic<[], [llvm_i64_ty, llvm_i32_ty]>; } // TargetPrefix = "loongarch" diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp index fd23aadcdb75..bc7ef23b8853 100644 --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -124,6 +124,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM, setOperationAction(ISD::READ_REGISTER, MVT::i64, Custom); setOperationAction(ISD::WRITE_REGISTER, MVT::i64, Custom); setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_VOID, MVT::i64, Custom); } static const ISD::CondCode FPCCToExpand[] = { @@ -660,6 +661,30 @@ LoongArchTargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op, Op0}, DL); } + case Intrinsic::loongarch_iocsrrd_d: { + if (Subtarget.is64Bit()) + return DAG.getMergeValues( + {DAG.getNode( + LoongArchISD::IOCSRRD_D, DL, GRLenVT, Op0, + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op.getOperand(2))), + Op0}, + DL); + else { + DAG.getContext()->emitError( + "llvm.loongarch.crc.w.d.w requires target: loongarch64"); + return DAG.getMergeValues({DAG.getUNDEF(Op.getValueType()), Op0}, DL); + } + } +#define IOCSRRD_CASE(NAME, NODE) \ + case Intrinsic::loongarch_##NAME: { \ + return DAG.getMergeValues( \ + {DAG.getNode(LoongArchISD::NODE, DL, GRLenVT, Op0, Op.getOperand(2)), \ + Op0}, \ + DL); \ + } + IOCSRRD_CASE(iocsrrd_b, IOCSRRD_B); + IOCSRRD_CASE(iocsrrd_h, IOCSRRD_H); + IOCSRRD_CASE(iocsrrd_w, IOCSRRD_W); } } @@ -717,6 +742,30 @@ SDValue LoongArchTargetLowering::lowerINTRINSIC_VOID(SDValue Op, return DAG.getNode(LoongArchISD::SYSCALL, DL, MVT::Other, Op0, DAG.getConstant(Imm, DL, GRLenVT)); } +#define IOCSRWR_CASE(NAME, NODE) \ + case Intrinsic::loongarch_##NAME: { \ + SDValue Op3 = Op.getOperand(3); \ + if (Subtarget.is64Bit()) \ + return DAG.getNode(LoongArchISD::NODE, DL, MVT::Other, Op0, \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2), \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op3)); \ + else \ + return DAG.getNode(LoongArchISD::NODE, DL, MVT::Other, Op0, Op2, Op3); \ + } + IOCSRWR_CASE(iocsrwr_b, IOCSRWR_B); + IOCSRWR_CASE(iocsrwr_h, IOCSRWR_H); + IOCSRWR_CASE(iocsrwr_w, IOCSRWR_W); + case Intrinsic::loongarch_iocsrwr_d: { + if (Subtarget.is64Bit()) + return DAG.getNode( + LoongArchISD::IOCSRWR_D, DL, MVT::Other, Op0, Op2, + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op.getOperand(3))); + else { + DAG.getContext()->emitError( + "llvm.loongarch.iocsrwr.d requires target: loongarch64"); + return Op.getOperand(0); + } + } } } @@ -1038,6 +1087,7 @@ void LoongArchTargetLowering::ReplaceNodeResults( CSR_CASE(csrrd_d); CSR_CASE(csrwr_d); CSR_CASE(csrxchg_d); + CSR_CASE(iocsrrd_d); case Intrinsic::loongarch_csrrd_w: { unsigned Imm = cast(Op2)->getZExtValue(); if (!isUInt<14>(Imm)) { @@ -1090,6 +1140,18 @@ void LoongArchTargetLowering::ReplaceNodeResults( Results.push_back(N->getOperand(0)); break; } +#define IOCSRRD_CASE(NAME, NODE) \ + case Intrinsic::loongarch_##NAME: { \ + Results.push_back(DAG.getNode( \ + ISD::TRUNCATE, DL, N->getValueType(0), \ + DAG.getNode(LoongArchISD::NODE, DL, MVT::i64, Op0, \ + DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, Op2)))); \ + Results.push_back(N->getOperand(0)); \ + break; \ + } + IOCSRRD_CASE(iocsrrd_b, IOCSRRD_B); + IOCSRRD_CASE(iocsrrd_h, IOCSRRD_H); + IOCSRRD_CASE(iocsrrd_w, IOCSRRD_W); } break; } @@ -1574,6 +1636,14 @@ const char *LoongArchTargetLowering::getTargetNodeName(unsigned Opcode) const { NODE_NAME_CASE(CSRRD) NODE_NAME_CASE(CSRWR) NODE_NAME_CASE(CSRXCHG) + NODE_NAME_CASE(IOCSRRD_B) + NODE_NAME_CASE(IOCSRRD_H) + NODE_NAME_CASE(IOCSRRD_W) + NODE_NAME_CASE(IOCSRRD_D) + NODE_NAME_CASE(IOCSRWR_B) + NODE_NAME_CASE(IOCSRWR_H) + NODE_NAME_CASE(IOCSRWR_W) + NODE_NAME_CASE(IOCSRWR_D) } #undef NODE_NAME_CASE return nullptr; diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h index 9dcc5bce0fa5..36d36682d2d6 100644 --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h @@ -78,6 +78,16 @@ enum NodeType : unsigned { CSRRD, CSRWR, CSRXCHG, + + // IOCSR access operations + IOCSRRD_B, + IOCSRRD_W, + IOCSRRD_H, + IOCSRRD_D, + IOCSRWR_B, + IOCSRWR_H, + IOCSRWR_W, + IOCSRWR_D, }; } // end namespace LoongArchISD diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td index 83ca6a395b8e..43cab04dd212 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -46,6 +46,8 @@ def SDT_LoongArchCsrxchg : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisVT<3, GRLenVT>]>; +def SDT_LoongArchIocsrwr : SDTypeProfile<0, 2, [SDTCisInt<0>, + SDTCisSameAs<0, 1>]>; // TODO: Add LoongArch specific DAG Nodes // Target-independent nodes, but with target-specific formats. @@ -109,6 +111,26 @@ def loongarch_csrwr : SDNode<"LoongArchISD::CSRWR", SDT_LoongArchCsrwr, def loongarch_csrxchg : SDNode<"LoongArchISD::CSRXCHG", SDT_LoongArchCsrxchg, [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrrd_b : SDNode<"LoongArchISD::IOCSRRD_B", SDTUnaryOp, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrrd_h : SDNode<"LoongArchISD::IOCSRRD_H", SDTUnaryOp, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrrd_w : SDNode<"LoongArchISD::IOCSRRD_W", SDTUnaryOp, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrrd_d : SDNode<"LoongArchISD::IOCSRRD_D", SDTUnaryOp, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrwr_b : SDNode<"LoongArchISD::IOCSRWR_B", + SDT_LoongArchIocsrwr, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrwr_h : SDNode<"LoongArchISD::IOCSRWR_H", + SDT_LoongArchIocsrwr, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrwr_w : SDNode<"LoongArchISD::IOCSRWR_W", + SDT_LoongArchIocsrwr, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_iocsrwr_d : SDNode<"LoongArchISD::IOCSRWR_D", + SDT_LoongArchIocsrwr, + [SDNPHasChain, SDNPSideEffect]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. @@ -1654,3 +1676,16 @@ 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)>; + +def : Pat<(loongarch_iocsrrd_b GPR:$rj), (IOCSRRD_B GPR:$rj)>; +def : Pat<(loongarch_iocsrrd_h GPR:$rj), (IOCSRRD_H GPR:$rj)>; +def : Pat<(loongarch_iocsrrd_w GPR:$rj), (IOCSRRD_W GPR:$rj)>; + +def : Pat<(loongarch_iocsrwr_b GPR:$rd, GPR:$rj), (IOCSRWR_B GPR:$rd, GPR:$rj)>; +def : Pat<(loongarch_iocsrwr_h GPR:$rd, GPR:$rj), (IOCSRWR_H GPR:$rd, GPR:$rj)>; +def : Pat<(loongarch_iocsrwr_w GPR:$rd, GPR:$rj), (IOCSRWR_W GPR:$rd, GPR:$rj)>; + +let Predicates = [IsLA64] in { +def : Pat<(loongarch_iocsrrd_d GPR:$rj), (IOCSRRD_D GPR:$rj)>; +def : Pat<(loongarch_iocsrwr_d GPR:$rd, GPR:$rj), (IOCSRWR_D GPR:$rd, GPR:$rj)>; +} // Predicates = [IsLA64] diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll index 19930886a79f..3bbeb13df034 100644 --- a/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la32-error.ll @@ -11,6 +11,8 @@ 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) +declare i64 @llvm.loongarch.iocsrrd.d(i32) +declare void @llvm.loongarch.iocsrwr.d(i64, i32) define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind { ; CHECK: llvm.loongarch.crc.w.b.w requires target: loongarch64 @@ -88,3 +90,17 @@ entry: %0 = tail call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 1) ret i64 %0 } + +define i64 @iocsrrd_d(i32 %a) { +; CHECK: llvm.loongarch.iocsrrd.d requires target: loongarch64 +entry: + %0 = tail call i64 @llvm.loongarch.iocsrrd.d(i32 %a) + ret i64 %0 +} + +define void @iocsrwr_d(i64 %a, i32 signext %b) { +; CHECK: llvm.loongarch.iocsrwr.d requires target: loongarch64 +entry: + tail call void @llvm.loongarch.iocsrwr.d(i64 %a, i32 %b) + ret void +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll b/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll index 8b439b8b6cfe..b2ad3776fdc4 100644 --- a/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-la64.ll @@ -12,6 +12,8 @@ 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) +declare i64 @llvm.loongarch.iocsrrd.d(i32) +declare void @llvm.loongarch.iocsrwr.d(i64, i32) define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind { ; CHECK-LABEL: crc_w_b_w: @@ -114,3 +116,23 @@ entry: %0 = tail call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 1) ret i64 %0 } + +define i64 @iocsrrd_d(i32 %a) { +; CHECK-LABEL: iocsrrd_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrrd.d $a0, $a0 +; CHECK-NEXT: ret +entry: + %0 = tail call i64 @llvm.loongarch.iocsrrd.d(i32 %a) + ret i64 %0 +} + +define void @iocsrwr_d(i64 %a, i32 signext %b) { +; CHECK-LABEL: iocsrwr_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrwr.d $a0, $a1 +; CHECK-NEXT: ret +entry: + tail call void @llvm.loongarch.iocsrwr.d(i64 %a, i32 %b) + ret void +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic.ll b/llvm/test/CodeGen/LoongArch/intrinsic.ll index 297e4d34cfee..b3bf8159ce0a 100644 --- a/llvm/test/CodeGen/LoongArch/intrinsic.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic.ll @@ -9,6 +9,12 @@ 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) +declare i32 @llvm.loongarch.iocsrrd.b(i32) +declare i32 @llvm.loongarch.iocsrrd.h(i32) +declare i32 @llvm.loongarch.iocsrrd.w(i32) +declare void @llvm.loongarch.iocsrwr.b(i32, i32) +declare void @llvm.loongarch.iocsrwr.h(i32, i32) +declare void @llvm.loongarch.iocsrwr.w(i32, i32) define void @foo() nounwind { ; CHECK-LABEL: foo: @@ -79,3 +85,63 @@ entry: %0 = tail call i32 @llvm.loongarch.csrxchg.w(i32 %a, i32 %b, i32 1) ret i32 %0 } + +define i32 @iocsrrd_b(i32 %a) { +; CHECK-LABEL: iocsrrd_b: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrrd.b $a0, $a0 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.iocsrrd.b(i32 %a) + ret i32 %0 +} + +define i32 @iocsrrd_h(i32 %a) { +; CHECK-LABEL: iocsrrd_h: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrrd.h $a0, $a0 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.iocsrrd.h(i32 %a) + ret i32 %0 +} + +define i32 @iocsrrd_w(i32 %a) { +; CHECK-LABEL: iocsrrd_w: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrrd.w $a0, $a0 +; CHECK-NEXT: ret +entry: + %0 = tail call i32 @llvm.loongarch.iocsrrd.w(i32 %a) + ret i32 %0 +} + +define void @iocsrwr_b(i32 %a, i32 %b) { +; CHECK-LABEL: iocsrwr_b: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrwr.b $a0, $a1 +; CHECK-NEXT: ret +entry: + tail call void @llvm.loongarch.iocsrwr.b(i32 %a, i32 %b) + ret void +} + +define void @iocsrwr_h(i32 %a, i32 %b) { +; CHECK-LABEL: iocsrwr_h: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrwr.h $a0, $a1 +; CHECK-NEXT: ret +entry: + tail call void @llvm.loongarch.iocsrwr.h(i32 %a, i32 %b) + ret void +} + +define void @iocsrwr_w(i32 %a, i32 %b) { +; CHECK-LABEL: iocsrwr_w: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: iocsrwr.w $a0, $a1 +; CHECK-NEXT: ret +entry: + tail call void @llvm.loongarch.iocsrwr.w(i32 %a, i32 %b) + ret void +} -- 2.34.1