[clang] 8e4f0d8 - [CIR] Upstream minimal builtin function call support (#142981)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 11 09:24:49 PDT 2025
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: }
More information about the cfe-commits
mailing list