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 @@ -96,6 +96,11 @@ BUILTIN(__builtin_ppc_swdivs_nochk, "fff", "") BUILTIN(__builtin_ppc_alignx, "vIivC*", "nc") BUILTIN(__builtin_ppc_rdlam, "UWiUWiUWiUWIi", "nc") +BUILTIN(__builtin_ppc_compare_exp_uo, "idd", "") +BUILTIN(__builtin_ppc_compare_exp_lt, "idd", "") +BUILTIN(__builtin_ppc_compare_exp_gt, "idd", "") +BUILTIN(__builtin_ppc_compare_exp_eq, "idd", "") +BUILTIN(__builtin_ppc_test_data_class, "idIi", "t") // Compare BUILTIN(__builtin_ppc_cmpeqb, "LLiLLiLLi", "") BUILTIN(__builtin_ppc_cmprb, "iCIiii", "") 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 @@ -9786,8 +9786,12 @@ "this builtin requires 'msa' ASE, please use -mmsa">; def err_ppc_builtin_only_on_arch : Error< "this builtin is only valid on POWER%0 or later CPUs">; +def err_ppc_builtin_requires_vsx : Error< + "this builtin requires VSX to be enabled">; def err_ppc_invalid_use_mma_type : Error< "invalid use of PPC MMA type">; +def err_ppc_invalid_test_data_class_type : Error< + "expected a 'float' or 'double' for the first argument">; def err_x86_builtin_invalid_rounding : Error< "invalid rounding argument">; def err_x86_builtin_invalid_scale : Error< 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 @@ -238,6 +238,11 @@ Builder.defineMacro("__fsqrts", "__builtin_ppc_fsqrts"); Builder.defineMacro("__addex", "__builtin_ppc_addex"); Builder.defineMacro("__cmplxl", "__builtin_complex"); + Builder.defineMacro("__compare_exp_uo", "__builtin_ppc_compare_exp_uo"); + Builder.defineMacro("__compare_exp_lt", "__builtin_ppc_compare_exp_lt"); + Builder.defineMacro("__compare_exp_gt", "__builtin_ppc_compare_exp_gt"); + Builder.defineMacro("__compare_exp_eq", "__builtin_ppc_compare_exp_eq"); + Builder.defineMacro("__test_data_class", "__builtin_ppc_test_data_class"); } /// 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 @@ -16035,6 +16035,17 @@ *this, E, Intrinsic::sqrt, Intrinsic::experimental_constrained_sqrt)) .getScalarVal(); + case PPC::BI__builtin_ppc_test_data_class: + llvm::Type *ArgType = EmitScalarExpr(E->getArg(0))->getType(); + unsigned IntrinsicID; + if (ArgType->isDoubleTy()) + IntrinsicID = Intrinsic::ppc_test_data_class_d; + else if (ArgType->isFloatTy()) + IntrinsicID = Intrinsic::ppc_test_data_class_f; + else + llvm_unreachable("Invalid Argument Type"); + return Builder.CreateCall(CGM.getIntrinsic(IntrinsicID), Ops, + "test_data_class"); } } 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 @@ -3478,6 +3478,29 @@ case PPC::BI__builtin_altivec_vcntmbw: case PPC::BI__builtin_altivec_vcntmbd: return SemaBuiltinConstantArgRange(TheCall, 1, 0, 1); + case PPC::BI__builtin_ppc_compare_exp_uo: + case PPC::BI__builtin_ppc_compare_exp_lt: + case PPC::BI__builtin_ppc_compare_exp_gt: + case PPC::BI__builtin_ppc_compare_exp_eq: + return SemaFeatureCheck(*this, TheCall, "isa-v30-instructions", + diag::err_ppc_builtin_only_on_arch, "9") || + SemaFeatureCheck(*this, TheCall, "vsx", + diag::err_ppc_builtin_requires_vsx); + case PPC::BI__builtin_ppc_test_data_class: { + // Check if the first argument of the __builtin_ppc_test_data_class call is + // valid. The argument must be either a 'float' or a 'double'. + QualType ArgType = TheCall->getArg(0)->getType(); + if (ArgType != QualType(Context.FloatTy) && + ArgType != QualType(Context.DoubleTy)) { + Diag(TheCall->getBeginLoc(), diag::err_ppc_invalid_test_data_class_type); + return true; + } + return SemaFeatureCheck(*this, TheCall, "isa-v30-instructions", + diag::err_ppc_builtin_only_on_arch, "9") || + SemaFeatureCheck(*this, TheCall, "vsx", + diag::err_ppc_builtin_requires_vsx) || + SemaBuiltinConstantArgRange(TheCall, 1, 0, 127); + } #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-pwr9-error.c b/clang/test/CodeGen/builtins-ppc-xlcompat-pwr9-error.c --- a/clang/test/CodeGen/builtins-ppc-xlcompat-pwr9-error.c +++ b/clang/test/CodeGen/builtins-ppc-xlcompat-pwr9-error.c @@ -11,6 +11,8 @@ extern unsigned int ui; extern unsigned long long ull; extern long long ll; +extern float f; +extern double d; void test_builtin_ppc_cmprb() { int res = __builtin_ppc_cmprb(3, ui, ui); // expected-error {{argument value 3 is outside the valid range [0, 1]}} @@ -24,3 +26,23 @@ } #endif + +int test_builtin_ppc_test_data_class_d() { + return __builtin_ppc_test_data_class(d, -1); // expected-error {{argument value -1 is outside the valid range [0, 127]}} +} + +int test_builtin_ppc_test_data_class_f() { + return __builtin_ppc_test_data_class(f, -1); // expected-error {{argument value -1 is outside the valid range [0, 127]}} +} + +int test_test_data_class_d() { + return __test_data_class(d, 128); // expected-error {{argument value 128 is outside the valid range [0, 127]}} +} + +int test_test_data_class_f() { + return __test_data_class(f, 128); // expected-error {{argument value 128 is outside the valid range [0, 127]}} +} + +int test_test_data_class_type() { + return __test_data_class(ui, 0); // expected-error {{expected a 'float' or 'double' for the first argument}} +} diff --git a/clang/test/CodeGen/builtins-ppc-xlcompat-test.c b/clang/test/CodeGen/builtins-ppc-xlcompat-test.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtins-ppc-xlcompat-test.c @@ -0,0 +1,124 @@ +// REQUIRES: powerpc-registered-target +// RUN: %clang_cc1 -triple powerpc64-unknown-unknown -emit-llvm %s \ +// RUN: -target-cpu pwr9 -o - | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64le-unknown-unknown -emit-llvm %s \ +// RUN: -target-cpu pwr9 -o - | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-unknown-aix -emit-llvm %s \ +// RUN: -target-cpu pwr9 -o - | FileCheck %s +// RUN: %clang_cc1 -triple powerpc-unknown-aix %s -emit-llvm %s \ +// RUN: -target-cpu pwr9 -o - | FileCheck %s +// RUN: not %clang_cc1 -triple powerpc64-unknown-unknown -emit-llvm-only %s \ +// RUN: -target-cpu pwr8 2>&1 | FileCheck %s --check-prefix=CHECK-NONPWR9-ERR +// RUN: not %clang_cc1 -target-feature -vsx -triple powerpc64-unknown-unknown -emit-llvm-only %s \ +// RUN: -target-cpu pwr9 2>&1 | FileCheck %s --check-prefix=CHECK-NOVSX-ERR + +extern double d; +extern float f; + +int test_builtin_ppc_compare_exp_uo() { +// CHECK-LABEL: @test_builtin_ppc_compare_exp_uo +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.uo(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_compare_exp_uo(d, d); +} + +int test_builtin_ppc_compare_exp_lt() { +// CHECK-LABEL: @test_builtin_ppc_compare_exp_lt +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.lt(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_compare_exp_lt(d, d); +} + +int test_builtin_ppc_compare_exp_gt() { +// CHECK-LABEL: @test_builtin_ppc_compare_exp_gt +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.gt(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_compare_exp_gt(d, d); +} + +int test_builtin_ppc_compare_exp_eq() { +// CHECK-LABEL: @test_builtin_ppc_compare_exp_eq +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.eq(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_compare_exp_eq(d, d); +} + +int test_builtin_ppc_test_data_class_d() { +// CHECK-LABEL: @test_builtin_ppc_test_data_class_d +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.test.data.class.d(double %0, i32 0) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_test_data_class(d, 0); +} + +int test_builtin_ppc_test_data_class_f() { +// CHECK-LABEL: @test_builtin_ppc_test_data_class_f +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.test.data.class.f(float %0, i32 0) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __builtin_ppc_test_data_class(f, 0); +} + +int test_compare_exp_uo() { +// CHECK-LABEL: @test_compare_exp_uo +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.uo(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __compare_exp_uo(d, d); +} + +int test_compare_exp_lt() { +// CHECK-LABEL: @test_compare_exp_lt +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.lt(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __compare_exp_lt(d, d); +} + +int test_compare_exp_gt() { +// CHECK-LABEL: @test_compare_exp_gt +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.gt(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __compare_exp_gt(d, d); +} + +int test_compare_exp_eq() { +// CHECK-LABEL: @test_compare_exp_eq +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.compare.exp.eq(double %0, double %1) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __compare_exp_eq(d, d); +} + +int test_test_data_class_d() { +// CHECK-LABEL: @test_test_data_class_d +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.test.data.class.d(double %0, i32 127) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __test_data_class(d, 127); +} + +int test_test_data_class_f() { +// CHECK-LABEL: @test_test_data_class_f +// CHECK: [[TMP:%.*]] = call i32 @llvm.ppc.test.data.class.f(float %0, i32 127) +// CHECK-NEXT: ret i32 [[TMP]] +// CHECK-NONPWR9-ERR: error: this builtin is only valid on POWER9 or later CPUs +// CHECK-NOVSX-ERR: error: this builtin requires VSX to be enabled + return __test_data_class(f, 127); +} diff --git a/llvm/include/llvm/IR/IntrinsicsPowerPC.td b/llvm/include/llvm/IR/IntrinsicsPowerPC.td --- a/llvm/include/llvm/IR/IntrinsicsPowerPC.td +++ b/llvm/include/llvm/IR/IntrinsicsPowerPC.td @@ -1720,6 +1720,28 @@ Intrinsic<[llvm_double_ty], [llvm_double_ty], [IntrNoMem]>; def int_ppc_frsqrtes : GCCBuiltin<"__builtin_ppc_frsqrtes">, Intrinsic<[llvm_float_ty], [llvm_float_ty], [IntrNoMem]>; + def int_ppc_compare_exp_uo : GCCBuiltin<"__builtin_ppc_compare_exp_uo">, + Intrinsic<[llvm_i32_ty], + [llvm_double_ty, llvm_double_ty], + [IntrNoMem]>; + def int_ppc_compare_exp_lt : GCCBuiltin<"__builtin_ppc_compare_exp_lt">, + Intrinsic<[llvm_i32_ty], + [llvm_double_ty, llvm_double_ty], + [IntrNoMem]>; + def int_ppc_compare_exp_gt : GCCBuiltin<"__builtin_ppc_compare_exp_gt">, + Intrinsic<[llvm_i32_ty], + [llvm_double_ty, llvm_double_ty], + [IntrNoMem]>; + def int_ppc_compare_exp_eq : GCCBuiltin<"__builtin_ppc_compare_exp_eq">, + Intrinsic<[llvm_i32_ty], + [llvm_double_ty, llvm_double_ty], + [IntrNoMem]>; + def int_ppc_test_data_class_d : Intrinsic<[llvm_i32_ty], + [llvm_double_ty, llvm_i32_ty], + [IntrNoMem, ImmArg>]>; + def int_ppc_test_data_class_f : Intrinsic<[llvm_i32_ty], + [llvm_float_ty, llvm_i32_ty], + [IntrNoMem, ImmArg>]>; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -10373,6 +10373,52 @@ } return DAG.getMergeValues(RetOps, dl); } + case Intrinsic::ppc_compare_exp_lt: + case Intrinsic::ppc_compare_exp_gt: + case Intrinsic::ppc_compare_exp_eq: + case Intrinsic::ppc_compare_exp_uo: { + unsigned Pred; + switch (IntrinsicID) { + case Intrinsic::ppc_compare_exp_lt: + Pred = PPC::PRED_LT; + break; + case Intrinsic::ppc_compare_exp_gt: + Pred = PPC::PRED_GT; + break; + case Intrinsic::ppc_compare_exp_eq: + Pred = PPC::PRED_EQ; + break; + case Intrinsic::ppc_compare_exp_uo: + Pred = PPC::PRED_UN; + break; + } + return SDValue( + DAG.getMachineNode( + PPC::SELECT_CC_I4, dl, MVT::i32, + {SDValue(DAG.getMachineNode(PPC::XSCMPEXPDP, dl, MVT::i32, + Op.getOperand(1), Op.getOperand(2)), + 0), + DAG.getConstant(1, dl, MVT::i32), DAG.getConstant(0, dl, MVT::i32), + DAG.getTargetConstant(Pred, dl, MVT::i32)}), + 0); + } + case Intrinsic::ppc_test_data_class_d: + case Intrinsic::ppc_test_data_class_f: { + unsigned CmprOpc; + if (IntrinsicID == Intrinsic::ppc_test_data_class_d) + CmprOpc = PPC::XSTSTDCDP; + else + CmprOpc = PPC::XSTSTDCSP; + return SDValue( + DAG.getMachineNode( + PPC::SELECT_CC_I4, dl, MVT::i32, + {SDValue(DAG.getMachineNode(CmprOpc, dl, MVT::i32, Op.getOperand(2), + Op.getOperand(1)), + 0), + DAG.getConstant(1, dl, MVT::i32), DAG.getConstant(0, dl, MVT::i32), + DAG.getTargetConstant(PPC::PRED_EQ, dl, MVT::i32)}), + 0); + } } // If this is a lowered altivec predicate compare, CompareOpc is set to the diff --git a/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-test.ll b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-test.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-test.ll @@ -0,0 +1,99 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-unknown \ +; RUN: -mcpu=pwr9 < %s | FileCheck %s +; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-unknown \ +; RUN: -mcpu=pwr9 < %s | FileCheck %s +; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-aix \ +; RUN: -mcpu=pwr9 < %s | FileCheck %s +; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \ +; RUN: -mcpu=pwr9 < %s | FileCheck %s + +define i32 @test_builtin_ppc_compare_exp_eq(double %d) { +; CHECK-LABEL: test_builtin_ppc_compare_exp_eq: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xscmpexpdp 0, 1, 1 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: iseleq 3, 4, 3 +; CHECK-NEXT: blr +entry: + %0 = tail call i32 @llvm.ppc.compare.exp.eq(double %d, double %d) + ret i32 %0 +} + +declare i32 @llvm.ppc.compare.exp.eq(double, double) + +define i32 @test_builtin_ppc_compare_exp_lt(double %d) { +; CHECK-LABEL: test_builtin_ppc_compare_exp_lt: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xscmpexpdp 0, 1, 1 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: isellt 3, 4, 3 +; CHECK-NEXT: blr +entry: + %0 = tail call i32 @llvm.ppc.compare.exp.lt(double %d, double %d) + ret i32 %0 +} + +declare i32 @llvm.ppc.compare.exp.lt(double, double) + +define i32 @test_builtin_ppc_compare_exp_gt(double %d) { +; CHECK-LABEL: test_builtin_ppc_compare_exp_gt: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xscmpexpdp 0, 1, 1 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: iselgt 3, 4, 3 +; CHECK-NEXT: blr +entry: + %0 = tail call i32 @llvm.ppc.compare.exp.gt(double %d, double %d) + ret i32 %0 +} + +declare i32 @llvm.ppc.compare.exp.gt(double, double) + +define i32 @test_builtin_ppc_compare_exp_uo(double %d) { +; CHECK-LABEL: test_builtin_ppc_compare_exp_uo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xscmpexpdp 0, 1, 1 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: isel 3, 4, 3, 3 +; CHECK-NEXT: blr +entry: + %0 = tail call i32 @llvm.ppc.compare.exp.uo(double %d, double %d) + ret i32 %0 +} + +declare i32 @llvm.ppc.compare.exp.uo(double, double) + +define i32 @test_builtin_ppc_test_data_class_d(double %d) { +; CHECK-LABEL: test_builtin_ppc_test_data_class_d: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xststdcdp 0, 1, 0 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: iseleq 3, 4, 3 +; CHECK-NEXT: blr +entry: + %test_data_class = tail call i32 @llvm.ppc.test.data.class.d(double %d, i32 0) + ret i32 %test_data_class +} + +declare i32 @llvm.ppc.test.data.class.d(double, i32 immarg) + +define i32 @test_builtin_ppc_test_data_class_f(float %f) { +; CHECK-LABEL: test_builtin_ppc_test_data_class_f: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xststdcsp 0, 1, 127 +; CHECK-NEXT: li 3, 0 +; CHECK-NEXT: li 4, 1 +; CHECK-NEXT: iseleq 3, 4, 3 +; CHECK-NEXT: blr +entry: + %test_data_class = tail call i32 @llvm.ppc.test.data.class.f(float %f, i32 127) + ret i32 %test_data_class +} + +declare i32 @llvm.ppc.test.data.class.f(float, i32 immarg)