[clang] [clang][Interp] Implement __builtin_classify_type (PR #71972)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 15 23:34:11 PST 2023
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?= <tbaeder at 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
More information about the cfe-commits
mailing list