Skip to content

Commit

Permalink
[clang][Interp] Implement __builtin_classify_type (#71972)
Browse files Browse the repository at this point in the history
This adds some infrastructure for unevaluated builtin calls, and uses the implementation from ExprConstant.cpp
  • Loading branch information
tbaederr committed Nov 17, 2023
1 parent 8f81c60 commit 965d301
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 46 deletions.
58 changes: 58 additions & 0 deletions clang/lib/AST/ExprConstShared.h
Original file line number Diff line number Diff line change
@@ -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
38 changes: 3 additions & 35 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
//
//===----------------------------------------------------------------------===//

#include "ExprConstShared.h"
#include "Interp/Context.h"
#include "Interp/Frame.h"
#include "Interp/State.h"
Expand Down Expand Up @@ -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();
Expand Down
15 changes: 11 additions & 4 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/Interp/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/AST/Interp/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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().
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<int32_t>(ResultClass);
pushInt(S, ReturnVal);
return true;
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions clang/test/Sema/builtin-classify-type.c
Original file line number Diff line number Diff line change
@@ -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

Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaCXX/builtin-classify-type.cpp
Original file line number Diff line number Diff line change
@@ -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

Expand Down

0 comments on commit 965d301

Please sign in to comment.