https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/142981
>From 939ad27d0d0dacc5b796e36ca9bca32d26f6b714 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/4] [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 038a59b8ff4eb..77fe75d27fae4 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 7fcb6cb95db4a..8c5edc7b272c7 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -217,6 +217,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 5620821a5375a..f7d00078f96dc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -38,3 +38,25 @@ mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin, const mlir::Type flatPtrTy = basePtr.getType(); 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)); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 5f33ae1af35ee..a2c6ae2ab6ab6 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 1175fdc0be2cf..704489c8dee9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1004,8 +1004,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); @@ -1081,7 +1121,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"); @@ -1137,10 +1177,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 ee014adc961be..7fe1f7e4a807c 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. @@ -623,6 +627,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, @@ -669,6 +675,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, @@ -849,6 +858,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); @@ -868,6 +880,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 ac25d52472050..65a0b2dfe5fed 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -260,6 +260,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 185a0e10547af..403ebf6d820e7 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangCIR CIRGenCall.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 2b77bc6c8718d16d10d28e85b2fc4499be01ad81 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/4] 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 65a0b2dfe5fed..adddd9a39b6de 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -264,7 +264,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 ce6bdf976f0e14130020547c5bb29fb088b0f13e 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/4] 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 fe89ed2f1b1eca35e49f05fca6f5b7fb72fc22b7 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/4] - 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 77fe75d27fae4..038a59b8ff4eb 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 8c5edc7b272c7..9d54fc0eeb214 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -217,7 +217,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 f7d00078f96dc..beefebe85ca65 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 a2c6ae2ab6ab6..95a2cc939b279 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 704489c8dee9c..b9de5c9307fa2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1018,11 +1018,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 7fe1f7e4a807c..5cb42a184ef09 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. @@ -858,9 +854,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); @@ -880,10 +873,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 adddd9a39b6de..ac25d52472050 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -260,10 +260,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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits