Skip to content

Commit 965d301

Browse files
authored
[clang][Interp] Implement __builtin_classify_type (#71972)
This adds some infrastructure for unevaluated builtin calls, and uses the implementation from ExprConstant.cpp
1 parent 8f81c60 commit 965d301

File tree

10 files changed

+111
-46
lines changed

10 files changed

+111
-46
lines changed

clang/lib/AST/ExprConstShared.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===--- ExprConstShared.h - Shared consetxpr functionality ----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Shared functionality between the new constant expression
10+
// interpreter (AST/Interp/) and the current one (ExprConstant.cpp).
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
15+
#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
16+
17+
namespace clang {
18+
class QualType;
19+
class LangOptions;
20+
} // namespace clang
21+
using namespace clang;
22+
/// Values returned by __builtin_classify_type, chosen to match the values
23+
/// produced by GCC's builtin.
24+
enum class GCCTypeClass {
25+
None = -1,
26+
Void = 0,
27+
Integer = 1,
28+
// GCC reserves 2 for character types, but instead classifies them as
29+
// integers.
30+
Enum = 3,
31+
Bool = 4,
32+
Pointer = 5,
33+
// GCC reserves 6 for references, but appears to never use it (because
34+
// expressions never have reference type, presumably).
35+
PointerToDataMember = 7,
36+
RealFloat = 8,
37+
Complex = 9,
38+
// GCC reserves 10 for functions, but does not use it since GCC version 6 due
39+
// to decay to pointer. (Prior to version 6 it was only used in C++ mode).
40+
// GCC claims to reserve 11 for pointers to member functions, but *actually*
41+
// uses 12 for that purpose, same as for a class or struct. Maybe it
42+
// internally implements a pointer to member as a struct? Who knows.
43+
PointerToMemberFunction = 12, // Not a bug, see above.
44+
ClassOrStruct = 12,
45+
Union = 13,
46+
// GCC reserves 14 for arrays, but does not use it since GCC version 6 due to
47+
// decay to pointer. (Prior to version 6 it was only used in C++ mode).
48+
// GCC reserves 15 for strings, but actually uses 5 (pointer) for string
49+
// literals.
50+
// Lang = 16,
51+
// OpaqueType = 17,
52+
BitInt = 18
53+
};
54+
55+
GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
56+
const LangOptions &LangOpts);
57+
58+
#endif

clang/lib/AST/ExprConstant.cpp

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
//
3333
//===----------------------------------------------------------------------===//
3434

35+
#include "ExprConstShared.h"
3536
#include "Interp/Context.h"
3637
#include "Interp/Frame.h"
3738
#include "Interp/State.h"
@@ -11492,43 +11493,10 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) {
1149211493
return false;
1149311494
}
1149411495

11495-
/// Values returned by __builtin_classify_type, chosen to match the values
11496-
/// produced by GCC's builtin.
11497-
enum class GCCTypeClass {
11498-
None = -1,
11499-
Void = 0,
11500-
Integer = 1,
11501-
// GCC reserves 2 for character types, but instead classifies them as
11502-
// integers.
11503-
Enum = 3,
11504-
Bool = 4,
11505-
Pointer = 5,
11506-
// GCC reserves 6 for references, but appears to never use it (because
11507-
// expressions never have reference type, presumably).
11508-
PointerToDataMember = 7,
11509-
RealFloat = 8,
11510-
Complex = 9,
11511-
// GCC reserves 10 for functions, but does not use it since GCC version 6 due
11512-
// to decay to pointer. (Prior to version 6 it was only used in C++ mode).
11513-
// GCC claims to reserve 11 for pointers to member functions, but *actually*
11514-
// uses 12 for that purpose, same as for a class or struct. Maybe it
11515-
// internally implements a pointer to member as a struct? Who knows.
11516-
PointerToMemberFunction = 12, // Not a bug, see above.
11517-
ClassOrStruct = 12,
11518-
Union = 13,
11519-
// GCC reserves 14 for arrays, but does not use it since GCC version 6 due to
11520-
// decay to pointer. (Prior to version 6 it was only used in C++ mode).
11521-
// GCC reserves 15 for strings, but actually uses 5 (pointer) for string
11522-
// literals.
11523-
// Lang = 16,
11524-
// OpaqueType = 17,
11525-
BitInt = 18
11526-
};
11527-
1152811496
/// EvaluateBuiltinClassifyType - Evaluate __builtin_classify_type the same way
1152911497
/// as GCC.
11530-
static GCCTypeClass
11531-
EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) {
11498+
GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
11499+
const LangOptions &LangOpts) {
1153211500
assert(!T->isDependentType() && "unexpected dependent type");
1153311501

1153411502
QualType CanTy = T.getCanonicalType();

clang/lib/AST/Interp/ByteCodeEmitter.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "Program.h"
1515
#include "clang/AST/ASTLambda.h"
1616
#include "clang/AST/DeclCXX.h"
17+
#include "clang/Basic/Builtins.h"
1718
#include <type_traits>
1819

1920
using namespace clang;
@@ -84,10 +85,16 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
8485

8586
// Create a handle over the emitted code.
8687
Function *Func = P.getFunction(FuncDecl);
87-
if (!Func)
88-
Func = P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
89-
std::move(ParamDescriptors),
90-
std::move(ParamOffsets), HasThisPointer, HasRVO);
88+
if (!Func) {
89+
bool IsUnevaluatedBuiltin = false;
90+
if (unsigned BI = FuncDecl->getBuiltinID())
91+
IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI);
92+
93+
Func =
94+
P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
95+
std::move(ParamDescriptors), std::move(ParamOffsets),
96+
HasThisPointer, HasRVO, IsUnevaluatedBuiltin);
97+
}
9198

9299
assert(Func);
93100
// For not-yet-defined functions, we only create a Function instance and

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,10 +2231,12 @@ bool ByteCodeExprGen<Emitter>::VisitBuiltinCallExpr(const CallExpr *E) {
22312231
if (!Func)
22322232
return false;
22332233

2234-
// Put arguments on the stack.
2235-
for (const auto *Arg : E->arguments()) {
2236-
if (!this->visit(Arg))
2237-
return false;
2234+
if (!Func->isUnevaluatedBuiltin()) {
2235+
// Put arguments on the stack.
2236+
for (const auto *Arg : E->arguments()) {
2237+
if (!this->visit(Arg))
2238+
return false;
2239+
}
22382240
}
22392241

22402242
if (!this->emitCallBI(Func, E, E))

clang/lib/AST/Interp/Function.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
2020
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
2121
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
2222
llvm::SmallVectorImpl<unsigned> &&ParamOffsets,
23-
bool HasThisPointer, bool HasRVO)
23+
bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin)
2424
: P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
2525
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
2626
ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer),
27-
HasRVO(HasRVO), Variadic(F->isVariadic()) {}
27+
HasRVO(HasRVO), Variadic(F->isVariadic()),
28+
IsUnevaluatedBuiltin(UnevaluatedBuiltin) {}
2829

2930
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
3031
auto It = Params.find(Offset);

clang/lib/AST/Interp/Function.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ class Function final {
179179

180180
bool isBuiltin() const { return F->getBuiltinID() != 0; }
181181

182+
bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; }
183+
182184
unsigned getNumParams() const { return ParamTypes.size(); }
183185

184186
unsigned getParamOffset(unsigned ParamIndex) const {
@@ -191,7 +193,7 @@ class Function final {
191193
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
192194
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
193195
llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer,
194-
bool HasRVO);
196+
bool HasRVO, bool UnevaluatedBuiltin);
195197

196198
/// Sets the code of a function.
197199
void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode,
@@ -250,6 +252,7 @@ class Function final {
250252
bool HasBody = false;
251253
bool Defined = false;
252254
bool Variadic = false;
255+
bool IsUnevaluatedBuiltin = false;
253256

254257
public:
255258
/// Dumps the disassembled bytecode to \c llvm::errs().

clang/lib/AST/Interp/Interp.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC) {
131131
const Function *CurFunc = S.Current->getFunction();
132132
assert(CurFunc);
133133

134+
if (CurFunc->isUnevaluatedBuiltin())
135+
return;
136+
134137
if (S.Current->Caller && CurFunc->isVariadic()) {
135138
// CallExpr we're look for is at the return PC of the current function, i.e.
136139
// in the caller.

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8+
#include "../ExprConstShared.h"
89
#include "Boolean.h"
910
#include "Interp.h"
1011
#include "PrimType.h"
@@ -517,6 +518,21 @@ static bool interp__builtin_bitreverse(InterpState &S, CodePtr OpPC,
517518
return true;
518519
}
519520

521+
static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC,
522+
const InterpFrame *Frame,
523+
const Function *Func,
524+
const CallExpr *Call) {
525+
// This is an unevaluated call, so there are no arguments on the stack.
526+
assert(Call->getNumArgs() == 1);
527+
const Expr *Arg = Call->getArg(0);
528+
529+
GCCTypeClass ResultClass =
530+
EvaluateBuiltinClassifyType(Arg->getType(), S.getLangOpts());
531+
int32_t ReturnVal = static_cast<int32_t>(ResultClass);
532+
pushInt(S, ReturnVal);
533+
return true;
534+
}
535+
520536
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
521537
const CallExpr *Call) {
522538
InterpFrame *Frame = S.Current;
@@ -681,6 +697,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
681697
return false;
682698
break;
683699

700+
case Builtin::BI__builtin_classify_type:
701+
if (!interp__builtin_classify_type(S, OpPC, Frame, F, Call))
702+
return false;
703+
break;
704+
684705
default:
685706
return false;
686707
}

clang/test/Sema/builtin-classify-type.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks
2+
// RUN: %clang_cc1 -fsyntax-only -verify %s -fblocks -fexperimental-new-constant-interpreter
23

34
// expected-no-diagnostics
45

clang/test/SemaCXX/builtin-classify-type.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -fblocks %s -fexperimental-new-constant-interpreter
23

34
// expected-no-diagnostics
45

0 commit comments

Comments
 (0)