mirror of
https://github.com/intel/llvm.git
synced 2026-02-02 01:30:24 +08:00
giving them rough classifications (normal types, never-canonical types, always-dependent types, abstract type representations) and making it far easier to make sure that we've hit all of the cases when decoding types. Switched some switch() statements on the type class over to using this mechanism, and filtering out those things we don't care about. For example, CodeGen should never see always-dependent or non-canonical types, while debug info generation should never see always-dependent types. More switch() statements on the type class need to be moved over to using this approach, so that we'll get warnings when we add a new type then fail to account for it somewhere in the compiler. As part of this, some types have been renamed: TypeOfExpr -> TypeOfExprType FunctionTypeProto -> FunctionProtoType FunctionTypeNoProto -> FunctionNoProtoType There shouldn't be any functionality change... llvm-svn: 65591
271 lines
8.7 KiB
C++
271 lines
8.7 KiB
C++
//===--- Builtins.cpp - Builtin function implementation -------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements various things for builtin functions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/Builtins.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/Basic/IdentifierTable.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
using namespace clang;
|
|
|
|
static const Builtin::Info BuiltinInfo[] = {
|
|
{ "not a builtin function", 0, 0, 0, false },
|
|
#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, false },
|
|
#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER, false },
|
|
#include "clang/AST/Builtins.def"
|
|
};
|
|
|
|
const Builtin::Info &Builtin::Context::GetRecord(unsigned ID) const {
|
|
if (ID < Builtin::FirstTSBuiltin)
|
|
return BuiltinInfo[ID];
|
|
assert(ID - Builtin::FirstTSBuiltin < NumTSRecords && "Invalid builtin ID!");
|
|
return TSRecords[ID - Builtin::FirstTSBuiltin];
|
|
}
|
|
|
|
|
|
/// InitializeBuiltins - Mark the identifiers for all the builtins with their
|
|
/// appropriate builtin ID # and mark any non-portable builtin identifiers as
|
|
/// such.
|
|
void Builtin::Context::InitializeBuiltins(IdentifierTable &Table,
|
|
const TargetInfo &Target,
|
|
bool Freestanding) {
|
|
// Step #1: mark all target-independent builtins with their ID's.
|
|
for (unsigned i = Builtin::NotBuiltin+1; i != Builtin::FirstTSBuiltin; ++i)
|
|
if (!BuiltinInfo[i].Suppressed &&
|
|
(!Freestanding ||
|
|
!strchr(BuiltinInfo[i].Attributes, 'f')))
|
|
Table.get(BuiltinInfo[i].Name).setBuiltinID(i);
|
|
|
|
// Step #2: Get target builtins.
|
|
Target.getTargetBuiltins(TSRecords, NumTSRecords);
|
|
|
|
// Step #3: Register target-specific builtins.
|
|
for (unsigned i = 0, e = NumTSRecords; i != e; ++i)
|
|
if (!TSRecords[i].Suppressed &&
|
|
(!Freestanding ||
|
|
(TSRecords[i].Attributes &&
|
|
!strchr(TSRecords[i].Attributes, 'f'))))
|
|
Table.get(TSRecords[i].Name).setBuiltinID(i+Builtin::FirstTSBuiltin);
|
|
}
|
|
|
|
bool
|
|
Builtin::Context::isPrintfLike(unsigned ID, unsigned &FormatIdx,
|
|
bool &HasVAListArg) {
|
|
const char *Printf = strpbrk(GetRecord(ID).Attributes, "pP");
|
|
if (!Printf)
|
|
return false;
|
|
|
|
HasVAListArg = (*Printf == 'P');
|
|
|
|
++Printf;
|
|
assert(*Printf == ':' && "p or P specifier must have be followed by a ':'");
|
|
++Printf;
|
|
|
|
assert(strchr(Printf, ':') && "printf specifier must end with a ':'");
|
|
FormatIdx = strtol(Printf, 0, 10);
|
|
return true;
|
|
}
|
|
|
|
/// DecodeTypeFromStr - This decodes one type descriptor from Str, advancing the
|
|
/// pointer over the consumed characters. This returns the resultant type.
|
|
static QualType DecodeTypeFromStr(const char *&Str, ASTContext &Context,
|
|
Builtin::Context::GetBuiltinTypeError &Error,
|
|
bool AllowTypeModifiers = true) {
|
|
// Modifiers.
|
|
bool Long = false, LongLong = false, Signed = false, Unsigned = false;
|
|
|
|
// Read the modifiers first.
|
|
bool Done = false;
|
|
while (!Done) {
|
|
switch (*Str++) {
|
|
default: Done = true; --Str; break;
|
|
case 'S':
|
|
assert(!Unsigned && "Can't use both 'S' and 'U' modifiers!");
|
|
assert(!Signed && "Can't use 'S' modifier multiple times!");
|
|
Signed = true;
|
|
break;
|
|
case 'U':
|
|
assert(!Signed && "Can't use both 'S' and 'U' modifiers!");
|
|
assert(!Unsigned && "Can't use 'S' modifier multiple times!");
|
|
Unsigned = true;
|
|
break;
|
|
case 'L':
|
|
assert(!LongLong && "Can't have LLL modifier");
|
|
if (Long)
|
|
LongLong = true;
|
|
else
|
|
Long = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
QualType Type;
|
|
|
|
// Read the base type.
|
|
switch (*Str++) {
|
|
default: assert(0 && "Unknown builtin type letter!");
|
|
case 'v':
|
|
assert(!Long && !Signed && !Unsigned && "Bad modifiers used with 'v'!");
|
|
Type = Context.VoidTy;
|
|
break;
|
|
case 'f':
|
|
assert(!Long && !Signed && !Unsigned && "Bad modifiers used with 'f'!");
|
|
Type = Context.FloatTy;
|
|
break;
|
|
case 'd':
|
|
assert(!LongLong && !Signed && !Unsigned && "Bad modifiers used with 'd'!");
|
|
if (Long)
|
|
Type = Context.LongDoubleTy;
|
|
else
|
|
Type = Context.DoubleTy;
|
|
break;
|
|
case 's':
|
|
assert(!LongLong && "Bad modifiers used with 's'!");
|
|
if (Unsigned)
|
|
Type = Context.UnsignedShortTy;
|
|
else
|
|
Type = Context.ShortTy;
|
|
break;
|
|
case 'i':
|
|
if (LongLong)
|
|
Type = Unsigned ? Context.UnsignedLongLongTy : Context.LongLongTy;
|
|
else if (Long)
|
|
Type = Unsigned ? Context.UnsignedLongTy : Context.LongTy;
|
|
else if (Unsigned)
|
|
Type = Context.UnsignedIntTy;
|
|
else
|
|
Type = Context.IntTy; // default is signed.
|
|
break;
|
|
case 'c':
|
|
assert(!Long && !LongLong && "Bad modifiers used with 'c'!");
|
|
if (Signed)
|
|
Type = Context.SignedCharTy;
|
|
else if (Unsigned)
|
|
Type = Context.UnsignedCharTy;
|
|
else
|
|
Type = Context.CharTy;
|
|
break;
|
|
case 'b': // boolean
|
|
assert(!Long && !Signed && !Unsigned && "Bad modifiers for 'b'!");
|
|
Type = Context.BoolTy;
|
|
break;
|
|
case 'z': // size_t.
|
|
assert(!Long && !Signed && !Unsigned && "Bad modifiers for 'z'!");
|
|
Type = Context.getSizeType();
|
|
break;
|
|
case 'F':
|
|
Type = Context.getCFConstantStringType();
|
|
break;
|
|
case 'a':
|
|
Type = Context.getBuiltinVaListType();
|
|
assert(!Type.isNull() && "builtin va list type not initialized!");
|
|
break;
|
|
case 'A':
|
|
// This is a "reference" to a va_list; however, what exactly
|
|
// this means depends on how va_list is defined. There are two
|
|
// different kinds of va_list: ones passed by value, and ones
|
|
// passed by reference. An example of a by-value va_list is
|
|
// x86, where va_list is a char*. An example of by-ref va_list
|
|
// is x86-64, where va_list is a __va_list_tag[1]. For x86,
|
|
// we want this argument to be a char*&; for x86-64, we want
|
|
// it to be a __va_list_tag*.
|
|
Type = Context.getBuiltinVaListType();
|
|
assert(!Type.isNull() && "builtin va list type not initialized!");
|
|
if (Type->isArrayType()) {
|
|
Type = Context.getArrayDecayedType(Type);
|
|
} else {
|
|
Type = Context.getReferenceType(Type);
|
|
}
|
|
break;
|
|
case 'V': {
|
|
char *End;
|
|
|
|
unsigned NumElements = strtoul(Str, &End, 10);
|
|
assert(End != Str && "Missing vector size");
|
|
|
|
Str = End;
|
|
|
|
QualType ElementType = DecodeTypeFromStr(Str, Context, Error, false);
|
|
Type = Context.getVectorType(ElementType, NumElements);
|
|
break;
|
|
}
|
|
case 'P': {
|
|
IdentifierInfo *II = &Context.Idents.get("FILE");
|
|
DeclContext::lookup_result Lookup
|
|
= Context.getTranslationUnitDecl()->lookup(II);
|
|
if (Lookup.first != Lookup.second && isa<TypeDecl>(*Lookup.first)) {
|
|
Type = Context.getTypeDeclType(cast<TypeDecl>(*Lookup.first));
|
|
break;
|
|
}
|
|
else {
|
|
Error = Builtin::Context::GE_Missing_FILE;
|
|
return QualType();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!AllowTypeModifiers)
|
|
return Type;
|
|
|
|
Done = false;
|
|
while (!Done) {
|
|
switch (*Str++) {
|
|
default: Done = true; --Str; break;
|
|
case '*':
|
|
Type = Context.getPointerType(Type);
|
|
break;
|
|
case '&':
|
|
Type = Context.getReferenceType(Type);
|
|
break;
|
|
case 'C':
|
|
Type = Type.getQualifiedType(QualType::Const);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Type;
|
|
}
|
|
|
|
/// GetBuiltinType - Return the type for the specified builtin.
|
|
QualType Builtin::Context::GetBuiltinType(unsigned id, ASTContext &Context,
|
|
GetBuiltinTypeError &Error) const {
|
|
const char *TypeStr = GetRecord(id).Type;
|
|
|
|
llvm::SmallVector<QualType, 8> ArgTypes;
|
|
|
|
Error = GE_None;
|
|
QualType ResType = DecodeTypeFromStr(TypeStr, Context, Error);
|
|
if (Error != GE_None)
|
|
return QualType();
|
|
while (TypeStr[0] && TypeStr[0] != '.') {
|
|
QualType Ty = DecodeTypeFromStr(TypeStr, Context, Error);
|
|
if (Error != GE_None)
|
|
return QualType();
|
|
|
|
// Do array -> pointer decay. The builtin should use the decayed type.
|
|
if (Ty->isArrayType())
|
|
Ty = Context.getArrayDecayedType(Ty);
|
|
|
|
ArgTypes.push_back(Ty);
|
|
}
|
|
|
|
assert((TypeStr[0] != '.' || TypeStr[1] == 0) &&
|
|
"'.' should only occur at end of builtin type list!");
|
|
|
|
// handle untyped/variadic arguments "T c99Style();" or "T cppStyle(...);".
|
|
if (ArgTypes.size() == 0 && TypeStr[0] == '.')
|
|
return Context.getFunctionNoProtoType(ResType);
|
|
return Context.getFunctionType(ResType, &ArgTypes[0], ArgTypes.size(),
|
|
TypeStr[0] == '.', 0);
|
|
}
|