661 lines · cpp
1//===- XCore.cpp ----------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ABIInfoImpl.h"10#include "TargetInfo.h"11 12using namespace clang;13using namespace clang::CodeGen;14 15//===----------------------------------------------------------------------===//16// XCore ABI Implementation17//===----------------------------------------------------------------------===//18 19namespace {20 21/// A SmallStringEnc instance is used to build up the TypeString by passing22/// it by reference between functions that append to it.23typedef llvm::SmallString<128> SmallStringEnc;24 25/// TypeStringCache caches the meta encodings of Types.26///27/// The reason for caching TypeStrings is two fold:28/// 1. To cache a type's encoding for later uses;29/// 2. As a means to break recursive member type inclusion.30///31/// A cache Entry can have a Status of:32/// NonRecursive: The type encoding is not recursive;33/// Recursive: The type encoding is recursive;34/// Incomplete: An incomplete TypeString;35/// IncompleteUsed: An incomplete TypeString that has been used in a36/// Recursive type encoding.37///38/// A NonRecursive entry will have all of its sub-members expanded as fully39/// as possible. Whilst it may contain types which are recursive, the type40/// itself is not recursive and thus its encoding may be safely used whenever41/// the type is encountered.42///43/// A Recursive entry will have all of its sub-members expanded as fully as44/// possible. The type itself is recursive and it may contain other types which45/// are recursive. The Recursive encoding must not be used during the expansion46/// of a recursive type's recursive branch. For simplicity the code uses47/// IncompleteCount to reject all usage of Recursive encodings for member types.48///49/// An Incomplete entry is always a RecordType and only encodes its50/// identifier e.g. "s(S){}". Incomplete 'StubEnc' entries are ephemeral and51/// are placed into the cache during type expansion as a means to identify and52/// handle recursive inclusion of types as sub-members. If there is recursion53/// the entry becomes IncompleteUsed.54///55/// During the expansion of a RecordType's members:56///57/// If the cache contains a NonRecursive encoding for the member type, the58/// cached encoding is used;59///60/// If the cache contains a Recursive encoding for the member type, the61/// cached encoding is 'Swapped' out, as it may be incorrect, and...62///63/// If the member is a RecordType, an Incomplete encoding is placed into the64/// cache to break potential recursive inclusion of itself as a sub-member;65///66/// Once a member RecordType has been expanded, its temporary incomplete67/// entry is removed from the cache. If a Recursive encoding was swapped out68/// it is swapped back in;69///70/// If an incomplete entry is used to expand a sub-member, the incomplete71/// entry is marked as IncompleteUsed. The cache keeps count of how many72/// IncompleteUsed entries it currently contains in IncompleteUsedCount;73///74/// If a member's encoding is found to be a NonRecursive or Recursive viz:75/// IncompleteUsedCount==0, the member's encoding is added to the cache.76/// Else the member is part of a recursive type and thus the recursion has77/// been exited too soon for the encoding to be correct for the member.78///79class TypeStringCache {80 enum Status {NonRecursive, Recursive, Incomplete, IncompleteUsed};81 struct Entry {82 std::string Str; // The encoded TypeString for the type.83 enum Status State; // Information about the encoding in 'Str'.84 std::string Swapped; // A temporary place holder for a Recursive encoding85 // during the expansion of RecordType's members.86 };87 std::map<const IdentifierInfo *, struct Entry> Map;88 unsigned IncompleteCount; // Number of Incomplete entries in the Map.89 unsigned IncompleteUsedCount; // Number of IncompleteUsed entries in the Map.90public:91 TypeStringCache() : IncompleteCount(0), IncompleteUsedCount(0) {}92 void addIncomplete(const IdentifierInfo *ID, std::string StubEnc);93 bool removeIncomplete(const IdentifierInfo *ID);94 void addIfComplete(const IdentifierInfo *ID, StringRef Str,95 bool IsRecursive);96 StringRef lookupStr(const IdentifierInfo *ID);97};98 99/// TypeString encodings for enum & union fields must be order.100/// FieldEncoding is a helper for this ordering process.101class FieldEncoding {102 bool HasName;103 std::string Enc;104public:105 FieldEncoding(bool b, SmallStringEnc &e) : HasName(b), Enc(e.c_str()) {}106 StringRef str() { return Enc; }107 bool operator<(const FieldEncoding &rhs) const {108 if (HasName != rhs.HasName) return HasName;109 return Enc < rhs.Enc;110 }111};112 113class XCoreABIInfo : public DefaultABIInfo {114public:115 XCoreABIInfo(CodeGen::CodeGenTypes &CGT) : DefaultABIInfo(CGT) {}116 RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,117 AggValueSlot Slot) const override;118};119 120class XCoreTargetCodeGenInfo : public TargetCodeGenInfo {121 mutable TypeStringCache TSC;122 void emitTargetMD(const Decl *D, llvm::GlobalValue *GV,123 const CodeGen::CodeGenModule &M) const;124 125public:126 XCoreTargetCodeGenInfo(CodeGenTypes &CGT)127 : TargetCodeGenInfo(std::make_unique<XCoreABIInfo>(CGT)) {}128 void emitTargetMetadata(CodeGen::CodeGenModule &CGM,129 const llvm::MapVector<GlobalDecl, StringRef>130 &MangledDeclNames) const override;131};132 133} // End anonymous namespace.134 135// TODO: this implementation is likely now redundant with the default136// EmitVAArg.137RValue XCoreABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,138 QualType Ty, AggValueSlot Slot) const {139 CGBuilderTy &Builder = CGF.Builder;140 141 // Get the VAList.142 CharUnits SlotSize = CharUnits::fromQuantity(4);143 Address AP = Address(Builder.CreateLoad(VAListAddr),144 getVAListElementType(CGF), SlotSize);145 146 // Handle the argument.147 ABIArgInfo AI = classifyArgumentType(Ty);148 CharUnits TypeAlign = getContext().getTypeAlignInChars(Ty);149 llvm::Type *ArgTy = CGT.ConvertType(Ty);150 if (AI.canHaveCoerceToType() && !AI.getCoerceToType())151 AI.setCoerceToType(ArgTy);152 llvm::Type *ArgPtrTy = llvm::PointerType::getUnqual(ArgTy->getContext());153 154 Address Val = Address::invalid();155 CharUnits ArgSize = CharUnits::Zero();156 switch (AI.getKind()) {157 case ABIArgInfo::Expand:158 case ABIArgInfo::CoerceAndExpand:159 case ABIArgInfo::InAlloca:160 case ABIArgInfo::TargetSpecific:161 llvm_unreachable("Unsupported ABI kind for va_arg");162 case ABIArgInfo::Ignore:163 Val = Address(llvm::UndefValue::get(ArgPtrTy), ArgTy, TypeAlign);164 ArgSize = CharUnits::Zero();165 break;166 case ABIArgInfo::Extend:167 case ABIArgInfo::Direct:168 Val = AP.withElementType(ArgTy);169 ArgSize = CharUnits::fromQuantity(170 getDataLayout().getTypeAllocSize(AI.getCoerceToType()));171 ArgSize = ArgSize.alignTo(SlotSize);172 break;173 case ABIArgInfo::Indirect:174 case ABIArgInfo::IndirectAliased:175 Val = AP.withElementType(ArgPtrTy);176 Val = Address(Builder.CreateLoad(Val), ArgTy, TypeAlign);177 ArgSize = SlotSize;178 break;179 }180 181 // Increment the VAList.182 if (!ArgSize.isZero()) {183 Address APN = Builder.CreateConstInBoundsByteGEP(AP, ArgSize);184 Builder.CreateStore(APN.emitRawPointer(CGF), VAListAddr);185 }186 187 return CGF.EmitLoadOfAnyValue(CGF.MakeAddrLValue(Val, Ty), Slot);188}189 190/// During the expansion of a RecordType, an incomplete TypeString is placed191/// into the cache as a means to identify and break recursion.192/// If there is a Recursive encoding in the cache, it is swapped out and will193/// be reinserted by removeIncomplete().194/// All other types of encoding should have been used rather than arriving here.195void TypeStringCache::addIncomplete(const IdentifierInfo *ID,196 std::string StubEnc) {197 if (!ID)198 return;199 Entry &E = Map[ID];200 assert( (E.Str.empty() || E.State == Recursive) &&201 "Incorrectly use of addIncomplete");202 assert(!StubEnc.empty() && "Passing an empty string to addIncomplete()");203 E.Swapped.swap(E.Str); // swap out the Recursive204 E.Str.swap(StubEnc);205 E.State = Incomplete;206 ++IncompleteCount;207}208 209/// Once the RecordType has been expanded, the temporary incomplete TypeString210/// must be removed from the cache.211/// If a Recursive was swapped out by addIncomplete(), it will be replaced.212/// Returns true if the RecordType was defined recursively.213bool TypeStringCache::removeIncomplete(const IdentifierInfo *ID) {214 if (!ID)215 return false;216 auto I = Map.find(ID);217 assert(I != Map.end() && "Entry not present");218 Entry &E = I->second;219 assert( (E.State == Incomplete ||220 E.State == IncompleteUsed) &&221 "Entry must be an incomplete type");222 bool IsRecursive = false;223 if (E.State == IncompleteUsed) {224 // We made use of our Incomplete encoding, thus we are recursive.225 IsRecursive = true;226 --IncompleteUsedCount;227 }228 if (E.Swapped.empty())229 Map.erase(I);230 else {231 // Swap the Recursive back.232 E.Swapped.swap(E.Str);233 E.Swapped.clear();234 E.State = Recursive;235 }236 --IncompleteCount;237 return IsRecursive;238}239 240/// Add the encoded TypeString to the cache only if it is NonRecursive or241/// Recursive (viz: all sub-members were expanded as fully as possible).242void TypeStringCache::addIfComplete(const IdentifierInfo *ID, StringRef Str,243 bool IsRecursive) {244 if (!ID || IncompleteUsedCount)245 return; // No key or it is an incomplete sub-type so don't add.246 Entry &E = Map[ID];247 if (IsRecursive && !E.Str.empty()) {248 assert(E.State==Recursive && E.Str.size() == Str.size() &&249 "This is not the same Recursive entry");250 // The parent container was not recursive after all, so we could have used251 // this Recursive sub-member entry after all, but we assumed the worse when252 // we started viz: IncompleteCount!=0.253 return;254 }255 assert(E.Str.empty() && "Entry already present");256 E.Str = Str.str();257 E.State = IsRecursive? Recursive : NonRecursive;258}259 260/// Return a cached TypeString encoding for the ID. If there isn't one, or we261/// are recursively expanding a type (IncompleteCount != 0) and the cached262/// encoding is Recursive, return an empty StringRef.263StringRef TypeStringCache::lookupStr(const IdentifierInfo *ID) {264 if (!ID)265 return StringRef(); // We have no key.266 auto I = Map.find(ID);267 if (I == Map.end())268 return StringRef(); // We have no encoding.269 Entry &E = I->second;270 if (E.State == Recursive && IncompleteCount)271 return StringRef(); // We don't use Recursive encodings for member types.272 273 if (E.State == Incomplete) {274 // The incomplete type is being used to break out of recursion.275 E.State = IncompleteUsed;276 ++IncompleteUsedCount;277 }278 return E.Str;279}280 281/// The XCore ABI includes a type information section that communicates symbol282/// type information to the linker. The linker uses this information to verify283/// safety/correctness of things such as array bound and pointers et al.284/// The ABI only requires C (and XC) language modules to emit TypeStrings.285/// This type information (TypeString) is emitted into meta data for all global286/// symbols: definitions, declarations, functions & variables.287///288/// The TypeString carries type, qualifier, name, size & value details.289/// Please see 'Tools Development Guide' section 2.16.2 for format details:290/// https://www.xmos.com/download/public/Tools-Development-Guide%28X9114A%29.pdf291/// The output is tested by test/CodeGen/xcore-stringtype.c.292///293static bool getTypeString(SmallStringEnc &Enc, const Decl *D,294 const CodeGen::CodeGenModule &CGM,295 TypeStringCache &TSC);296 297/// XCore uses emitTargetMD to emit TypeString metadata for global symbols.298void XCoreTargetCodeGenInfo::emitTargetMD(299 const Decl *D, llvm::GlobalValue *GV,300 const CodeGen::CodeGenModule &CGM) const {301 SmallStringEnc Enc;302 if (getTypeString(Enc, D, CGM, TSC)) {303 llvm::LLVMContext &Ctx = CGM.getModule().getContext();304 llvm::Metadata *MDVals[] = {llvm::ConstantAsMetadata::get(GV),305 llvm::MDString::get(Ctx, Enc.str())};306 llvm::NamedMDNode *MD =307 CGM.getModule().getOrInsertNamedMetadata("xcore.typestrings");308 MD->addOperand(llvm::MDNode::get(Ctx, MDVals));309 }310}311 312void XCoreTargetCodeGenInfo::emitTargetMetadata(313 CodeGen::CodeGenModule &CGM,314 const llvm::MapVector<GlobalDecl, StringRef> &MangledDeclNames) const {315 // Warning, new MangledDeclNames may be appended within this loop.316 // We rely on MapVector insertions adding new elements to the end317 // of the container.318 for (unsigned I = 0; I != MangledDeclNames.size(); ++I) {319 auto Val = *(MangledDeclNames.begin() + I);320 llvm::GlobalValue *GV = CGM.GetGlobalValue(Val.second);321 if (GV) {322 const Decl *D = Val.first.getDecl()->getMostRecentDecl();323 emitTargetMD(D, GV, CGM);324 }325 }326}327 328static bool appendType(SmallStringEnc &Enc, QualType QType,329 const CodeGen::CodeGenModule &CGM,330 TypeStringCache &TSC);331 332/// Helper function for appendRecordType().333/// Builds a SmallVector containing the encoded field types in declaration334/// order.335static bool extractFieldType(SmallVectorImpl<FieldEncoding> &FE,336 const RecordDecl *RD,337 const CodeGen::CodeGenModule &CGM,338 TypeStringCache &TSC) {339 for (const auto *Field : RD->fields()) {340 SmallStringEnc Enc;341 Enc += "m(";342 Enc += Field->getName();343 Enc += "){";344 if (Field->isBitField()) {345 Enc += "b(";346 llvm::raw_svector_ostream OS(Enc);347 OS << Field->getBitWidthValue();348 Enc += ':';349 }350 if (!appendType(Enc, Field->getType(), CGM, TSC))351 return false;352 if (Field->isBitField())353 Enc += ')';354 Enc += '}';355 FE.emplace_back(!Field->getName().empty(), Enc);356 }357 return true;358}359 360/// Appends structure and union types to Enc and adds encoding to cache.361/// Recursively calls appendType (via extractFieldType) for each field.362/// Union types have their fields ordered according to the ABI.363static bool appendRecordType(SmallStringEnc &Enc, const RecordType *RT,364 const CodeGen::CodeGenModule &CGM,365 TypeStringCache &TSC, const IdentifierInfo *ID) {366 // Append the cached TypeString if we have one.367 StringRef TypeString = TSC.lookupStr(ID);368 if (!TypeString.empty()) {369 Enc += TypeString;370 return true;371 }372 373 // Start to emit an incomplete TypeString.374 size_t Start = Enc.size();375 Enc += (RT->isUnionType()? 'u' : 's');376 Enc += '(';377 if (ID)378 Enc += ID->getName();379 Enc += "){";380 381 // We collect all encoded fields and order as necessary.382 bool IsRecursive = false;383 const RecordDecl *RD = RT->getDecl()->getDefinition();384 if (RD && !RD->field_empty()) {385 // An incomplete TypeString stub is placed in the cache for this RecordType386 // so that recursive calls to this RecordType will use it whilst building a387 // complete TypeString for this RecordType.388 SmallVector<FieldEncoding, 16> FE;389 std::string StubEnc(Enc.substr(Start).str());390 StubEnc += '}'; // StubEnc now holds a valid incomplete TypeString.391 TSC.addIncomplete(ID, std::move(StubEnc));392 if (!extractFieldType(FE, RD, CGM, TSC)) {393 (void) TSC.removeIncomplete(ID);394 return false;395 }396 IsRecursive = TSC.removeIncomplete(ID);397 // The ABI requires unions to be sorted but not structures.398 // See FieldEncoding::operator< for sort algorithm.399 if (RT->isUnionType())400 llvm::sort(FE);401 // We can now complete the TypeString.402 unsigned E = FE.size();403 for (unsigned I = 0; I != E; ++I) {404 if (I)405 Enc += ',';406 Enc += FE[I].str();407 }408 }409 Enc += '}';410 TSC.addIfComplete(ID, Enc.substr(Start), IsRecursive);411 return true;412}413 414/// Appends enum types to Enc and adds the encoding to the cache.415static bool appendEnumType(SmallStringEnc &Enc, const EnumType *ET,416 TypeStringCache &TSC,417 const IdentifierInfo *ID) {418 // Append the cached TypeString if we have one.419 StringRef TypeString = TSC.lookupStr(ID);420 if (!TypeString.empty()) {421 Enc += TypeString;422 return true;423 }424 425 size_t Start = Enc.size();426 Enc += "e(";427 if (ID)428 Enc += ID->getName();429 Enc += "){";430 431 // We collect all encoded enumerations and order them alphanumerically.432 if (const EnumDecl *ED = ET->getDecl()->getDefinition()) {433 SmallVector<FieldEncoding, 16> FE;434 for (auto I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E;435 ++I) {436 SmallStringEnc EnumEnc;437 EnumEnc += "m(";438 EnumEnc += I->getName();439 EnumEnc += "){";440 I->getInitVal().toString(EnumEnc);441 EnumEnc += '}';442 FE.push_back(FieldEncoding(!I->getName().empty(), EnumEnc));443 }444 llvm::sort(FE);445 unsigned E = FE.size();446 for (unsigned I = 0; I != E; ++I) {447 if (I)448 Enc += ',';449 Enc += FE[I].str();450 }451 }452 Enc += '}';453 TSC.addIfComplete(ID, Enc.substr(Start), false);454 return true;455}456 457/// Appends type's qualifier to Enc.458/// This is done prior to appending the type's encoding.459static void appendQualifier(SmallStringEnc &Enc, QualType QT) {460 // Qualifiers are emitted in alphabetical order.461 static const char *const Table[]={"","c:","r:","cr:","v:","cv:","rv:","crv:"};462 int Lookup = 0;463 if (QT.isConstQualified())464 Lookup += 1<<0;465 if (QT.isRestrictQualified())466 Lookup += 1<<1;467 if (QT.isVolatileQualified())468 Lookup += 1<<2;469 Enc += Table[Lookup];470}471 472/// Appends built-in types to Enc.473static bool appendBuiltinType(SmallStringEnc &Enc, const BuiltinType *BT) {474 const char *EncType;475 switch (BT->getKind()) {476 case BuiltinType::Void:477 EncType = "0";478 break;479 case BuiltinType::Bool:480 EncType = "b";481 break;482 case BuiltinType::Char_U:483 EncType = "uc";484 break;485 case BuiltinType::UChar:486 EncType = "uc";487 break;488 case BuiltinType::SChar:489 EncType = "sc";490 break;491 case BuiltinType::UShort:492 EncType = "us";493 break;494 case BuiltinType::Short:495 EncType = "ss";496 break;497 case BuiltinType::UInt:498 EncType = "ui";499 break;500 case BuiltinType::Int:501 EncType = "si";502 break;503 case BuiltinType::ULong:504 EncType = "ul";505 break;506 case BuiltinType::Long:507 EncType = "sl";508 break;509 case BuiltinType::ULongLong:510 EncType = "ull";511 break;512 case BuiltinType::LongLong:513 EncType = "sll";514 break;515 case BuiltinType::Float:516 EncType = "ft";517 break;518 case BuiltinType::Double:519 EncType = "d";520 break;521 case BuiltinType::LongDouble:522 EncType = "ld";523 break;524 default:525 return false;526 }527 Enc += EncType;528 return true;529}530 531/// Appends a pointer encoding to Enc before calling appendType for the pointee.532static bool appendPointerType(SmallStringEnc &Enc, const PointerType *PT,533 const CodeGen::CodeGenModule &CGM,534 TypeStringCache &TSC) {535 Enc += "p(";536 if (!appendType(Enc, PT->getPointeeType(), CGM, TSC))537 return false;538 Enc += ')';539 return true;540}541 542/// Appends array encoding to Enc before calling appendType for the element.543static bool appendArrayType(SmallStringEnc &Enc, QualType QT,544 const ArrayType *AT,545 const CodeGen::CodeGenModule &CGM,546 TypeStringCache &TSC, StringRef NoSizeEnc) {547 if (AT->getSizeModifier() != ArraySizeModifier::Normal)548 return false;549 Enc += "a(";550 if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT))551 CAT->getSize().toStringUnsigned(Enc);552 else553 Enc += NoSizeEnc; // Global arrays use "*", otherwise it is "".554 Enc += ':';555 // The Qualifiers should be attached to the type rather than the array.556 appendQualifier(Enc, QT);557 if (!appendType(Enc, AT->getElementType(), CGM, TSC))558 return false;559 Enc += ')';560 return true;561}562 563/// Appends a function encoding to Enc, calling appendType for the return type564/// and the arguments.565static bool appendFunctionType(SmallStringEnc &Enc, const FunctionType *FT,566 const CodeGen::CodeGenModule &CGM,567 TypeStringCache &TSC) {568 Enc += "f{";569 if (!appendType(Enc, FT->getReturnType(), CGM, TSC))570 return false;571 Enc += "}(";572 if (const FunctionProtoType *FPT = FT->getAs<FunctionProtoType>()) {573 // N.B. we are only interested in the adjusted param types.574 auto I = FPT->param_type_begin();575 auto E = FPT->param_type_end();576 if (I != E) {577 do {578 if (!appendType(Enc, *I, CGM, TSC))579 return false;580 ++I;581 if (I != E)582 Enc += ',';583 } while (I != E);584 if (FPT->isVariadic())585 Enc += ",va";586 } else {587 if (FPT->isVariadic())588 Enc += "va";589 else590 Enc += '0';591 }592 }593 Enc += ')';594 return true;595}596 597/// Handles the type's qualifier before dispatching a call to handle specific598/// type encodings.599static bool appendType(SmallStringEnc &Enc, QualType QType,600 const CodeGen::CodeGenModule &CGM,601 TypeStringCache &TSC) {602 603 QualType QT = QType.getCanonicalType();604 605 if (const ArrayType *AT = QT->getAsArrayTypeUnsafe())606 // The Qualifiers should be attached to the type rather than the array.607 // Thus we don't call appendQualifier() here.608 return appendArrayType(Enc, QT, AT, CGM, TSC, "");609 610 appendQualifier(Enc, QT);611 612 if (const BuiltinType *BT = QT->getAs<BuiltinType>())613 return appendBuiltinType(Enc, BT);614 615 if (const PointerType *PT = QT->getAs<PointerType>())616 return appendPointerType(Enc, PT, CGM, TSC);617 618 if (const EnumType *ET = QT->getAsCanonical<EnumType>())619 return appendEnumType(Enc, ET, TSC, QT.getBaseTypeIdentifier());620 621 if (const RecordType *RT = QT->getAsCanonical<RecordType>())622 return appendRecordType(Enc, RT, CGM, TSC, QT.getBaseTypeIdentifier());623 624 if (const FunctionType *FT = QT->getAs<FunctionType>())625 return appendFunctionType(Enc, FT, CGM, TSC);626 627 return false;628}629 630static bool getTypeString(SmallStringEnc &Enc, const Decl *D,631 const CodeGen::CodeGenModule &CGM,632 TypeStringCache &TSC) {633 if (!D)634 return false;635 636 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {637 if (FD->getLanguageLinkage() != CLanguageLinkage)638 return false;639 return appendType(Enc, FD->getType(), CGM, TSC);640 }641 642 if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {643 if (VD->getLanguageLinkage() != CLanguageLinkage)644 return false;645 QualType QT = VD->getType().getCanonicalType();646 if (const ArrayType *AT = QT->getAsArrayTypeUnsafe()) {647 // Global ArrayTypes are given a size of '*' if the size is unknown.648 // The Qualifiers should be attached to the type rather than the array.649 // Thus we don't call appendQualifier() here.650 return appendArrayType(Enc, QT, AT, CGM, TSC, "*");651 }652 return appendType(Enc, QT, CGM, TSC);653 }654 return false;655}656 657std::unique_ptr<TargetCodeGenInfo>658CodeGen::createXCoreTargetCodeGenInfo(CodeGenModule &CGM) {659 return std::make_unique<XCoreTargetCodeGenInfo>(CGM.getTypes());660}661