-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[CIR] Call to variadic functions #141942
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CIR] Call to variadic functions #141942
Conversation
@llvm/pr-subscribers-clangir Author: Sirui Mu (Lancern) ChangesThis PR adds support for calling variadic functions in CIR. Full diff: https://github.com/llvm/llvm-project/pull/141942.diff 7 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 56bf9b1130f12..f7385a672f6af 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -100,7 +100,6 @@ 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; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 1317f8c6c073a..ea76ddb7f40ec 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -122,7 +122,7 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
if (proto->isVariadic())
- cgm.errorNYI("call to variadic function");
+ required = RequiredArgs::forPrototypePlus(proto, 0);
if (proto->hasExtParameterInfos())
cgm.errorNYI("call to functions with extra parameter info");
} else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
@@ -409,6 +409,18 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
args.add(emitAnyExprToTemp(e), argType);
}
+QualType CIRGenFunction::getVarArgType(const Expr *arg) {
+ // System headers on Windows define NULL to 0 instead of 0LL on Win64. MSVC
+ // implicitly widens null pointer constants that are arguments to varargs
+ // functions to pointer-sized ints.
+ if (!getTarget().getTriple().isOSWindows())
+ return arg->getType();
+
+ assert(!cir::MissingFeatures::msabi());
+ cgm.errorNYI(arg->getSourceRange(), "getVarArgType: NYI for Windows target");
+ return {};
+}
+
/// Similar to emitAnyExpr(), however, the result will always be accessible
/// even if no aggregate location is provided.
RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
@@ -429,18 +441,20 @@ void CIRGenFunction::emitCallArgs(
assert(!cir::MissingFeatures::opCallCallConv());
// First, if a prototype was provided, use those argument types.
- assert(!cir::MissingFeatures::opCallVariadic());
+ bool isVariadic = false;
if (prototype.p) {
assert(!cir::MissingFeatures::opCallObjCMethod());
const auto *fpt = cast<const FunctionProtoType *>(prototype.p);
+ isVariadic = fpt->isVariadic();
+ assert(!cir::MissingFeatures::opCallCallConv());
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());
+ argTypes.push_back(isVariadic ? getVarArgType(a) : 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
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 74f2e4043933d..0badde024b166 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -106,6 +106,7 @@ class CIRGenFunction : public CIRGenTypeCache {
CIRGenTypes &getTypes() const { return cgm.getTypes(); }
+ const TargetInfo &getTarget() const { return cgm.getTarget(); }
mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
private:
@@ -791,6 +792,9 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitOpenACCDeclare(const OpenACCDeclareDecl &d);
void emitOpenACCRoutine(const OpenACCRoutineDecl &d);
+
+private:
+ QualType getVarArgType(const Expr *arg);
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index 9886574fd463b..759d019de52f0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -39,6 +39,26 @@ class RequiredArgs {
bool allowsOptionalArgs() const { return numRequired != ~0U; }
+ /// Compute the arguments required by the given formal prototype, given that
+ /// there may be some additional, non-formal arguments in play.
+ ///
+ /// If FD is not null, this will consider pass_object_size params in FD.
+ static RequiredArgs
+ forPrototypePlus(const clang::FunctionProtoType *prototype,
+ unsigned additional) {
+ if (!prototype->isVariadic())
+ return All;
+
+ if (prototype->hasExtParameterInfos())
+ additional += llvm::count_if(
+ prototype->getExtParameterInfos(),
+ [](const clang::FunctionProtoType::ExtParameterInfo &extInfo) {
+ return extInfo.hasPassObjectSize();
+ });
+
+ return RequiredArgs(prototype->getNumParams() + additional);
+ }
+
/// Compute the arguments required by the given formal prototype, given that
/// there may be some additional, non-formal arguments in play.
///
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9e2b2908b22d8..3e6d22879a78c 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -601,10 +601,10 @@ verifyCallCommInSymbolUses(mlir::Operation *op,
unsigned numCallOperands = callIf.getNumArgOperands();
unsigned numFnOpOperands = fnType.getNumInputs();
- assert(!cir::MissingFeatures::opCallVariadic());
-
- if (numCallOperands != numFnOpOperands)
+ if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
return op->emitOpError("incorrect number of operands for callee");
+ if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
+ return op->emitOpError("too few operands for callee");
for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
index 792f57afd6bd1..741cadeb5c764 100644
--- a/clang/test/CIR/CodeGen/call.cpp
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -56,3 +56,17 @@ int f7(int (*ptr)(int, int)) {
// LLVM-LABEL: define i32 @_Z2f7PFiiiE
// LLVM: %[[#ptr:]] = load ptr, ptr %{{.+}}
// LLVM-NEXT: %{{.+}} = call i32 %[[#ptr]](i32 1, i32 2)
+
+void f8(int a, ...);
+void f9() {
+ f8(1);
+ f8(1, 2, 3, 4);
+}
+
+// CIR-LABEL: cir.func @_Z2f9v()
+// CIR: cir.call @_Z2f8iz(%{{.+}}) : (!s32i) -> ()
+// CIR: cir.call @_Z2f8iz(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) : (!s32i, !s32i, !s32i, !s32i) -> ()
+
+// LLVM-LABEL: define void @_Z2f9v()
+// LLVM: call void (i32, ...) @_Z2f8iz(i32 1)
+// LLVM: call void (i32, ...) @_Z2f8iz(i32 1, i32 2, i32 3, i32 4)
diff --git a/clang/test/CIR/IR/invalid-call.cir b/clang/test/CIR/IR/invalid-call.cir
index 8a584bae70878..3ebb771ed72e7 100644
--- a/clang/test/CIR/IR/invalid-call.cir
+++ b/clang/test/CIR/IR/invalid-call.cir
@@ -68,3 +68,15 @@ cir.func @f11() {
cir.call @f10(%0, %1) : (!s32i, !u32i) -> ()
cir.return
}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @f12(!s32i, !s32i, ...)
+cir.func @f13() {
+ %0 = cir.const #cir.int<1> : !s32i
+ // expected-error @below {{too few operands for callee}}
+ cir.call @f12(%0) : (!s32i) -> ()
+ cir.return
+}
|
@llvm/pr-subscribers-clang Author: Sirui Mu (Lancern) ChangesThis PR adds support for calling variadic functions in CIR. Full diff: https://github.com/llvm/llvm-project/pull/141942.diff 7 Files Affected:
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 56bf9b1130f12..f7385a672f6af 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -100,7 +100,6 @@ 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; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 1317f8c6c073a..ea76ddb7f40ec 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -122,7 +122,7 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
if (proto->isVariadic())
- cgm.errorNYI("call to variadic function");
+ required = RequiredArgs::forPrototypePlus(proto, 0);
if (proto->hasExtParameterInfos())
cgm.errorNYI("call to functions with extra parameter info");
} else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
@@ -409,6 +409,18 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
args.add(emitAnyExprToTemp(e), argType);
}
+QualType CIRGenFunction::getVarArgType(const Expr *arg) {
+ // System headers on Windows define NULL to 0 instead of 0LL on Win64. MSVC
+ // implicitly widens null pointer constants that are arguments to varargs
+ // functions to pointer-sized ints.
+ if (!getTarget().getTriple().isOSWindows())
+ return arg->getType();
+
+ assert(!cir::MissingFeatures::msabi());
+ cgm.errorNYI(arg->getSourceRange(), "getVarArgType: NYI for Windows target");
+ return {};
+}
+
/// Similar to emitAnyExpr(), however, the result will always be accessible
/// even if no aggregate location is provided.
RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
@@ -429,18 +441,20 @@ void CIRGenFunction::emitCallArgs(
assert(!cir::MissingFeatures::opCallCallConv());
// First, if a prototype was provided, use those argument types.
- assert(!cir::MissingFeatures::opCallVariadic());
+ bool isVariadic = false;
if (prototype.p) {
assert(!cir::MissingFeatures::opCallObjCMethod());
const auto *fpt = cast<const FunctionProtoType *>(prototype.p);
+ isVariadic = fpt->isVariadic();
+ assert(!cir::MissingFeatures::opCallCallConv());
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());
+ argTypes.push_back(isVariadic ? getVarArgType(a) : 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
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 74f2e4043933d..0badde024b166 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -106,6 +106,7 @@ class CIRGenFunction : public CIRGenTypeCache {
CIRGenTypes &getTypes() const { return cgm.getTypes(); }
+ const TargetInfo &getTarget() const { return cgm.getTarget(); }
mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
private:
@@ -791,6 +792,9 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitOpenACCDeclare(const OpenACCDeclareDecl &d);
void emitOpenACCRoutine(const OpenACCRoutineDecl &d);
+
+private:
+ QualType getVarArgType(const Expr *arg);
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index 9886574fd463b..759d019de52f0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -39,6 +39,26 @@ class RequiredArgs {
bool allowsOptionalArgs() const { return numRequired != ~0U; }
+ /// Compute the arguments required by the given formal prototype, given that
+ /// there may be some additional, non-formal arguments in play.
+ ///
+ /// If FD is not null, this will consider pass_object_size params in FD.
+ static RequiredArgs
+ forPrototypePlus(const clang::FunctionProtoType *prototype,
+ unsigned additional) {
+ if (!prototype->isVariadic())
+ return All;
+
+ if (prototype->hasExtParameterInfos())
+ additional += llvm::count_if(
+ prototype->getExtParameterInfos(),
+ [](const clang::FunctionProtoType::ExtParameterInfo &extInfo) {
+ return extInfo.hasPassObjectSize();
+ });
+
+ return RequiredArgs(prototype->getNumParams() + additional);
+ }
+
/// Compute the arguments required by the given formal prototype, given that
/// there may be some additional, non-formal arguments in play.
///
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9e2b2908b22d8..3e6d22879a78c 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -601,10 +601,10 @@ verifyCallCommInSymbolUses(mlir::Operation *op,
unsigned numCallOperands = callIf.getNumArgOperands();
unsigned numFnOpOperands = fnType.getNumInputs();
- assert(!cir::MissingFeatures::opCallVariadic());
-
- if (numCallOperands != numFnOpOperands)
+ if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
return op->emitOpError("incorrect number of operands for callee");
+ if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
+ return op->emitOpError("too few operands for callee");
for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
index 792f57afd6bd1..741cadeb5c764 100644
--- a/clang/test/CIR/CodeGen/call.cpp
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -56,3 +56,17 @@ int f7(int (*ptr)(int, int)) {
// LLVM-LABEL: define i32 @_Z2f7PFiiiE
// LLVM: %[[#ptr:]] = load ptr, ptr %{{.+}}
// LLVM-NEXT: %{{.+}} = call i32 %[[#ptr]](i32 1, i32 2)
+
+void f8(int a, ...);
+void f9() {
+ f8(1);
+ f8(1, 2, 3, 4);
+}
+
+// CIR-LABEL: cir.func @_Z2f9v()
+// CIR: cir.call @_Z2f8iz(%{{.+}}) : (!s32i) -> ()
+// CIR: cir.call @_Z2f8iz(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) : (!s32i, !s32i, !s32i, !s32i) -> ()
+
+// LLVM-LABEL: define void @_Z2f9v()
+// LLVM: call void (i32, ...) @_Z2f8iz(i32 1)
+// LLVM: call void (i32, ...) @_Z2f8iz(i32 1, i32 2, i32 3, i32 4)
diff --git a/clang/test/CIR/IR/invalid-call.cir b/clang/test/CIR/IR/invalid-call.cir
index 8a584bae70878..3ebb771ed72e7 100644
--- a/clang/test/CIR/IR/invalid-call.cir
+++ b/clang/test/CIR/IR/invalid-call.cir
@@ -68,3 +68,15 @@ cir.func @f11() {
cir.call @f10(%0, %1) : (!s32i, !u32i) -> ()
cir.return
}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @f12(!s32i, !s32i, ...)
+cir.func @f13() {
+ %0 = cir.const #cir.int<1> : !s32i
+ // expected-error @below {{too few operands for callee}}
+ cir.call @f12(%0) : (!s32i) -> ()
+ cir.return
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Straight forward and small PR, LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
This PR adds support for calling variadic functions in CIR.