diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h new file mode 100644 index 0000000000000..53ec9c6c7a3ef --- /dev/null +++ b/clang/lib/AST/ExprConstShared.h @@ -0,0 +1,58 @@ +//===--- ExprConstShared.h - Shared consetxpr functionality ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Shared functionality between the new constant expression +// interpreter (AST/Interp/) and the current one (ExprConstant.cpp). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H + +namespace clang { +class QualType; +class LangOptions; +} // namespace clang +using namespace clang; +/// Values returned by __builtin_classify_type, chosen to match the values +/// produced by GCC's builtin. +enum class GCCTypeClass { + None = -1, + Void = 0, + Integer = 1, + // GCC reserves 2 for character types, but instead classifies them as + // integers. + Enum = 3, + Bool = 4, + Pointer = 5, + // GCC reserves 6 for references, but appears to never use it (because + // expressions never have reference type, presumably). + PointerToDataMember = 7, + RealFloat = 8, + Complex = 9, + // GCC reserves 10 for functions, but does not use it since GCC version 6 due + // to decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC claims to reserve 11 for pointers to member functions, but *actually* + // uses 12 for that purpose, same as for a class or struct. Maybe it + // internally implements a pointer to member as a struct? Who knows. + PointerToMemberFunction = 12, // Not a bug, see above. + ClassOrStruct = 12, + Union = 13, + // GCC reserves 14 for arrays, but does not use it since GCC version 6 due to + // decay to pointer. (Prior to version 6 it was only used in C++ mode). + // GCC reserves 15 for strings, but actually uses 5 (pointer) for string + // literals. + // Lang = 16, + // OpaqueType = 17, + BitInt = 18 +}; + +GCCTypeClass EvaluateBuiltinClassifyType(QualType T, + const LangOptions &LangOpts); + +#endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 4fb444e3b9f7e..3a41e9718bb58 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -32,6 +32,7 @@ // //===----------------------------------------------------------------------===// +#include "ExprConstShared.h" #include "Interp/Context.h" #include "Interp/Frame.h" #include "Interp/State.h" @@ -11492,43 +11493,10 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) { return false; } -/// Values returned by __builtin_classify_type, chosen to match the values -/// produced by GCC's builtin. -enum class GCCTypeClass { - None = -1, - Void = 0, - Integer = 1, - // GCC reserves 2 for character types, but instead classifies them as - // integers. - Enum = 3, - Bool = 4, - Pointer = 5, - // GCC reserves 6 for references, but appears to never use it (because - // expressions never have reference type, presumably). - PointerToDataMember = 7, - RealFloat = 8, - Complex = 9, - // GCC reserves 10 for functions, but does not use it since GCC version 6 due - // to decay to pointer. (Prior to version 6 it was only used in C++ mode). - // GCC claims to reserve 11 for pointers to member functions, but *actually* - // uses 12 for that purpose, same as for a class or struct. Maybe it - // internally implements a pointer to member as a struct? Who knows. - PointerToMemberFunction = 12, // Not a bug, see above. - ClassOrStruct = 12, - Union = 13, - // GCC reserves 14 for arrays, but does not use it since GCC version 6 due to - // decay to pointer. (Prior to version 6 it was only used in C++ mode). - // GCC reserves 15 for strings, but actually uses 5 (pointer) for string - // literals. - // Lang = 16, - // OpaqueType = 17, - BitInt = 18 -}; - /// EvaluateBuiltinClassifyType - Evaluate __builtin_classify_type the same way /// as GCC. -static GCCTypeClass -EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { +GCCTypeClass EvaluateBuiltinClassifyType(QualType T, + const LangOptions &LangOpts) { assert(!T->isDependentType() && "unexpected dependent type"); QualType CanTy = T.getCanonicalType(); diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index c8abb7c17a38b..89b7708c0c2a1 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -14,6 +14,7 @@ #include "Program.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/DeclCXX.h" +#include "clang/Basic/Builtins.h" #include using namespace clang; @@ -84,10 +85,16 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { // Create a handle over the emitted code. Function *Func = P.getFunction(FuncDecl); - if (!Func) - Func = P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors), - std::move(ParamOffsets), HasThisPointer, HasRVO); + if (!Func) { + bool IsUnevaluatedBuiltin = false; + if (unsigned BI = FuncDecl->getBuiltinID()) + IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI); + + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), std::move(ParamOffsets), + HasThisPointer, HasRVO, IsUnevaluatedBuiltin); + } assert(Func); // For not-yet-defined functions, we only create a Function instance and diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 70032cce27751..a1f45f5e3658d 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -2231,10 +2231,12 @@ bool ByteCodeExprGen::VisitBuiltinCallExpr(const CallExpr *E) { if (!Func) return false; - // Put arguments on the stack. - for (const auto *Arg : E->arguments()) { - if (!this->visit(Arg)) - return false; + if (!Func->isUnevaluatedBuiltin()) { + // Put arguments on the stack. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) + return false; + } } if (!this->emitCallBI(Func, E, E)) diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 69ab1e57b6330..1d04998d5dd15 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -20,11 +20,12 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVectorImpl &&ParamTypes, llvm::DenseMap &&Params, llvm::SmallVectorImpl &&ParamOffsets, - bool HasThisPointer, bool HasRVO) + bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin) : 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), Variadic(F->isVariadic()) {} + HasRVO(HasRVO), Variadic(F->isVariadic()), + IsUnevaluatedBuiltin(UnevaluatedBuiltin) {} 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 94eb2a611771b..7c3e0f6302490 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -179,6 +179,8 @@ class Function final { bool isBuiltin() const { return F->getBuiltinID() != 0; } + bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; } + unsigned getNumParams() const { return ParamTypes.size(); } unsigned getParamOffset(unsigned ParamIndex) const { @@ -191,7 +193,7 @@ class Function final { llvm::SmallVectorImpl &&ParamTypes, llvm::DenseMap &&Params, llvm::SmallVectorImpl &&ParamOffsets, bool HasThisPointer, - bool HasRVO); + bool HasRVO, bool UnevaluatedBuiltin); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector &&NewCode, @@ -250,6 +252,7 @@ class Function final { bool HasBody = false; bool Defined = false; bool Variadic = false; + bool IsUnevaluatedBuiltin = 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 5fe9cf80fc947..13b77e9a87725 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -131,6 +131,9 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) { const Function *CurFunc = S.Current->getFunction(); assert(CurFunc); + if (CurFunc->isUnevaluatedBuiltin()) + return; + 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. diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index bb3e13599b01d..9cf206ecc212a 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "../ExprConstShared.h" #include "Boolean.h" #include "Interp.h" #include "PrimType.h" @@ -517,6 +518,21 @@ static bool interp__builtin_bitreverse(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const Function *Func, + const CallExpr *Call) { + // This is an unevaluated call, so there are no arguments on the stack. + assert(Call->getNumArgs() == 1); + const Expr *Arg = Call->getArg(0); + + GCCTypeClass ResultClass = + EvaluateBuiltinClassifyType(Arg->getType(), S.getLangOpts()); + int32_t ReturnVal = static_cast(ResultClass); + pushInt(S, ReturnVal); + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { InterpFrame *Frame = S.Current; @@ -681,6 +697,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; + case Builtin::BI__builtin_classify_type: + if (!interp__builtin_classify_type(S, OpPC, Frame, F, Call)) + return false; + break; + default: return false; } diff --git a/clang/test/Sema/builtin-classify-type.c b/clang/test/Sema/builtin-classify-type.c index 9a4de34e823f2..50f517fcbc852 100644 --- a/clang/test/Sema/builtin-classify-type.c +++ b/clang/test/Sema/builtin-classify-type.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks +// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks -fexperimental-new-constant-interpreter // expected-no-diagnostics diff --git a/clang/test/SemaCXX/builtin-classify-type.cpp b/clang/test/SemaCXX/builtin-classify-type.cpp index ed54309600010..651dc8b24bf94 100644 --- a/clang/test/SemaCXX/builtin-classify-type.cpp +++ b/clang/test/SemaCXX/builtin-classify-type.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s -fexperimental-new-constant-interpreter // expected-no-diagnostics