[clang] [CIR] Upstream initial function call support (PR #134673)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 7 08:43:17 PDT 2025
https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/134673
This PR upstreams initial support for making function calls in CIR. Function arguments and return values are not included to keep the patch small for review.
Related to #132487
>From 82f6ce93ca22dd778173100231523706e2739546 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Mon, 7 Apr 2025 22:54:49 +0800
Subject: [PATCH] [CIR] Upstream initial function call support
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 13 +++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 37 +++++++
.../clang/CIR/Interfaces/CIROpInterfaces.td | 9 ++
clang/include/clang/CIR/MissingFeatures.h | 24 +++++
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 95 ++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenCall.h | 58 +++++++++++
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 97 +++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 13 +++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 20 ++++
clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 34 +++++++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 13 ++-
clang/lib/CIR/CodeGen/CIRGenModule.h | 6 ++
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 30 ++++++
clang/lib/CIR/CodeGen/CIRGenTypes.h | 11 +++
clang/lib/CIR/CodeGen/CMakeLists.txt | 1 +
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 81 ++++++++++++++++
clang/test/CIR/CodeGen/call.cpp | 9 ++
17 files changed, 550 insertions(+), 1 deletion(-)
create mode 100644 clang/lib/CIR/CodeGen/CIRGenCall.cpp
create mode 100644 clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
create mode 100644 clang/test/CIR/CodeGen/call.cpp
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index c1e93fe790c08..dda3ecf492506 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -201,6 +201,19 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::PtrStrideOp>(loc, base.getType(), base, stride);
}
+ //===--------------------------------------------------------------------===//
+ // Call operators
+ //===--------------------------------------------------------------------===//
+
+ cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee) {
+ auto op = create<cir::CallOp>(loc, callee);
+ return op;
+ }
+
+ cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) {
+ return createCallOp(loc, mlir::SymbolRefAttr::get(callee));
+ }
+
//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 609e60ca74b49..aa805ee4108da 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1242,6 +1242,43 @@ def FuncOp : CIR_Op<"func", [
let hasVerifier = 1;
}
+//===----------------------------------------------------------------------===//
+// CallOp
+//===----------------------------------------------------------------------===//
+
+class CIR_CallOp<string mnemonic, list<Trait> extra_traits = []>
+ : Op<CIR_Dialect, mnemonic,
+ !listconcat(extra_traits,
+ [DeclareOpInterfaceMethods<CIRCallOpInterface>,
+ DeclareOpInterfaceMethods<SymbolUserOpInterface>])> {
+ let hasCustomAssemblyFormat = 1;
+ let skipDefaultBuilders = 1;
+ let hasVerifier = 0;
+
+ dag commonArgs = (ins FlatSymbolRefAttr:$callee);
+}
+
+def CallOp : CIR_CallOp<"call", [NoRegionArguments]> {
+ let summary = "call a function";
+ let description = [{
+ The `cir.call` operation represents a direct call to a function that is
+ within the same symbol scope as the call. The callee is encoded as a symbol
+ reference attribute named `callee`.
+
+ Example:
+
+ ```mlir
+ %0 = cir.call @foo()
+ ```
+ }];
+
+ let arguments = commonArgs;
+
+ let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee), [{
+ $_state.addAttribute("callee", callee);
+ }]>];
+}
+
//===----------------------------------------------------------------------===//
// UnreachableOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index 39ef402c59e43..c6c6356118ac6 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -15,8 +15,17 @@
include "mlir/IR/OpBase.td"
include "mlir/IR/SymbolInterfaces.td"
+include "mlir/Interfaces/CallInterfaces.td"
let cppNamespace = "::cir" in {
+ // The CIRCallOpInterface must be used instead of CallOpInterface when looking
+ // at arguments and other bits of CallOp. This creates a level of abstraction
+ // that's useful for handling indirect calls and other details.
+ def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", []> {
+ // Currently we don't have any methods defined in CIRCallOpInterface. We'll
+ // add more methods as the upstreaming proceeds.
+ }
+
def CIRGlobalValueInterface
: OpInterface<"CIRGlobalValueInterface", [Symbol]> {
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 86fdaf1ddaf51..491fb31c4b7e2 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -72,6 +72,24 @@ struct MissingFeatures {
static bool opFuncLinkage() { return false; }
static bool opFuncVisibility() { return false; }
+ // CallOp handling
+ static bool opCallBuiltinFunc() { return false; }
+ static bool opCallPseudoDtor() { return false; }
+ static bool opCallArgs() { return false; }
+ static bool opCallReturn() { return false; }
+ static bool opCallArgEvaluationOrder() { return false; }
+ static bool opCallCallConv() { return false; }
+ static bool opCallSideEffect() { return false; }
+ static bool opCallChainCall() { return false; }
+ static bool opCallNoPrototypeFunc() { return false; }
+ static bool opCallMustTail() { return false; }
+ static bool opCallIndirect() { return false; }
+ static bool opCallVirtual() { return false; }
+ static bool opCallInAlloca() { return false; }
+ static bool opCallAttrs() { return false; }
+ static bool opCallSurroundingTry() { return false; }
+ static bool opCallASTAttr() { return false; }
+
// ScopeOp handling
static bool opScopeCleanupRegion() { return false; }
@@ -90,7 +108,10 @@ struct MissingFeatures {
static bool opTBAA() { return false; }
static bool opCmp() { return false; }
static bool objCLifetime() { return false; }
+ static bool objCBlocks() { return false; }
static bool emitNullabilityCheck() { return false; }
+ static bool emitLValueAlignmentAssumption() { return false; }
+ static bool emitLifetimeMarkers() { return false; }
static bool astVarDeclInterface() { return false; }
static bool stackSaveOp() { return false; }
static bool aggValueSlot() { return false; }
@@ -108,6 +129,8 @@ struct MissingFeatures {
static bool cgFPOptionsRAII() { return false; }
static bool metaDataNode() { return false; }
static bool fastMathFlags() { return false; }
+ static bool weakRefReference() { return false; }
+ static bool hip() { return false; }
// Missing types
static bool dataMemberType() { return false; }
@@ -127,6 +150,7 @@ struct MissingFeatures {
static bool complexImagOp() { return false; }
static bool complexRealOp() { return false; }
static bool ifOp() { return false; }
+ static bool invokeOp() { return false; }
static bool labelOp() { return false; }
static bool ptrDiffOp() { return false; }
static bool ptrStrideOp() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
new file mode 100644
index 0000000000000..1a936458782ea
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -0,0 +1,95 @@
+//===--- CIRGenCall.cpp - Encapsulate calling convention details ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes wrap the information about a call or function definition used
+// to handle ABI compliancy.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenCall.h"
+#include "CIRGenFunction.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+CIRGenFunctionInfo *CIRGenFunctionInfo::create() {
+ // For now we just create an empty CIRGenFunctionInfo.
+ CIRGenFunctionInfo *fi = new CIRGenFunctionInfo();
+ return fi;
+}
+
+CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
+ assert(!cir::MissingFeatures::opCallVirtual());
+ return *this;
+}
+
+static const CIRGenFunctionInfo &arrangeFreeFunctionLikeCall(CIRGenTypes &cgt) {
+ assert(!cir::MissingFeatures::opCallArgs());
+ return cgt.arrangeCIRFunctionInfo();
+}
+
+const CIRGenFunctionInfo &CIRGenTypes::arrangeFreeFunctionCall() {
+ return arrangeFreeFunctionLikeCall(*this);
+}
+
+static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
+ mlir::Location callLoc,
+ cir::FuncOp directFuncOp) {
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+
+ assert(!cir::MissingFeatures::opCallSurroundingTry());
+ assert(!cir::MissingFeatures::invokeOp());
+
+ assert(builder.getInsertionBlock() && "expected valid basic block");
+ assert(!cir::MissingFeatures::opCallIndirect());
+
+ return builder.createCallOp(callLoc, directFuncOp);
+}
+
+RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
+ const CIRGenCallee &callee,
+ cir::CIRCallOpInterface *callOp,
+ mlir::Location loc) {
+ assert(!cir::MissingFeatures::opCallArgs());
+ assert(!cir::MissingFeatures::emitLifetimeMarkers());
+
+ const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this);
+ mlir::Operation *calleePtr = concreteCallee.getFunctionPointer();
+
+ assert(!cir::MissingFeatures::opCallInAlloca());
+
+ mlir::NamedAttrList attrs;
+ StringRef funcName;
+ if (auto calleeFuncOp = dyn_cast<cir::FuncOp>(calleePtr))
+ funcName = calleeFuncOp.getName();
+
+ assert(!cir::MissingFeatures::opCallCallConv());
+ assert(!cir::MissingFeatures::opCallSideEffect());
+ assert(!cir::MissingFeatures::opCallAttrs());
+
+ assert(!cir::MissingFeatures::invokeOp());
+
+ auto directFuncOp = dyn_cast<cir::FuncOp>(calleePtr);
+ assert(!cir::MissingFeatures::opCallIndirect());
+ assert(!cir::MissingFeatures::opCallAttrs());
+
+ cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, directFuncOp);
+
+ if (callOp)
+ *callOp = theCall;
+
+ assert(!cir::MissingFeatures::opCallMustTail());
+ assert(!cir::MissingFeatures::opCallReturn());
+
+ // For now we just return nothing because we don't have support for return
+ // values yet.
+ RValue ret = RValue::get(nullptr);
+
+ return ret;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index 0996167feeef6..76fefdca9e45e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -14,15 +14,73 @@
#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H
#define CLANG_LIB_CODEGEN_CIRGENCALL_H
+#include "mlir/IR/Operation.h"
#include "clang/AST/GlobalDecl.h"
#include "llvm/ADT/SmallVector.h"
namespace clang::CIRGen {
+class CIRGenFunction;
+
+/// Abstract information about a function or function prototype.
+class CIRGenCalleeInfo {
+ clang::GlobalDecl calleeDecl;
+
+public:
+ explicit CIRGenCalleeInfo() : calleeDecl() {}
+ CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) : calleeDecl(calleeDecl) {}
+};
+
+class CIRGenCallee {
+ enum class SpecialKind : uintptr_t {
+ Invalid,
+
+ Last = Invalid,
+ };
+
+ SpecialKind kindOrFunctionPtr;
+
+ union {
+ CIRGenCalleeInfo abstractInfo;
+ };
+
+public:
+ CIRGenCallee() : kindOrFunctionPtr(SpecialKind::Invalid) {}
+
+ CIRGenCallee(const CIRGenCalleeInfo &abstractInfo, mlir::Operation *funcPtr)
+ : kindOrFunctionPtr(SpecialKind(reinterpret_cast<uintptr_t>(funcPtr))),
+ abstractInfo(abstractInfo) {
+ assert(funcPtr && "configuring callee without function pointer");
+ }
+
+ static CIRGenCallee
+ forDirect(mlir::Operation *funcPtr,
+ const CIRGenCalleeInfo &abstractInfo = CIRGenCalleeInfo()) {
+ return CIRGenCallee(abstractInfo, funcPtr);
+ }
+
+ bool isOrdinary() const {
+ return uintptr_t(kindOrFunctionPtr) > uintptr_t(SpecialKind::Last);
+ }
+
+ /// If this is a delayed callee computation of some sort, prepare a concrete
+ /// callee
+ CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const;
+
+ mlir::Operation *getFunctionPointer() const {
+ assert(isOrdinary());
+ return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);
+ }
+};
+
/// Type for representing both the decl and type of parameters to a function.
/// The decl must be either a ParmVarDecl or ImplicitParamDecl.
class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {};
+struct CallArg {};
+
+class CallArgList : public llvm::SmallVector<CallArg, 8> {};
+
} // namespace clang::CIRGen
#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index f01e03a89981d..1e5fb985b0b8c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
@@ -304,6 +305,102 @@ RValue CIRGenFunction::emitAnyExpr(const Expr *e) {
llvm_unreachable("bad evaluation kind");
}
+static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) {
+ assert(!cir::MissingFeatures::weakRefReference());
+ return cgm.getAddrOfFunction(gd);
+}
+
+static CIRGenCallee emitDirectCallee(CIRGenModule &cgm, GlobalDecl gd) {
+ assert(!cir::MissingFeatures::opCallBuiltinFunc());
+
+ cir::FuncOp callee = emitFunctionDeclPointer(cgm, gd);
+
+ assert(!cir::MissingFeatures::hip());
+
+ return CIRGenCallee::forDirect(callee, gd);
+}
+
+RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
+ const CIRGenCallee &callee,
+ const clang::CallExpr *e) {
+ // Get the actual function type. The callee type will always be a pointer to
+ // function type or a block pointer type.
+ assert(calleeTy->isFunctionPointerType() &&
+ "Callee must have function pointer type!");
+
+ calleeTy = getContext().getCanonicalType(calleeTy);
+
+ if (getLangOpts().CPlusPlus)
+ assert(!cir::MissingFeatures::sanitizers());
+
+ assert(!cir::MissingFeatures::sanitizers());
+ assert(!cir::MissingFeatures::opCallArgs());
+
+ const CIRGenFunctionInfo &funcInfo = cgm.getTypes().arrangeFreeFunctionCall();
+
+ assert(!cir::MissingFeatures::opCallNoPrototypeFunc());
+ assert(!cir::MissingFeatures::opCallChainCall());
+ assert(!cir::MissingFeatures::hip());
+ assert(!cir::MissingFeatures::opCallMustTail());
+
+ cir::CIRCallOpInterface callOp;
+ RValue callResult =
+ emitCall(funcInfo, callee, &callOp, getLoc(e->getExprLoc()));
+
+ assert(!cir::MissingFeatures::generateDebugInfo());
+
+ return callResult;
+}
+
+CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) {
+ e = e->IgnoreParens();
+
+ // Look through function-to-pointer decay.
+ if (const auto *implicitCast = dyn_cast<ImplicitCastExpr>(e)) {
+ if (implicitCast->getCastKind() == CK_FunctionToPointerDecay ||
+ implicitCast->getCastKind() == CK_BuiltinFnToFnPtr) {
+ return emitCallee(implicitCast->getSubExpr());
+ }
+ // Resolve direct calls.
+ } else if (const auto *declRef = dyn_cast<DeclRefExpr>(e)) {
+ const auto *funcDecl = dyn_cast<FunctionDecl>(declRef->getDecl());
+ assert(
+ funcDecl &&
+ "DeclRef referring to FunctionDecl is the only thing supported so far");
+ return emitDirectCallee(cgm, funcDecl);
+ }
+
+ llvm_unreachable("Nothing else supported yet!");
+}
+
+RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e) {
+ assert(!cir::MissingFeatures::objCBlocks());
+
+ if (isa<CXXMemberCallExpr>(e)) {
+ cgm.errorNYI(e->getSourceRange(), "call to member function");
+ return RValue::get(nullptr);
+ }
+
+ if (isa<CUDAKernelCallExpr>(e)) {
+ cgm.errorNYI(e->getSourceRange(), "call to CUDA kernel");
+ return RValue::get(nullptr);
+ }
+
+ if (const auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(e)) {
+ if (isa_and_nonnull<CXXMethodDecl>(operatorCall->getCalleeDecl())) {
+ cgm.errorNYI(e->getSourceRange(), "call to member operator");
+ return RValue::get(nullptr);
+ }
+ }
+
+ CIRGenCallee callee = emitCallee(e->getCallee());
+
+ assert(!cir::MissingFeatures::opCallBuiltinFunc());
+ assert(!cir::MissingFeatures::opCallPseudoDtor());
+
+ return emitCall(e->getCallee()->getType(), callee, e);
+}
+
/// Emit code to compute the specified expression, ignoring the result.
void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
if (e->isPRValue()) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 3863d21487531..ca6f35e0f7319 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -156,6 +156,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
}
mlir::Value VisitCastExpr(CastExpr *e);
+ mlir::Value VisitCallExpr(const CallExpr *e);
mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) {
return VisitCastExpr(e);
@@ -1345,6 +1346,18 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
return {};
}
+mlir::Value ScalarExprEmitter::VisitCallExpr(const CallExpr *e) {
+ if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
+ cgf.getCIRGenModule().errorNYI(
+ e->getSourceRange(), "call to function with non-void return type");
+ return {};
+ }
+
+ auto v = cgf.emitCallExpr(e).getScalarVal();
+ assert(!cir::MissingFeatures::emitLValueAlignmentAssumption());
+ return v;
+}
+
mlir::Value CIRGenFunction::emitScalarConversion(mlir::Value src,
QualType srcTy, QualType dstTy,
SourceLocation loc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 1bedbe28ae625..1a0ab51c759f5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -403,6 +403,26 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
+ /// An abstract representation of regular/ObjC call/message targets.
+ class AbstractCallee {
+ /// The function declaration of the callee.
+ const clang::Decl *calleeDecl;
+
+ public:
+ AbstractCallee() : calleeDecl(nullptr) {}
+ AbstractCallee(const clang::FunctionDecl *fd) : calleeDecl(fd) {}
+ };
+
+ RValue emitCall(const CIRGenFunctionInfo &funcInfo,
+ const CIRGenCallee &callee, cir::CIRCallOpInterface *callOp,
+ mlir::Location loc);
+ RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee,
+ const clang::CallExpr *e);
+
+ CIRGenCallee emitCallee(const clang::Expr *e);
+
+ RValue emitCallExpr(const clang::CallExpr *e);
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
new file mode 100644
index 0000000000000..37191ee300eda
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -0,0 +1,34 @@
+//==-- CIRGenFunctionInfo.h - Representation of fn argument/return types ---==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines CIRGenFunctionInfo and associated types used in representing the
+// CIR source types and ABI-coerced types for function arguments and
+// return values.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H
+#define LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H
+
+#include "llvm/ADT/FoldingSet.h"
+
+namespace clang::CIRGen {
+
+class CIRGenFunctionInfo final : public llvm::FoldingSetNode {
+public:
+ static CIRGenFunctionInfo *create();
+
+ // NOLINTNEXTLINE(readability-identifier-naming)
+ static void Profile(llvm::FoldingSetNodeID &id) {
+ // We don't have anything to profile yet.
+ }
+};
+
+} // namespace clang::CIRGen
+
+#endif
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index d3b3b0632c2f0..a07a1e3d52e2c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -133,10 +133,12 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
}
CIRGenFunction cgf(*this, builder);
+ curCGF = &cgf;
{
mlir::OpBuilder::InsertionGuard guard(builder);
cgf.generateCode(gd, funcOp, funcType);
}
+ curCGF = nullptr;
}
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
@@ -466,8 +468,17 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
{
mlir::OpBuilder::InsertionGuard guard(builder);
+ // Some global emissions are triggered while emitting a function, e.g.
+ // void s() { x.method() }
+ //
+ // Be sure to insert a new function before a current one.
+ if (curCGF)
+ builder.setInsertionPoint(curCGF->curFn);
+
func = builder.create<cir::FuncOp>(loc, name, funcType);
- theModule.push_back(func);
+
+ if (!curCGF)
+ theModule.push_back(func);
}
return func;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 6ba1ccc4ddd9f..7ee24699b521e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -41,6 +41,8 @@ class VarDecl;
namespace CIRGen {
+class CIRGenFunction;
+
enum ForDefinition_t : bool { NotForDefinition = false, ForDefinition = true };
/// This class organizes the cross-function state that is used while generating
@@ -75,6 +77,10 @@ class CIRGenModule : public CIRGenTypeCache {
CIRGenTypes genTypes;
+ /// Per-function codegen information. Updated everytime emitCIR is called
+ /// for FunctionDecls's.
+ CIRGenFunction *curCGF = nullptr;
+
public:
mlir::ModuleOp getModule() const { return theModule; }
CIRGenBuilderTy &getBuilder() { return builder; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 1e47ccc451b86..6be11a9d5a7ff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -281,3 +281,33 @@ bool CIRGenTypes::isZeroInitializable(clang::QualType t) {
return true;
}
+
+const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo() {
+ // Lookup or create unique function info.
+ llvm::FoldingSetNodeID id;
+ CIRGenFunctionInfo::Profile(id);
+
+ void *insertPos = nullptr;
+ CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos);
+ if (fi)
+ return *fi;
+
+ assert(!cir::MissingFeatures::opCallCallConv());
+
+ // Construction the function info. We co-allocate the ArgInfos.
+ fi = CIRGenFunctionInfo::create();
+ functionInfos.InsertNode(fi, insertPos);
+
+ bool inserted = functionsBeingProcessed.insert(fi).second;
+ (void)inserted;
+ assert(inserted && "Recursively being processed?");
+
+ assert(!cir::MissingFeatures::opCallCallConv());
+ assert(!cir::MissingFeatures::opCallArgs());
+
+ bool erased = functionsBeingProcessed.erase(fi);
+ (void)erased;
+ assert(erased && "Not in set?");
+
+ return *fi;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 73948f5c63e6a..ca43f96ed78a6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_LIB_CODEGEN_CODEGENTYPES_H
#define LLVM_CLANG_LIB_CODEGEN_CODEGENTYPES_H
+#include "CIRGenFunctionInfo.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/AST/Type.h"
@@ -33,6 +34,7 @@ class Type;
namespace clang::CIRGen {
+class CallArgList;
class CIRGenBuilderTy;
class CIRGenModule;
@@ -43,6 +45,11 @@ class CIRGenTypes {
clang::ASTContext &astContext;
CIRGenBuilderTy &builder;
+ /// Hold memoized CIRGenFunctionInfo results
+ llvm::FoldingSet<CIRGenFunctionInfo> functionInfos;
+
+ llvm::SmallPtrSet<const CIRGenFunctionInfo *, 4> functionsBeingProcessed;
+
/// Heper for convertType.
mlir::Type convertFunctionTypeInternal(clang::QualType ft);
@@ -75,6 +82,10 @@ class CIRGenTypes {
/// Return whether a type can be zero-initialized (in the C++ sense) with an
/// LLVM zeroinitializer.
bool isZeroInitializable(clang::QualType t);
+
+ const CIRGenFunctionInfo &arrangeFreeFunctionCall();
+
+ const CIRGenFunctionInfo &arrangeCIRFunctionInfo();
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index da8d63ca569af..794a63ba36d50 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
add_clang_library(clangCIR
CIRGenerator.cpp
+ CIRGenCall.cpp
CIRGenDecl.cpp
CIRGenExpr.cpp
CIRGenExprAggregate.cpp
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 798aca602c4e3..6ff798c46691e 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -441,6 +441,87 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
return tryFoldCastChain(*this);
}
+//===----------------------------------------------------------------------===//
+// CallOp
+//===----------------------------------------------------------------------===//
+
+static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ mlir::FlatSymbolRefAttr calleeAttr;
+
+ if (!parser.parseOptionalAttribute(calleeAttr, "callee", result.attributes)
+ .has_value())
+ return mlir::failure();
+
+ if (parser.parseLParen())
+ return mlir::failure();
+
+ // TODO(cir): parse argument list here
+
+ if (parser.parseRParen())
+ return mlir::failure();
+
+ if (parser.parseOptionalAttrDict(result.attributes))
+ return ::mlir::failure();
+
+ if (parser.parseColon())
+ return ::mlir::failure();
+
+ mlir::FunctionType opsFnTy;
+ if (parser.parseType(opsFnTy))
+ return mlir::failure();
+
+ return mlir::success();
+}
+
+static void printCallCommon(mlir::Operation *op,
+ mlir::FlatSymbolRefAttr calleeSym,
+ mlir::OpAsmPrinter &printer) {
+ printer << ' ';
+
+ printer.printAttributeWithoutType(calleeSym);
+ printer << "(";
+ // TODO(cir): print call args here
+ printer << ")";
+
+ printer.printOptionalAttrDict(op->getAttrs(), {"callee"});
+
+ printer << " : ";
+ printer.printFunctionalType(op->getOperands().getTypes(),
+ op->getResultTypes());
+}
+
+mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
+ mlir::OperationState &result) {
+ return parseCallCommon(parser, result);
+}
+
+void cir::CallOp::print(mlir::OpAsmPrinter &p) {
+ printCallCommon(*this, getCalleeAttr(), p);
+}
+
+static LogicalResult
+verifyCallCommInSymbolUses(mlir::Operation *op,
+ SymbolTableCollection &symbolTable) {
+ auto fnAttr = op->getAttrOfType<FlatSymbolRefAttr>("callee");
+ if (!fnAttr)
+ return mlir::failure();
+
+ auto fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr);
+ if (!fn)
+ return op->emitOpError() << "'" << fnAttr.getValue()
+ << "' does not reference a valid function";
+
+ // TODO(cir): verify function arguments and return type
+
+ return mlir::success();
+}
+
+LogicalResult
+cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ return verifyCallCommInSymbolUses(*this, symbolTable);
+}
+
//===----------------------------------------------------------------------===//
// ReturnOp
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
new file mode 100644
index 0000000000000..e69b347c2ca99
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
+
+void f1();
+void f2() {
+ f1();
+}
+
+// CHECK-LABEL: cir.func @f2
+// CHECK: cir.call @f1() : () -> ()
More information about the cfe-commits
mailing list