Author: Morris Hafner Date: 2025-06-11T18:24:46+02:00 New Revision: 8e4f0d8614dcd48cfe2d885a021e2927c1bc8616
URL: https://github.com/llvm/llvm-project/commit/8e4f0d8614dcd48cfe2d885a021e2927c1bc8616 DIFF: https://github.com/llvm/llvm-project/commit/8e4f0d8614dcd48cfe2d885a021e2927c1bc8616.diff LOG: [CIR] Upstream minimal builtin function call support (#142981) 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). Added: clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp clang/test/CIR/CodeGen/builtin_call.cpp Modified: clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenBuilder.cpp clang/lib/CIR/CodeGen/CIRGenBuilder.h clang/lib/CIR/CodeGen/CIRGenCall.h clang/lib/CIR/CodeGen/CIRGenExpr.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CMakeLists.txt Removed: ################################################################################ diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index f89d386378e51..87908e2ec08ac 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; } @@ -225,6 +224,8 @@ struct MissingFeatures { static bool isMemcpyEquivalentSpecialMember() { return false; } static bool isTrivialCtorOrDtor() { return false; } static bool implicitConstructorArgs() { 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..9cec17bcb2fd0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp @@ -39,6 +39,34 @@ 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(); + unsigned 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) { + return getConstInt(loc, llvm::APSInt(intVal)); +} + +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)); +} + +cir::ConstantOp +clang::CIRGen::CIRGenBuilderTy::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)); +} + // 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..fb1a290c18fa2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -11,10 +11,12 @@ #include "Address.h" #include "CIRGenTypeCache.h" +#include "clang/CIR/Interfaces/CIRFPTypeInterface.h" #include "clang/CIR/MissingFeatures.h" #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 { @@ -229,6 +231,15 @@ 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); + 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..c59ac78210f81 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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 "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Value.h" +#include "mlir/Support/LLVM.h" +#include "clang/AST/Expr.h" +#include "clang/AST/GlobalDecl.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace clang::CIRGen; + +RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, + const CallExpr *e, + ReturnValueSlot returnValue) { + // 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. 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 type = convertType(e->getType()); + return RValue::get(builder.getConstFP(getLoc(e->getExprLoc()), type, + result.Val.getFloat())); + } + } + + mlir::Location loc = getLoc(e->getExprLoc()); + cgm.errorNYI(loc, "non constant foldable builtin calls"); + return getUndefRValue(e->getType()); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 605625705a75c..15c9080448c8b 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, + Last = Builtin, + }; + + 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); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index f2c2de7a4f59d..f1f86509c9a9b 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()) { + if (fd->getAttr<AsmLabelAttr>()) { + cgm.errorNYI("AsmLabelAttr"); + } + + StringRef ident = 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 7db7f6928fd8f..b08dd540e6289 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -665,6 +665,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 +713,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, 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..2706ea7f8f857 --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin_call.cpp @@ -0,0 +1,78 @@ +// 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 + +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(); +} + +// 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: } + +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