https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/71972
>From ddda59905a49415c9c5387d7a05ed4cf768cb74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Fri, 10 Nov 2023 19:33:21 +0100 Subject: [PATCH] [clang][Interp] Implement __builtin_classify_type --- clang/lib/AST/ExprConstShared.h | 49 ++++++++++++++++++++ clang/lib/AST/ExprConstant.cpp | 35 ++------------ clang/lib/AST/Interp/ByteCodeEmitter.cpp | 15 ++++-- clang/lib/AST/Interp/ByteCodeExprGen.cpp | 10 ++-- clang/lib/AST/Interp/Function.cpp | 5 +- clang/lib/AST/Interp/Function.h | 5 +- clang/lib/AST/Interp/Interp.cpp | 3 +- clang/lib/AST/Interp/InterpBuiltin.cpp | 21 +++++++++ clang/test/Sema/builtin-classify-type.c | 1 + clang/test/SemaCXX/builtin-classify-type.cpp | 1 + 10 files changed, 101 insertions(+), 44 deletions(-) create mode 100644 clang/lib/AST/ExprConstShared.h diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h new file mode 100644 index 000000000000000..84dd9b8d951b5b1 --- /dev/null +++ b/clang/lib/AST/ExprConstShared.h @@ -0,0 +1,49 @@ +//===--- 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 +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H + +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. +}; + +GCCTypeClass EvaluateBuiltinClassifyType(QualType T, + const LangOptions &LangOpts); + +#endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 373972eb6cab11b..fadf62c95845be1 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,40 +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. -}; - /// 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 c8abb7c17a38ba2..89b7708c0c2a12f 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 <type_traits> 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 70032cce2775148..a1f45f5e3658d26 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -2231,10 +2231,12 @@ bool ByteCodeExprGen<Emitter>::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 69ab1e57b633018..1d04998d5dd1588 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<PrimType> &&ParamTypes, llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&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 94eb2a611771b0c..7c3e0f630249089 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<PrimType> &&ParamTypes, llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer, - bool HasRVO); + bool HasRVO, bool UnevaluatedBuiltin); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector<std::byte> &&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 5fe9cf80fc94709..49d9ba0c07588f8 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -131,7 +131,8 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) { const Function *CurFunc = S.Current->getFunction(); assert(CurFunc); - if (S.Current->Caller && CurFunc->isVariadic()) { + if (S.Current->Caller && + (CurFunc->isVariadic() || CurFunc->isUnevaluatedBuiltin())) { // 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. diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index 8c5efe2df909b34..0a75358edf1e312 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" @@ -457,6 +458,21 @@ static bool interp__builtin_clrsb(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<int32_t>(ResultClass); + pushInt(S, ReturnVal); + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { InterpFrame *Frame = S.Current; @@ -608,6 +624,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return retInt(S, OpPC, Dummy); break; + case Builtin::BI__builtin_classify_type: + if (interp__builtin_classify_type(S, OpPC, Frame, F, Call)) + return retInt(S, OpPC, Dummy); + break; + default: return false; } diff --git a/clang/test/Sema/builtin-classify-type.c b/clang/test/Sema/builtin-classify-type.c index a222ac8af0e32fd..ea96785550c9c72 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 ebc81425e401f11..f8b2ca2b4807cbb 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits