diff --git a/clang/include/clang/Basic/BuiltinsPPC.def b/clang/include/clang/Basic/BuiltinsPPC.def --- a/clang/include/clang/Basic/BuiltinsPPC.def +++ b/clang/include/clang/Basic/BuiltinsPPC.def @@ -83,6 +83,10 @@ BUILTIN(__builtin_ppc_maddhd, "LLiLLiLLiLLi", "") BUILTIN(__builtin_ppc_maddhdu, "ULLiULLiULLiULLi", "") BUILTIN(__builtin_ppc_maddld, "LLiLLiLLiLLi", "") +// Rotate +BUILTIN(__builtin_ppc_rlwnm, "UiUiIUiIUi", "") +BUILTIN(__builtin_ppc_rlwimi, "UiUiUiIUiIUi", "") +BUILTIN(__builtin_ppc_rldimi, "ULLiULLiULLiIUiIULLi", "") BUILTIN(__builtin_ppc_get_timebase, "ULLi", "n") 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 @@ -9726,6 +9726,8 @@ "argument should be an 8-bit value shifted by a multiple of 8 bits">; def err_argument_not_shifted_byte_or_xxff : Error< "argument should be an 8-bit value shifted by a multiple of 8 bits, or in the form 0x??FF">; +def err_argument_not_contiguous_bit_field : Error< + "argument %0 value should represent a contiguous bit field">; def err_rotation_argument_to_cadd : Error<"argument should be the value 90 or 270">; def err_rotation_argument_to_cmla diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12573,6 +12573,7 @@ bool SemaBuiltinComplex(CallExpr *TheCall); bool SemaBuiltinVSX(CallExpr *TheCall); bool SemaBuiltinOSLogFormat(CallExpr *TheCall); + bool SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum); public: // Used by C++ template instantiation. diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp --- a/clang/lib/Basic/Targets/PPC.cpp +++ b/clang/lib/Basic/Targets/PPC.cpp @@ -140,6 +140,9 @@ Builder.defineMacro("__maddhd", "__builtin_ppc_maddhd"); Builder.defineMacro("__maddhdu", "__builtin_ppc_maddhdu"); Builder.defineMacro("__maddld", "__builtin_ppc_maddld"); + Builder.defineMacro("__rlwnm", "__builtin_ppc_rlwnm"); + Builder.defineMacro("__rlwimi", "__builtin_ppc_rlwimi"); + Builder.defineMacro("__rldimi", "__builtin_ppc_rldimi"); } /// PPCTargetInfo::getTargetDefines - Return a set of the PowerPC-specific 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 @@ -15172,6 +15172,31 @@ else return Builder.CreateSub(Ops[0], Ops[1], "vsubuqm"); } + // Rotate and insert under mask operation. + // __rldimi(rs, is, shift, mask) + // (rotl64(rs, shift) & mask) | (is & ~mask) + // __rlwimi(rs, is, shift, mask) + // (rotl(rs, shift) & mask) | (is & ~mask) + case PPC::BI__builtin_ppc_rldimi: + case PPC::BI__builtin_ppc_rlwimi: { + llvm::Type *Ty = Ops[0]->getType(); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, Ty); + if (BuiltinID == PPC::BI__builtin_ppc_rldimi) + Ops[2] = Builder.CreateZExt(Ops[2], Int64Ty); + Value *Shift = Builder.CreateCall(F, {Ops[0], Ops[0], Ops[2]}); + Value *X = Builder.CreateAnd(Shift, Ops[3]); + Value *Y = Builder.CreateAnd(Ops[1], Builder.CreateNot(Ops[3])); + return Builder.CreateOr(X, Y); + } + // Rotate and insert under mask operation. + // __rlwnm(rs, shift, mask) + // rotl(rs, shift) & mask + case PPC::BI__builtin_ppc_rlwnm: { + llvm::Type *Ty = Ops[0]->getType(); + Function *F = CGM.getIntrinsic(Intrinsic::fshl, Ty); + Value *Shift = Builder.CreateCall(F, {Ops[0], Ops[0], Ops[1]}); + return Builder.CreateAnd(Shift, Ops[2]); + } // Copy sign case PPC::BI__builtin_vsx_xvcpsgnsp: case PPC::BI__builtin_vsx_xvcpsgndp: { 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 @@ -3296,10 +3296,35 @@ return true; } +/// Returns true if the argument consists of one contiguous run of 1s with any +/// number of 0s on either side. The 1s are allowed to wrap from LSB to MSB, so +/// 0x000FFF0, 0x0000FFFF, 0xFF0000FF, 0x0 are all runs. 0x0F0F0000 is not, +/// since all 1s are not contiguous. +bool Sema::SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum) { + llvm::APSInt Result; + // We can't check the value of a dependent argument. + Expr *Arg = TheCall->getArg(ArgNum); + if (Arg->isTypeDependent() || Arg->isValueDependent()) + return false; + + // Check constant-ness first. + if (SemaBuiltinConstantArg(TheCall, ArgNum, Result)) + return true; + + // Check contiguous run of 1s, 0xFF0000FF is also a run of 1s. + if (Result.isShiftedMask() || (~Result).isShiftedMask()) + return false; + + return Diag(TheCall->getBeginLoc(), + diag::err_argument_not_contiguous_bit_field) + << ArgNum << Arg->getSourceRange(); +} + bool Sema::CheckPPCBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { unsigned i = 0, l = 0, u = 0; bool IsTarget64Bit = TI.getTypeWidth(TI.getIntPtrType()) == 64; + llvm::APSInt Result; if (isPPC_64Builtin(BuiltinID) && !IsTarget64Bit) return Diag(TheCall->getBeginLoc(), diag::err_64_bit_builtin_32_bit_tgt) @@ -3378,6 +3403,15 @@ return SemaFeatureCheck(*this, TheCall, "isa-v30-instructions", diag::err_ppc_builtin_only_on_arch, "9") || SemaBuiltinConstantArgRange(TheCall, 0, 0, 1); + // For __rlwnm, __rlwimi and __rldimi, the last parameter mask must + // be a constant that represents a contiguous bit field. + case PPC::BI__builtin_ppc_rlwnm: + return SemaBuiltinConstantArg(TheCall, 1, Result) || + SemaValueIsRunOfOnes(TheCall, 2); + case PPC::BI__builtin_ppc_rlwimi: + case PPC::BI__builtin_ppc_rldimi: + return SemaBuiltinConstantArg(TheCall, 2, Result) || + SemaValueIsRunOfOnes(TheCall, 3); #define CUSTOM_BUILTIN(Name, Intr, Types, Acc) \ case PPC::BI__builtin_##Name: \ return SemaBuiltinPPCMMACall(TheCall, Types); diff --git a/clang/test/CodeGen/builtins-ppc-xlcompat-error.c b/clang/test/CodeGen/builtins-ppc-xlcompat-error.c --- a/clang/test/CodeGen/builtins-ppc-xlcompat-error.c +++ b/clang/test/CodeGen/builtins-ppc-xlcompat-error.c @@ -10,6 +10,8 @@ extern long long lla, llb; extern int ia, ib; +extern unsigned int ui; +extern unsigned long long ull; void test_trap(void) { #ifdef __PPC64__ @@ -17,3 +19,27 @@ #endif __tw(ia, ib, 50); //expected-error {{argument value 50 is outside the valid range [0, 31]}} } + +void test_builtin_ppc_rldimi() { + unsigned int shift; + unsigned long long mask; + unsigned long long res = __builtin_ppc_rldimi(ull, ull, shift, 7); // expected-error {{argument to '__builtin_ppc_rldimi' must be a constant integer}} + res = __builtin_ppc_rldimi(ull, ull, 63, mask); // expected-error {{argument to '__builtin_ppc_rldimi' must be a constant integer}} + res = __builtin_ppc_rldimi(ull, ull, 63, 0xFFFF000000000F00); // expected-error {{argument 3 value should represent a contiguous bit field}} +} + +void test_builtin_ppc_rlwimi() { + unsigned int shift; + unsigned int mask; + unsigned int res = __builtin_ppc_rlwimi(ui, ui, shift, 7); // expected-error {{argument to '__builtin_ppc_rlwimi' must be a constant integer}} + res = __builtin_ppc_rlwimi(ui, ui, 31, mask); // expected-error {{argument to '__builtin_ppc_rlwimi' must be a constant integer}} + res = __builtin_ppc_rlwimi(ui, ui, 31, 0xFFFF0F00); // expected-error {{argument 3 value should represent a contiguous bit field}} +} + +void test_builtin_ppc_rlwnm() { + unsigned int shift; + unsigned int mask; + unsigned int res = __builtin_ppc_rlwnm(ui, shift, 7); // expected-error {{argument to '__builtin_ppc_rlwnm' must be a constant integer}} + res = __builtin_ppc_rlwnm(ui, 31, mask); // expected-error {{argument to '__builtin_ppc_rlwnm' must be a constant integer}} + res = __builtin_ppc_rlwnm(ui, 31, 0xFF0F0F00); // expected-error {{argument 2 value should represent a contiguous bit field}} +} diff --git a/clang/test/CodeGen/builtins-ppc-xlcompat-rotate.c b/clang/test/CodeGen/builtins-ppc-xlcompat-rotate.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtins-ppc-xlcompat-rotate.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -triple powerpc64-unknown-unknown \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64le-unknown-unknown \ +// RUN: -emit-llvm %s -o - -target-cpu pwr8 | FileCheck %s +// RUN: %clang_cc1 -triple powerpc-unknown-aix \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-unknown-aix \ +// RUN: -emit-llvm %s -o - -target-cpu pwr7 | FileCheck %s + +extern unsigned int ui; +extern unsigned long long ull; + +void test_builtin_ppc_rldimi() { + // CHECK-LABEL: test_builtin_ppc_rldimi + // CHECK: %res = alloca i64, align 8 + // CHECK-NEXT: [[RA:%[0-9]+]] = load i64, i64* @ull, align 8 + // CHECK-NEXT: [[RB:%[0-9]+]] = load i64, i64* @ull, align 8 + // CHECK-NEXT: [[RC:%[0-9]+]] = call i64 @llvm.fshl.i64(i64 [[RA]], i64 [[RA]], i64 63) + // CHECK-NEXT: [[RD:%[0-9]+]] = and i64 [[RC]], 72057593769492480 + // CHECK-NEXT: [[RE:%[0-9]+]] = and i64 [[RB]], -72057593769492481 + // CHECK-NEXT: [[RF:%[0-9]+]] = or i64 [[RD]], [[RE]] + // CHECK-NEXT: store i64 [[RF]], i64* %res, align 8 + // CHECK-NEXT: ret void + + /*shift = 63, mask = 0x00FFFFFFF0000000 = 72057593769492480, ~mask = 0xFF0000000FFFFFFF = -72057593769492481*/ + unsigned long long res = __builtin_ppc_rldimi(ull, ull, 63, 0x00FFFFFFF0000000); +} + +void test_builtin_ppc_rlwimi() { + // CHECK-LABEL: test_builtin_ppc_rlwimi + // CHECK: %res = alloca i32, align 4 + // CHECK-NEXT: [[RA:%[0-9]+]] = load i32, i32* @ui, align 4 + // CHECK-NEXT: [[RB:%[0-9]+]] = load i32, i32* @ui, align 4 + // CHECK-NEXT: [[RC:%[0-9]+]] = call i32 @llvm.fshl.i32(i32 [[RA]], i32 [[RA]], i32 31) + // CHECK-NEXT: [[RD:%[0-9]+]] = and i32 [[RC]], 16776960 + // CHECK-NEXT: [[RE:%[0-9]+]] = and i32 [[RB]], -16776961 + // CHECK-NEXT: [[RF:%[0-9]+]] = or i32 [[RD]], [[RE]] + // CHECK-NEXT: store i32 [[RF]], i32* %res, align 4 + // CHECK-NEXT: ret void + + /*shift = 31, mask = 0xFFFF00 = 16776960, ~mask = 0xFFFFFFFFFF0000FF = -16776961*/ + unsigned int res = __builtin_ppc_rlwimi(ui, ui, 31, 0xFFFF00); +} + +void test_builtin_ppc_rlwnm() { + // CHECK-LABEL: test_builtin_ppc_rlwnm + // CHECK: %res = alloca i32, align 4 + // CHECK-NEXT: [[RA:%[0-9]+]] = load i32, i32* @ui, align 4 + // CHECK-NEXT: [[RB:%[0-9]+]] = call i32 @llvm.fshl.i32(i32 [[RA]], i32 [[RA]], i32 31) + // CHECK-NEXT: [[RC:%[0-9]+]] = and i32 [[RB]], 511 + // CHECK-NEXT: store i32 [[RC]], i32* %res, align 4 + // CHECK-NEXT: ret void + + /*shift = 31, mask = 0x1FF = 511*/ + unsigned int res = __builtin_ppc_rlwnm(ui, 31, 0x1FF); +}