[clang] [CIR] Upstream cir.call with scalar arguments (PR #136810)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 26 08:58:15 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/136810
>From 8f7b72f8dfe2e4fb32669a344f95be7ce04dae3d Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Wed, 23 Apr 2025 12:14:40 +0800
Subject: [PATCH] [CIR] Upstream cir.call with scalar arguments
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 10 +-
.../include/clang/CIR/Dialect/IR/CIRDialect.h | 1 +
clang/include/clang/CIR/Dialect/IR/CIROps.td | 49 +++-
.../clang/CIR/Interfaces/CIROpInterfaces.td | 17 +-
clang/include/clang/CIR/MissingFeatures.h | 15 +-
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 275 +++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenCall.h | 32 +-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 24 +-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 48 ++-
clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 27 +-
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 15 +-
clang/lib/CIR/CodeGen/CIRGenTypes.h | 7 +-
clang/lib/CIR/CodeGen/TargetInfo.cpp | 7 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 58 +++-
clang/test/CIR/CodeGen/call.cpp | 12 +
clang/test/CIR/IR/call.cir | 15 +
clang/test/CIR/IR/invalid-call.cir | 27 ++
17 files changed, 591 insertions(+), 48 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 539268c6270f4..a10a660b5ed23 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -214,14 +214,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
//===--------------------------------------------------------------------===//
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
- mlir::Type returnType) {
- auto op = create<cir::CallOp>(loc, callee, returnType);
- return op;
+ mlir::Type returnType, mlir::ValueRange operands) {
+ return create<cir::CallOp>(loc, callee, returnType, operands);
}
- cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee) {
+ cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee,
+ mlir::ValueRange operands) {
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
- callee.getFunctionType().getReturnType());
+ callee.getFunctionType().getReturnType(), operands);
}
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
index 36c5ad1388afa..f742789093998 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
@@ -31,6 +31,7 @@
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Interfaces/CIRLoopOpInterface.h"
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
+#include "clang/CIR/MissingFeatures.h"
namespace mlir {
namespace OpTrait {
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bb19de31b4fa5..7975c36adf89c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1496,6 +1496,13 @@ def FuncOp : CIR_Op<"func", [
return getFunctionType().getReturnTypes();
}
+ // TODO(cir): this should be an operand attribute, but for now we just hard-
+ // wire this as a function. Will later add a $no_proto argument to this op.
+ bool getNoProto() {
+ assert(!cir::MissingFeatures::opFuncNoProto());
+ return false;
+ }
+
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
@@ -1516,6 +1523,41 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
!listconcat(extra_traits,
[DeclareOpInterfaceMethods<CIRCallOpInterface>,
DeclareOpInterfaceMethods<SymbolUserOpInterface>])> {
+ let extraClassDeclaration = [{
+ /// Get the argument operands to the called function.
+ mlir::OperandRange getArgOperands() {
+ return {arg_operand_begin(), arg_operand_end()};
+ }
+
+ mlir::MutableOperandRange getArgOperandsMutable() {
+ llvm_unreachable("NYI");
+ }
+
+ /// Return the callee of this operation
+ mlir::CallInterfaceCallable getCallableForCallee() {
+ return (*this)->getAttrOfType<mlir::SymbolRefAttr>("callee");
+ }
+
+ /// Set the callee for this operation.
+ void setCalleeFromCallable(::mlir::CallInterfaceCallable callee) {
+ (*this)->setAttr(getCalleeAttrName(),
+ mlir::cast<mlir::SymbolRefAttr>(callee));
+ }
+
+ mlir::ArrayAttr getArgAttrsAttr() { return {}; }
+ ::mlir::ArrayAttr getResAttrsAttr() { return {}; }
+
+ void setResAttrsAttr(::mlir::ArrayAttr attrs) {}
+ void setArgAttrsAttr(::mlir::ArrayAttr attrs) {}
+
+ ::mlir::Attribute removeArgAttrsAttr() { return {}; }
+ ::mlir::Attribute removeResAttrsAttr() { return {}; }
+
+ void setArg(unsigned index, mlir::Value value) {
+ setOperand(index, value);
+ }
+ }];
+
let hasCustomAssemblyFormat = 1;
let skipDefaultBuilders = 1;
let hasVerifier = 0;
@@ -1525,7 +1567,8 @@ 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);
+ dag commonArgs = (ins FlatSymbolRefAttr:$callee,
+ Variadic<CIR_AnyType>:$args);
}
def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
@@ -1546,7 +1589,9 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let arguments = commonArgs;
let builders = [OpBuilder<(ins "mlir::SymbolRefAttr":$callee,
- "mlir::Type":$resType), [{
+ "mlir::Type":$resType,
+ "mlir::ValueRange":$operands), [{
+ $_state.addOperands(operands);
$_state.addAttribute("callee", callee);
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index c6c6356118ac6..8227ce4bea5a3 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -21,9 +21,24 @@ 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", []> {
+ def CIRCallOpInterface : OpInterface<"CIRCallOpInterface", [CallOpInterface]> {
// Currently we don't have any methods defined in CIRCallOpInterface. We'll
// add more methods as the upstreaming proceeds.
+ let methods = [
+ InterfaceMethod<"", "mlir::Operation::operand_iterator",
+ "arg_operand_begin", (ins)>,
+ InterfaceMethod<"", "mlir::Operation::operand_iterator",
+ "arg_operand_end", (ins)>,
+ InterfaceMethod<
+ "Return the operand at index 'i', accounts for indirect call or "
+ "exception info",
+ "mlir::Value", "getArgOperand",
+ (ins "unsigned":$i)>,
+ InterfaceMethod<
+ "Return the number of operands, accounts for indirect call or "
+ "exception info",
+ "unsigned", "getNumArgOperands", (ins)>,
+ ];
}
def CIRGlobalValueInterface
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 6bfc1199aea55..21b22ad63f353 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -72,11 +72,18 @@ struct MissingFeatures {
static bool opFuncDsolocal() { return false; }
static bool opFuncLinkage() { return false; }
static bool opFuncVisibility() { return false; }
+ static bool opFuncNoProto() { return false; }
// CallOp handling
static bool opCallBuiltinFunc() { return false; }
static bool opCallPseudoDtor() { return false; }
- static bool opCallArgs() { return false; }
+ static bool opCallAggregateArgs() { return false; }
+ static bool opCallPaddingArgs() { return false; }
+ static bool opCallABIExtendArg() { return false; }
+ static bool opCallABIIndirectArg() { return false; }
+ static bool opCallWidenArg() { return false; }
+ static bool opCallBitcastArg() { return false; }
+ static bool opCallImplicitObjectSizeArgs() { return false; }
static bool opCallReturn() { return false; }
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
@@ -90,6 +97,11 @@ struct MissingFeatures {
static bool opCallAttrs() { return false; }
static bool opCallSurroundingTry() { return false; }
static bool opCallASTAttr() { return false; }
+ static bool opCallVariadic() { return false; }
+ static bool opCallObjCMethod() { return false; }
+ static bool opCallExtParameterInfo() { return false; }
+ static bool opCallCIRGenFuncInfoParamInfo() { return false; }
+ static bool opCallCIRGenFuncInfoExtParamInfo() { return false; }
// ScopeOp handling
static bool opScopeCleanupRegion() { return false; }
@@ -157,6 +169,7 @@ struct MissingFeatures {
static bool emitCheckedInBoundsGEP() { return false; }
static bool preservedAccessIndexRegion() { return false; }
static bool bitfields() { return false; }
+ static bool msabi() { return false; }
static bool typeChecks() { return false; }
static bool lambdaFieldToName() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 69266f79a88a5..0ba5be5199050 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -18,15 +18,107 @@
using namespace clang;
using namespace clang::CIRGen;
-CIRGenFunctionInfo *CIRGenFunctionInfo::create(CanQualType resultType) {
- void *buffer = operator new(totalSizeToAlloc<ArgInfo>(1));
+CIRGenFunctionInfo *
+CIRGenFunctionInfo::create(CanQualType resultType,
+ llvm::ArrayRef<CanQualType> argTypes) {
+ // The first slot allocated for ArgInfo is for the return value.
+ void *buffer = operator new(totalSizeToAlloc<ArgInfo>(argTypes.size() + 1));
CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo();
- fi->getArgsBuffer()[0].type = resultType;
+ fi->numArgs = argTypes.size();
+
+ assert(!cir::MissingFeatures::opCallCIRGenFuncInfoParamInfo());
+
+ ArgInfo *argsBuffer = fi->getArgsBuffer();
+ (argsBuffer++)->type = resultType;
+ for (CanQualType ty : argTypes)
+ (argsBuffer++)->type = ty;
+
+ assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
return fi;
}
+namespace {
+
+/// Encapsulates information about the way function arguments from
+/// CIRGenFunctionInfo should be passed to actual CIR function.
+class ClangToCIRArgMapping {
+ static constexpr unsigned invalidIndex = ~0U;
+ unsigned totalNumCIRArgs;
+
+ /// Arguments of CIR function corresponding to single Clang argument.
+ struct CIRArgs {
+ // Argument is expanded to CIR arguments at positions
+ // [FirstArgIndex, FirstArgIndex + NumberOfArgs).
+ unsigned firstArgIndex = 0;
+ unsigned numberOfArgs = 0;
+
+ CIRArgs() : firstArgIndex(invalidIndex), numberOfArgs(0) {}
+ };
+
+ SmallVector<CIRArgs, 8> argInfo;
+
+public:
+ ClangToCIRArgMapping(const ASTContext &astContext,
+ const CIRGenFunctionInfo &funcInfo)
+ : totalNumCIRArgs(0), argInfo(funcInfo.arg_size()) {
+ unsigned cirArgNo = 0;
+
+ assert(!cir::MissingFeatures::opCallABIIndirectArg());
+
+ unsigned argNo = 0;
+ for (const CIRGenFunctionInfoArgInfo &i : funcInfo.arguments()) {
+ // Collect data about CIR arguments corresponding to Clang argument ArgNo.
+ CIRArgs &cirArgs = argInfo[argNo];
+
+ assert(!cir::MissingFeatures::opCallPaddingArgs());
+
+ switch (i.info.getKind()) {
+ default:
+ assert(!cir::MissingFeatures::abiArgInfo());
+ // For now we just fall through. More argument kinds will be added later
+ // as the upstreaming proceeds.
+ [[fallthrough]];
+ case cir::ABIArgInfo::Direct:
+ // Postpone splitting structs into elements since this makes it way
+ // more complicated for analysis to obtain information on the original
+ // arguments.
+ //
+ // TODO(cir): a LLVM lowering prepare pass should break this down into
+ // the appropriated pieces.
+ assert(!cir::MissingFeatures::opCallABIExtendArg());
+ cirArgs.numberOfArgs = 1;
+ break;
+ }
+
+ if (cirArgs.numberOfArgs > 0) {
+ cirArgs.firstArgIndex = cirArgNo;
+ cirArgNo += cirArgs.numberOfArgs;
+ }
+
+ ++argNo;
+ }
+
+ assert(argNo == argInfo.size());
+ assert(!cir::MissingFeatures::opCallInAlloca());
+
+ totalNumCIRArgs = cirArgNo;
+ }
+
+ unsigned totalCIRArgs() const { return totalNumCIRArgs; }
+
+ /// Returns index of first CIR argument corresponding to argNo, and their
+ /// quantity.
+ std::pair<unsigned, unsigned> getCIRArgs(unsigned argNo) const {
+ assert(argNo < argInfo.size());
+ return std::make_pair(argInfo[argNo].firstArgIndex,
+ argInfo[argNo].numberOfArgs);
+ }
+};
+
+} // namespace
+
CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
assert(!cir::MissingFeatures::opCallVirtual());
return *this;
@@ -34,6 +126,7 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
static const CIRGenFunctionInfo &
arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
+ const CallArgList &args,
const FunctionType *fnType) {
if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
if (proto->isVariadic())
@@ -44,22 +137,26 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
cast<FunctionNoProtoType>(fnType)))
cgm.errorNYI("call to function without a prototype");
- assert(!cir::MissingFeatures::opCallArgs());
+ SmallVector<CanQualType, 16> argTypes;
+ for (const CallArg &arg : args)
+ argTypes.push_back(cgt.getASTContext().getCanonicalParamType(arg.ty));
CanQualType retType = fnType->getReturnType()
->getCanonicalTypeUnqualified()
.getUnqualifiedType();
- return cgt.arrangeCIRFunctionInfo(retType);
+ return cgt.arrangeCIRFunctionInfo(retType, argTypes);
}
const CIRGenFunctionInfo &
-CIRGenTypes::arrangeFreeFunctionCall(const FunctionType *fnType) {
- return arrangeFreeFunctionLikeCall(*this, cgm, fnType);
+CIRGenTypes::arrangeFreeFunctionCall(const CallArgList &args,
+ const FunctionType *fnType) {
+ return arrangeFreeFunctionLikeCall(*this, cgm, args, fnType);
}
-static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
- mlir::Location callLoc,
- cir::FuncOp directFuncOp) {
+static cir::CIRCallOpInterface
+emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
+ cir::FuncOp directFuncOp,
+ const SmallVectorImpl<mlir::Value> &cirCallArgs) {
CIRGenBuilderTy &builder = cgf.getBuilder();
assert(!cir::MissingFeatures::opCallSurroundingTry());
@@ -68,20 +165,68 @@ static cir::CIRCallOpInterface emitCallLikeOp(CIRGenFunction &cgf,
assert(builder.getInsertionBlock() && "expected valid basic block");
assert(!cir::MissingFeatures::opCallIndirect());
- return builder.createCallOp(callLoc, directFuncOp);
+ return builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
}
RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
const CIRGenCallee &callee,
ReturnValueSlot returnValue,
+ const CallArgList &args,
cir::CIRCallOpInterface *callOp,
mlir::Location loc) {
QualType retTy = funcInfo.getReturnType();
const cir::ABIArgInfo &retInfo = funcInfo.getReturnInfo();
- assert(!cir::MissingFeatures::opCallArgs());
+ ClangToCIRArgMapping cirFuncArgs(cgm.getASTContext(), funcInfo);
+ SmallVector<mlir::Value, 16> cirCallArgs(cirFuncArgs.totalCIRArgs());
+
assert(!cir::MissingFeatures::emitLifetimeMarkers());
+ // Translate all of the arguments as necessary to match the CIR lowering.
+ assert(funcInfo.arg_size() == args.size() &&
+ "Mismatch between function signature & arguments.");
+ unsigned argNo = 0;
+ for (const auto &[arg, argInfo] : llvm::zip(args, funcInfo.arguments())) {
+ // Insert a padding argument to ensure proper alignment.
+ assert(!cir::MissingFeatures::opCallPaddingArgs());
+
+ unsigned firstCIRArg;
+ unsigned numCIRArgs;
+ std::tie(firstCIRArg, numCIRArgs) = cirFuncArgs.getCIRArgs(argNo);
+
+ switch (argInfo.info.getKind()) {
+ case cir::ABIArgInfo::Direct: {
+ if (!mlir::isa<cir::RecordType>(argInfo.info.getCoerceToType()) &&
+ argInfo.info.getCoerceToType() == convertType(argInfo.type) &&
+ argInfo.info.getDirectOffset() == 0) {
+ assert(numCIRArgs == 1);
+ assert(!cir::MissingFeatures::opCallAggregateArgs());
+ mlir::Value v = arg.getKnownRValue().getScalarVal();
+
+ assert(!cir::MissingFeatures::opCallExtParameterInfo());
+
+ // We might have to widen integers, but we should never truncate.
+ assert(!cir::MissingFeatures::opCallWidenArg());
+
+ // If the argument doesn't match, perform a bitcast to coerce it. This
+ // can happen due to trivial type mismatches.
+ assert(!cir::MissingFeatures::opCallBitcastArg());
+
+ cirCallArgs[firstCIRArg] = v;
+ break;
+ }
+
+ assert(!cir::MissingFeatures::opCallAggregateArgs());
+ cgm.errorNYI("emitCall: aggregate function call argument");
+ break;
+ }
+ default:
+ cgm.errorNYI("unsupported argument kind");
+ }
+
+ ++argNo;
+ }
+
const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this);
mlir::Operation *calleePtr = concreteCallee.getFunctionPointer();
@@ -102,7 +247,8 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
assert(!cir::MissingFeatures::opCallIndirect());
assert(!cir::MissingFeatures::opCallAttrs());
- cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, directFuncOp);
+ cir::CIRCallOpInterface theCall =
+ emitCallLikeOp(*this, loc, directFuncOp, cirCallArgs);
if (callOp)
*callOp = theCall;
@@ -152,3 +298,106 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
return ret;
}
+
+void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
+ clang::QualType argType) {
+ assert(argType->isReferenceType() == e->isGLValue() &&
+ "reference binding to unmaterialized r-value!");
+
+ if (e->isGLValue()) {
+ assert(e->getObjectKind() == OK_Ordinary);
+ args.add(emitReferenceBindingToExpr(e), argType);
+ }
+
+ bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType);
+
+ if (hasAggregateEvalKind) {
+ assert(!cir::MissingFeatures::opCallAggregateArgs());
+ cgm.errorNYI(e->getSourceRange(),
+ "emitCallArg: aggregate function call argument");
+ }
+
+ args.add(emitAnyExprToTemp(e), argType);
+}
+
+/// Similar to emitAnyExpr(), however, the result will always be accessible
+/// even if no aggregate location is provided.
+RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
+ assert(!cir::MissingFeatures::opCallAggregateArgs());
+
+ if (hasAggregateEvaluationKind(e->getType()))
+ cgm.errorNYI(e->getSourceRange(), "emit aggregate value to temp");
+
+ return emitAnyExpr(e);
+}
+
+void CIRGenFunction::emitCallArgs(
+ CallArgList &args, PrototypeWrapper prototype,
+ llvm::iterator_range<clang::CallExpr::const_arg_iterator> argRange,
+ AbstractCallee callee, unsigned paramsToSkip) {
+ llvm::SmallVector<QualType, 16> argTypes;
+
+ assert(!cir::MissingFeatures::opCallCallConv());
+
+ // First, if a prototype was provided, use those argument types.
+ assert(!cir::MissingFeatures::opCallVariadic());
+ if (prototype.p) {
+ assert(!cir::MissingFeatures::opCallObjCMethod());
+
+ const auto *fpt = cast<const FunctionProtoType *>(prototype.p);
+ argTypes.assign(fpt->param_type_begin() + paramsToSkip,
+ fpt->param_type_end());
+ }
+
+ // If we still have any arguments, emit them using the type of the argument.
+ for (const clang::Expr *a : llvm::drop_begin(argRange, argTypes.size()))
+ argTypes.push_back(a->getType());
+ assert(argTypes.size() == (size_t)(argRange.end() - argRange.begin()));
+
+ // We must evaluate arguments from right to left in the MS C++ ABI, because
+ // arguments are destroyed left to right in the callee. As a special case,
+ // there are certain language constructs taht require left-to-right
+ // evaluation, and in those cases we consider the evaluation order requirement
+ // to trump the "destruction order is reverse construction order" guarantee.
+ auto leftToRight = true;
+ assert(!cir::MissingFeatures::msabi());
+
+ auto maybeEmitImplicitObjectSize = [&](size_t i, const Expr *arg,
+ RValue emittedArg) {
+ if (callee.hasFunctionDecl() || i >= callee.getNumParams())
+ return;
+ auto *ps = callee.getParamDecl(i)->getAttr<PassObjectSizeAttr>();
+ if (!ps)
+ return;
+
+ assert(!cir::MissingFeatures::opCallImplicitObjectSizeArgs());
+ cgm.errorNYI("emit implicit object size for call arg");
+ };
+
+ // Evaluate each argument in the appropriate order.
+ size_t callArgsStart = args.size();
+ for (size_t i = 0; i != argTypes.size(); ++i) {
+ size_t idx = leftToRight ? i : argTypes.size() - i - 1;
+ CallExpr::const_arg_iterator currentArg = argRange.begin() + idx;
+ size_t initialArgSize = args.size();
+
+ emitCallArg(args, *currentArg, argTypes[idx]);
+
+ // In particular, we depend on it being the last arg in Args, and the
+ // objectsize bits depend on there only being one arg if !LeftToRight.
+ assert(initialArgSize + 1 == args.size() &&
+ "The code below depends on only adding one arg per emitCallArg");
+ (void)initialArgSize;
+
+ // Since pointer argument are never emitted as LValue, it is safe to emit
+ // non-null argument check for r-value only.
+ if (!args.back().hasLValue()) {
+ RValue rvArg = args.back().getKnownRValue();
+ assert(!cir::MissingFeatures::sanitizers());
+ maybeEmitImplicitObjectSize(idx, *currentArg, rvArg);
+ }
+
+ if (!leftToRight)
+ std::reverse(args.begin() + callArgsStart, args.end());
+ }
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index 4427fda863d7e..0e7ab11bfa96c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -14,6 +14,7 @@
#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H
#define CLANG_LIB_CODEGEN_CIRGENCALL_H
+#include "CIRGenValue.h"
#include "mlir/IR/Operation.h"
#include "clang/AST/GlobalDecl.h"
#include "llvm/ADT/SmallVector.h"
@@ -77,9 +78,36 @@ class CIRGenCallee {
/// The decl must be either a ParmVarDecl or ImplicitParamDecl.
class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {};
-struct CallArg {};
+struct CallArg {
+private:
+ union {
+ RValue rv;
+ LValue lv; // This argument is semantically a load from this l-value
+ };
+ bool hasLV;
+
+ /// A data-flow flag to make sure getRValue and/or copyInto are not
+ /// called twice for duplicated IR emission.
+ mutable bool isUsed;
+
+public:
+ clang::QualType ty;
+
+ CallArg(RValue rv, clang::QualType ty)
+ : rv(rv), hasLV(false), isUsed(false), ty(ty) {}
-class CallArgList : public llvm::SmallVector<CallArg, 8> {};
+ bool hasLValue() const { return hasLV; }
+
+ RValue getKnownRValue() const {
+ assert(!hasLV && !isUsed);
+ return rv;
+ }
+};
+
+class CallArgList : public llvm::SmallVector<CallArg, 8> {
+public:
+ void add(RValue rvalue, clang::QualType type) { emplace_back(rvalue, type); }
+};
/// Contains the address where the return value of a function can be stored, and
/// whether the address is volatile or not.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 0a518c0fd935d..562daf0e5c897 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -393,6 +393,17 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue,
return loadOp;
}
+RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) {
+ // Emit the expression as an lvalue.
+ LValue lv = emitLValue(e);
+ assert(lv.isSimple());
+ auto value = lv.getPointer();
+
+ assert(!cir::MissingFeatures::sanitizers());
+
+ return RValue::get(value);
+}
+
/// Given an expression that represents a value lvalue, this
/// method emits the address of the lvalue, then loads the result as an rvalue,
/// returning the rvalue.
@@ -855,10 +866,15 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
const auto *fnType = cast<FunctionType>(pointeeTy);
assert(!cir::MissingFeatures::sanitizers());
- assert(!cir::MissingFeatures::opCallArgs());
+
+ CallArgList args;
+ assert(!cir::MissingFeatures::opCallArgEvaluationOrder());
+
+ emitCallArgs(args, dyn_cast<FunctionProtoType>(fnType), e->arguments(),
+ e->getDirectCallee());
const CIRGenFunctionInfo &funcInfo =
- cgm.getTypes().arrangeFreeFunctionCall(fnType);
+ cgm.getTypes().arrangeFreeFunctionCall(args, fnType);
assert(!cir::MissingFeatures::opCallNoPrototypeFunc());
assert(!cir::MissingFeatures::opCallChainCall());
@@ -866,8 +882,8 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy,
assert(!cir::MissingFeatures::opCallMustTail());
cir::CIRCallOpInterface callOp;
- RValue callResult =
- emitCall(funcInfo, callee, returnValue, &callOp, getLoc(e->getExprLoc()));
+ RValue callResult = emitCall(funcInfo, callee, returnValue, args, &callOp,
+ getLoc(e->getExprLoc()));
assert(!cir::MissingFeatures::generateDebugInfo());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index f533d0ab53cd2..04caea04cf303 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -93,6 +93,10 @@ class CIRGenFunction : public CIRGenTypeCache {
return getEvaluationKind(type) == cir::TEK_Scalar;
}
+ static bool hasAggregateEvaluationKind(clang::QualType type) {
+ return getEvaluationKind(type) == cir::TEK_Aggregate;
+ }
+
CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder,
bool suppressNewContext = false);
~CIRGenFunction();
@@ -161,6 +165,17 @@ class CIRGenFunction : public CIRGenTypeCache {
const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); }
+ // Wrapper for function prototype sources. Wraps either a FunctionProtoType or
+ // an ObjCMethodDecl.
+ struct PrototypeWrapper {
+ llvm::PointerUnion<const clang::FunctionProtoType *,
+ const clang::ObjCMethodDecl *>
+ p;
+
+ PrototypeWrapper(const clang::FunctionProtoType *ft) : p(ft) {}
+ PrototypeWrapper(const clang::ObjCMethodDecl *md) : p(md) {}
+ };
+
/// An abstract representation of regular/ObjC call/message targets.
class AbstractCallee {
/// The function declaration of the callee.
@@ -169,6 +184,23 @@ class CIRGenFunction : public CIRGenTypeCache {
public:
AbstractCallee() : calleeDecl(nullptr) {}
AbstractCallee(const clang::FunctionDecl *fd) : calleeDecl(fd) {}
+
+ bool hasFunctionDecl() const {
+ return llvm::isa_and_nonnull<clang::FunctionDecl>(calleeDecl);
+ }
+
+ unsigned getNumParams() const {
+ if (const auto *fd = llvm::dyn_cast<clang::FunctionDecl>(calleeDecl))
+ return fd->getNumParams();
+ return llvm::cast<clang::ObjCMethodDecl>(calleeDecl)->param_size();
+ }
+
+ const clang::ParmVarDecl *getParamDecl(unsigned I) const {
+ if (const auto *fd = llvm::dyn_cast<clang::FunctionDecl>(calleeDecl))
+ return fd->getParamDecl(I);
+ return *(llvm::cast<clang::ObjCMethodDecl>(calleeDecl)->param_begin() +
+ I);
+ }
};
void finishFunction(SourceLocation endLoc);
@@ -444,6 +476,10 @@ class CIRGenFunction : public CIRGenTypeCache {
/// should be returned.
RValue emitAnyExpr(const clang::Expr *e);
+ /// Similarly to emitAnyExpr(), however, the result will always be accessible
+ /// even if no aggregate location is provided.
+ RValue emitAnyExprToTemp(const clang::Expr *e);
+
LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);
@@ -462,9 +498,16 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCall(const CIRGenFunctionInfo &funcInfo,
const CIRGenCallee &callee, ReturnValueSlot returnValue,
- cir::CIRCallOpInterface *callOp, mlir::Location loc);
+ const CallArgList &args, cir::CIRCallOpInterface *callOp,
+ mlir::Location loc);
RValue emitCall(clang::QualType calleeTy, const CIRGenCallee &callee,
const clang::CallExpr *e, ReturnValueSlot returnValue);
+ void emitCallArg(CallArgList &args, const clang::Expr *e,
+ clang::QualType argType);
+ void emitCallArgs(
+ CallArgList &args, PrototypeWrapper prototype,
+ llvm::iterator_range<clang::CallExpr::const_arg_iterator> argRange,
+ AbstractCallee callee = AbstractCallee(), unsigned paramsToSkip = 0);
RValue emitCallExpr(const clang::CallExpr *e,
ReturnValueSlot returnValue = ReturnValueSlot());
CIRGenCallee emitCallee(const clang::Expr *e);
@@ -489,6 +532,9 @@ class CIRGenFunction : public CIRGenTypeCache {
mlir::Value emitPromotedScalarExpr(const Expr *e, QualType promotionType);
+ /// Emits a reference binding to the passed in expression.
+ RValue emitReferenceBindingToExpr(const Expr *e);
+
/// Emit the computation of the specified expression of scalar type.
mlir::Value emitScalarExpr(const clang::Expr *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index c4a2b238c96ae..4319f7a2be225 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -33,11 +33,14 @@ class CIRGenFunctionInfo final
CIRGenFunctionInfoArgInfo> {
using ArgInfo = CIRGenFunctionInfoArgInfo;
+ unsigned numArgs;
+
ArgInfo *getArgsBuffer() { return getTrailingObjects<ArgInfo>(); }
const ArgInfo *getArgsBuffer() const { return getTrailingObjects<ArgInfo>(); }
public:
- static CIRGenFunctionInfo *create(CanQualType resultType);
+ static CIRGenFunctionInfo *create(CanQualType resultType,
+ llvm::ArrayRef<CanQualType> argTypes);
void operator delete(void *p) { ::operator delete(p); }
@@ -45,14 +48,34 @@ class CIRGenFunctionInfo final
// these have to be public.
friend class TrailingObjects;
+ using const_arg_iterator = const ArgInfo *;
+ using arg_iterator = ArgInfo *;
+
// This function has to be CamelCase because llvm::FoldingSet requires so.
// NOLINTNEXTLINE(readability-identifier-naming)
- static void Profile(llvm::FoldingSetNodeID &id, CanQualType resultType) {
+ static void Profile(llvm::FoldingSetNodeID &id, CanQualType resultType,
+ llvm::ArrayRef<clang::CanQualType> argTypes) {
resultType.Profile(id);
+ for (auto i : argTypes)
+ i.Profile(id);
}
void Profile(llvm::FoldingSetNodeID &id) { getReturnType().Profile(id); }
+ llvm::MutableArrayRef<ArgInfo> arguments() {
+ return llvm::MutableArrayRef<ArgInfo>(arg_begin(), numArgs);
+ }
+ llvm::ArrayRef<ArgInfo> arguments() const {
+ return llvm::ArrayRef<ArgInfo>(arg_begin(), numArgs);
+ }
+
+ const_arg_iterator arg_begin() const { return getArgsBuffer() + 1; }
+ const_arg_iterator arg_end() const { return getArgsBuffer() + 1 + numArgs; }
+ arg_iterator arg_begin() { return getArgsBuffer() + 1; }
+ arg_iterator arg_end() { return getArgsBuffer() + 1 + numArgs; }
+
+ unsigned arg_size() const { return numArgs; }
+
CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
cir::ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index b11f8466607f8..071b8c856c45a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -493,11 +493,14 @@ bool CIRGenTypes::isZeroInitializable(clang::QualType t) {
return true;
}
-const CIRGenFunctionInfo &
-CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType) {
+const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
+ CanQualType returnType, llvm::ArrayRef<clang::CanQualType> argTypes) {
+ assert(llvm::all_of(argTypes,
+ [](CanQualType T) { return T.isCanonicalAsParam(); }));
+
// Lookup or create unique function info.
llvm::FoldingSetNodeID id;
- CIRGenFunctionInfo::Profile(id, returnType);
+ CIRGenFunctionInfo::Profile(id, returnType, argTypes);
void *insertPos = nullptr;
CIRGenFunctionInfo *fi = functionInfos.FindNodeOrInsertPos(id, insertPos);
@@ -507,7 +510,7 @@ CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType) {
assert(!cir::MissingFeatures::opCallCallConv());
// Construction the function info. We co-allocate the ArgInfos.
- fi = CIRGenFunctionInfo::create(returnType);
+ fi = CIRGenFunctionInfo::create(returnType, argTypes);
functionInfos.InsertNode(fi, insertPos);
bool inserted = functionsBeingProcessed.insert(fi).second;
@@ -524,7 +527,9 @@ CIRGenTypes::arrangeCIRFunctionInfo(CanQualType returnType) {
if (retInfo.canHaveCoerceToType() && retInfo.getCoerceToType() == nullptr)
retInfo.setCoerceToType(convertType(fi->getReturnType()));
- assert(!cir::MissingFeatures::opCallArgs());
+ for (CIRGenFunctionInfoArgInfo &i : fi->arguments())
+ if (i.info.canHaveCoerceToType() && i.info.getCoerceToType() == nullptr)
+ i.info.setCoerceToType(convertType(i.type));
bool erased = functionsBeingProcessed.erase(fi);
(void)erased;
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 5b4027601ca3a..38f4b389c8db9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -121,9 +121,12 @@ class CIRGenTypes {
/// LLVM zeroinitializer.
bool isZeroInitializable(clang::QualType ty);
- const CIRGenFunctionInfo &arrangeFreeFunctionCall(const FunctionType *fnType);
+ const CIRGenFunctionInfo &arrangeFreeFunctionCall(const CallArgList &args,
+ const FunctionType *fnType);
- const CIRGenFunctionInfo &arrangeCIRFunctionInfo(CanQualType returnType);
+ const CIRGenFunctionInfo &
+ arrangeCIRFunctionInfo(CanQualType returnType,
+ llvm::ArrayRef<clang::CanQualType> argTypes);
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index 0d0ffb93d4e7e..0b70170cadb69 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -32,7 +32,12 @@ void X8664ABIInfo::computeInfo(CIRGenFunctionInfo &funcInfo) const {
// Top level CIR has unlimited arguments and return types. Lowering for ABI
// specific concerns should happen during a lowering phase. Assume everything
// is direct for now.
- assert(!cir::MissingFeatures::opCallArgs());
+ for (CIRGenFunctionInfoArgInfo &info : funcInfo.arguments()) {
+ if (testIfIsVoidTy(info.type))
+ info.info = cir::ABIArgInfo::getIgnore();
+ else
+ info.info = cir::ABIArgInfo::getDirect(cgt.convertType(info.type));
+ }
CanQualType retTy = funcInfo.getReturnType();
if (testIfIsVoidTy(retTy))
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 146c91b253f39..3036a354407b1 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -446,8 +446,31 @@ OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
// CallOp
//===----------------------------------------------------------------------===//
+mlir::Operation::operand_iterator cir::CallOp::arg_operand_begin() {
+ assert(!cir::MissingFeatures::opCallIndirect());
+ return operand_begin();
+}
+
+mlir::Operation::operand_iterator cir::CallOp::arg_operand_end() {
+ return operand_end();
+}
+
+/// Return the operand at index 'i'.
+Value cir::CallOp::getArgOperand(unsigned i) {
+ assert(!cir::MissingFeatures::opCallIndirect());
+ return getOperand(i);
+}
+
+/// Return the number of operands.
+unsigned cir::CallOp::getNumArgOperands() {
+ assert(!cir::MissingFeatures::opCallIndirect());
+ return this->getOperation()->getNumOperands();
+}
+
static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
+ llvm::SmallVector<mlir::OpAsmParser::UnresolvedOperand, 4> ops;
+ llvm::SMLoc opsLoc;
mlir::FlatSymbolRefAttr calleeAttr;
llvm::ArrayRef<mlir::Type> allResultTypes;
@@ -458,9 +481,9 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.parseLParen())
return mlir::failure();
- // TODO(cir): parse argument list here
- assert(!cir::MissingFeatures::opCallArgs());
-
+ opsLoc = parser.getCurrentLocation();
+ if (parser.parseOperandList(ops))
+ return mlir::failure();
if (parser.parseRParen())
return mlir::failure();
@@ -477,6 +500,9 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
allResultTypes = opsFnTy.getResults();
result.addTypes(allResultTypes);
+ if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
+ return mlir::failure();
+
return mlir::success();
}
@@ -485,11 +511,11 @@ static void printCallCommon(mlir::Operation *op,
mlir::OpAsmPrinter &printer) {
printer << ' ';
+ auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
+ auto ops = callLikeOp.getArgOperands();
+
printer.printAttributeWithoutType(calleeSym);
- printer << "(";
- // TODO(cir): print call args here
- assert(!cir::MissingFeatures::opCallArgs());
- printer << ")";
+ printer << "(" << ops << ")";
printer.printOptionalAttrDict(op->getAttrs(), {"callee"});
@@ -525,9 +551,23 @@ verifyCallCommInSymbolUses(mlir::Operation *op,
// Verify that the operand and result types match the callee. Note that
// argument-checking is disabled for functions without a prototype.
auto fnType = fn.getFunctionType();
+ if (!fn.getNoProto()) {
+ unsigned numCallOperands = callIf.getNumArgOperands();
+ unsigned numFnOpOperands = fnType.getNumInputs();
+
+ assert(!cir::MissingFeatures::opCallVariadic());
+
+ if (numCallOperands != numFnOpOperands)
+ return op->emitOpError("incorrect number of operands for callee");
+
+ for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
+ if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
+ return op->emitOpError("operand type mismatch: expected operand type ")
+ << fnType.getInput(i) << ", but provided "
+ << op->getOperand(i).getType() << " for operand number " << i;
+ }
- // TODO(cir): verify function arguments
- assert(!cir::MissingFeatures::opCallArgs());
+ assert(!cir::MissingFeatures::opCallCallConv());
// Void function must not return any results.
if (fnType.hasVoidReturn() && op->getNumResults() != 0)
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
index 9082fbc9f6860..8fec8673cd691 100644
--- a/clang/test/CIR/CodeGen/call.cpp
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -17,3 +17,15 @@ int f4() {
// CHECK-LABEL: cir.func @f4() -> !s32i
// CHECK: %[[#x:]] = cir.call @f3() : () -> !s32i
// CHECK-NEXT: cir.store %[[#x]], %{{.+}} : !s32i, !cir.ptr<!s32i>
+
+int f5(int a, int *b, bool c);
+int f6() {
+ int b = 1;
+ return f5(2, &b, false);
+}
+
+// CHECK-LABEL: cir.func @f6() -> !s32i
+// CHECK: %[[#b:]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
+// CHECK: %[[#a:]] = cir.const #cir.int<2> : !s32i
+// CHECK-NEXT: %[[#c:]] = cir.const #false
+// CHECK-NEXT: %5 = cir.call @f5(%[[#a]], %[[#b:]], %[[#c]]) : (!s32i, !cir.ptr<!s32i>, !cir.bool) -> !s32i
diff --git a/clang/test/CIR/IR/call.cir b/clang/test/CIR/IR/call.cir
index 3c3fbf3d4d987..8276c0cb9e39d 100644
--- a/clang/test/CIR/IR/call.cir
+++ b/clang/test/CIR/IR/call.cir
@@ -28,4 +28,19 @@ cir.func @f4() -> !s32i {
// CHECK-NEXT: cir.return %[[#x]] : !s32i
// CHECK-NEXT: }
+cir.func @f5(!s32i, !s32i) -> !s32i
+cir.func @f6() -> !s32i {
+ %0 = cir.const #cir.int<1> : !s32i
+ %1 = cir.const #cir.int<2> : !s32i
+ %2 = cir.call @f5(%0, %1) : (!s32i, !s32i) -> !s32i
+ cir.return %2 : !s32i
+}
+
+// CHECK: cir.func @f6() -> !s32i {
+// CHECK-NEXT: %[[#a:]] = cir.const #cir.int<1> : !s32i
+// CHECK-NEXT: %[[#b:]] = cir.const #cir.int<2> : !s32i
+// CHECK-NEXT: %[[#c:]] = cir.call @f5(%[[#a]], %[[#b]]) : (!s32i, !s32i) -> !s32i
+// CHECK-NEXT: cir.return %[[#c]] : !s32i
+// CHECK-NEXT: }
+
}
diff --git a/clang/test/CIR/IR/invalid-call.cir b/clang/test/CIR/IR/invalid-call.cir
index 64b6d56e0fa88..8a584bae70878 100644
--- a/clang/test/CIR/IR/invalid-call.cir
+++ b/clang/test/CIR/IR/invalid-call.cir
@@ -41,3 +41,30 @@ cir.func @f7() {
%0 = cir.call @f6() : () -> !s32i
cir.return
}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
+cir.func @f8(!s32i, !s32i)
+cir.func @f9() {
+ %0 = cir.const #cir.int<1> : !s32i
+ // expected-error @below {{incorrect number of operands for callee}}
+ cir.call @f8(%0) : (!s32i) -> ()
+ cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
+cir.func @f10(!s32i, !s32i)
+cir.func @f11() {
+ %0 = cir.const #cir.int<1> : !s32i
+ %1 = cir.const #cir.int<2> : !u32i
+ // expected-error @below {{operand type mismatch: expected operand type '!cir.int<s, 32>', but provided '!cir.int<u, 32>' for operand number 1}}
+ cir.call @f10(%0, %1) : (!s32i, !u32i) -> ()
+ cir.return
+}
More information about the cfe-commits
mailing list