From d73a8b26d2068c7d5eee964e0d92936b6f970836 Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Fri, 31 May 2024 07:11:55 +0800 Subject: [PATCH] [CIR][CIRGen] Add CIRGen for binary fp2fp builtin operations (#616) This PR adds the following operations for the builtin binary fp2fp functions: - `cir.copysign` for `__builtin_copysign`; - `cir.fmax` for `__builtin_fmax`; - `cir.fmin` for `__builtin_fmin`; - `cir.fmod` for `__builtin_fmod`; - `cir.pow` for `__builtin_pow`. This PR also includes CIRGen support for these new operations. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 20 ++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 56 ++++- .../Dialect/Transforms/LoweringPrepare.cpp | 53 ++++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 28 ++- .../test/CIR/CodeGen/builtin-floating-point.c | 224 +++++++++++++++++- .../test/CIR/Lowering/builtin-binary-fp2fp.c | 132 +++++++++++ 6 files changed, 501 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/Lowering/builtin-binary-fp2fp.c diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 1c4624ef35b51f..7b5967066b3022 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3324,6 +3324,26 @@ def SinOp : UnaryFPToFPBuiltinOp<"sin">; def SqrtOp : UnaryFPToFPBuiltinOp<"sqrt">; def TruncOp : UnaryFPToFPBuiltinOp<"trunc">; +class BinaryFPToFPBuiltinOp + : CIR_Op { + let summary = [{ + libc builtin equivalent ignoring floating-point exceptions and errno. + }]; + + let arguments = (ins CIR_AnyFloat:$lhs, CIR_AnyFloat:$rhs); + let results = (outs CIR_AnyFloat:$result); + + let assemblyFormat = [{ + $lhs `,` $rhs `:` qualified(type($lhs)) attr-dict + }]; +} + +def CopysignOp : BinaryFPToFPBuiltinOp<"copysign">; +def FMaxOp : BinaryFPToFPBuiltinOp<"fmax">; +def FMinOp : BinaryFPToFPBuiltinOp<"fmin">; +def FModOp : BinaryFPToFPBuiltinOp<"fmod">; +def PowOp : BinaryFPToFPBuiltinOp<"pow">; + //===----------------------------------------------------------------------===// // Branch Probability Operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 9d2ac841327205..dbb9cd6b77ad96 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -57,6 +57,36 @@ static RValue buildUnaryFPBuiltin(CIRGenFunction &CGF, const CallExpr &E) { return RValue::get(Call->getResult(0)); } +template +static RValue buildBinaryFPBuiltin(CIRGenFunction &CGF, const CallExpr &E) { + auto Arg0 = CGF.buildScalarExpr(E.getArg(0)); + auto Arg1 = CGF.buildScalarExpr(E.getArg(1)); + + auto Loc = CGF.getLoc(E.getExprLoc()); + auto Ty = CGF.ConvertType(E.getType()); + auto Call = CGF.getBuilder().create(Loc, Ty, Arg0, Arg1); + + return RValue::get(Call->getResult(0)); +} + +template +static mlir::Value buildBinaryMaybeConstrainedFPBuiltin(CIRGenFunction &CGF, + const CallExpr &E) { + auto Arg0 = CGF.buildScalarExpr(E.getArg(0)); + auto Arg1 = CGF.buildScalarExpr(E.getArg(1)); + + auto Loc = CGF.getLoc(E.getExprLoc()); + auto Ty = CGF.ConvertType(E.getType()); + + if (CGF.getBuilder().getIsFPConstrained()) { + CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(CGF, &E); + llvm_unreachable("constrained FP operations are NYI"); + } else { + auto Call = CGF.getBuilder().create(Loc, Ty, Arg0, Arg1); + return Call->getResult(0); + } +} + template static RValue buildBuiltinBitOp(CIRGenFunction &CGF, const CallExpr *E, @@ -290,8 +320,10 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BIcopysignl: case Builtin::BI__builtin_copysign: case Builtin::BI__builtin_copysignf: - case Builtin::BI__builtin_copysignf16: case Builtin::BI__builtin_copysignl: + return buildBinaryFPBuiltin(*this, *E); + + case Builtin::BI__builtin_copysignf16: case Builtin::BI__builtin_copysignf128: llvm_unreachable("NYI"); @@ -360,8 +392,11 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BIfmaxl: case Builtin::BI__builtin_fmax: case Builtin::BI__builtin_fmaxf: - case Builtin::BI__builtin_fmaxf16: case Builtin::BI__builtin_fmaxl: + return RValue::get( + buildBinaryMaybeConstrainedFPBuiltin(*this, *E)); + + case Builtin::BI__builtin_fmaxf16: case Builtin::BI__builtin_fmaxf128: llvm_unreachable("NYI"); @@ -370,8 +405,11 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BIfminl: case Builtin::BI__builtin_fmin: case Builtin::BI__builtin_fminf: - case Builtin::BI__builtin_fminf16: case Builtin::BI__builtin_fminl: + return RValue::get( + buildBinaryMaybeConstrainedFPBuiltin(*this, *E)); + + case Builtin::BI__builtin_fminf16: case Builtin::BI__builtin_fminf128: llvm_unreachable("NYI"); @@ -382,11 +420,12 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BIfmodl: case Builtin::BI__builtin_fmod: case Builtin::BI__builtin_fmodf: - case Builtin::BI__builtin_fmodf16: case Builtin::BI__builtin_fmodl: - case Builtin::BI__builtin_fmodf128: { + return buildBinaryFPBuiltin(*this, *E); + + case Builtin::BI__builtin_fmodf16: + case Builtin::BI__builtin_fmodf128: llvm_unreachable("NYI"); - } case Builtin::BIlog: case Builtin::BIlogf: @@ -432,8 +471,11 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BIpowl: case Builtin::BI__builtin_pow: case Builtin::BI__builtin_powf: - case Builtin::BI__builtin_powf16: case Builtin::BI__builtin_powl: + return RValue::get( + buildBinaryMaybeConstrainedFPBuiltin(*this, *E)); + + case Builtin::BI__builtin_powf16: case Builtin::BI__builtin_powf128: llvm_unreachable("NYI"); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index bb8c452b46ba00..bbda97e45c80f4 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -80,6 +80,8 @@ struct LoweringPreparePass : public LoweringPrepareBase { void lowerIterEndOp(IterEndOp op); void lowerArrayDtor(ArrayDtor op); void lowerArrayCtor(ArrayCtor op); + void lowerFModOp(FModOp op); + void lowerPowOp(PowOp op); /// Build the function that initializes the specified global FuncOp buildCXXGlobalVarDeclInitFunc(GlobalOp op); @@ -625,6 +627,49 @@ void LoweringPreparePass::lowerIterEndOp(IterEndOp op) { op.erase(); } +static void lowerBinaryFPToFPBuiltinOp(LoweringPreparePass &pass, + mlir::Operation *op, + llvm::StringRef floatRtFuncName, + llvm::StringRef doubleRtFuncName, + llvm::StringRef longDoubleRtFuncName) { + mlir::Type ty = op->getResult(0).getType(); + + llvm::StringRef rtFuncName; + if (ty.isa()) + rtFuncName = floatRtFuncName; + else if (ty.isa()) + rtFuncName = doubleRtFuncName; + else if (ty.isa()) + rtFuncName = longDoubleRtFuncName; + else + llvm_unreachable("unknown binary fp2fp builtin operand type"); + + CIRBaseBuilderTy builder(*pass.theModule.getContext()); + builder.setInsertionPointToStart(pass.theModule.getBody()); + + auto rtFuncTy = mlir::cir::FuncType::get({ty, ty}, ty); + FuncOp rtFunc = + pass.buildRuntimeFunction(builder, rtFuncName, op->getLoc(), rtFuncTy); + + auto lhs = op->getOperand(0); + auto rhs = op->getOperand(1); + + builder.setInsertionPointAfter(op); + auto call = builder.create(op->getLoc(), rtFunc, + mlir::ValueRange{lhs, rhs}); + + op->replaceAllUsesWith(call); + op->erase(); +} + +void LoweringPreparePass::lowerFModOp(FModOp op) { + lowerBinaryFPToFPBuiltinOp(*this, op, "fmodf", "fmod", "fmodl"); +} + +void LoweringPreparePass::lowerPowOp(PowOp op) { + lowerBinaryFPToFPBuiltinOp(*this, op, "powf", "pow", "powl"); +} + void LoweringPreparePass::runOnOp(Operation *op) { if (auto threeWayCmp = dyn_cast(op)) { lowerThreeWayCmpOp(threeWayCmp); @@ -650,6 +695,10 @@ void LoweringPreparePass::runOnOp(Operation *op) { } else if (auto globalDtor = fnOp.getGlobalDtorAttr()) { globalDtorList.push_back(globalDtor); } + } else if (auto fmodOp = dyn_cast(op)) { + lowerFModOp(fmodOp); + } else if (auto powOp = dyn_cast(op)) { + lowerPowOp(powOp); } } @@ -663,8 +712,8 @@ void LoweringPreparePass::runOnOperation() { SmallVector opsToTransform; op->walk([&](Operation *op) { if (isa( - op)) + IterEndOp, IterBeginOp, ArrayCtor, ArrayDtor, mlir::cir::FuncOp, + FModOp, PowOp>(op)) opsToTransform.push_back(op); }); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1d4e50e78e2ba2..28054f42f0f0a1 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -3161,6 +3161,31 @@ class CIRCmpThreeWayOpLowering } }; +template +class CIRBinaryFPToFPBuiltinOpLowering + : public mlir::OpConversionPattern { +public: + using mlir::OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(CIROp op, + typename mlir::OpConversionPattern::OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + auto resTy = this->getTypeConverter()->convertType(op.getType()); + rewriter.replaceOpWithNewOp(op, resTy, adaptor.getLhs(), + adaptor.getRhs()); + return mlir::success(); + } +}; + +using CIRCopysignOpLowering = + CIRBinaryFPToFPBuiltinOpLowering; +using CIRFMaxOpLowering = + CIRBinaryFPToFPBuiltinOpLowering; +using CIRFMinOpLowering = + CIRBinaryFPToFPBuiltinOpLowering; + void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, mlir::TypeConverter &converter) { patterns.add(patterns.getContext()); @@ -3185,7 +3210,8 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, CIRStackRestoreLowering, CIRUnreachableLowering, CIRTrapLowering, CIRInlineAsmOpLowering, CIRSetBitfieldLowering, CIRGetBitfieldLowering, CIRPrefetchLowering, CIRObjSizeOpLowering, CIRIsConstantOpLowering, - CIRCmpThreeWayOpLowering>(converter, patterns.getContext()); + CIRCmpThreeWayOpLowering, CIRCopysignOpLowering, CIRFMaxOpLowering, + CIRFMinOpLowering>(converter, patterns.getContext()); } namespace { diff --git a/clang/test/CIR/CodeGen/builtin-floating-point.c b/clang/test/CIR/CodeGen/builtin-floating-point.c index 82099f666f45ab..c47f390b8eac03 100644 --- a/clang/test/CIR/CodeGen/builtin-floating-point.c +++ b/clang/test/CIR/CodeGen/builtin-floating-point.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffast-math -fclangir -emit-cir %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple aarch64-apple-darwin-macho -ffast-math -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffast-math -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t1.cir 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-apple-darwin-macho -ffast-math -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t1.cir 2>&1 | FileCheck %s --check-prefix=AARCH64 // ceil @@ -616,3 +616,223 @@ long double call_truncl(long double f) { // CHECK: {{.+}} = cir.trunc {{.+}} : !cir.long_double // AARCH64: {{.+}} = cir.trunc {{.+}} : !cir.long_double } + +// copysign + +float my_copysignf(float x, float y) { + return __builtin_copysignf(x, y); + // CHECK: cir.func @my_copysignf + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.float +} + +double my_copysign(double x, double y) { + return __builtin_copysign(x, y); + // CHECK: cir.func @my_copysign + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.double +} + +long double my_copysignl(long double x, long double y) { + return __builtin_copysignl(x, y); + // CHECK: cir.func @my_copysignl + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.long_double +} + +float copysignf(float, float); +double copysign(double, double); +long double copysignl(long double, long double); + +float call_copysignf(float x, float y) { + return copysignf(x, y); + // CHECK: cir.func @call_copysignf + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.float +} + +double call_copysign(double x, double y) { + return copysign(x, y); + // CHECK: cir.func @call_copysign + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.double +} + +long double call_copysignl(long double x, long double y) { + return copysignl(x, y); + // CHECK: cir.func @call_copysignl + // CHECK: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.copysign %{{.+}}, %{{.+}} : !cir.long_double +} + +// fmax + +float my_fmaxf(float x, float y) { + return __builtin_fmaxf(x, y); + // CHECK: cir.func @my_fmaxf + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.float +} + +double my_fmax(double x, double y) { + return __builtin_fmax(x, y); + // CHECK: cir.func @my_fmax + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.double +} + +long double my_fmaxl(long double x, long double y) { + return __builtin_fmaxl(x, y); + // CHECK: cir.func @my_fmaxl + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.long_double +} + +float fmaxf(float, float); +double fmax(double, double); +long double fmaxl(long double, long double); + +float call_fmaxf(float x, float y) { + return fmaxf(x, y); + // CHECK: cir.func @call_fmaxf + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.float +} + +double call_fmax(double x, double y) { + return fmax(x, y); + // CHECK: cir.func @call_fmax + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.double +} + +long double call_fmaxl(long double x, long double y) { + return fmaxl(x, y); + // CHECK: cir.func @call_fmaxl + // CHECK: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmax %{{.+}}, %{{.+}} : !cir.long_double +} + +// fmin + +float my_fminf(float x, float y) { + return __builtin_fminf(x, y); + // CHECK: cir.func @my_fminf + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.float +} + +double my_fmin(double x, double y) { + return __builtin_fmin(x, y); + // CHECK: cir.func @my_fmin + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.double +} + +long double my_fminl(long double x, long double y) { + return __builtin_fminl(x, y); + // CHECK: cir.func @my_fminl + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.long_double +} + +float fminf(float, float); +double fmin(double, double); +long double fminl(long double, long double); + +float call_fminf(float x, float y) { + return fminf(x, y); + // CHECK: cir.func @call_fminf + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.float +} + +double call_fmin(double x, double y) { + return fmin(x, y); + // CHECK: cir.func @call_fmin + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.double +} + +long double call_fminl(long double x, long double y) { + return fminl(x, y); + // CHECK: cir.func @call_fminl + // CHECK: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmin %{{.+}}, %{{.+}} : !cir.long_double +} + +// fmod + +float my_fmodf(float x, float y) { + return __builtin_fmodf(x, y); + // CHECK: cir.func @my_fmodf + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.float +} + +double my_fmod(double x, double y) { + return __builtin_fmod(x, y); + // CHECK: cir.func @my_fmod + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.double +} + +long double my_fmodl(long double x, long double y) { + return __builtin_fmodl(x, y); + // CHECK: cir.func @my_fmodl + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.long_double +} + +float fmodf(float, float); +double fmod(double, double); +long double fmodl(long double, long double); + +float call_fmodf(float x, float y) { + return fmodf(x, y); + // CHECK: cir.func @call_fmodf + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.float +} + +double call_fmod(double x, double y) { + return fmod(x, y); + // CHECK: cir.func @call_fmod + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.double +} + +long double call_fmodl(long double x, long double y) { + return fmodl(x, y); + // CHECK: cir.func @call_fmodl + // CHECK: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.fmod %{{.+}}, %{{.+}} : !cir.long_double +} + +// pow + +float my_powf(float x, float y) { + return __builtin_powf(x, y); + // CHECK: cir.func @my_powf + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.float +} + +double my_pow(double x, double y) { + return __builtin_pow(x, y); + // CHECK: cir.func @my_pow + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.double +} + +long double my_powl(long double x, long double y) { + return __builtin_powl(x, y); + // CHECK: cir.func @my_powl + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.long_double +} + +float powf(float, float); +double pow(double, double); +long double powl(long double, long double); + +float call_powf(float x, float y) { + return powf(x, y); + // CHECK: cir.func @call_powf + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.float +} + +double call_pow(double x, double y) { + return pow(x, y); + // CHECK: cir.func @call_pow + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.double +} + +long double call_powl(long double x, long double y) { + return powl(x, y); + // CHECK: cir.func @call_powl + // CHECK: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.long_double + // AARCH64: %{{.+}} = cir.pow %{{.+}}, %{{.+}} : !cir.long_double +} diff --git a/clang/test/CIR/Lowering/builtin-binary-fp2fp.c b/clang/test/CIR/Lowering/builtin-binary-fp2fp.c new file mode 100644 index 00000000000000..acde798fdf11c7 --- /dev/null +++ b/clang/test/CIR/Lowering/builtin-binary-fp2fp.c @@ -0,0 +1,132 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +// copysign + +float my_copysignf(float x, float y) { + return __builtin_copysignf(x, y); +} + +// LLVM: define float @my_copysignf +// LLVM: %{{.+}} = call float @llvm.copysign.f32(float %{{.+}}, float %{{.+}}) +// LLVM: } + +double my_copysign(double x, double y) { + return __builtin_copysign(x, y); +} + +// LLVM: define double @my_copysign +// LLVM: %{{.+}} = call double @llvm.copysign.f64(double %{{.+}}, double %{{.+}}) +// LLVM: } + +long double my_copysignl(long double x, long double y) { + return __builtin_copysignl(x, y); +} + +// LLVM: define x86_fp80 @my_copysignl +// LLVM: %{{.+}} = call x86_fp80 @llvm.copysign.f80(x86_fp80 %{{.+}}, x86_fp80 %{{.+}}) +// LLVM: } + +// fmax + +float my_fmaxf(float x, float y) { + return __builtin_fmaxf(x, y); +} + +// LLVM: define float @my_fmaxf +// LLVM: %{{.+}} = call float @llvm.maxnum.f32(float %{{.+}}, float %{{.+}}) +// LLVM: } + +double my_fmax(double x, double y) { + return __builtin_fmax(x, y); +} + +// LLVM: define double @my_fmax +// LLVM: %{{.+}} = call double @llvm.maxnum.f64(double %{{.+}}, double %{{.+}}) +// LLVM: } + +long double my_fmaxl(long double x, long double y) { + return __builtin_fmaxl(x, y); +} + +// LLVM: define x86_fp80 @my_fmaxl +// LLVM: %{{.+}} = call x86_fp80 @llvm.maxnum.f80(x86_fp80 %{{.+}}, x86_fp80 %{{.+}}) +// LLVM: } + +// fmin + +float my_fminf(float x, float y) { + return __builtin_fminf(x, y); +} + +// LLVM: define float @my_fminf +// LLVM: %{{.+}} = call float @llvm.minnum.f32(float %{{.+}}, float %{{.+}}) +// LLVM: } + +double my_fmin(double x, double y) { + return __builtin_fmin(x, y); +} + +// LLVM: define double @my_fmin +// LLVM: %{{.+}} = call double @llvm.minnum.f64(double %{{.+}}, double %{{.+}}) +// LLVM: } + +long double my_fminl(long double x, long double y) { + return __builtin_fminl(x, y); +} + +// LLVM: define x86_fp80 @my_fminl +// LLVM: %{{.+}} = call x86_fp80 @llvm.minnum.f80(x86_fp80 %{{.+}}, x86_fp80 %{{.+}}) +// LLVM: } + +// fmod + +float my_fmodf(float x, float y) { + return __builtin_fmodf(x, y); +} + +// LLVM: define float @my_fmodf +// LLVM: %{{.+}} = call float @fmodf(float %{{.+}}, float %{{.+}}) +// LLVM: } + +double my_fmod(double x, double y) { + return __builtin_fmod(x, y); +} + +// LLVM: define double @my_fmod +// LLVM: %{{.+}} = call double @fmod(double %{{.+}}, double %{{.+}}) +// LLVM: } + +long double my_fmodl(long double x, long double y) { + return __builtin_fmodl(x, y); +} + +// LLVM: define x86_fp80 @my_fmodl +// LLVM: %{{.+}} = call x86_fp80 @fmodl(x86_fp80 %{{.+}}, x86_fp80 %{{.+}}) +// LLVM: } + +// pow + +float my_powf(float x, float y) { + return __builtin_powf(x, y); +} + +// LLVM: define float @my_powf +// LLVM: %{{.+}} = call float @powf(float %{{.+}}, float %{{.+}}) +// LLVM: } + +double my_pow(double x, double y) { + return __builtin_pow(x, y); +} + +// LLVM: define double @my_pow +// LLVM: %{{.+}} = call double @pow(double %{{.+}}, double %{{.+}}) +// LLVM: } + +long double my_powl(long double x, long double y) { + return __builtin_powl(x, y); +} + +// LLVM: define x86_fp80 @my_powl +// LLVM: %{{.+}} = call x86_fp80 @powl(x86_fp80 %{{.+}}, x86_fp80 %{{.+}}) +// LLVM: }