0
0
mirror of https://github.com/llvm/llvm-project.git synced 2025-04-21 12:56:52 +00:00

[CIR] Upstream initial function call support ()

This patch 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 
This commit is contained in:
Sirui Mu 2025-04-10 22:41:00 +08:00 committed by GitHub
parent 4b267bb7c2
commit 85614e160b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 580 additions and 1 deletions

@ -201,6 +201,19 @@ public:
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
//===--------------------------------------------------------------------===//

@ -1342,6 +1342,48 @@ def FuncOp : CIR_Op<"func", [
let hasVerifier = 1;
}
//===----------------------------------------------------------------------===//
// CallOp
//===----------------------------------------------------------------------===//
class CIR_CallOpBase<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;
// TODO(cir): for now cir.call is just a tiny shell of what it will become.
// More attributes, arguments, and properties will be added in the future as
// the upstreaming process moves on. The verifiers is also missing for now,
// will add in the future.
dag commonArgs = (ins FlatSymbolRefAttr:$callee);
}
def CallOp : CIR_CallOpBase<"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
//===----------------------------------------------------------------------===//

@ -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]> {

@ -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 lowerModeOptLevel() { return false; }
static bool opTBAA() { 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; }
@ -113,6 +134,8 @@ struct MissingFeatures {
static bool incrementProfileCounter() { return false; }
static bool insertBuiltinUnpredictable() { return false; }
static bool objCGC() { return false; }
static bool weakRefReference() { return false; }
static bool hip() { return false; }
// Missing types
static bool dataMemberType() { return false; }
@ -132,6 +155,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; }

@ -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;
}

@ -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

@ -465,6 +465,107 @@ 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());
}
} else if (const auto *declRef = dyn_cast<DeclRefExpr>(e)) {
// Resolve direct calls.
if (const auto *funcDecl = dyn_cast<FunctionDecl>(declRef->getDecl()))
return emitDirectCallee(cgm, funcDecl);
}
cgm.errorNYI(e->getSourceRange(), "Unsupported callee kind");
return {};
}
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());
if (e->getBuiltinCallee()) {
cgm.errorNYI(e->getSourceRange(), "call to builtin functions");
}
assert(!cir::MissingFeatures::opCallBuiltinFunc());
if (isa<CXXPseudoDestructorExpr>(e->getCallee())) {
cgm.errorNYI(e->getSourceRange(), "call to pseudo destructor");
}
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()) {

@ -156,6 +156,7 @@ public:
}
mlir::Value VisitCastExpr(CastExpr *e);
mlir::Value VisitCallExpr(const CallExpr *e);
mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) {
return VisitCastExpr(e);
@ -1447,6 +1448,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) {

@ -161,6 +161,16 @@ public:
const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); }
/// 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) {}
};
void finishFunction(SourceLocation endLoc);
/// Determine whether the given initializer is trivial in the sense
@ -437,6 +447,15 @@ public:
LValue emitBinaryOperatorLValue(const BinaryOperator *e);
mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s);
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);
RValue emitCallExpr(const clang::CallExpr *e);
CIRGenCallee emitCallee(const clang::Expr *e);
mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s);
mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);

@ -0,0 +1,35 @@
//==-- 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();
// This function has to be CamelCase because llvm::FoldingSet requires so.
// NOLINTNEXTLINE(readability-identifier-naming)
static void Profile(llvm::FoldingSetNodeID &id) {
// We don't have anything to profile yet.
}
};
} // namespace clang::CIRGen
#endif

@ -194,10 +194,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,
@ -533,8 +535,18 @@ 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.
CIRGenFunction *cgf = this->curCGF;
if (cgf)
builder.setInsertionPoint(cgf->curFn);
func = builder.create<cir::FuncOp>(loc, name, funcType);
theModule.push_back(func);
if (!cgf)
theModule.push_back(func);
}
return func;
}

@ -42,6 +42,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
@ -76,6 +78,10 @@ private:
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; }

@ -289,3 +289,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 && "Are functions being processed recursively?");
assert(!cir::MissingFeatures::opCallCallConv());
assert(!cir::MissingFeatures::opCallArgs());
bool erased = functionsBeingProcessed.erase(fi);
(void)erased;
assert(erased && "Not in set?");
return *fi;
}

@ -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 @@ public:
/// Return whether a type can be zero-initialized (in the C++ sense) with an
/// LLVM zeroinitializer.
bool isZeroInitializable(clang::QualType ty);
const CIRGenFunctionInfo &arrangeFreeFunctionCall();
const CIRGenFunctionInfo &arrangeCIRFunctionInfo();
};
} // namespace clang::CIRGen

@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
add_clang_library(clangCIR
CIRGenerator.cpp
CIRGenCall.cpp
CIRGenDecl.cpp
CIRGenDeclOpenACC.cpp
CIRGenExpr.cpp

@ -442,6 +442,90 @@ 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
assert(!cir::MissingFeatures::opCallArgs());
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
assert(!cir::MissingFeatures::opCallArgs());
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
assert(!cir::MissingFeatures::opCallArgs());
return mlir::success();
}
LogicalResult
cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return verifyCallCommInSymbolUses(*this, symbolTable);
}
//===----------------------------------------------------------------------===//
// ReturnOp
//===----------------------------------------------------------------------===//

@ -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() : () -> ()

@ -0,0 +1,17 @@
// RUN: cir-opt %s | FileCheck %s
module {
cir.func @f1()
cir.func @f2() {
cir.call @f1() : () -> ()
cir.return
}
// CHECK: cir.func @f2() {
// CHECK-NEXT: cir.call @f1() : () -> ()
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
}