Skip to content

[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

Merged
merged 1 commit into from
May 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
20 changes: 17 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::getFromProtoWithExtraSlots(proto, 0);
if (proto->hasExtParameterInfos())
cgm.errorNYI("call to functions with extra parameter info");
} else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
Expand Down Expand Up @@ -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 arg->getType();
}

/// Similar to emitAnyExpr(), however, the result will always be accessible
/// even if no aggregate location is provided.
RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
14 changes: 14 additions & 0 deletions clang/test/CIR/CodeGen/call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
12 changes: 12 additions & 0 deletions clang/test/CIR/IR/invalid-call.cir
Original file line number Diff line number Diff line change
Expand Up @@ -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
}