Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1484,6 +1484,8 @@ } } + getTargetCodeGenInfo().emitTargetMD(D, F, *this); + // Make sure the result is of the requested type. if (!IsIncompleteFunction) { assert(F->getType()->getElementType() == Ty); @@ -1640,6 +1642,8 @@ isExternallyVisible(D->getLinkageAndVisibility().getLinkage())) GV->setSection(".cp.rodata"); + getTargetCodeGenInfo().emitTargetMD(D, GV, *this); + return GV; } Index: lib/CodeGen/TargetInfo.h =================================================================== --- lib/CodeGen/TargetInfo.h +++ lib/CodeGen/TargetInfo.h @@ -56,6 +56,11 @@ virtual void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const { } + /// EmitTargetMD - Provides a convenient hook to handle extra + /// target-specific metadata for the given global. + virtual void emitTargetMD(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &M) const { } + /// Determines the size of struct _Unwind_Exception on this platform, /// in 8-bit units. The Itanium ABI defines this as: /// struct _Unwind_Exception { Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -23,6 +23,9 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Type.h" #include "llvm/Support/raw_ostream.h" + +#include // std::sort + using namespace clang; using namespace CodeGen; @@ -6051,7 +6054,85 @@ //===----------------------------------------------------------------------===// // XCore ABI Implementation //===----------------------------------------------------------------------===// + namespace { + +/// A SmallStringEnc instance is used to build up the TypeString by passing +/// it by reference between functions that append to it. +typedef llvm::SmallString<128> SmallStringEnc; + +/// TypeStringCache caches the meta encodings of Types. +/// +/// The reason for caching TypeStrings is two fold: +/// 1. To cache a type's encoding for later uses; +/// 2. As a means to break recursive member type inclusion. +/// +/// A cache Entry can have a Status of: +/// NonRecursive: The type encoding is not recursive; +/// Recursive: The type encoding is recursive; +/// Incomplete: An incomplete TypesString; +/// IncompleteUsed: The Incomplete was used in a Recursive encoding. +/// +/// A NonRecursive entry will have all of its sub-members expanded as fully +/// as possible. Whilst it may contain types which are recursive, the type +/// itself is not recursive and thus its encoding may be safely used whenever +/// the type is encountered. +/// +/// A Recursive entry will have all of its sub-members expanded as fully as +/// possible. The type itself is self recursive and it may contain other types +/// which are recursive. This Recursive encoding must not be used during +/// expansion of another type which was involved in the self recursion. For +/// simplicity the code uses IncompleteCount to reject all usage of Recursive +/// encodings for member types. +/// +/// An Incomplete entry is always a RecordType and only encodes its +/// identifier e.g. "s(S){}". Incomplete 'StubEnc' entries are ephemeral and +/// are placed into the cache during type expansion as a means to identify and +/// handle recursive inclusion of types as sub-members. +/// +/// During the expansion of a type, if a RecordType is encountered, an +/// Incomplete is placed into the cache to break potential recursive +/// inclusion of itself as a sub-member. Once the RecordType has been expanded, +/// its temporary incomplete entry is removed from the cache. +/// +/// If an incomplete entry is used to expand a sub-member, the incomplete entry +/// is marked as IncompleteUsed. The cache keeps count of how many +/// IncompleteUsed entries it currently contains in IncompleteUsedCount. +/// +/// Whilst encoding a type, if a sub-member is found to be a NonRecursive or +/// Recursive (IncompleteUsedCount==0), it will be added to the cache. +/// If IncompleteUsedCount is not zero, the sub-member is part of a recursive +/// type which contains containing-type Incomplete entries. It thus breaks the +/// recursion correctly for the containing type but not for itself. +/// +class TypeStringCache { + enum Status {NonRecursive, Recursive, Incomplete, IncompleteUsed}; + struct Entry {std::string Str; enum Status State; std::string Swapped;}; + std::map Map; + unsigned IncompleteCount; // Number of Incomplete entries in Map. + unsigned IncompleteUsedCount; // Number of IncompleteUsed entries in Map. +public: + void addIncomplete(const IdentifierInfo *ID, std::string StubEnc); + bool removeIncomplete(const IdentifierInfo *ID); + void addIfComplete(const IdentifierInfo *ID, StringRef Str, + bool SelfRecursive); + StringRef str(const IdentifierInfo *ID); +}; + +/// TypeString encodings for union fields must be order. +/// FieldEncoding is a helper for this ordering process. +class FieldEncoding { + bool HasName; + std::string Enc; +public: + FieldEncoding(bool b, SmallStringEnc &e) : HasName(b), Enc(e.c_str()) {}; + StringRef str() {return Enc.c_str();}; + bool operator<(const FieldEncoding& rhs) const { + if (HasName != rhs.HasName) return HasName; + return Enc < rhs.Enc; + } +}; + class XCoreABIInfo : public DefaultABIInfo { public: XCoreABIInfo(CodeGen::CodeGenTypes &CGT) : DefaultABIInfo(CGT) {} @@ -6060,10 +6141,14 @@ }; class XCoreTargetCodeGenInfo : public TargetCodeGenInfo { + mutable TypeStringCache TSC; public: XCoreTargetCodeGenInfo(CodeGenTypes &CGT) :TargetCodeGenInfo(new XCoreABIInfo(CGT)) {} + virtual void emitTargetMD(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &M) const; }; + } // End anonymous namespace. llvm::Value *XCoreABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, @@ -6115,6 +6200,448 @@ return Val; } +/// During the expansion of a RecordType, an incomplete TypeString is placed +/// into the cache as a means to identify and break recursion. +/// If there is a Recursive encoding in the cache, it is swapped out and will +/// be reinserted by removeIncomplete(). +/// All other types of encoding should have been used rather than arriving here. +void TypeStringCache::addIncomplete(const IdentifierInfo *ID, + std::string StubEnc) { + if (!ID) + return; + Entry& E = Map[ID]; + assert( (E.Str.empty() || E.State == Recursive) && + "Incorrectly use of addIncomplete"); + assert(!StubEnc.empty() && "Passing an empty string to addIncomplete()"); + E.Swapped.swap(E.Str); // swap out the Recursive + E.Str.swap(StubEnc); + E.State = Incomplete; + ++IncompleteCount; +} + +/// Once the RecordType has been expanded, the temporary incomplete TypeString +/// must be removed from the cache. +/// If a Recursive was swapped out by addIncomplete(), it will be replaced. +/// Returns true if the RecordType was defined recursively by itself. +bool TypeStringCache::removeIncomplete(const IdentifierInfo *ID) { + if (!ID) + return false; + auto I = Map.find(ID); + assert(I != Map.end() && "Entry not present"); + Entry& E = I->second; + assert( (E.State == Incomplete || + E.State == IncompleteUsed) && + "Entry must be an incomplete type"); + bool SelfRecursive = false; + if (E.State == IncompleteUsed) { + // We made use of our Incomplete encoding, thus we are self recursive. + SelfRecursive = true; + --IncompleteUsedCount; + } + if (E.Swapped.empty()) + Map.erase(I); + else { + // Swap the Recursive back. + E.Swapped.swap(E.Str); + E.Swapped.clear(); + E.State = Recursive; + } + --IncompleteCount; + return SelfRecursive; +} + +/// Add the encoded TypeString to the cache if it is NonRecursive or +/// Recursive (viz: all sub-members were expanded as fully as possible). +void TypeStringCache::addIfComplete(const IdentifierInfo *ID, StringRef Str, + bool SelfRecursive) { + if (!ID || IncompleteUsedCount) + return; // No key or it is is an incomplete sub-type so don't add. + Entry& E = Map[ID]; + if (SelfRecursive && !E.Str.empty()) { + assert(E.State==Recursive && E.Str.size() == Str.size() && + "This is not the same SelfRecursive entry"); + // The parent container was not recursive, so we could have used this + // Recursive sub-member entry after all, but we assumed the worse when we + // started viz: IncompleteCount!=0. + return; + } + assert(E.Str.empty() && "Entry already present"); + E.Str = Str.str(); + E.State = SelfRecursive? Recursive : NonRecursive; +} + +/// Return a cached TypeString encoding for the ID. If there isn't one, or we +/// are recursively expanding a type (IncompleteCount != 0) and the cached +/// encoding is Recursive, return an empty StringRef. +StringRef TypeStringCache::str(const IdentifierInfo *ID) { + if (!ID) + return StringRef(); // We have no key. + auto I = Map.find(ID); + if (I == Map.end()) + return StringRef(); // We have no encoding. + Entry& E = I->second; + if (E.State == Recursive && IncompleteCount) + return StringRef(); // We don't use Recursive encodings for member types. + + if (E.State == Incomplete) { + // The incomplete type is being used to break out of recursion. + E.State = IncompleteUsed; + ++IncompleteUsedCount; + } + return E.Str.c_str(); +} + +/// The XCore ABI includes a type information section that communicates symbol +/// type information to the linker. The linker uses this information to verify +/// safety/correctness of things such as array bound and pointers et al. +/// The ABI only requires C (and XC) language modules to emit TypeStrings. +/// This type information (TypeString) is emitted into meta data for all global +/// symbols: definitions, declarations, functions & variables. +/// +/// The TypeString carries type, qualifier, name, size & value details. +/// Please see 'Tools Development Guide' section 2.16.2 for format details: +/// +/// The output is tested by test/CodeGen/xcore-stringtype.c. +/// +static bool getTypeString(SmallStringEnc &Enc, const Decl *D, + CodeGen::CodeGenModule &CGM, TypeStringCache &TSC); + +/// XCore uses emitTargetMD to emit TypeString metadata for global symbols. +void XCoreTargetCodeGenInfo::emitTargetMD(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const { + SmallStringEnc Enc; + if (getTypeString(Enc, D, CGM, TSC)) { + llvm::LLVMContext &Ctx = CGM.getModule().getContext(); + llvm::SmallVector MDVals; + MDVals.push_back(GV); + MDVals.push_back(llvm::MDString::get(Ctx, Enc.str())); + llvm::NamedMDNode *MD = + CGM.getModule().getOrInsertNamedMetadata("xcore.typestrings"); + MD->addOperand(llvm::MDNode::get(Ctx, MDVals)); + } +} + +static bool appendType(SmallStringEnc &Enc, QualType QType, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC); + +/// Helper function for appendRecordType(). +/// Builds a SmallVector containing the encoded field types in declaration order. +static bool extractFieldType(SmallVectorImpl &FE, + const RecordDecl *RD, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC) { + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) { + SmallStringEnc Enc; + Enc += "m("; + Enc += I->getName(); + Enc += "){"; + if (I->isBitField()) { + Enc += "b("; + llvm::raw_svector_ostream::raw_svector_ostream OS(Enc); + OS.resync(); + OS << I->getBitWidthValue(CGM.getContext()); + OS.flush(); + Enc += ':'; + } + if (!appendType(Enc, I->getType(), CGM, TSC)) + return false; + if (I->isBitField()) + Enc += ')'; + Enc += '}'; + FE.push_back(FieldEncoding(!I->getName().empty(), Enc)); + } + return true; +} + +/// Appends structure and union types to Enc and adds encoding to cache. +/// Recursively calls appendType (via extractFieldType) for each field. +/// Union types have their fields ordered according to the ABI. +static bool appendRecordType(SmallStringEnc &Enc, const RecordType *RT, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC, const IdentifierInfo *ID) { + // Append the cached TypeString if we have one. + StringRef TypeString = TSC.str(ID); + if (!TypeString.empty()) { + Enc += TypeString; + return true; + } + + // Start to emit an incomplete TypeString. + size_t Start = Enc.size(); + Enc += (RT->isUnionType()? 'u' : 's'); + Enc += '('; + if (ID) + Enc += ID->getName(); + Enc += "){"; + + // We collect all encoded fields and order as necessary. + bool SelfRecursive = false; + SmallVector FE; + const RecordDecl *RD = RT->getDecl()->getDefinition(); + if (RD && !RD->field_empty()) { + // An incomplete TypeString stub is placed in the cache for this RecordType + // so that recursive calls to this RecordType will use it whilst building a + // complete TypeString for this RecordType. + std::string StubEnc(Enc.substr(Start).str()); + StubEnc += '}'; // StubEnc now holds a valid incomplete TypeString. + TSC.addIncomplete(ID, std::move(StubEnc)); + if (!extractFieldType(FE, RD, CGM, TSC)) { + (void) TSC.removeIncomplete(ID); + return false; + } + SelfRecursive = TSC.removeIncomplete(ID); + // The ABI requires unions to be sorted but not structures. + // See FieldEncoding::operator< for sort algorithm. + if (RT->isUnionType()) + std::sort(FE.begin(), FE.end()); + } + + // We can now complete the TypeString. + if (unsigned E = FE.size()) + for (unsigned I = 0; I != E; ++I) { + if (I) + Enc += ','; + Enc += FE[I].str(); + } + Enc += '}'; + TSC.addIfComplete(ID, Enc.substr(Start), SelfRecursive); + return true; +} + +/// Appends enum types to Enc and adds the encoding to the cache. +static bool appendEnumType(SmallStringEnc &Enc, const EnumType *ET, + TypeStringCache &TSC, + const IdentifierInfo *ID) { + // Append the cached TypeString if we have one. + StringRef TypeString = TSC.str(ID); + if (!TypeString.empty()) { + Enc += TypeString; + return true; + } + + size_t Start = Enc.size(); + Enc += "e("; + if (ID) + Enc += ID->getName(); + Enc += "){"; + if (const EnumDecl *ED = ET->getDecl()->getDefinition()) { + auto I = ED->enumerator_begin(); + auto E = ED->enumerator_end(); + while (I != E) { + Enc += "m("; + Enc += I->getName(); + Enc += "){"; + I->getInitVal().toString(Enc); + Enc += '}'; + ++I; + if (I != E) + Enc += ','; + } + } + Enc += '}'; + TSC.addIfComplete(ID, Enc.substr(Start), false); + return true; +} + +/// Appends type's qualifier to Enc. +/// This is done prior to appending the type's encoding. +static void appendQualifier(SmallStringEnc &Enc, QualType QT) { + // Qualifiers are emitted in alphabetical order. + static const char *Table[] = {"","c:","r:","cr:","v:","cv:","rv:","crv:"}; + int Lookup = 0; + if (QT.isConstQualified()) + Lookup += 1<<0; + if (QT.isRestrictQualified()) + Lookup += 1<<1; + if (QT.isVolatileQualified()) + Lookup += 1<<2; + Enc += Table[Lookup]; +} + +/// Appends built-in types to Enc. +static bool appendBuiltinType(SmallStringEnc &Enc, const BuiltinType *BT) { + const char *EncType; + switch (BT->getKind()) { + case BuiltinType::Void: + EncType = "0"; + break; + case BuiltinType::Bool: + EncType = "b"; + break; + case BuiltinType::Char_U: + EncType = "uc"; + break; + case BuiltinType::UChar: + EncType = "uc"; + break; + case BuiltinType::SChar: + EncType = "sc"; + break; + case BuiltinType::UShort: + EncType = "us"; + break; + case BuiltinType::Short: + EncType = "ss"; + break; + case BuiltinType::UInt: + EncType = "ui"; + break; + case BuiltinType::Int: + EncType = "si"; + break; + case BuiltinType::ULong: + EncType = "ul"; + break; + case BuiltinType::Long: + EncType = "sl"; + break; + case BuiltinType::ULongLong: + EncType = "ull"; + break; + case BuiltinType::LongLong: + EncType = "sll"; + break; + case BuiltinType::Float: + EncType = "ft"; + break; + case BuiltinType::Double: + EncType = "d"; + break; + case BuiltinType::LongDouble: + EncType = "ld"; + break; + default: + return false; + } + Enc += EncType; + return true; +} + +/// Appends a pointer encoding to Enc before calling appendType for the pointee. +static bool appendPointerType(SmallStringEnc &Enc, const PointerType *PT, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC) { + Enc += "p("; + if (!appendType(Enc, PT->getPointeeType(), CGM, TSC)) + return false; + Enc += ')'; + return true; +} + +/// Appends array encoding to Enc before calling appendType for the element. +static bool appendArrayType(SmallStringEnc &Enc, const ArrayType *AT, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC, StringRef NoSizeEnc) { + if (AT->getSizeModifier() != ArrayType::Normal) + return false; + Enc += "a("; + if (const ConstantArrayType *CAT = dyn_cast(AT)) + CAT->getSize().toStringUnsigned(Enc); + else + Enc += NoSizeEnc; // Global arrays use "*", otherwise it is "". + Enc += ':'; + if (!appendType(Enc, AT->getElementType(), CGM, TSC)) + return false; + Enc += ')'; + return true; +} + +/// Appends a function encoding to Enc, calling appendType for the return type +/// and the arguments. +static bool appendFunctionType(SmallStringEnc &Enc, const FunctionType *FT, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC) { + Enc += "f{"; + if (!appendType(Enc, FT->getReturnType(), CGM, TSC)) + return false; + Enc += "}("; + if (const FunctionProtoType *FPT = FT->getAs()) { + // N.B. we are only interested in the adjusted param types. + auto I = FPT->param_type_begin(); + auto E = FPT->param_type_end(); + if (I != E) { + do { + if (!appendType(Enc, *I, CGM, TSC)) + return false; + ++I; + if (I != E) + Enc += ','; + } while (I != E); + if (FPT->isVariadic()) + Enc += ",va"; + } else { + if (FPT->isVariadic()) + Enc += "va"; + else + Enc += '0'; + } + } + Enc += ')'; + return true; +} + +/// Handles the type's qualifier before dispatching a call to handle specific +/// type encodings. +static bool appendType(SmallStringEnc &Enc, QualType QType, + const CodeGen::CodeGenModule &CGM, + TypeStringCache &TSC) { + + QualType QT = QType.getCanonicalType(); + + appendQualifier(Enc, QT); + + if (const BuiltinType *BT = QT->getAs()) + return appendBuiltinType(Enc, BT); + + if (const ArrayType *AT = QT->getAsArrayTypeUnsafe()) + return appendArrayType(Enc, AT, CGM, TSC, ""); + + if (const PointerType *PT = QT->getAs()) + return appendPointerType(Enc, PT, CGM, TSC); + + if (const EnumType *ET = QT->getAs()) + return appendEnumType(Enc, ET, TSC, QT.getBaseTypeIdentifier()); + + if (const RecordType *RT = QT->getAsStructureType()) + return appendRecordType(Enc, RT, CGM, TSC, QT.getBaseTypeIdentifier()); + + if (const RecordType *RT = QT->getAsUnionType()) + return appendRecordType(Enc, RT, CGM, TSC, QT.getBaseTypeIdentifier()); + + if (const FunctionType *FT = QT->getAs()) + return appendFunctionType(Enc, FT, CGM, TSC); + + return false; +} + +static bool getTypeString(SmallStringEnc &Enc, const Decl *D, + CodeGen::CodeGenModule &CGM, TypeStringCache &TSC) { + if (!D) + return false; + + if (const FunctionDecl *FD = dyn_cast(D)) { + if (FD->getLanguageLinkage() != CLanguageLinkage) + return false; + return appendType(Enc, FD->getType(), CGM, TSC); + } + + if (const VarDecl *VD = dyn_cast(D)) { + if (VD->getLanguageLinkage() != CLanguageLinkage) + return false; + QualType QT = VD->getType().getCanonicalType(); + if (const ArrayType *AT = QT->getAsArrayTypeUnsafe()) { + // Global ArrayTypes are given a size of '*' if the size is unknown. + appendQualifier(Enc, QT); + return appendArrayType(Enc, AT, CGM, TSC, "*"); + } + return appendType(Enc, QT, CGM, TSC); + } + return false; +} + + //===----------------------------------------------------------------------===// // Driver code //===----------------------------------------------------------------------===// Index: test/CodeGen/xcore-stringtype.c =================================================================== --- /dev/null +++ test/CodeGen/xcore-stringtype.c @@ -0,0 +1,169 @@ +// REQUIRES: xcore-registered-target +// RUN: %clang_cc1 -triple xcore-unknown-unknown -fno-signed-char -fno-common -emit-llvm -o - %s | FileCheck %s + +// CHECK: target triple = "xcore-unknown-unknown" + +// In the tests below, some types are not supported by the ABI (_Complex, +// variable length arrays) and will thus emit no meta data. +// The 33 tests that do emit typstrings are gathered into '!xcore.typestrings' +// Please see 'Tools Developement Guide' section 2.16.2 for format details: +// + +// CHECK: !xcore.typestrings = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, +// CHECK: !11, !12, !13, !14, !15, !16, !17, !18, !19, !20, !21, !22, !23, +// CHECK: !24, !25, !26, !27, !28, !29, !30, !31, !32, !33, !34} + + +// test BuiltinType +// CHECK: !0 = metadata !{void (i1, i8, i8, i8, i16, i16, i16, i32, i32, i32, +// CHECK: i32, i32, i32, i64, i64, i64, float, double, double)* +// CHECK: @builtinType, metadata !"f{0}(b,uc,uc,sc,ss,us,ss,si,ui,si,sl, +// CHECK: ul,sl,sll,ull,sll,ft,d,ld)"} +void builtinType(_Bool B, char C, unsigned char UC, signed char SC, short S, + unsigned short US, signed short SS, int I, unsigned int UI, + signed int SI, long L, unsigned long UL, signed long SL, + long long LL, unsigned long long ULL, signed long long SLL, + float F, double D, long double LD) {} +double _Complex Complex; // not supported + + +// test FunctionType & Qualifiers +// CHECK: !1 = metadata !{void ()* @gI, metadata !"f{0}()"} +// CHECK: !2 = metadata !{void (...)* @eI, metadata !"f{0}()"} +// CHECK: !3 = metadata !{void ()* @gV, metadata !"f{0}(0)"} +// CHECK: !4 = metadata !{void ()* @eV, metadata !"f{0}(0)"} +// CHECK: !5 = metadata !{void (i32, ...)* @gVA, metadata !"f{0}(si,va)"} +// CHECK: !6 = metadata !{void (i32, ...)* @eVA, metadata !"f{0}(si,va)"} +// CHECK: !7 = metadata !{i32* (i32*)* @gQ, metadata !"f{crv:p(cv:si)}(p(cv:si))"} +// CHECK: !8 = metadata !{i32* (i32*)* @eQ, metadata !"f{crv:p(cv:si)}(p(cv:si))"} +extern void eI(); +void gI() {eI();}; +extern void eV(void); +void gV(void) {eV();} +extern void eVA(int, ...); +void gVA(int i, ...) {eVA(i);} +extern const volatile int* volatile restrict const + eQ(const volatile int * volatile restrict const); +const volatile int* volatile restrict const + gQ(const volatile int * volatile restrict const i) {return eQ(i);} + + +// test PointerType +// CHECK: !9 = metadata !{i32* (i32*, i32* (i32*)*)* +// CHECK: @pointerType, metadata !"f{p(si)}(p(si),p(f{p(si)}(p(si))))"} +// CHECK: !10 = metadata !{i32** @EP, metadata !"p(si)"} +// CHECK: !11 = metadata !{i32** @GP, metadata !"p(si)"} +extern int* EP; +int* GP; +int* pointerType(int *I, int * (*FP)(int *)) { + return I? EP : GP; +} + + +// test ArrayType +// CHECK: !12 = metadata !{[2 x i32]* (i32*, i32*, [2 x i32]*, [2 x i32]*, i32*)* +// CHECK: @arrayType, metadata !"f{p(a(2:si))}(p(si),p(si),p(a(2:si)), +// CHECK: p(a(2:si)),p(si))"} +// CHECK: !13 = metadata !{[0 x i32]* @EA1, metadata !"a(*:si)"} +// CHECK: !14 = metadata !{[2 x i32]* @EA2, metadata !"a(2:si)"} +// CHECK: !15 = metadata !{[0 x [2 x i32]]* @EA3, metadata !"a(*:a(2:si))"} +// CHECK: !16 = metadata !{[3 x [2 x i32]]* @EA4, metadata !"a(3:a(2:si))"} +// CHECK: !17 = metadata !{[2 x i32]* @GA1, metadata !"a(2:si)"} +// CHECK: !18 = metadata !{void ([2 x i32]*)* @arrayTypeVariable1, +// CHECK: metadata !"f{0}(p(a(2:si)))"} +// CHECK: !19 = metadata !{void (void ([2 x i32]*)*)* @arrayTypeVariable2, +// CHECK: metadata !"f{0}(p(f{0}(p(a(2:si)))))"} +// CHECK: !20 = metadata !{[3 x [2 x i32]]* @GA2, metadata !"a(3:a(2:si))"} +extern int EA1[]; +extern int EA2[2]; +extern int EA3[][2]; +extern int EA4[3][2]; +int GA1[2]; +int GA2[3][2]; +extern void arrayTypeVariable1(int[*][2]); +extern void arrayTypeVariable2( void(*fp)(int[*][2]) ); +extern void arrayTypeVariable3(int[3][*]); // not supported +extern void arrayTypeVariable4( void(*fp)(int[3][*]) ); // not supported +typedef int RetType[2]; +RetType* arrayType(int A1[], int A2[2], int A3[][2], int A4[3][2], + int A5[const volatile restrict static 2]) { + if (A1) return &EA1; + if (A2) return &EA2; + if (A3) return EA3; + if (A4) return EA4; + if (A5) return &GA1; + arrayTypeVariable1(EA4); + arrayTypeVariable2(arrayTypeVariable1); + arrayTypeVariable3(EA4); + arrayTypeVariable4(arrayTypeVariable3); + return GA2; +} + + +// test StructureType +// CHECK: !21 = metadata !{void (%struct.S1*)* @structureType1, metadata +// CHECK: !"f{0}(s(S1){m(ps2){p(s(S2){m(ps3){p(s(S3){m(s1){s(S1){}}})}})}})"} +// CHECK: !22 = metadata !{void (%struct.S2*)* @structureType2, metadata +// CHECK: !"f{0}(s(S2){m(ps3){p(s(S3){m(s1){s(S1){m(ps2){p(s(S2){})}}}})}})"} +// CHECK: !23 = metadata !{void (%struct.S3*)* @structureType3, metadata +// CHECK: !"f{0}(s(S3){m(s1){s(S1){m(ps2){p(s(S2){m(ps3){p(s(S3){})}})}}}})"} +// CHECK: !24 = metadata !{void (%struct.S4*)* @structureType4, metadata +// CHECK: !"f{0}(s(S4){m(s1){s(S1){m(ps2){p(s(S2){m(ps3){p(s(S3){m(s1){s(S1){}}})}})}}}})"} +// CHECK: !25 = metadata !{%struct.anon* @StructAnon, metadata !"s(){m(A){si}}"} +// CHECK: !26 = metadata !{i32 (%struct.SB*)* @structureTypeB, metadata +// CHECK: !"f{si}(s(SB){m(){b(4:si)},m(){b(2:si)},m(N4){b(4:si)}, +// CHECK: m(N2){b(2:si)},m(){b(4:ui)},m(){b(4:si)},m(){b(4:c:si)}, +// CHECK: m(){b(4:c:si)},m(){b(4:cv:si)}})"} +struct S2; +struct S1{struct S2 *ps2;}; +struct S3; +struct S2{struct S3 *ps3;}; +struct S3{struct S1 s1;}; +struct S4{struct S1 s1;}; +void structureType1(struct S1 s1){} +void structureType2(struct S2 s2){} +void structureType3(struct S3 s3){} +void structureType4(struct S4 s4){} +struct {int A;} StructAnon = {1}; +struct SB{int:4; int:2; int N4:4; int N2:2; unsigned int:4; signed int:4; + const int:4; int const :4; volatile const int:4;}; +int structureTypeB(struct SB sb){return StructAnon.A;} + + +// test UnionType +// CHECK: !27 = metadata !{void (%union.U1*)* @unionType1, metadata +// CHECK: !"f{0}(u(U1){m(pu2){p(u(U2){m(pu3){p(u(U3){m(u1){u(U1){}}})}})}})"} +// CHECK: !28 = metadata !{void (%union.U2*)* @unionType2, metadata +// CHECK: !"f{0}(u(U2){m(pu3){p(u(U3){m(u1){u(U1){m(pu2){p(u(U2){})}}}})}})"} +// CHECK: !29 = metadata !{void (%union.U3*)* @unionType3, metadata +// CHECK: !"f{0}(u(U3){m(u1){u(U1){m(pu2){p(u(U2){m(pu3){p(u(U3){})}})}}}})"} +// CHECK: !30 = metadata !{void (%union.U4*)* @unionType4, metadata +// CHECK: !"f{0}(u(U4){m(u1){u(U1){m(pu2){p(u(U2){m(pu3){p(u(U3){m(u1){u(U1){}}})}})}}}})"} +// CHECK: !31 = metadata !{%union.anon* @UnionAnon, metadata !"u(){m(A){si}}"} +// CHECK: !32 = metadata !{i32 (%union.UB*)* @unionTypeB, metadata +// CHECK: !"f{si}(u(UB){m(N2){b(2:si)},m(N4){b(4:si)},m(){b(2:si)}, +// CHECK: m(){b(4:c:si)},m(){b(4:c:si)},m(){b(4:cv:si)},m(){b(4:si)}, +// CHECK: m(){b(4:si)},m(){b(4:ui)}})"} +union U2; +union U1{union U2 *pu2;}; +union U3; +union U2{union U3 *pu3;}; +union U3{union U1 u1;}; +union U4{union U1 u1;}; +void unionType1(union U1 u1) {} +void unionType2(union U2 u2) {} +void unionType3(union U3 u3) {} +void unionType4(union U4 u4) {} +union UB{int:4; int:2; int N4:4; int N2:2; unsigned int:4; signed int:4; + const int:4; int const :4; volatile const int:4;}; +union {int A;} UnionAnon = {1}; +int unionTypeB(union UB ub) {return UnionAnon.A;} + + +// test EnumType +// CHECK: !33 = metadata !{i32* @EnumAnon, metadata !"e(){m(EA){3}}"} +// CHECK: !34 = metadata !{i32 (i32)* @enumType, metadata +// CHECK: !"f{si}(e(E){m(A){0},m(B){1},m(C){5},m(D){6}})"} +enum E {A, B, C=5, D}; +enum {EA=3} EnumAnon = EA; +int enumType(enum E e) {return EnumAnon;}