Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm/llvm-project/pull/67814/cl...@github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/67814 >From 64aae1cdf960f3edb34f1cf82ae3e66eb692247a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Fri, 29 Sep 2023 16:43:59 +0200 Subject: [PATCH 1/2] [clang][Interp] Handle variadic functions Similarly to the code we already had for builtin functions, we need to check the call expression for the arguments passed. --- clang/lib/AST/Interp/Function.cpp | 2 +- clang/lib/AST/Interp/Function.h | 3 ++ clang/lib/AST/Interp/Interp.cpp | 49 +++++++++++++++++++++++------ clang/lib/AST/Interp/Interp.h | 18 +++-------- clang/test/AST/Interp/functions.cpp | 21 +++++++++++++ 5 files changed, 69 insertions(+), 24 deletions(-) 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 0bae314e97701d9..15bdd39dfde9d99 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -172,6 +172,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; } @@ -250,6 +252,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 e1951574edb6288..639ceb140e99de3 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 CallExpr *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 CallExpr *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 dd37150b63f6db0..d67b8b978031aac 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) { @@ -220,16 +219,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(); @@ -247,8 +238,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 5cdecbff1e9d3d4..f28a525e2b4f008 100644 --- a/clang/test/AST/Interp/functions.cpp +++ b/clang/test/AST/Interp/functions.cpp @@ -343,3 +343,24 @@ namespace TemplateUndefined { constexpr int l = consume(0); static_assert(l == 0, ""); } + +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, ""); +} >From 707f2eb03cada1fa815bcc9895f513999d8066ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Fri, 29 Sep 2023 18:00:08 +0200 Subject: [PATCH 2/2] [clang][Interp] Handle delegating constructors --- clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 8 +++++++ clang/test/AST/Interp/records.cpp | 23 +++++++++++++++++++ .../SemaCXX/constant-expression-cxx1z.cpp | 1 + 3 files changed, 32 insertions(+) diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 15eae8e20b3a678..838e7bb67d0c076 100644 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -193,6 +193,14 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { return false; if (!this->emitPopPtr(InitExpr)) return false; + } else { + assert(Init->isDelegatingInitializer()); + if (!this->emitThis(InitExpr)) + return false; + if (!this->visitInitializer(Init->getInit())) + return false; + if (!this->emitPopPtr(InitExpr)) + return false; } } } diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index bcc84087fc54020..3c866825d1f077c 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -1066,3 +1066,26 @@ namespace ParenInit { constexpr B b(A(1),2); } #endif + +namespace DelegatingConstructors { + struct S { + int a; + constexpr S() : S(10) {} + constexpr S(int a) : a(a) {} + }; + constexpr S s = {}; + static_assert(s.a == 10, ""); + + struct B { + int a; + int b; + + constexpr B(int a) : a(a), b(a + 2) {} + }; + struct A : B { + constexpr A() : B(10) {}; + }; + constexpr A d4 = {}; + static_assert(d4.a == 10, ""); + static_assert(d4.b == 12, ""); +} diff --git a/clang/test/SemaCXX/constant-expression-cxx1z.cpp b/clang/test/SemaCXX/constant-expression-cxx1z.cpp index 9335626a5c90a4f..8b5fcf439a7da26 100644 --- a/clang/test/SemaCXX/constant-expression-cxx1z.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1z.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu +// RUN: %clang_cc1 -std=c++1z -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu -fexperimental-new-constant-interpreter namespace BaseClassAggregateInit { struct A { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits