[clang] c8b267e - [clang][Interp] Handle variadic functions (#67814)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 24 03:33:33 PDT 2023
Author: Timm Baeder
Date: 2023-10-24T12:33:29+02:00
New Revision: c8b267e98f8f5c0bc4c3b3b34f9cb54dbdf76205
URL: https://github.com/llvm/llvm-project/commit/c8b267e98f8f5c0bc4c3b3b34f9cb54dbdf76205
DIFF: https://github.com/llvm/llvm-project/commit/c8b267e98f8f5c0bc4c3b3b34f9cb54dbdf76205.diff
LOG: [clang][Interp] Handle variadic functions (#67814)
Similarly to the code we already had for builtin functions, we need to
check the call expression for the arguments passed.
Added:
Modified:
clang/lib/AST/Interp/Function.cpp
clang/lib/AST/Interp/Function.h
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Interp.h
clang/test/AST/Interp/functions.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp
index 0b7cfc4e28883f0..357aff7fe6229b9 100644
--- a/clang/lib/AST/Interp/Function.cpp
+++ b/clang/lib/AST/Interp/Function.cpp
@@ -24,7 +24,7 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
: P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer),
- HasRVO(HasRVO) {}
+ HasRVO(HasRVO), Variadic(F->isVariadic()) {}
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
auto It = Params.find(Offset);
diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h
index b93477c56346a9d..be9b1733635f725 100644
--- a/clang/lib/AST/Interp/Function.h
+++ b/clang/lib/AST/Interp/Function.h
@@ -173,6 +173,8 @@ class Function final {
/// Checks if the function is defined.
bool isDefined() const { return Defined; }
+ bool isVariadic() const { return Variadic; }
+
unsigned getBuiltinID() const { return F->getBuiltinID(); }
bool isBuiltin() const { return F->getBuiltinID() != 0; }
@@ -251,6 +253,7 @@ class Function final {
/// If we've already compiled the function's body.
bool HasBody = false;
bool Defined = false;
+ bool Variadic = false;
public:
/// Dumps the disassembled bytecode to \c llvm::errs().
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 8b0e7beb4a1acc1..c87bb2fa6b02f16 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -121,18 +121,47 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
namespace clang {
namespace interp {
+static void popArg(InterpState &S, const Expr *Arg) {
+ PrimType Ty = S.getContext().classify(Arg->getType()).value_or(PT_Ptr);
+ TYPE_SWITCH(Ty, S.Stk.discard<T>());
+}
+
+void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) {
+ assert(S.Current);
+ const Function *CurFunc = S.Current->getFunction();
+ assert(CurFunc);
+
+ // Certain builtin functions are declared as func-name(...), so the
+ // parameters are checked in Sema and only available through the CallExpr.
+ // The interp::Function we create for them has 0 parameters, so we need to
+ // remove them from the stack by checking the CallExpr.
+ // FIXME: This is potentially just a special case and could be handled more
+ // generally with the code just below?
+ if (CurFunc->needsRuntimeArgPop(S.getCtx())) {
+ const auto *CE = cast<CallExpr>(S.Current->getExpr(OpPC));
+ for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) {
+ popArg(S, CE->getArg(I));
+ }
+ return;
+ }
-bool popBuiltinArgs(InterpState &S, CodePtr OpPC) {
- assert(S.Current && S.Current->getFunction()->needsRuntimeArgPop(S.getCtx()));
- const Expr *E = S.Current->getExpr(OpPC);
- assert(isa<CallExpr>(E));
- const CallExpr *CE = cast<CallExpr>(E);
- for (int32_t I = CE->getNumArgs() - 1; I >= 0; --I) {
- const Expr *A = CE->getArg(I);
- PrimType Ty = S.getContext().classify(A->getType()).value_or(PT_Ptr);
- TYPE_SWITCH(Ty, S.Stk.discard<T>());
+ if (S.Current->Caller && CurFunc->isVariadic()) {
+ // CallExpr we're look for is at the return PC of the current function, i.e.
+ // in the caller.
+ // This code path should be executed very rarely.
+ const auto *CE =
+ cast<CallExpr>(S.Current->Caller->getExpr(S.Current->getRetPC()));
+ unsigned FixedParams = CurFunc->getNumParams();
+ int32_t ArgsToPop = CE->getNumArgs() - FixedParams;
+ assert(ArgsToPop >= 0);
+ for (int32_t I = ArgsToPop - 1; I >= 0; --I) {
+ const Expr *A = CE->getArg(FixedParams + I);
+ popArg(S, A);
+ }
}
- return true;
+ // And in any case, remove the fixed parameters (the non-variadic ones)
+ // at the end.
+ S.Current->popArgs();
}
bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 7ef1e344224a3c3..2132e8b0a8cfa29 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -200,8 +200,7 @@ enum class ArithOp { Add, Sub };
// Returning values
//===----------------------------------------------------------------------===//
-/// Pop arguments of builtins defined as func-name(...).
-bool popBuiltinArgs(InterpState &S, CodePtr OpPC);
+void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC);
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
@@ -221,16 +220,8 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
assert(S.Current);
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
- if (!S.checkingPotentialConstantExpression() || S.Current->Caller) {
- // Certain builtin functions are declared as func-name(...), so the
- // parameters are checked in Sema and only available through the CallExpr.
- // The interp::Function we create for them has 0 parameters, so we need to
- // remove them from the stack by checking the CallExpr.
- if (S.Current->getFunction()->needsRuntimeArgPop(S.getCtx()))
- popBuiltinArgs(S, PC);
- else
- S.Current->popArgs();
- }
+ if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
+ cleanupAfterFunctionCall(S, PC);
if (InterpFrame *Caller = S.Current->Caller) {
PC = S.Current->getRetPC();
@@ -248,8 +239,9 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
inline bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+
if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
- S.Current->popArgs();
+ cleanupAfterFunctionCall(S, PC);
if (InterpFrame *Caller = S.Current->Caller) {
PC = S.Current->getRetPC();
diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
index 68082576f2735e9..4bef9c2f7c0d1fa 100644
--- a/clang/test/AST/Interp/functions.cpp
+++ b/clang/test/AST/Interp/functions.cpp
@@ -350,3 +350,24 @@ namespace PtrReturn {
}
static_assert(a() == nullptr, "");
}
+
+namespace Variadic {
+ struct S { int a; bool b; };
+
+ constexpr void variadic_function(int a, ...) {}
+ constexpr int f1() {
+ variadic_function(1, S{'a', false});
+ return 1;
+ }
+ static_assert(f1() == 1, "");
+
+ constexpr int variadic_function2(...) {
+ return 12;
+ }
+ static_assert(variadic_function2() == 12, "");
+ static_assert(variadic_function2(1, 2, 3, 4, 5) == 12, "");
+ static_assert(variadic_function2(1, variadic_function2()) == 12, "");
+
+ constexpr int (*VFP)(...) = variadic_function2;
+ static_assert(VFP() == 12, "");
+}
More information about the cfe-commits
mailing list