https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/142981
>From 1e759f0ec3b010e64c44cc4e5f87ffbe8f4e3d21 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 9 Jun 2025 14:52:55 +0200 Subject: [PATCH 1/5] [CIR] Upstream minimal builtin function call support This patch adds all bits required to implement builtin function calls to ClangIR. It doesn't actually implement any of the builtins except those that fold to a constant ahead of CodeGen (__builtin_is_constant_evaluated() being one example). It also adds the LLVMIntrinsicCallOp instruction to the CIR dialect. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 34 ++ clang/include/clang/CIR/MissingFeatures.h | 2 + clang/lib/CIR/CodeGen/CIRGenBuilder.cpp | 22 + clang/lib/CIR/CodeGen/CIRGenBuilder.h | 13 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 453 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenCall.h | 43 +- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 53 ++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 16 + clang/lib/CIR/CodeGen/CIRGenModule.h | 5 + clang/lib/CIR/CodeGen/CIRGenValue.h | 15 + clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/test/CIR/CodeGen/builtin_call.cpp | 30 ++ 12 files changed, 679 insertions(+), 8 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp create mode 100644 clang/test/CIR/CodeGen/builtin_call.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 8579f7066e4fe..787e89fd06b56 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1854,6 +1854,40 @@ def FuncOp : CIR_Op<"func", [ let hasVerifier = 1; } +//===----------------------------------------------------------------------===// +// LLVMIntrinsicCallOp +//===----------------------------------------------------------------------===// + +def LLVMIntrinsicCallOp : CIR_Op<"llvm.intrinsic"> { + let summary = "Call to llvm intrinsic functions that is not defined in CIR"; + let description = [{ + The `cir.llvm.intrinsic` operation represents a call-like expression which has + a return type and arguments that map directly to an llvm intrinsic. + It only records its own name (`intrinsic_name`). + }]; + + let results = (outs Optional<CIR_AnyType>:$result); + let arguments = (ins + StrAttr:$intrinsic_name, Variadic<CIR_AnyType>:$arg_ops); + + let skipDefaultBuilders = 1; + + let assemblyFormat = [{ + $intrinsic_name $arg_ops `:` functional-type($arg_ops, $result) attr-dict + }]; + + let builders = [ + OpBuilder<(ins "mlir::StringAttr":$intrinsic_name, "mlir::Type":$resType, + CArg<"mlir::ValueRange", "{}">:$operands), [{ + $_state.addAttribute("intrinsic_name", intrinsic_name); + $_state.addOperands(operands); + if (resType) + $_state.addTypes(resType); + }]>, + ]; + +} + //===----------------------------------------------------------------------===// // CallOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 72d882beb2244..7db13a846e23a 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -222,6 +222,8 @@ struct MissingFeatures { static bool instrumentation() { return false; } static bool cleanupAfterErrorDiags() { return false; } static bool cxxRecordStaticMembers() { return false; } + static bool intrinsics() { return false; } + static bool attributeNoBuiltin() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp index 4c8c6ed289c3b..bdb6914768b2a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -39,6 +39,28 @@ mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin, return create<cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr, idx); } +cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, + llvm::APSInt intVal) { + bool isSigned = intVal.isSigned(); + auto width = intVal.getBitWidth(); + cir::IntType t = isSigned ? getSIntNTy(width) : getUIntNTy(width); + return getConstInt(loc, t, + isSigned ? intVal.getSExtValue() : intVal.getZExtValue()); +} + +cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, + llvm::APInt intVal) { + auto width = intVal.getBitWidth(); + cir::IntType t = getUIntNTy(width); + return getConstInt(loc, t, intVal.getZExtValue()); +} + +cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, mlir::Type t, + uint64_t c) { + assert(mlir::isa<cir::IntType>(t) && "expected cir::IntType"); + return create<cir::ConstantOp>(loc, cir::IntAttr::get(t, c)); +} + // This can't be defined in Address.h because that file is included by // CIRGenBuilder.h Address Address::withElementType(CIRGenBuilderTy &builder, diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 03077ee062a65..f2c318b37ad43 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -229,6 +229,19 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { cir::IntType getUInt32Ty() { return typeCache.UInt32Ty; } cir::IntType getUInt64Ty() { return typeCache.UInt64Ty; } + cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal); + + cir::ConstantOp getConstInt(mlir::Location loc, llvm::APInt intVal); + + cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t, uint64_t c); + + cir::ConstantOp getConstFP(mlir::Location loc, mlir::Type t, + llvm::APFloat fpVal) { + assert((mlir::isa<cir::SingleType, cir::DoubleType>(t)) && + "expected cir::SingleType or cir::DoubleType"); + return create<cir::ConstantOp>(loc, getAttr<cir::FPAttr>(t, fpVal)); + } + bool isInt8Ty(mlir::Type i) { return i == typeCache.UInt8Ty || i == typeCache.SInt8Ty; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp new file mode 100644 index 0000000000000..60a69f1182b5c --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -0,0 +1,453 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code to emit Builtin calls as CIR or a function call to be +// later resolved. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenCall.h" +#include "CIRGenFunction.h" +#include "CIRGenModule.h" +#include "CIRGenValue.h" +#include "clang/AST/Expr.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "clang/CIR/MissingFeatures.h" +#include "llvm/IR/Intrinsics.h" + +#include "clang/AST/GlobalDecl.h" +#include "clang/Basic/Builtins.h" + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Value.h" +#include "mlir/Support/LLVM.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::CIRGen; +using namespace cir; +using namespace llvm; + +static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd, + const CallExpr *e, mlir::Operation *calleeValue) { + CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd)); + return cgf.emitCall(e->getCallee()->getType(), callee, e, ReturnValueSlot()); +} + +static mlir::Type +decodeFixedType(CIRGenFunction &cgf, + ArrayRef<llvm::Intrinsic::IITDescriptor> &infos) { + using namespace llvm::Intrinsic; + + auto *context = &cgf.getMLIRContext(); + IITDescriptor descriptor = infos.front(); + infos = infos.slice(1); + + switch (descriptor.Kind) { + case IITDescriptor::Void: + return VoidType::get(context); + case IITDescriptor::Integer: + return IntType::get(context, descriptor.Integer_Width, /*isSigned=*/true); + case IITDescriptor::Float: + return SingleType::get(context); + case IITDescriptor::Double: + return DoubleType::get(context); + default: + cgf.cgm.errorNYI("intrinsic return types"); + return VoidType::get(context); + } +} + +// llvm::Intrinsics accepts only LLVMContext. We need to reimplement it here. +static cir::FuncType getIntrinsicType(CIRGenFunction &cgf, + llvm::Intrinsic::ID id) { + using namespace llvm::Intrinsic; + + SmallVector<IITDescriptor, 8> table; + getIntrinsicInfoTableEntries(id, table); + + ArrayRef<IITDescriptor> tableRef = table; + mlir::Type resultTy = decodeFixedType(cgf, tableRef); + + SmallVector<mlir::Type, 8> argTypes; + while (!tableRef.empty()) + argTypes.push_back(decodeFixedType(cgf, tableRef)); + + return FuncType::get(argTypes, resultTy); +} + +static mlir::Value emitTargetArchBuiltinExpr(CIRGenFunction *cgf, + unsigned builtinID, + const CallExpr *e, + ReturnValueSlot returnValue, + llvm::Triple::ArchType arch) { + return {}; +} + +mlir::Value CIRGenFunction::emitTargetBuiltinExpr(unsigned builtinID, + const CallExpr *e, + ReturnValueSlot returnValue) { + if (getContext().BuiltinInfo.isAuxBuiltinID(builtinID)) { + assert(getContext().getAuxTargetInfo() && "Missing aux target info"); + return emitTargetArchBuiltinExpr( + this, getContext().BuiltinInfo.getAuxBuiltinID(builtinID), e, + returnValue, getContext().getAuxTargetInfo()->getTriple().getArch()); + } + + return emitTargetArchBuiltinExpr(this, builtinID, e, returnValue, + getTarget().getTriple().getArch()); +} + +mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg(unsigned iceArguments, + unsigned idx, + const CallExpr *e) { + mlir::Value arg = {}; + if ((iceArguments & (1 << idx)) == 0) { + arg = emitScalarExpr(e->getArg(idx)); + } else { + // If this is required to be a constant, constant fold it so that we + // know that the generated intrinsic gets a ConstantInt. + std::optional<llvm::APSInt> result = + e->getArg(idx)->getIntegerConstantExpr(getContext()); + assert(result && "Expected argument to be a constant"); + arg = builder.getConstInt(getLoc(e->getSourceRange()), *result); + } + return arg; +} + +RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, + const CallExpr *e, + ReturnValueSlot returnValue) { + const FunctionDecl *fd = gd.getDecl()->getAsFunction(); + + // See if we can constant fold this builtin. If so, don't emit it at all. + // TODO: Extend this handling to all builtin calls that we can constant-fold. + Expr::EvalResult result; + if (e->isPRValue() && e->EvaluateAsRValue(result, cgm.getASTContext()) && + !result.hasSideEffects()) { + if (result.Val.isInt()) { + return RValue::get(builder.getConstInt(getLoc(e->getSourceRange()), + result.Val.getInt())); + } + if (result.Val.isFloat()) { + // Note: we are using result type of CallExpr to determine the type of + // the constant. Clang Codegen uses the result value to make judgement + // of the type. We feel it should be Ok to use expression type because + // it is hard to imagine a builtin function evaluates to + // a value that over/underflows its own defined type. + mlir::Type resTy = convertType(e->getType()); + return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), resTy, + result.Val.getFloat())); + } + } + + // If current long-double semantics is IEEE 128-bit, replace math builtins + // of long-double with f128 equivalent. + // TODO: This mutation should also be applied to other targets other than PPC, + // after backend supports IEEE 128-bit style libcalls. + if (getTarget().getTriple().isPPC64() && + &getTarget().getLongDoubleFormat() == &llvm::APFloat::IEEEquad()) { + cgm.errorNYI("long double builtin mutation"); + } + + // If the builtin has been declared explicitly with an assembler label, + // disable the specialized emitting below. Ideally we should communicate the + // rename in IR, or at least avoid generating the intrinsic calls that are + // likely to get lowered to the renamed library functions. + const unsigned builtinIDIfNoAsmLabel = + fd->hasAttr<AsmLabelAttr>() ? 0 : builtinID; + + std::optional<bool> errnoOverriden; + // ErrnoOverriden is true if math-errno is overriden via the + // '#pragma float_control(precise, on)'. This pragma disables fast-math, + // which implies math-errno. + if (e->hasStoredFPFeatures()) { + FPOptionsOverride op = e->getFPFeatures(); + if (op.hasMathErrnoOverride()) + errnoOverriden = op.getMathErrnoOverride(); + } + // True if 'atttibute__((optnone)) is used. This attibute overrides + // fast-math which implies math-errno. + bool optNone = curFuncDecl && curFuncDecl->hasAttr<OptimizeNoneAttr>(); + + // True if we are compiling at -O2 and errno has been disabled + // using the '#pragma float_control(precise, off)', and + // attribute opt-none hasn't been seen. + [[maybe_unused]] bool errnoOverridenToFalseWithOpt = + errnoOverriden.has_value() && !errnoOverriden.value() && !optNone && + cgm.getCodeGenOpts().OptimizationLevel != 0; + + // There are LLVM math intrinsics/instructions corresponding to math library + // functions except the LLVM op will never set errno while the math library + // might. Also, math builtins have the same semantics as their math library + // twins. Thus, we can transform math library and builtin calls to their + // LLVM counterparts if the call is marked 'const' (known to never set errno). + // In case FP exceptions are enabled, the experimental versions of the + // intrinsics model those. + [[maybe_unused]] bool constAlways = + getContext().BuiltinInfo.isConst(builtinID); + + // There's a special case with the fma builtins where they are always const + // if the target environment is GNU or the target is OS is Windows and we're + // targeting the MSVCRT.dll environment. + // FIXME: This list can be become outdated. Need to find a way to get it some + // other way. + switch (builtinID) { + case Builtin::BI__builtin_fma: + case Builtin::BI__builtin_fmaf: + case Builtin::BI__builtin_fmal: + case Builtin::BIfma: + case Builtin::BIfmaf: + case Builtin::BIfmal: + cgm.errorNYI("FMA builtins"); + break; + } + + bool constWithoutErrnoAndExceptions = + getContext().BuiltinInfo.isConstWithoutErrnoAndExceptions(builtinID); + bool constWithoutExceptions = + getContext().BuiltinInfo.isConstWithoutExceptions(builtinID); + + // ConstAttr is enabled in fast-math mode. In fast-math mode, math-errno is + // disabled. + // Math intrinsics are generated only when math-errno is disabled. Any pragmas + // or attributes that affect math-errno should prevent or allow math + // intrincs to be generated. Intrinsics are generated: + // 1- In fast math mode, unless math-errno is overriden + // via '#pragma float_control(precise, on)', or via an + // 'attribute__((optnone))'. + // 2- If math-errno was enabled on command line but overriden + // to false via '#pragma float_control(precise, off))' and + // 'attribute__((optnone))' hasn't been used. + // 3- If we are compiling with optimization and errno has been disabled + // via '#pragma float_control(precise, off)', and + // 'attribute__((optnone))' hasn't been used. + + bool constWithoutErrnoOrExceptions = + constWithoutErrnoAndExceptions || constWithoutExceptions; + bool generateIntrinsics = + (constAlways && !optNone) || + (!getLangOpts().MathErrno && + !(errnoOverriden.has_value() && errnoOverriden.value()) && !optNone); + if (!generateIntrinsics) { + generateIntrinsics = + constWithoutErrnoOrExceptions && !constWithoutErrnoAndExceptions; + if (!generateIntrinsics) + generateIntrinsics = + constWithoutErrnoOrExceptions && + (!getLangOpts().MathErrno && + !(errnoOverriden.has_value() && errnoOverriden.value()) && !optNone); + if (!generateIntrinsics) + generateIntrinsics = + constWithoutErrnoOrExceptions && errnoOverridenToFalseWithOpt; + } + + if (generateIntrinsics) { + assert(!cir::MissingFeatures::intrinsics()); + return {}; + } + + switch (builtinIDIfNoAsmLabel) { + default: + break; + } + + // If this is an alias for a lib function (e.g. __builtin_sin), emit + // the call using the normal call path, but using the unmangled + // version of the function name. + if (getContext().BuiltinInfo.isLibFunction(builtinID)) + return emitLibraryCall(*this, fd, e, + cgm.getBuiltinLibFunction(fd, builtinID)); + + // If this is a predefined lib function (e.g. malloc), emit the call + // using exactly the normal call path. + if (getContext().BuiltinInfo.isPredefinedLibFunction(builtinID)) + return emitLibraryCall(*this, fd, e, + emitScalarExpr(e->getCallee()).getDefiningOp()); + + // Check that a call to a target specific builtin has the correct target + // features. + // This is down here to avoid non-target specific builtins, however, if + // generic builtins start to require generic target features then we + // can move this up to the beginning of the function. + // checkTargetFeatures(E, FD); + + if ([[maybe_unused]] unsigned vectorWidth = + getContext().BuiltinInfo.getRequiredVectorWidth(builtinID)) + largestVectorWidth = std::max(largestVectorWidth, vectorWidth); + + // See if we have a target specific intrinsic. + std::string name = getContext().BuiltinInfo.getName(builtinID); + Intrinsic::ID intrinsicID = Intrinsic::not_intrinsic; + StringRef prefix = + llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch()); + if (!prefix.empty()) { + intrinsicID = Intrinsic::getIntrinsicForClangBuiltin(prefix.data(), name); + // NOTE we don't need to perform a compatibility flag check here since the + // intrinsics are declared in Builtins*.def via LANGBUILTIN which filter the + // MS builtins via ALL_MS_LANGUAGES and are filtered earlier. + if (intrinsicID == Intrinsic::not_intrinsic) + intrinsicID = Intrinsic::getIntrinsicForMSBuiltin(prefix.data(), name); + } + + if (intrinsicID != Intrinsic::not_intrinsic) { + unsigned iceArguments = 0; + ASTContext::GetBuiltinTypeError error; + getContext().GetBuiltinType(builtinID, error, &iceArguments); + assert(error == ASTContext::GE_None && "Should not codegen an error"); + + llvm::StringRef name = llvm::Intrinsic::getName(intrinsicID); + // cir::LLVMIntrinsicCallOp expects intrinsic name to not have prefix + // "llvm." For example, `llvm.nvvm.barrier0` should be passed as + // `nvvm.barrier0`. + if (!name.consume_front("llvm.")) + assert(false && "bad intrinsic name!"); + + cir::FuncType intrinsicType = getIntrinsicType(*this, intrinsicID); + + SmallVector<mlir::Value> args; + for (unsigned i = 0; i < e->getNumArgs(); i++) { + mlir::Value arg = emitScalarOrConstFoldImmArg(iceArguments, i, e); + mlir::Type argType = arg.getType(); + if (argType != intrinsicType.getInput(i)) { + // vector of pointers? + assert(!cir::MissingFeatures::addressSpace()); + } + + args.push_back(arg); + } + + auto intrinsicCall = builder.create<cir::LLVMIntrinsicCallOp>( + getLoc(e->getExprLoc()), builder.getStringAttr(name), + intrinsicType.getReturnType(), args); + + mlir::Type builtinReturnType = intrinsicCall.getResult().getType(); + mlir::Type retTy = intrinsicType.getReturnType(); + + if (builtinReturnType != retTy) { + // vector of pointers? + if (isa<cir::PointerType>(retTy)) { + assert(!cir::MissingFeatures::addressSpace()); + } + } + + if (isa<cir::VoidType>(retTy)) + return RValue::get(nullptr); + + return RValue::get(intrinsicCall.getResult()); + } + + // Some target-specific builtins can have aggregate return values, e.g. + // __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force + // ReturnValue to be non-null, so that the target-specific emission code can + // always just emit into it. + cir::TypeEvaluationKind evalKind = getEvaluationKind(e->getType()); + if (evalKind == cir::TEK_Aggregate && returnValue.isNull()) { + Address destPtr = + createMemTemp(e->getType(), getLoc(e->getSourceRange()), "agg.tmp"); + returnValue = ReturnValueSlot(destPtr, false); + } + + // Now see if we can emit a target-specific builtin. + if (auto v = emitTargetBuiltinExpr(builtinID, e, returnValue)) { + switch (evalKind) { + case cir::TEK_Scalar: + if (mlir::isa<cir::VoidType>(v.getType())) + return RValue::get(nullptr); + return RValue::get(v); + case cir::TEK_Aggregate: + return RValue::getAggregate(returnValue.getAddress(), + returnValue.isVolatile()); + case cir::TEK_Complex: + llvm_unreachable("No current target builtin returns complex"); + } + llvm_unreachable("Bad evaluation kind in EmitBuiltinExpr"); + } + + // cgm.errorUnsupported(e, "builtin function"); + + // Unknown builtin, for now just dump it out and return undef. + return getUndefRValue(e->getType()); +} + +/// Given a builtin id for a function like "__builtin_fabsf", return a Function* +/// for "fabsf". +cir::FuncOp CIRGenModule::getBuiltinLibFunction(const FunctionDecl *fd, + unsigned builtinID) { + assert(astContext.BuiltinInfo.isLibFunction(builtinID)); + + // Get the name, skip over the __builtin_ prefix (if necessary). We may have + // to build this up so provide a small stack buffer to handle the vast + // majority of names. + llvm::SmallString<64> name; + GlobalDecl d(fd); + + // TODO: This list should be expanded or refactored after all GCC-compatible + // std libcall builtins are implemented. + static SmallDenseMap<unsigned, StringRef, 64> f128Builtins{ + {Builtin::BI__builtin___fprintf_chk, "__fprintf_chkieee128"}, + {Builtin::BI__builtin___printf_chk, "__printf_chkieee128"}, + {Builtin::BI__builtin___snprintf_chk, "__snprintf_chkieee128"}, + {Builtin::BI__builtin___sprintf_chk, "__sprintf_chkieee128"}, + {Builtin::BI__builtin___vfprintf_chk, "__vfprintf_chkieee128"}, + {Builtin::BI__builtin___vprintf_chk, "__vprintf_chkieee128"}, + {Builtin::BI__builtin___vsnprintf_chk, "__vsnprintf_chkieee128"}, + {Builtin::BI__builtin___vsprintf_chk, "__vsprintf_chkieee128"}, + {Builtin::BI__builtin_fprintf, "__fprintfieee128"}, + {Builtin::BI__builtin_printf, "__printfieee128"}, + {Builtin::BI__builtin_snprintf, "__snprintfieee128"}, + {Builtin::BI__builtin_sprintf, "__sprintfieee128"}, + {Builtin::BI__builtin_vfprintf, "__vfprintfieee128"}, + {Builtin::BI__builtin_vprintf, "__vprintfieee128"}, + {Builtin::BI__builtin_vsnprintf, "__vsnprintfieee128"}, + {Builtin::BI__builtin_vsprintf, "__vsprintfieee128"}, + {Builtin::BI__builtin_fscanf, "__fscanfieee128"}, + {Builtin::BI__builtin_scanf, "__scanfieee128"}, + {Builtin::BI__builtin_sscanf, "__sscanfieee128"}, + {Builtin::BI__builtin_vfscanf, "__vfscanfieee128"}, + {Builtin::BI__builtin_vscanf, "__vscanfieee128"}, + {Builtin::BI__builtin_vsscanf, "__vsscanfieee128"}, + {Builtin::BI__builtin_nexttowardf128, "__nexttowardieee128"}, + }; + + // The AIX library functions frexpl, ldexpl, and modfl are for 128-bit + // IBM 'long double' (i.e. __ibm128). Map to the 'double' versions + // if it is 64-bit 'long double' mode. + static SmallDenseMap<unsigned, StringRef, 4> aixLongDouble64Builtins{ + {Builtin::BI__builtin_frexpl, "frexp"}, + {Builtin::BI__builtin_ldexpl, "ldexp"}, + {Builtin::BI__builtin_modfl, "modf"}, + }; + + // If the builtin has been declared explicitly with an assembler label, + // use the mangled name. This differs from the plain label on platforms + // that prefix labels. + if (fd->hasAttr<AsmLabelAttr>()) + name = getMangledName(d); + else { + // TODO: This mutation should also be applied to other targets other than + // PPC, after backend supports IEEE 128-bit style libcalls. + if (getTriple().isPPC64() && + &getTarget().getLongDoubleFormat() == &llvm::APFloat::IEEEquad() && + f128Builtins.find(builtinID) != f128Builtins.end()) + name = f128Builtins[builtinID]; + else if (getTriple().isOSAIX() && + &getTarget().getLongDoubleFormat() == + &llvm::APFloat::IEEEdouble() && + aixLongDouble64Builtins.find(builtinID) != + aixLongDouble64Builtins.end()) + name = aixLongDouble64Builtins[builtinID]; + else + name = astContext.BuiltinInfo.getName(builtinID).substr(10); + } + + auto ty = convertType(fd->getType()); + return getOrCreateCIRFunction(name, ty, d, /*ForVTable=*/false); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 605625705a75c..34e9e30ec4589 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -44,16 +44,25 @@ class CIRGenCalleeInfo { class CIRGenCallee { enum class SpecialKind : uintptr_t { Invalid, + Builtin, Last = Invalid, }; + struct BuiltinInfoStorage { + const clang::FunctionDecl *decl; + unsigned id; + }; + SpecialKind kindOrFunctionPtr; union { CIRGenCalleeInfo abstractInfo; + BuiltinInfoStorage builtinInfo; }; + explicit CIRGenCallee(SpecialKind kind) : kindOrFunctionPtr(kind) {} + public: CIRGenCallee() : kindOrFunctionPtr(SpecialKind::Invalid) {} @@ -69,6 +78,25 @@ class CIRGenCallee { return CIRGenCallee(abstractInfo, funcPtr); } + bool isBuiltin() const { return kindOrFunctionPtr == SpecialKind::Builtin; } + + const clang::FunctionDecl *getBuiltinDecl() const { + assert(isBuiltin()); + return builtinInfo.decl; + } + unsigned getBuiltinID() const { + assert(isBuiltin()); + return builtinInfo.id; + } + + static CIRGenCallee forBuiltin(unsigned builtinID, + const clang::FunctionDecl *builtinDecl) { + CIRGenCallee result(SpecialKind::Builtin); + result.builtinInfo.decl = builtinDecl; + result.builtinInfo.id = builtinID; + return result; + } + bool isOrdinary() const { return uintptr_t(kindOrFunctionPtr) > uintptr_t(SpecialKind::Last); } @@ -134,7 +162,20 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> { /// Contains the address where the return value of a function can be stored, and /// whether the address is volatile or not. -class ReturnValueSlot {}; +class ReturnValueSlot { + Address addr = Address::invalid(); + + // Return value slot flags + LLVM_PREFERRED_TYPE(bool) + unsigned isVolatileFlag : 1; + +public: + ReturnValueSlot() = default; + ReturnValueSlot(Address addr, bool isVolatile) : addr(addr), isVolatileFlag(isVolatile) {} + bool isNull() const { return !addr.isValid(); } + bool isVolatile() const { return isVolatileFlag; } + Address getAddress() const { return addr; } +}; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 8129fe0ad7db7..f16af3b4dd79f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1029,8 +1029,48 @@ static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) { return cgm.getAddrOfFunction(gd); } -static CIRGenCallee emitDirectCallee(CIRGenModule &cgm, GlobalDecl gd) { - assert(!cir::MissingFeatures::opCallBuiltinFunc()); +// Detect the unusual situation where an inline version is shadowed by a +// non-inline version. In that case we should pick the external one +// everywhere. That's GCC behavior too. +static bool onlyHasInlineBuiltinDeclaration(const FunctionDecl *fd) { + for (const FunctionDecl *pd = fd; pd; pd = pd->getPreviousDecl()) + if (!pd->isInlineBuiltinDeclaration()) + return false; + return true; +} + +CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) { + const auto *fd = cast<FunctionDecl>(gd.getDecl()); + + if (unsigned builtinID = fd->getBuiltinID()) { + std::string noBuiltinFD = ("no-builtin-" + fd->getName()).str(); + std::string noBuiltins = "no-builtins"; + + auto *a = fd->getAttr<AsmLabelAttr>(); + StringRef ident = a ? a->getLabel() : fd->getName(); + std::string fdInlineName = (ident + ".inline").str(); + + bool isPredefinedLibFunction = + cgm.getASTContext().BuiltinInfo.isPredefinedLibFunction(builtinID); + bool hasAttributeNoBuiltin = false; + assert(!cir::MissingFeatures::attributeNoBuiltin()); + + // When directing calling an inline builtin, call it through it's mangled + // name to make it clear it's not the actual builtin. + auto fn = cast<cir::FuncOp>(curFn); + if (fn.getName() != fdInlineName && onlyHasInlineBuiltinDeclaration(fd)) { + cgm.errorNYI("Inline only builtin function calls"); + } + + // Replaceable builtins provide their own implementation of a builtin. If we + // are in an inline builtin implementation, avoid trivial infinite + // recursion. Honor __attribute__((no_builtin("foo"))) or + // __attribute__((no_builtin)) on the current function unless foo is + // not a predefined library function which means we must generate the + // builtin no matter what. + else if (!isPredefinedLibFunction || !hasAttributeNoBuiltin) + return CIRGenCallee::forBuiltin(builtinID, fd); + } cir::FuncOp callee = emitFunctionDeclPointer(cgm, gd); @@ -1106,7 +1146,7 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) { } else if (const auto *declRef = dyn_cast<DeclRefExpr>(e)) { // Resolve direct calls. const auto *funcDecl = cast<FunctionDecl>(declRef->getDecl()); - return emitDirectCallee(cgm, funcDecl); + return emitDirectCallee(funcDecl); } else if (isa<MemberExpr>(e)) { cgm.errorNYI(e->getSourceRange(), "emitCallee: call to member function is NYI"); @@ -1162,10 +1202,9 @@ RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e, CIRGenCallee callee = emitCallee(e->getCallee()); - if (e->getBuiltinCallee()) { - cgm.errorNYI(e->getSourceRange(), "call to builtin functions"); - } - assert(!cir::MissingFeatures::opCallBuiltinFunc()); + if (callee.isBuiltin()) + return emitBuiltinExpr(callee.getBuiltinDecl(), callee.getBuiltinID(), e, + returnValue); if (isa<CXXPseudoDestructorExpr>(e->getCallee())) { cgm.errorNYI(e->getSourceRange(), "call to pseudo destructor"); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index d6002c3e4d4d9..e621487002f8e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -47,6 +47,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// is where the next operations will be introduced. CIRGenBuilderTy &builder; + /// Largest vector width used in ths function. Will be used to create a + /// function attribute. + unsigned largestVectorWidth = 0; + public: /// The GlobalDecl for the current function being compiled or the global /// variable currently being initialized. @@ -665,6 +669,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitAndUpdateRetAlloca(clang::QualType type, mlir::Location loc, clang::CharUnits alignment); + CIRGenCallee emitDirectCallee(const GlobalDecl &gd); + public: Address emitAddrOfFieldStorage(Address base, const FieldDecl *field, llvm::StringRef fieldName, @@ -711,6 +717,9 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s); + RValue emitBuiltinExpr(const clang::GlobalDecl &gd, unsigned builtinID, + const clang::CallExpr *e, ReturnValueSlot returnValue); + RValue emitCall(const CIRGenFunctionInfo &funcInfo, const CIRGenCallee &callee, ReturnValueSlot returnValue, const CallArgList &args, cir::CIRCallOpInterface *callOp, @@ -893,6 +902,9 @@ class CIRGenFunction : public CIRGenTypeCache { void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit = false); + mlir::Value emitScalarOrConstFoldImmArg(unsigned iceArguments, unsigned idx, + const CallExpr *e); + void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, bool isInit = false, bool isNontemporal = false); @@ -912,6 +924,10 @@ class CIRGenFunction : public CIRGenTypeCache { bool buildingTopLevelCase); mlir::LogicalResult emitSwitchStmt(const clang::SwitchStmt &s); + mlir::Value emitTargetBuiltinExpr(unsigned builtinID, + const clang::CallExpr *e, + ReturnValueSlot returnValue); + /// Given a value and its clang type, returns the value casted to its memory /// representation. /// Note: CIR defers most of the special casting to the final lowering passes diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 24ec9ca6403bc..65d5615488b1b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -264,6 +264,11 @@ class CIRGenModule : public CIRGenTypeCache { cir::FuncType funcType, const clang::FunctionDecl *funcDecl); + /// Given a builtin id for a function like "__builtin_fabsf", return a + /// Function* for "fabsf". + cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID); + + mlir::IntegerAttr getSize(CharUnits size) { return builder.getSizeFromCharUnits(size); } diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 208247e16e531..deb2c1049a8d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -113,6 +113,21 @@ class RValue { er.isVolatile = isVolatile; return er; } + + // FIXME: Aggregate rvalues need to retain information about whether they are + // volatile or not. Remove default to find all places that probably get this + // wrong. + + /// Convert an Address to an RValue. If the Address is not + /// signed, create an RValue using the unsigned address. Otherwise, resign the + /// address using the provided type. + static RValue getAggregate(Address addr, bool isVolatile = false) { + RValue ER; + ER.aggregateAddr = addr; + ER.flavor = Aggregate; + ER.isVolatile = isVolatile; + return ER; + } }; /// The source of the alignment of an l-value; an expression of diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 8bfcd2773d07a..beaa9afb31f93 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -13,6 +13,7 @@ add_clang_library(clangCIR CIRGenClass.cpp CIRGenCXXABI.cpp CIRGenCXXExpr.cpp + CIRGenBuiltin.cpp CIRGenDecl.cpp CIRGenDeclOpenACC.cpp CIRGenExpr.cpp diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp b/clang/test/CIR/CodeGen/builtin_call.cpp new file mode 100644 index 0000000000000..91bcedb18d36c --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin_call.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +constexpr extern int cx_var = __builtin_is_constant_evaluated(); + +// CIR: cir.global {{.*}} @cx_var = #cir.int<1> : !s32i +// LLVM: @cx_var = {{.*}} i32 1 +// OGCG: @cx_var = {{.*}} i32 1 + +int is_constant_evaluated() { + return __builtin_is_constant_evaluated(); +} + +// CIR: cir.func @_Z21is_constant_evaluatedv() -> !s32i +// CIR: %[[ZERO:.+]] = cir.const #cir.int<0> + +// LLVM: define {{.*}}i32 @_Z21is_constant_evaluatedv() +// LLVM: %[[MEM:.+]] = alloca i32 +// LLVM: store i32 0, ptr %[[MEM]] +// LLVM: %[[RETVAL:.+]] = load i32, ptr %[[MEM]] +// LLVM: ret i32 %[[RETVAL]] +// LLVM: } + +// OGCG: define {{.*}}i32 @_Z21is_constant_evaluatedv() +// OGCG: ret i32 0 +// OGCG: } >From a9445a2bd669ab90f1575a5a6f9c8b2d4d306294 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 5 Jun 2025 16:39:16 +0200 Subject: [PATCH 2/5] clang-format --- clang/lib/CIR/CodeGen/CIRGenModule.h | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 65d5615488b1b..5cbc3564cbe8b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -268,7 +268,6 @@ class CIRGenModule : public CIRGenTypeCache { /// Function* for "fabsf". cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID); - mlir::IntegerAttr getSize(CharUnits size) { return builder.getSizeFromCharUnits(size); } >From 246ee16c6364c9ebfc5569398eb41fe893e94a1f Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 5 Jun 2025 16:44:58 +0200 Subject: [PATCH 3/5] more clang-format, remove duplicated code --- clang/lib/CIR/CodeGen/CIRGenCall.h | 3 ++- clang/lib/CIR/CodeGen/CIRGenValue.h | 15 --------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 34e9e30ec4589..d80f807cce181 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -171,7 +171,8 @@ class ReturnValueSlot { public: ReturnValueSlot() = default; - ReturnValueSlot(Address addr, bool isVolatile) : addr(addr), isVolatileFlag(isVolatile) {} + ReturnValueSlot(Address addr, bool isVolatile) + : addr(addr), isVolatileFlag(isVolatile) {} bool isNull() const { return !addr.isValid(); } bool isVolatile() const { return isVolatileFlag; } Address getAddress() const { return addr; } diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index deb2c1049a8d6..208247e16e531 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -113,21 +113,6 @@ class RValue { er.isVolatile = isVolatile; return er; } - - // FIXME: Aggregate rvalues need to retain information about whether they are - // volatile or not. Remove default to find all places that probably get this - // wrong. - - /// Convert an Address to an RValue. If the Address is not - /// signed, create an RValue using the unsigned address. Otherwise, resign the - /// address using the provided type. - static RValue getAggregate(Address addr, bool isVolatile = false) { - RValue ER; - ER.aggregateAddr = addr; - ER.flavor = Aggregate; - ER.isVolatile = isVolatile; - return ER; - } }; /// The source of the alignment of an l-value; an expression of >From 14cb55e97493f0a77bbeb86567ed50a56685da57 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 9 Jun 2025 18:06:57 +0200 Subject: [PATCH 4/5] - Remove all builtin handling not needed for constant foldable builtin calls - Address review feedback --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 34 -- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenBuilder.cpp | 4 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 28 +- clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 414 +------------------ clang/lib/CIR/CodeGen/CIRGenCall.h | 18 +- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 8 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 11 - clang/lib/CIR/CodeGen/CIRGenModule.h | 4 - 9 files changed, 39 insertions(+), 483 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 787e89fd06b56..8579f7066e4fe 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1854,40 +1854,6 @@ def FuncOp : CIR_Op<"func", [ let hasVerifier = 1; } -//===----------------------------------------------------------------------===// -// LLVMIntrinsicCallOp -//===----------------------------------------------------------------------===// - -def LLVMIntrinsicCallOp : CIR_Op<"llvm.intrinsic"> { - let summary = "Call to llvm intrinsic functions that is not defined in CIR"; - let description = [{ - The `cir.llvm.intrinsic` operation represents a call-like expression which has - a return type and arguments that map directly to an llvm intrinsic. - It only records its own name (`intrinsic_name`). - }]; - - let results = (outs Optional<CIR_AnyType>:$result); - let arguments = (ins - StrAttr:$intrinsic_name, Variadic<CIR_AnyType>:$arg_ops); - - let skipDefaultBuilders = 1; - - let assemblyFormat = [{ - $intrinsic_name $arg_ops `:` functional-type($arg_ops, $result) attr-dict - }]; - - let builders = [ - OpBuilder<(ins "mlir::StringAttr":$intrinsic_name, "mlir::Type":$resType, - CArg<"mlir::ValueRange", "{}">:$operands), [{ - $_state.addAttribute("intrinsic_name", intrinsic_name); - $_state.addOperands(operands); - if (resType) - $_state.addTypes(resType); - }]>, - ]; - -} - //===----------------------------------------------------------------------===// // CallOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 7db13a846e23a..bab684839c1f2 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -222,7 +222,6 @@ struct MissingFeatures { static bool instrumentation() { return false; } static bool cleanupAfterErrorDiags() { return false; } static bool cxxRecordStaticMembers() { return false; } - static bool intrinsics() { return false; } static bool attributeNoBuiltin() { return false; } // Missing types diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp index bdb6914768b2a..26a649837a558 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -42,7 +42,7 @@ mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin, cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, llvm::APSInt intVal) { bool isSigned = intVal.isSigned(); - auto width = intVal.getBitWidth(); + unsigned width = intVal.getBitWidth(); cir::IntType t = isSigned ? getSIntNTy(width) : getUIntNTy(width); return getConstInt(loc, t, isSigned ? intVal.getSExtValue() : intVal.getZExtValue()); @@ -50,7 +50,7 @@ cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, llvm::APInt intVal) { - auto width = intVal.getBitWidth(); + unsigned width = intVal.getBitWidth(); cir::IntType t = getUIntNTy(width); return getConstInt(loc, t, intVal.getZExtValue()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index f2c318b37ad43..17d88646f5fb6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -15,6 +15,7 @@ #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" #include "clang/CIR/MissingFeatures.h" +#include "llvm/ADT/APFloat.h" #include "llvm/ADT/STLExtras.h" namespace clang::CIRGen { @@ -235,11 +236,28 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t, uint64_t c); - cir::ConstantOp getConstFP(mlir::Location loc, mlir::Type t, - llvm::APFloat fpVal) { - assert((mlir::isa<cir::SingleType, cir::DoubleType>(t)) && - "expected cir::SingleType or cir::DoubleType"); - return create<cir::ConstantOp>(loc, getAttr<cir::FPAttr>(t, fpVal)); + mlir::Type getFPType(llvm::fltSemantics const &sem) { + switch (llvm::APFloat::SemanticsToEnum(sem)) { + case llvm::APFloat::S_IEEEhalf: + return cir::FP16Type::get(getContext(), typeCache.FP16Ty); + case llvm::APFloat::S_BFloat: + return cir::BF16Type::get(getContext(), typeCache.BFloat16Ty); + case llvm::APFloat::S_IEEEsingle: + return cir::SingleType::get(getContext(), typeCache.FloatTy); + case llvm::APFloat::S_IEEEdouble: + return cir::DoubleType::get(getContext(), typeCache.DoubleTy); + case llvm::APFloat::S_IEEEquad: + return cir::FP128Type::get(getContext(), typeCache.FP128Ty); + case llvm::APFloat::S_x87DoubleExtended: + return cir::FP80Type::get(getContext(), typeCache.FP80Ty); + default: + llvm_unreachable("Unrecognized floating semantics"); + } + } + + cir::ConstantOp getConstFP(mlir::Location loc, llvm::APFloat fpVal) { + mlir::Type type = getFPType(fpVal.getSemantics()); + return create<cir::ConstantOp>(loc, getAttr<cir::FPAttr>(type, fpVal)); } bool isInt8Ty(mlir::Type i) { diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 60a69f1182b5c..39925f3ce67a5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -15,118 +15,19 @@ #include "CIRGenFunction.h" #include "CIRGenModule.h" #include "CIRGenValue.h" -#include "clang/AST/Expr.h" -#include "clang/CIR/Dialect/IR/CIRAttrs.h" -#include "clang/CIR/Dialect/IR/CIRTypes.h" -#include "clang/CIR/MissingFeatures.h" -#include "llvm/IR/Intrinsics.h" - -#include "clang/AST/GlobalDecl.h" -#include "clang/Basic/Builtins.h" - #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/Value.h" #include "mlir/Support/LLVM.h" -#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/AST/Expr.h" +#include "clang/AST/GlobalDecl.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; using namespace clang::CIRGen; -using namespace cir; -using namespace llvm; - -static RValue emitLibraryCall(CIRGenFunction &cgf, const FunctionDecl *fd, - const CallExpr *e, mlir::Operation *calleeValue) { - CIRGenCallee callee = CIRGenCallee::forDirect(calleeValue, GlobalDecl(fd)); - return cgf.emitCall(e->getCallee()->getType(), callee, e, ReturnValueSlot()); -} - -static mlir::Type -decodeFixedType(CIRGenFunction &cgf, - ArrayRef<llvm::Intrinsic::IITDescriptor> &infos) { - using namespace llvm::Intrinsic; - - auto *context = &cgf.getMLIRContext(); - IITDescriptor descriptor = infos.front(); - infos = infos.slice(1); - - switch (descriptor.Kind) { - case IITDescriptor::Void: - return VoidType::get(context); - case IITDescriptor::Integer: - return IntType::get(context, descriptor.Integer_Width, /*isSigned=*/true); - case IITDescriptor::Float: - return SingleType::get(context); - case IITDescriptor::Double: - return DoubleType::get(context); - default: - cgf.cgm.errorNYI("intrinsic return types"); - return VoidType::get(context); - } -} - -// llvm::Intrinsics accepts only LLVMContext. We need to reimplement it here. -static cir::FuncType getIntrinsicType(CIRGenFunction &cgf, - llvm::Intrinsic::ID id) { - using namespace llvm::Intrinsic; - - SmallVector<IITDescriptor, 8> table; - getIntrinsicInfoTableEntries(id, table); - - ArrayRef<IITDescriptor> tableRef = table; - mlir::Type resultTy = decodeFixedType(cgf, tableRef); - - SmallVector<mlir::Type, 8> argTypes; - while (!tableRef.empty()) - argTypes.push_back(decodeFixedType(cgf, tableRef)); - - return FuncType::get(argTypes, resultTy); -} - -static mlir::Value emitTargetArchBuiltinExpr(CIRGenFunction *cgf, - unsigned builtinID, - const CallExpr *e, - ReturnValueSlot returnValue, - llvm::Triple::ArchType arch) { - return {}; -} - -mlir::Value CIRGenFunction::emitTargetBuiltinExpr(unsigned builtinID, - const CallExpr *e, - ReturnValueSlot returnValue) { - if (getContext().BuiltinInfo.isAuxBuiltinID(builtinID)) { - assert(getContext().getAuxTargetInfo() && "Missing aux target info"); - return emitTargetArchBuiltinExpr( - this, getContext().BuiltinInfo.getAuxBuiltinID(builtinID), e, - returnValue, getContext().getAuxTargetInfo()->getTriple().getArch()); - } - - return emitTargetArchBuiltinExpr(this, builtinID, e, returnValue, - getTarget().getTriple().getArch()); -} - -mlir::Value CIRGenFunction::emitScalarOrConstFoldImmArg(unsigned iceArguments, - unsigned idx, - const CallExpr *e) { - mlir::Value arg = {}; - if ((iceArguments & (1 << idx)) == 0) { - arg = emitScalarExpr(e->getArg(idx)); - } else { - // If this is required to be a constant, constant fold it so that we - // know that the generated intrinsic gets a ConstantInt. - std::optional<llvm::APSInt> result = - e->getArg(idx)->getIntegerConstantExpr(getContext()); - assert(result && "Expected argument to be a constant"); - arg = builder.getConstInt(getLoc(e->getSourceRange()), *result); - } - return arg; -} RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, const CallExpr *e, ReturnValueSlot returnValue) { - const FunctionDecl *fd = gd.getDecl()->getAsFunction(); - // See if we can constant fold this builtin. If so, don't emit it at all. // TODO: Extend this handling to all builtin calls that we can constant-fold. Expr::EvalResult result; @@ -138,316 +39,17 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, } if (result.Val.isFloat()) { // Note: we are using result type of CallExpr to determine the type of - // the constant. Clang Codegen uses the result value to make judgement - // of the type. We feel it should be Ok to use expression type because - // it is hard to imagine a builtin function evaluates to - // a value that over/underflows its own defined type. + // the constant. Classic codegen uses the result value to determine the + // type. We feel it should be Ok to use expression type because it is + // hard to imagine a builtin function evaluates to a value that + // over/underflows its own defined type. mlir::Type resTy = convertType(e->getType()); return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), resTy, result.Val.getFloat())); } } - // If current long-double semantics is IEEE 128-bit, replace math builtins - // of long-double with f128 equivalent. - // TODO: This mutation should also be applied to other targets other than PPC, - // after backend supports IEEE 128-bit style libcalls. - if (getTarget().getTriple().isPPC64() && - &getTarget().getLongDoubleFormat() == &llvm::APFloat::IEEEquad()) { - cgm.errorNYI("long double builtin mutation"); - } - - // If the builtin has been declared explicitly with an assembler label, - // disable the specialized emitting below. Ideally we should communicate the - // rename in IR, or at least avoid generating the intrinsic calls that are - // likely to get lowered to the renamed library functions. - const unsigned builtinIDIfNoAsmLabel = - fd->hasAttr<AsmLabelAttr>() ? 0 : builtinID; - - std::optional<bool> errnoOverriden; - // ErrnoOverriden is true if math-errno is overriden via the - // '#pragma float_control(precise, on)'. This pragma disables fast-math, - // which implies math-errno. - if (e->hasStoredFPFeatures()) { - FPOptionsOverride op = e->getFPFeatures(); - if (op.hasMathErrnoOverride()) - errnoOverriden = op.getMathErrnoOverride(); - } - // True if 'atttibute__((optnone)) is used. This attibute overrides - // fast-math which implies math-errno. - bool optNone = curFuncDecl && curFuncDecl->hasAttr<OptimizeNoneAttr>(); - - // True if we are compiling at -O2 and errno has been disabled - // using the '#pragma float_control(precise, off)', and - // attribute opt-none hasn't been seen. - [[maybe_unused]] bool errnoOverridenToFalseWithOpt = - errnoOverriden.has_value() && !errnoOverriden.value() && !optNone && - cgm.getCodeGenOpts().OptimizationLevel != 0; - - // There are LLVM math intrinsics/instructions corresponding to math library - // functions except the LLVM op will never set errno while the math library - // might. Also, math builtins have the same semantics as their math library - // twins. Thus, we can transform math library and builtin calls to their - // LLVM counterparts if the call is marked 'const' (known to never set errno). - // In case FP exceptions are enabled, the experimental versions of the - // intrinsics model those. - [[maybe_unused]] bool constAlways = - getContext().BuiltinInfo.isConst(builtinID); - - // There's a special case with the fma builtins where they are always const - // if the target environment is GNU or the target is OS is Windows and we're - // targeting the MSVCRT.dll environment. - // FIXME: This list can be become outdated. Need to find a way to get it some - // other way. - switch (builtinID) { - case Builtin::BI__builtin_fma: - case Builtin::BI__builtin_fmaf: - case Builtin::BI__builtin_fmal: - case Builtin::BIfma: - case Builtin::BIfmaf: - case Builtin::BIfmal: - cgm.errorNYI("FMA builtins"); - break; - } - - bool constWithoutErrnoAndExceptions = - getContext().BuiltinInfo.isConstWithoutErrnoAndExceptions(builtinID); - bool constWithoutExceptions = - getContext().BuiltinInfo.isConstWithoutExceptions(builtinID); - - // ConstAttr is enabled in fast-math mode. In fast-math mode, math-errno is - // disabled. - // Math intrinsics are generated only when math-errno is disabled. Any pragmas - // or attributes that affect math-errno should prevent or allow math - // intrincs to be generated. Intrinsics are generated: - // 1- In fast math mode, unless math-errno is overriden - // via '#pragma float_control(precise, on)', or via an - // 'attribute__((optnone))'. - // 2- If math-errno was enabled on command line but overriden - // to false via '#pragma float_control(precise, off))' and - // 'attribute__((optnone))' hasn't been used. - // 3- If we are compiling with optimization and errno has been disabled - // via '#pragma float_control(precise, off)', and - // 'attribute__((optnone))' hasn't been used. - - bool constWithoutErrnoOrExceptions = - constWithoutErrnoAndExceptions || constWithoutExceptions; - bool generateIntrinsics = - (constAlways && !optNone) || - (!getLangOpts().MathErrno && - !(errnoOverriden.has_value() && errnoOverriden.value()) && !optNone); - if (!generateIntrinsics) { - generateIntrinsics = - constWithoutErrnoOrExceptions && !constWithoutErrnoAndExceptions; - if (!generateIntrinsics) - generateIntrinsics = - constWithoutErrnoOrExceptions && - (!getLangOpts().MathErrno && - !(errnoOverriden.has_value() && errnoOverriden.value()) && !optNone); - if (!generateIntrinsics) - generateIntrinsics = - constWithoutErrnoOrExceptions && errnoOverridenToFalseWithOpt; - } - - if (generateIntrinsics) { - assert(!cir::MissingFeatures::intrinsics()); - return {}; - } - - switch (builtinIDIfNoAsmLabel) { - default: - break; - } - - // If this is an alias for a lib function (e.g. __builtin_sin), emit - // the call using the normal call path, but using the unmangled - // version of the function name. - if (getContext().BuiltinInfo.isLibFunction(builtinID)) - return emitLibraryCall(*this, fd, e, - cgm.getBuiltinLibFunction(fd, builtinID)); - - // If this is a predefined lib function (e.g. malloc), emit the call - // using exactly the normal call path. - if (getContext().BuiltinInfo.isPredefinedLibFunction(builtinID)) - return emitLibraryCall(*this, fd, e, - emitScalarExpr(e->getCallee()).getDefiningOp()); - - // Check that a call to a target specific builtin has the correct target - // features. - // This is down here to avoid non-target specific builtins, however, if - // generic builtins start to require generic target features then we - // can move this up to the beginning of the function. - // checkTargetFeatures(E, FD); - - if ([[maybe_unused]] unsigned vectorWidth = - getContext().BuiltinInfo.getRequiredVectorWidth(builtinID)) - largestVectorWidth = std::max(largestVectorWidth, vectorWidth); - - // See if we have a target specific intrinsic. - std::string name = getContext().BuiltinInfo.getName(builtinID); - Intrinsic::ID intrinsicID = Intrinsic::not_intrinsic; - StringRef prefix = - llvm::Triple::getArchTypePrefix(getTarget().getTriple().getArch()); - if (!prefix.empty()) { - intrinsicID = Intrinsic::getIntrinsicForClangBuiltin(prefix.data(), name); - // NOTE we don't need to perform a compatibility flag check here since the - // intrinsics are declared in Builtins*.def via LANGBUILTIN which filter the - // MS builtins via ALL_MS_LANGUAGES and are filtered earlier. - if (intrinsicID == Intrinsic::not_intrinsic) - intrinsicID = Intrinsic::getIntrinsicForMSBuiltin(prefix.data(), name); - } - - if (intrinsicID != Intrinsic::not_intrinsic) { - unsigned iceArguments = 0; - ASTContext::GetBuiltinTypeError error; - getContext().GetBuiltinType(builtinID, error, &iceArguments); - assert(error == ASTContext::GE_None && "Should not codegen an error"); - - llvm::StringRef name = llvm::Intrinsic::getName(intrinsicID); - // cir::LLVMIntrinsicCallOp expects intrinsic name to not have prefix - // "llvm." For example, `llvm.nvvm.barrier0` should be passed as - // `nvvm.barrier0`. - if (!name.consume_front("llvm.")) - assert(false && "bad intrinsic name!"); - - cir::FuncType intrinsicType = getIntrinsicType(*this, intrinsicID); - - SmallVector<mlir::Value> args; - for (unsigned i = 0; i < e->getNumArgs(); i++) { - mlir::Value arg = emitScalarOrConstFoldImmArg(iceArguments, i, e); - mlir::Type argType = arg.getType(); - if (argType != intrinsicType.getInput(i)) { - // vector of pointers? - assert(!cir::MissingFeatures::addressSpace()); - } - - args.push_back(arg); - } - - auto intrinsicCall = builder.create<cir::LLVMIntrinsicCallOp>( - getLoc(e->getExprLoc()), builder.getStringAttr(name), - intrinsicType.getReturnType(), args); - - mlir::Type builtinReturnType = intrinsicCall.getResult().getType(); - mlir::Type retTy = intrinsicType.getReturnType(); - - if (builtinReturnType != retTy) { - // vector of pointers? - if (isa<cir::PointerType>(retTy)) { - assert(!cir::MissingFeatures::addressSpace()); - } - } - - if (isa<cir::VoidType>(retTy)) - return RValue::get(nullptr); - - return RValue::get(intrinsicCall.getResult()); - } - - // Some target-specific builtins can have aggregate return values, e.g. - // __builtin_arm_mve_vld2q_u32. So if the result is an aggregate, force - // ReturnValue to be non-null, so that the target-specific emission code can - // always just emit into it. - cir::TypeEvaluationKind evalKind = getEvaluationKind(e->getType()); - if (evalKind == cir::TEK_Aggregate && returnValue.isNull()) { - Address destPtr = - createMemTemp(e->getType(), getLoc(e->getSourceRange()), "agg.tmp"); - returnValue = ReturnValueSlot(destPtr, false); - } - - // Now see if we can emit a target-specific builtin. - if (auto v = emitTargetBuiltinExpr(builtinID, e, returnValue)) { - switch (evalKind) { - case cir::TEK_Scalar: - if (mlir::isa<cir::VoidType>(v.getType())) - return RValue::get(nullptr); - return RValue::get(v); - case cir::TEK_Aggregate: - return RValue::getAggregate(returnValue.getAddress(), - returnValue.isVolatile()); - case cir::TEK_Complex: - llvm_unreachable("No current target builtin returns complex"); - } - llvm_unreachable("Bad evaluation kind in EmitBuiltinExpr"); - } - - // cgm.errorUnsupported(e, "builtin function"); - - // Unknown builtin, for now just dump it out and return undef. + mlir::Location loc = getLoc(e->getExprLoc()); + cgm.errorNYI(loc, "non constant foldable builtin calls"); return getUndefRValue(e->getType()); } - -/// Given a builtin id for a function like "__builtin_fabsf", return a Function* -/// for "fabsf". -cir::FuncOp CIRGenModule::getBuiltinLibFunction(const FunctionDecl *fd, - unsigned builtinID) { - assert(astContext.BuiltinInfo.isLibFunction(builtinID)); - - // Get the name, skip over the __builtin_ prefix (if necessary). We may have - // to build this up so provide a small stack buffer to handle the vast - // majority of names. - llvm::SmallString<64> name; - GlobalDecl d(fd); - - // TODO: This list should be expanded or refactored after all GCC-compatible - // std libcall builtins are implemented. - static SmallDenseMap<unsigned, StringRef, 64> f128Builtins{ - {Builtin::BI__builtin___fprintf_chk, "__fprintf_chkieee128"}, - {Builtin::BI__builtin___printf_chk, "__printf_chkieee128"}, - {Builtin::BI__builtin___snprintf_chk, "__snprintf_chkieee128"}, - {Builtin::BI__builtin___sprintf_chk, "__sprintf_chkieee128"}, - {Builtin::BI__builtin___vfprintf_chk, "__vfprintf_chkieee128"}, - {Builtin::BI__builtin___vprintf_chk, "__vprintf_chkieee128"}, - {Builtin::BI__builtin___vsnprintf_chk, "__vsnprintf_chkieee128"}, - {Builtin::BI__builtin___vsprintf_chk, "__vsprintf_chkieee128"}, - {Builtin::BI__builtin_fprintf, "__fprintfieee128"}, - {Builtin::BI__builtin_printf, "__printfieee128"}, - {Builtin::BI__builtin_snprintf, "__snprintfieee128"}, - {Builtin::BI__builtin_sprintf, "__sprintfieee128"}, - {Builtin::BI__builtin_vfprintf, "__vfprintfieee128"}, - {Builtin::BI__builtin_vprintf, "__vprintfieee128"}, - {Builtin::BI__builtin_vsnprintf, "__vsnprintfieee128"}, - {Builtin::BI__builtin_vsprintf, "__vsprintfieee128"}, - {Builtin::BI__builtin_fscanf, "__fscanfieee128"}, - {Builtin::BI__builtin_scanf, "__scanfieee128"}, - {Builtin::BI__builtin_sscanf, "__sscanfieee128"}, - {Builtin::BI__builtin_vfscanf, "__vfscanfieee128"}, - {Builtin::BI__builtin_vscanf, "__vscanfieee128"}, - {Builtin::BI__builtin_vsscanf, "__vsscanfieee128"}, - {Builtin::BI__builtin_nexttowardf128, "__nexttowardieee128"}, - }; - - // The AIX library functions frexpl, ldexpl, and modfl are for 128-bit - // IBM 'long double' (i.e. __ibm128). Map to the 'double' versions - // if it is 64-bit 'long double' mode. - static SmallDenseMap<unsigned, StringRef, 4> aixLongDouble64Builtins{ - {Builtin::BI__builtin_frexpl, "frexp"}, - {Builtin::BI__builtin_ldexpl, "ldexp"}, - {Builtin::BI__builtin_modfl, "modf"}, - }; - - // If the builtin has been declared explicitly with an assembler label, - // use the mangled name. This differs from the plain label on platforms - // that prefix labels. - if (fd->hasAttr<AsmLabelAttr>()) - name = getMangledName(d); - else { - // TODO: This mutation should also be applied to other targets other than - // PPC, after backend supports IEEE 128-bit style libcalls. - if (getTriple().isPPC64() && - &getTarget().getLongDoubleFormat() == &llvm::APFloat::IEEEquad() && - f128Builtins.find(builtinID) != f128Builtins.end()) - name = f128Builtins[builtinID]; - else if (getTriple().isOSAIX() && - &getTarget().getLongDoubleFormat() == - &llvm::APFloat::IEEEdouble() && - aixLongDouble64Builtins.find(builtinID) != - aixLongDouble64Builtins.end()) - name = aixLongDouble64Builtins[builtinID]; - else - name = astContext.BuiltinInfo.getName(builtinID).substr(10); - } - - auto ty = convertType(fd->getType()); - return getOrCreateCIRFunction(name, ty, d, /*ForVTable=*/false); -} diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index d80f807cce181..15c9080448c8b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -46,7 +46,7 @@ class CIRGenCallee { Invalid, Builtin, - Last = Invalid, + Last = Builtin, }; struct BuiltinInfoStorage { @@ -162,21 +162,7 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> { /// Contains the address where the return value of a function can be stored, and /// whether the address is volatile or not. -class ReturnValueSlot { - Address addr = Address::invalid(); - - // Return value slot flags - LLVM_PREFERRED_TYPE(bool) - unsigned isVolatileFlag : 1; - -public: - ReturnValueSlot() = default; - ReturnValueSlot(Address addr, bool isVolatile) - : addr(addr), isVolatileFlag(isVolatile) {} - bool isNull() const { return !addr.isValid(); } - bool isVolatile() const { return isVolatileFlag; } - Address getAddress() const { return addr; } -}; +class ReturnValueSlot {}; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index f16af3b4dd79f..6a0825c26b041 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1043,11 +1043,11 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) { const auto *fd = cast<FunctionDecl>(gd.getDecl()); if (unsigned builtinID = fd->getBuiltinID()) { - std::string noBuiltinFD = ("no-builtin-" + fd->getName()).str(); - std::string noBuiltins = "no-builtins"; + if(fd->getAttr<AsmLabelAttr>()) { + cgm.errorNYI("AsmLabelAttr"); + } - auto *a = fd->getAttr<AsmLabelAttr>(); - StringRef ident = a ? a->getLabel() : fd->getName(); + StringRef ident = fd->getName(); std::string fdInlineName = (ident + ".inline").str(); bool isPredefinedLibFunction = diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index e621487002f8e..8f5ce2a46a153 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -47,10 +47,6 @@ class CIRGenFunction : public CIRGenTypeCache { /// is where the next operations will be introduced. CIRGenBuilderTy &builder; - /// Largest vector width used in ths function. Will be used to create a - /// function attribute. - unsigned largestVectorWidth = 0; - public: /// The GlobalDecl for the current function being compiled or the global /// variable currently being initialized. @@ -902,9 +898,6 @@ class CIRGenFunction : public CIRGenTypeCache { void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit = false); - mlir::Value emitScalarOrConstFoldImmArg(unsigned iceArguments, unsigned idx, - const CallExpr *e); - void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, bool isInit = false, bool isNontemporal = false); @@ -924,10 +917,6 @@ class CIRGenFunction : public CIRGenTypeCache { bool buildingTopLevelCase); mlir::LogicalResult emitSwitchStmt(const clang::SwitchStmt &s); - mlir::Value emitTargetBuiltinExpr(unsigned builtinID, - const clang::CallExpr *e, - ReturnValueSlot returnValue); - /// Given a value and its clang type, returns the value casted to its memory /// representation. /// Note: CIR defers most of the special casting to the final lowering passes diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 5cbc3564cbe8b..24ec9ca6403bc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -264,10 +264,6 @@ class CIRGenModule : public CIRGenTypeCache { cir::FuncType funcType, const clang::FunctionDecl *funcDecl); - /// Given a builtin id for a function like "__builtin_fabsf", return a - /// Function* for "fabsf". - cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID); - mlir::IntegerAttr getSize(CharUnits size) { return builder.getSizeFromCharUnits(size); } >From 3701246666b919acf1d48f927507803d88b1d6f5 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Tue, 10 Jun 2025 17:23:39 +0200 Subject: [PATCH 5/5] Add FP tests --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenBuilder.cpp | 4 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 28 +++---------- clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 4 +- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 2 +- clang/test/CIR/CodeGen/builtin_call.cpp | 48 +++++++++++++++++++++++ 6 files changed, 58 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index bab684839c1f2..3b3741dac7f88 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -83,7 +83,6 @@ struct MissingFeatures { static bool opFuncSetComdat() { return false; } // CallOp handling - static bool opCallBuiltinFunc() { return false; } static bool opCallPseudoDtor() { return false; } static bool opCallAggregateArgs() { return false; } static bool opCallPaddingArgs() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp index 26a649837a558..d6929ff3316ae 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -50,9 +50,7 @@ cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, llvm::APInt intVal) { - unsigned width = intVal.getBitWidth(); - cir::IntType t = getUIntNTy(width); - return getConstInt(loc, t, intVal.getZExtValue()); + return getConstInt(loc, llvm::APSInt(intVal)); } cir::ConstantOp CIRGenBuilderTy::getConstInt(mlir::Location loc, mlir::Type t, diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 17d88646f5fb6..da4ac8bd0b85d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -11,6 +11,7 @@ #include "Address.h" #include "CIRGenTypeCache.h" +#include "clang/CIR/Interfaces/CIRFPTypeInterface.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" @@ -236,28 +237,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t, uint64_t c); - mlir::Type getFPType(llvm::fltSemantics const &sem) { - switch (llvm::APFloat::SemanticsToEnum(sem)) { - case llvm::APFloat::S_IEEEhalf: - return cir::FP16Type::get(getContext(), typeCache.FP16Ty); - case llvm::APFloat::S_BFloat: - return cir::BF16Type::get(getContext(), typeCache.BFloat16Ty); - case llvm::APFloat::S_IEEEsingle: - return cir::SingleType::get(getContext(), typeCache.FloatTy); - case llvm::APFloat::S_IEEEdouble: - return cir::DoubleType::get(getContext(), typeCache.DoubleTy); - case llvm::APFloat::S_IEEEquad: - return cir::FP128Type::get(getContext(), typeCache.FP128Ty); - case llvm::APFloat::S_x87DoubleExtended: - return cir::FP80Type::get(getContext(), typeCache.FP80Ty); - default: - llvm_unreachable("Unrecognized floating semantics"); - } - } - - cir::ConstantOp getConstFP(mlir::Location loc, llvm::APFloat fpVal) { - mlir::Type type = getFPType(fpVal.getSemantics()); - return create<cir::ConstantOp>(loc, getAttr<cir::FPAttr>(type, fpVal)); + cir::ConstantOp getConstFP(mlir::Location loc, mlir::Type t, + llvm::APFloat fpVal) { + assert(mlir::isa<cir::CIRFPTypeInterface>(t) && + "expected floating point type"); + return create<cir::ConstantOp>(loc, getAttr<cir::FPAttr>(t, fpVal)); } bool isInt8Ty(mlir::Type i) { diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 39925f3ce67a5..c59ac78210f81 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -43,8 +43,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, // type. We feel it should be Ok to use expression type because it is // hard to imagine a builtin function evaluates to a value that // over/underflows its own defined type. - mlir::Type resTy = convertType(e->getType()); - return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), resTy, + mlir::Type type = convertType(e->getType()); + return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), type, result.Val.getFloat())); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 6a0825c26b041..e19dae62ab9a3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1043,7 +1043,7 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) { const auto *fd = cast<FunctionDecl>(gd.getDecl()); if (unsigned builtinID = fd->getBuiltinID()) { - if(fd->getAttr<AsmLabelAttr>()) { + if (fd->getAttr<AsmLabelAttr>()) { cgm.errorNYI("AsmLabelAttr"); } diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp b/clang/test/CIR/CodeGen/builtin_call.cpp index 91bcedb18d36c..71707285014b0 100644 --- a/clang/test/CIR/CodeGen/builtin_call.cpp +++ b/clang/test/CIR/CodeGen/builtin_call.cpp @@ -11,6 +11,18 @@ constexpr extern int cx_var = __builtin_is_constant_evaluated(); // LLVM: @cx_var = {{.*}} i32 1 // OGCG: @cx_var = {{.*}} i32 1 +constexpr extern float cx_var_single = __builtin_huge_valf(); + +// CIR: cir.global {{.*}} @cx_var_single = #cir.fp<0x7F800000> : !cir.float +// LLVM: @cx_var_single = {{.*}} float 0x7FF0000000000000 +// OGCG: @cx_var_single = {{.*}} float 0x7FF0000000000000 + +constexpr extern long double cx_var_ld = __builtin_huge_vall(); + +// CIR: cir.global {{.*}} @cx_var_ld = #cir.fp<0x7FFF8000000000000000> : !cir.long_double<!cir.f80> +// LLVM: @cx_var_ld = {{.*}} x86_fp80 0xK7FFF8000000000000000 +// OGCG: @cx_var_ld = {{.*}} x86_fp80 0xK7FFF8000000000000000 + int is_constant_evaluated() { return __builtin_is_constant_evaluated(); } @@ -28,3 +40,39 @@ int is_constant_evaluated() { // OGCG: define {{.*}}i32 @_Z21is_constant_evaluatedv() // OGCG: ret i32 0 // OGCG: } + +long double constant_fp_builtin_ld() { + return __builtin_fabsl(-0.1L); +} + +// CIR: cir.func @_Z22constant_fp_builtin_ldv() -> !cir.long_double<!cir.f80> +// CIR: %[[PONE:.+]] = cir.const #cir.fp<1.000000e-01> : !cir.long_double<!cir.f80> + +// LLVM: define {{.*}}x86_fp80 @_Z22constant_fp_builtin_ldv() +// LLVM: %[[MEM:.+]] = alloca x86_fp80 +// LLVM: store x86_fp80 0xK3FFBCCCCCCCCCCCCCCCD, ptr %[[MEM]] +// LLVM: %[[RETVAL:.+]] = load x86_fp80, ptr %[[MEM]] +// LLVM: ret x86_fp80 %[[RETVAL]] +// LLVM: } + +// OGCG: define {{.*}}x86_fp80 @_Z22constant_fp_builtin_ldv() +// OGCG: ret x86_fp80 0xK3FFBCCCCCCCCCCCCCCCD +// OGCG: } + +float constant_fp_builtin_single() { + return __builtin_fabsf(-0.1f); +} + +// CIR: cir.func @_Z26constant_fp_builtin_singlev() -> !cir.float +// CIR: %[[PONE:.+]] = cir.const #cir.fp<1.000000e-01> : !cir.float + +// LLVM: define {{.*}}float @_Z26constant_fp_builtin_singlev() +// LLVM: %[[MEM:.+]] = alloca float +// LLVM: store float 0x3FB99999A0000000, ptr %[[MEM]] +// LLVM: %[[RETVAL:.+]] = load float, ptr %[[MEM]] +// LLVM: ret float %[[RETVAL]] +// LLVM: } + +// OGCG: define {{.*}}float @_Z26constant_fp_builtin_singlev() +// OGCG: ret float 0x3FB99999A0000000 +// OGCG: } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits