[clang] 838ddc2 - [CIR] Add support for indirect calls (#139748)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 20 03:11:23 PDT 2025
Author: Sirui Mu
Date: 2025-05-20T18:11:19+08:00
New Revision: 838ddc28f222ae244626a827a433fd9235546d3d
URL: https://github.com/llvm/llvm-project/commit/838ddc28f222ae244626a827a433fd9235546d3d
DIFF: https://github.com/llvm/llvm-project/commit/838ddc28f222ae244626a827a433fd9235546d3d.diff
LOG: [CIR] Add support for indirect calls (#139748)
This PR adds support for indirect calls to the `cir.call` operation.
Added:
Modified:
clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenCall.cpp
clang/lib/CIR/CodeGen/CIRGenCall.h
clang/lib/CIR/CodeGen/CIRGenExpr.cpp
clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
clang/lib/CIR/CodeGen/CIRGenTypes.h
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/test/CIR/CodeGen/call.cpp
clang/test/CIR/IR/call.cir
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index a63bf4f8858d0..b680e4162a5ce 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -225,6 +225,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
callee.getFunctionType().getReturnType(), operands);
}
+ cir::CallOp createIndirectCallOp(mlir::Location loc,
+ mlir::Value indirectTarget,
+ cir::FuncType funcType,
+ mlir::ValueRange operands) {
+ return create<cir::CallOp>(loc, indirectTarget, funcType.getReturnType(),
+ operands);
+ }
+
//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d3411973129a4..8267df92e3187 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1843,13 +1843,8 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
DeclareOpInterfaceMethods<SymbolUserOpInterface>])> {
let extraClassDeclaration = [{
/// Get the argument operands to the called function.
- mlir::OperandRange getArgOperands() {
- return getArgs();
- }
-
- mlir::MutableOperandRange getArgOperandsMutable() {
- return getArgsMutable();
- }
+ mlir::OperandRange getArgOperands();
+ mlir::MutableOperandRange getArgOperandsMutable();
/// Return the callee of this operation
mlir::CallInterfaceCallable getCallableForCallee() {
@@ -1871,8 +1866,17 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
::mlir::Attribute removeArgAttrsAttr() { return {}; }
::mlir::Attribute removeResAttrsAttr() { return {}; }
+ bool isIndirect() { return !getCallee(); }
+ mlir::Value getIndirectCall();
+
void setArg(unsigned index, mlir::Value value) {
- setOperand(index, value);
+ if (!isIndirect()) {
+ setOperand(index, value);
+ return;
+ }
+
+ // For indirect call, the operand list is shifted by one.
+ setOperand(index + 1, value);
}
}];
@@ -1884,16 +1888,24 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
// the upstreaming process moves on. The verifiers is also missing for now,
// will add in the future.
- dag commonArgs = (ins FlatSymbolRefAttr:$callee,
- Variadic<CIR_AnyType>:$args);
+ dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee,
+ Variadic<CIR_AnyType>:$args);
}
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`.
+ The `cir.call` operation represents a function call. It could represent
+ either a direct call or an indirect call.
+
+ If the operation represents a direct call, the callee should be defined
+ within the same symbol scope as the call. The `callee` attribute contains a
+ symbol reference to the callee function. All operands of this operation are
+ arguments to the callee function.
+
+ If the operation represents an indirect call, the `callee` attribute is
+ empty. The first operand of this operation must be a pointer to the callee
+ function. The rest operands are arguments to the callee function.
Example:
@@ -1905,14 +1917,25 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let results = (outs Optional<CIR_AnyType>:$result);
let arguments = commonArgs;
- let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee,
- "mlir::Type":$resType,
- "mlir::ValueRange":$operands), [{
+ let builders = [
+ // Build a call op for a direct call
+ OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
+ "mlir::ValueRange":$operands), [{
+ assert(callee && "callee attribute is required for direct call");
$_state.addOperands(operands);
$_state.addAttribute("callee", callee);
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);
- }]>];
+ }]>,
+ // Build a call op for an indirect call
+ OpBuilder<(ins "mlir::Value":$calleePtr, "mlir::Type":$resType,
+ "mlir::ValueRange":$operands), [{
+ $_state.addOperands(calleePtr);
+ $_state.addOperands(operands);
+ if (resType && !isa<VoidType>(resType))
+ $_state.addTypes(resType);
+ }]>,
+ ];
}
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index c0207393832f3..7b33d94483d5f 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -92,7 +92,6 @@ struct MissingFeatures {
static bool opCallSideEffect() { 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; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index bf3851544a3a5..49c50891255c3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -241,6 +241,7 @@ CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) {
static cir::CIRCallOpInterface
emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
+ cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
cir::FuncOp directFuncOp,
const SmallVectorImpl<mlir::Value> &cirCallArgs) {
CIRGenBuilderTy &builder = cgf.getBuilder();
@@ -249,7 +250,13 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
assert(!cir::MissingFeatures::invokeOp());
assert(builder.getInsertionBlock() && "expected valid basic block");
- assert(!cir::MissingFeatures::opCallIndirect());
+
+ if (indirectFuncTy) {
+ // TODO(cir): Set calling convention for indirect calls.
+ assert(!cir::MissingFeatures::opCallCallConv());
+ return builder.createIndirectCallOp(callLoc, indirectFuncVal,
+ indirectFuncTy, cirCallArgs);
+ }
return builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
}
@@ -275,6 +282,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
cir::CIRCallOpInterface *callOp,
mlir::Location loc) {
QualType retTy = funcInfo.getReturnType();
+ cir::FuncType cirFuncTy = getTypes().getFunctionType(funcInfo);
SmallVector<mlir::Value, 16> cirCallArgs(args.size());
@@ -326,12 +334,27 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
assert(!cir::MissingFeatures::invokeOp());
- auto directFuncOp = dyn_cast<cir::FuncOp>(calleePtr);
- assert(!cir::MissingFeatures::opCallIndirect());
+ cir::FuncType indirectFuncTy;
+ mlir::Value indirectFuncVal;
+ cir::FuncOp directFuncOp;
+ if (auto fnOp = dyn_cast<cir::FuncOp>(calleePtr)) {
+ directFuncOp = fnOp;
+ } else {
+ [[maybe_unused]] mlir::ValueTypeRange<mlir::ResultRange> resultTypes =
+ calleePtr->getResultTypes();
+ [[maybe_unused]] auto funcPtrTy =
+ mlir::dyn_cast<cir::PointerType>(resultTypes.front());
+ assert(funcPtrTy && mlir::isa<cir::FuncType>(funcPtrTy.getPointee()) &&
+ "expected pointer to function");
+
+ indirectFuncTy = cirFuncTy;
+ indirectFuncVal = calleePtr->getResult(0);
+ }
+
assert(!cir::MissingFeatures::opCallAttrs());
- cir::CIRCallOpInterface theCall =
- emitCallLikeOp(*this, loc, directFuncOp, cirCallArgs);
+ cir::CIRCallOpInterface theCall = emitCallLikeOp(
+ *this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs);
if (callOp)
*callOp = theCall;
@@ -431,7 +454,7 @@ void CIRGenFunction::emitCallArgs(
auto maybeEmitImplicitObjectSize = [&](size_t i, const Expr *arg,
RValue emittedArg) {
- if (callee.hasFunctionDecl() || i >= callee.getNumParams())
+ if (!callee.hasFunctionDecl() || i >= callee.getNumParams())
return;
auto *ps = callee.getParamDecl(i)->getAttr<PassObjectSizeAttr>();
if (!ps)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index d3c241c27d048..605625705a75c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -25,11 +25,20 @@ class CIRGenFunction;
/// Abstract information about a function or function prototype.
class CIRGenCalleeInfo {
+ const clang::FunctionProtoType *calleeProtoTy;
clang::GlobalDecl calleeDecl;
public:
- explicit CIRGenCalleeInfo() : calleeDecl() {}
+ explicit CIRGenCalleeInfo() : calleeProtoTy(nullptr), calleeDecl() {}
+ CIRGenCalleeInfo(const clang::FunctionProtoType *calleeProtoTy,
+ clang::GlobalDecl calleeDecl)
+ : calleeProtoTy(calleeProtoTy), calleeDecl(calleeDecl) {}
CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) : calleeDecl(calleeDecl) {}
+
+ const clang::FunctionProtoType *getCalleeFunctionProtoType() const {
+ return calleeProtoTy;
+ }
+ clang::GlobalDecl getCalleeDecl() const { return calleeDecl; }
};
class CIRGenCallee {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index b8e644d80d747..3b0ade2c52d5b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -931,14 +931,43 @@ CIRGenCallee CIRGenFunction::emitCallee(const clang::Expr *e) {
implicitCast->getCastKind() == CK_BuiltinFnToFnPtr) {
return emitCallee(implicitCast->getSubExpr());
}
+ // When performing an indirect call through a function pointer lvalue, the
+ // function pointer lvalue is implicitly converted to an rvalue through an
+ // lvalue-to-rvalue conversion.
+ assert(implicitCast->getCastKind() == CK_LValueToRValue &&
+ "unexpected implicit cast on function pointers");
} 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);
+ const auto *funcDecl = cast<FunctionDecl>(declRef->getDecl());
+ return emitDirectCallee(cgm, funcDecl);
+ } else if (isa<MemberExpr>(e)) {
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCallee: call to member function is NYI");
+ return {};
}
- cgm.errorNYI(e->getSourceRange(), "Unsupported callee kind");
- return {};
+ assert(!cir::MissingFeatures::opCallPseudoDtor());
+
+ // Otherwise, we have an indirect reference.
+ mlir::Value calleePtr;
+ QualType functionType;
+ if (const auto *ptrType = e->getType()->getAs<clang::PointerType>()) {
+ calleePtr = emitScalarExpr(e);
+ functionType = ptrType->getPointeeType();
+ } else {
+ functionType = e->getType();
+ calleePtr = emitLValue(e).getPointer();
+ }
+ assert(functionType->isFunctionType());
+
+ GlobalDecl gd;
+ if (const auto *vd =
+ dyn_cast_or_null<VarDecl>(e->getReferencedDeclOfCallee()))
+ gd = GlobalDecl(vd);
+
+ CIRGenCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), gd);
+ CIRGenCallee callee(calleeInfo, calleePtr.getDefiningOp());
+ return callee;
}
RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index b74460b09a44e..59b6bc9109894 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H
#include "clang/AST/CanonicalType.h"
+#include "clang/CIR/MissingFeatures.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/TrailingObjects.h"
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index aaad7f933c60d..a0f546d0a7cd6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -68,7 +68,6 @@ class CIRGenTypes {
/// types will be in this set.
llvm::SmallPtrSet<const clang::Type *, 4> recordsBeingLaidOut;
- llvm::SmallPtrSet<const CIRGenFunctionInfo *, 4> functionsBeingProcessed;
/// Heper for convertType.
mlir::Type convertFunctionTypeInternal(clang::QualType ft);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index bd1aa1f4fe5bb..36dcbc6a4be4a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -465,15 +465,35 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
// CallOp
//===----------------------------------------------------------------------===//
+mlir::OperandRange cir::CallOp::getArgOperands() {
+ if (isIndirect())
+ return getArgs().drop_front(1);
+ return getArgs();
+}
+
+mlir::MutableOperandRange cir::CallOp::getArgOperandsMutable() {
+ mlir::MutableOperandRange args = getArgsMutable();
+ if (isIndirect())
+ return args.slice(1, args.size() - 1);
+ return args;
+}
+
+mlir::Value cir::CallOp::getIndirectCall() {
+ assert(isIndirect());
+ return getOperand(0);
+}
+
/// Return the operand at index 'i'.
Value cir::CallOp::getArgOperand(unsigned i) {
- assert(!cir::MissingFeatures::opCallIndirect());
+ if (isIndirect())
+ ++i;
return getOperand(i);
}
/// Return the number of operands.
unsigned cir::CallOp::getNumArgOperands() {
- assert(!cir::MissingFeatures::opCallIndirect());
+ if (isIndirect())
+ return this->getOperation()->getNumOperands() - 1;
return this->getOperation()->getNumOperands();
}
@@ -484,9 +504,15 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
mlir::FlatSymbolRefAttr calleeAttr;
llvm::ArrayRef<mlir::Type> allResultTypes;
+ // If we cannot parse a string callee, it means this is an indirect call.
if (!parser.parseOptionalAttribute(calleeAttr, "callee", result.attributes)
- .has_value())
- return mlir::failure();
+ .has_value()) {
+ OpAsmParser::UnresolvedOperand indirectVal;
+ // Do not resolve right now, since we need to figure out the type
+ if (parser.parseOperand(indirectVal).failed())
+ return failure();
+ ops.push_back(indirectVal);
+ }
if (parser.parseLParen())
return mlir::failure();
@@ -518,13 +544,21 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
static void printCallCommon(mlir::Operation *op,
mlir::FlatSymbolRefAttr calleeSym,
+ mlir::Value indirectCallee,
mlir::OpAsmPrinter &printer) {
printer << ' ';
auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
auto ops = callLikeOp.getArgOperands();
- printer.printAttributeWithoutType(calleeSym);
+ if (calleeSym) {
+ // Direct calls
+ printer.printAttributeWithoutType(calleeSym);
+ } else {
+ // Indirect calls
+ assert(indirectCallee);
+ printer << indirectCallee;
+ }
printer << "(" << ops << ")";
printer.printOptionalAttrDict(op->getAttrs(), {"callee"});
@@ -540,15 +574,18 @@ mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
}
void cir::CallOp::print(mlir::OpAsmPrinter &p) {
- printCallCommon(*this, getCalleeAttr(), p);
+ mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
+ printCallCommon(*this, getCalleeAttr(), indirectCallee, p);
}
static LogicalResult
verifyCallCommInSymbolUses(mlir::Operation *op,
SymbolTableCollection &symbolTable) {
auto fnAttr = op->getAttrOfType<FlatSymbolRefAttr>("callee");
- if (!fnAttr)
- return mlir::failure();
+ if (!fnAttr) {
+ // This is an indirect call, thus we don't have to check the symbol uses.
+ return mlir::success();
+ }
auto fn = symbolTable.lookupNearestSymbolFrom<cir::FuncOp>(op, fnAttr);
if (!fn)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 55cec3072bb86..365569ce1f48a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -674,8 +674,15 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
converter->convertType(fn.getFunctionType()));
} else { // indirect call
- assert(!cir::MissingFeatures::opCallIndirect());
- return op->emitError("Indirect calls are NYI");
+ assert(!op->getOperands().empty() &&
+ "operands list must no be empty for the indirect call");
+ auto calleeTy = op->getOperands().front().getType();
+ auto calleePtrTy = cast<cir::PointerType>(calleeTy);
+ auto calleeFuncTy = cast<cir::FuncType>(calleePtrTy.getPointee());
+ calleeFuncTy.dump();
+ converter->convertType(calleeFuncTy).dump();
+ llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
+ converter->convertType(calleeFuncTy));
}
assert(!cir::MissingFeatures::opCallLandingPad());
@@ -1505,6 +1512,15 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
return mlir::BFloat16Type::get(type.getContext());
});
+ converter.addConversion([&](cir::FuncType type) -> std::optional<mlir::Type> {
+ auto result = converter.convertType(type.getReturnType());
+ llvm::SmallVector<mlir::Type> arguments;
+ arguments.reserve(type.getNumInputs());
+ if (converter.convertTypes(type.getInputs(), arguments).failed())
+ return std::nullopt;
+ auto varArg = type.isVarArg();
+ return mlir::LLVM::LLVMFunctionType::get(result, arguments, varArg);
+ });
converter.addConversion([&](cir::RecordType type) -> mlir::Type {
// Convert struct members.
llvm::SmallVector<mlir::Type> llvmMembers;
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
index 3b1ab8b5fc498..8b8f1296b5108 100644
--- a/clang/test/CIR/CodeGen/call.cpp
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -42,3 +42,17 @@ int f6() {
// LLVM-LABEL: define i32 @_Z2f6v() {
// LLVM: %{{.+}} = call i32 @_Z2f5iPib(i32 2, ptr %{{.+}}, i1 false)
+
+int f7(int (*ptr)(int, int)) {
+ return ptr(1, 2);
+}
+
+// CIR-LABEL: cir.func @_Z2f7PFiiiE
+// CIR: %[[#ptr:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>>, !cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>
+// CIR-NEXT: %[[#a:]] = cir.const #cir.int<1> : !s32i
+// CIR-NEXT: %[[#b:]] = cir.const #cir.int<2> : !s32i
+// CIR-NEXT: %{{.+}} = cir.call %[[#ptr]](%[[#a]], %[[#b]]) : (!cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>, !s32i, !s32i) -> !s32i
+
+// LLVM-LABEL: define i32 @_Z2f7PFiiiE
+// LLVM: %[[#ptr:]] = load ptr, ptr %{{.+}}
+// LLVM-NEXT: %{{.+}} = call i32 %[[#ptr]](i32 1, i32 2)
diff --git a/clang/test/CIR/IR/call.cir b/clang/test/CIR/IR/call.cir
index 8276c0cb9e39d..e35c201b6ed48 100644
--- a/clang/test/CIR/IR/call.cir
+++ b/clang/test/CIR/IR/call.cir
@@ -43,4 +43,18 @@ cir.func @f6() -> !s32i {
// CHECK-NEXT: cir.return %[[#c]] : !s32i
// CHECK-NEXT: }
+cir.func @f7(%arg0: !cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>) -> !s32i {
+ %0 = cir.const #cir.int<1> : !s32i
+ %1 = cir.const #cir.int<2> : !s32i
+ %2 = cir.call %arg0(%0, %1) : (!cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>, !s32i, !s32i) -> !s32i
+ cir.return %2 : !s32i
+}
+
+// CHECK: cir.func @f7(%[[ptr:.+]]: !cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>) -> !s32i {
+// CHECK-NEXT: %[[#a:]] = cir.const #cir.int<1> : !s32i
+// CHECK-NEXT: %[[#b:]] = cir.const #cir.int<2> : !s32i
+// CHECK-NEXT: %[[#ret:]] = cir.call %[[ptr]](%[[#a]], %[[#b]]) : (!cir.ptr<!cir.func<(!s32i, !s32i) -> !s32i>>, !s32i, !s32i) -> !s32i
+// CHECK-NEXT: cir.return %[[#ret]] : !s32i
+// CHECK-NEXT: }
+
}
More information about the cfe-commits
mailing list