1*a7dea167SDimitry Andric //===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===// 2*a7dea167SDimitry Andric // 3*a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*a7dea167SDimitry Andric // 7*a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8*a7dea167SDimitry Andric 9*a7dea167SDimitry Andric #include "Program.h" 10*a7dea167SDimitry Andric #include "ByteCodeStmtGen.h" 11*a7dea167SDimitry Andric #include "Context.h" 12*a7dea167SDimitry Andric #include "Function.h" 13*a7dea167SDimitry Andric #include "Opcode.h" 14*a7dea167SDimitry Andric #include "PrimType.h" 15*a7dea167SDimitry Andric #include "clang/AST/Decl.h" 16*a7dea167SDimitry Andric #include "clang/AST/DeclCXX.h" 17*a7dea167SDimitry Andric 18*a7dea167SDimitry Andric using namespace clang; 19*a7dea167SDimitry Andric using namespace clang::interp; 20*a7dea167SDimitry Andric 21*a7dea167SDimitry Andric unsigned Program::createGlobalString(const StringLiteral *S) { 22*a7dea167SDimitry Andric const size_t CharWidth = S->getCharByteWidth(); 23*a7dea167SDimitry Andric const size_t BitWidth = CharWidth * Ctx.getCharBit(); 24*a7dea167SDimitry Andric 25*a7dea167SDimitry Andric PrimType CharType; 26*a7dea167SDimitry Andric switch (CharWidth) { 27*a7dea167SDimitry Andric case 1: 28*a7dea167SDimitry Andric CharType = PT_Sint8; 29*a7dea167SDimitry Andric break; 30*a7dea167SDimitry Andric case 2: 31*a7dea167SDimitry Andric CharType = PT_Uint16; 32*a7dea167SDimitry Andric break; 33*a7dea167SDimitry Andric case 4: 34*a7dea167SDimitry Andric CharType = PT_Uint32; 35*a7dea167SDimitry Andric break; 36*a7dea167SDimitry Andric default: 37*a7dea167SDimitry Andric llvm_unreachable("unsupported character width"); 38*a7dea167SDimitry Andric } 39*a7dea167SDimitry Andric 40*a7dea167SDimitry Andric // Create a descriptor for the string. 41*a7dea167SDimitry Andric Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, 42*a7dea167SDimitry Andric /*isConst=*/true, 43*a7dea167SDimitry Andric /*isTemporary=*/false, 44*a7dea167SDimitry Andric /*isMutable=*/false); 45*a7dea167SDimitry Andric 46*a7dea167SDimitry Andric // Allocate storage for the string. 47*a7dea167SDimitry Andric // The byte length does not include the null terminator. 48*a7dea167SDimitry Andric unsigned I = Globals.size(); 49*a7dea167SDimitry Andric unsigned Sz = Desc->getAllocSize(); 50*a7dea167SDimitry Andric auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, 51*a7dea167SDimitry Andric /*isExtern=*/false); 52*a7dea167SDimitry Andric Globals.push_back(G); 53*a7dea167SDimitry Andric 54*a7dea167SDimitry Andric // Construct the string in storage. 55*a7dea167SDimitry Andric const Pointer Ptr(G->block()); 56*a7dea167SDimitry Andric for (unsigned I = 0, N = S->getLength(); I <= N; ++I) { 57*a7dea167SDimitry Andric Pointer Field = Ptr.atIndex(I).narrow(); 58*a7dea167SDimitry Andric const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I); 59*a7dea167SDimitry Andric switch (CharType) { 60*a7dea167SDimitry Andric case PT_Sint8: { 61*a7dea167SDimitry Andric using T = PrimConv<PT_Sint8>::T; 62*a7dea167SDimitry Andric Field.deref<T>() = T::from(CodePoint, BitWidth); 63*a7dea167SDimitry Andric break; 64*a7dea167SDimitry Andric } 65*a7dea167SDimitry Andric case PT_Uint16: { 66*a7dea167SDimitry Andric using T = PrimConv<PT_Uint16>::T; 67*a7dea167SDimitry Andric Field.deref<T>() = T::from(CodePoint, BitWidth); 68*a7dea167SDimitry Andric break; 69*a7dea167SDimitry Andric } 70*a7dea167SDimitry Andric case PT_Uint32: { 71*a7dea167SDimitry Andric using T = PrimConv<PT_Uint32>::T; 72*a7dea167SDimitry Andric Field.deref<T>() = T::from(CodePoint, BitWidth); 73*a7dea167SDimitry Andric break; 74*a7dea167SDimitry Andric } 75*a7dea167SDimitry Andric default: 76*a7dea167SDimitry Andric llvm_unreachable("unsupported character type"); 77*a7dea167SDimitry Andric } 78*a7dea167SDimitry Andric } 79*a7dea167SDimitry Andric return I; 80*a7dea167SDimitry Andric } 81*a7dea167SDimitry Andric 82*a7dea167SDimitry Andric Pointer Program::getPtrGlobal(unsigned Idx) { 83*a7dea167SDimitry Andric assert(Idx < Globals.size()); 84*a7dea167SDimitry Andric return Pointer(Globals[Idx]->block()); 85*a7dea167SDimitry Andric } 86*a7dea167SDimitry Andric 87*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { 88*a7dea167SDimitry Andric auto It = GlobalIndices.find(VD); 89*a7dea167SDimitry Andric if (It != GlobalIndices.end()) 90*a7dea167SDimitry Andric return It->second; 91*a7dea167SDimitry Andric 92*a7dea167SDimitry Andric // Find any previous declarations which were aleady evaluated. 93*a7dea167SDimitry Andric llvm::Optional<unsigned> Index; 94*a7dea167SDimitry Andric for (const Decl *P = VD; P; P = P->getPreviousDecl()) { 95*a7dea167SDimitry Andric auto It = GlobalIndices.find(P); 96*a7dea167SDimitry Andric if (It != GlobalIndices.end()) { 97*a7dea167SDimitry Andric Index = It->second; 98*a7dea167SDimitry Andric break; 99*a7dea167SDimitry Andric } 100*a7dea167SDimitry Andric } 101*a7dea167SDimitry Andric 102*a7dea167SDimitry Andric // Map the decl to the existing index. 103*a7dea167SDimitry Andric if (Index) { 104*a7dea167SDimitry Andric GlobalIndices[VD] = *Index; 105*a7dea167SDimitry Andric return {}; 106*a7dea167SDimitry Andric } 107*a7dea167SDimitry Andric 108*a7dea167SDimitry Andric return Index; 109*a7dea167SDimitry Andric } 110*a7dea167SDimitry Andric 111*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) { 112*a7dea167SDimitry Andric if (auto Idx = getGlobal(VD)) 113*a7dea167SDimitry Andric return Idx; 114*a7dea167SDimitry Andric 115*a7dea167SDimitry Andric if (auto Idx = createGlobal(VD)) { 116*a7dea167SDimitry Andric GlobalIndices[VD] = *Idx; 117*a7dea167SDimitry Andric return Idx; 118*a7dea167SDimitry Andric } 119*a7dea167SDimitry Andric return {}; 120*a7dea167SDimitry Andric } 121*a7dea167SDimitry Andric 122*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { 123*a7dea167SDimitry Andric auto &ASTCtx = Ctx.getASTContext(); 124*a7dea167SDimitry Andric 125*a7dea167SDimitry Andric // Create a pointer to an incomplete array of the specified elements. 126*a7dea167SDimitry Andric QualType ElemTy = PD->getType()->castAs<PointerType>()->getPointeeType(); 127*a7dea167SDimitry Andric QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0); 128*a7dea167SDimitry Andric 129*a7dea167SDimitry Andric // Dedup blocks since they are immutable and pointers cannot be compared. 130*a7dea167SDimitry Andric auto It = DummyParams.find(PD); 131*a7dea167SDimitry Andric if (It != DummyParams.end()) 132*a7dea167SDimitry Andric return It->second; 133*a7dea167SDimitry Andric 134*a7dea167SDimitry Andric if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) { 135*a7dea167SDimitry Andric DummyParams[PD] = *Idx; 136*a7dea167SDimitry Andric return Idx; 137*a7dea167SDimitry Andric } 138*a7dea167SDimitry Andric return {}; 139*a7dea167SDimitry Andric } 140*a7dea167SDimitry Andric 141*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { 142*a7dea167SDimitry Andric bool IsStatic, IsExtern; 143*a7dea167SDimitry Andric if (auto *Var = dyn_cast<VarDecl>(VD)) { 144*a7dea167SDimitry Andric IsStatic = !Var->hasLocalStorage(); 145*a7dea167SDimitry Andric IsExtern = !Var->getAnyInitializer(); 146*a7dea167SDimitry Andric } else { 147*a7dea167SDimitry Andric IsStatic = false; 148*a7dea167SDimitry Andric IsExtern = true; 149*a7dea167SDimitry Andric } 150*a7dea167SDimitry Andric if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) { 151*a7dea167SDimitry Andric for (const Decl *P = VD; P; P = P->getPreviousDecl()) 152*a7dea167SDimitry Andric GlobalIndices[P] = *Idx; 153*a7dea167SDimitry Andric return *Idx; 154*a7dea167SDimitry Andric } 155*a7dea167SDimitry Andric return {}; 156*a7dea167SDimitry Andric } 157*a7dea167SDimitry Andric 158*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::createGlobal(const Expr *E) { 159*a7dea167SDimitry Andric return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false); 160*a7dea167SDimitry Andric } 161*a7dea167SDimitry Andric 162*a7dea167SDimitry Andric llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, 163*a7dea167SDimitry Andric bool IsStatic, bool IsExtern) { 164*a7dea167SDimitry Andric // Create a descriptor for the global. 165*a7dea167SDimitry Andric Descriptor *Desc; 166*a7dea167SDimitry Andric const bool IsConst = Ty.isConstQualified(); 167*a7dea167SDimitry Andric const bool IsTemporary = D.dyn_cast<const Expr *>(); 168*a7dea167SDimitry Andric if (auto T = Ctx.classify(Ty)) { 169*a7dea167SDimitry Andric Desc = createDescriptor(D, *T, IsConst, IsTemporary); 170*a7dea167SDimitry Andric } else { 171*a7dea167SDimitry Andric Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); 172*a7dea167SDimitry Andric } 173*a7dea167SDimitry Andric if (!Desc) 174*a7dea167SDimitry Andric return {}; 175*a7dea167SDimitry Andric 176*a7dea167SDimitry Andric // Allocate a block for storage. 177*a7dea167SDimitry Andric unsigned I = Globals.size(); 178*a7dea167SDimitry Andric 179*a7dea167SDimitry Andric auto *G = new (Allocator, Desc->getAllocSize()) 180*a7dea167SDimitry Andric Global(getCurrentDecl(), Desc, IsStatic, IsExtern); 181*a7dea167SDimitry Andric G->block()->invokeCtor(); 182*a7dea167SDimitry Andric 183*a7dea167SDimitry Andric Globals.push_back(G); 184*a7dea167SDimitry Andric 185*a7dea167SDimitry Andric return I; 186*a7dea167SDimitry Andric } 187*a7dea167SDimitry Andric 188*a7dea167SDimitry Andric Function *Program::getFunction(const FunctionDecl *F) { 189*a7dea167SDimitry Andric F = F->getDefinition(); 190*a7dea167SDimitry Andric auto It = Funcs.find(F); 191*a7dea167SDimitry Andric return It == Funcs.end() ? nullptr : It->second.get(); 192*a7dea167SDimitry Andric } 193*a7dea167SDimitry Andric 194*a7dea167SDimitry Andric llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) { 195*a7dea167SDimitry Andric if (Function *Func = getFunction(F)) { 196*a7dea167SDimitry Andric return Func; 197*a7dea167SDimitry Andric } 198*a7dea167SDimitry Andric 199*a7dea167SDimitry Andric // Try to compile the function if it wasn't compiled yet. 200*a7dea167SDimitry Andric if (const FunctionDecl *FD = F->getDefinition()) 201*a7dea167SDimitry Andric return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD); 202*a7dea167SDimitry Andric 203*a7dea167SDimitry Andric // A relocation which traps if not resolved. 204*a7dea167SDimitry Andric return nullptr; 205*a7dea167SDimitry Andric } 206*a7dea167SDimitry Andric 207*a7dea167SDimitry Andric Record *Program::getOrCreateRecord(const RecordDecl *RD) { 208*a7dea167SDimitry Andric // Use the actual definition as a key. 209*a7dea167SDimitry Andric RD = RD->getDefinition(); 210*a7dea167SDimitry Andric if (!RD) 211*a7dea167SDimitry Andric return nullptr; 212*a7dea167SDimitry Andric 213*a7dea167SDimitry Andric // Deduplicate records. 214*a7dea167SDimitry Andric auto It = Records.find(RD); 215*a7dea167SDimitry Andric if (It != Records.end()) { 216*a7dea167SDimitry Andric return It->second; 217*a7dea167SDimitry Andric } 218*a7dea167SDimitry Andric 219*a7dea167SDimitry Andric // Number of bytes required by fields and base classes. 220*a7dea167SDimitry Andric unsigned Size = 0; 221*a7dea167SDimitry Andric // Number of bytes required by virtual base. 222*a7dea167SDimitry Andric unsigned VirtSize = 0; 223*a7dea167SDimitry Andric 224*a7dea167SDimitry Andric // Helper to get a base descriptor. 225*a7dea167SDimitry Andric auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { 226*a7dea167SDimitry Andric if (!BR) 227*a7dea167SDimitry Andric return nullptr; 228*a7dea167SDimitry Andric return allocateDescriptor(BD, BR, /*isConst=*/false, 229*a7dea167SDimitry Andric /*isTemporary=*/false, 230*a7dea167SDimitry Andric /*isMutable=*/false); 231*a7dea167SDimitry Andric }; 232*a7dea167SDimitry Andric 233*a7dea167SDimitry Andric // Reserve space for base classes. 234*a7dea167SDimitry Andric Record::BaseList Bases; 235*a7dea167SDimitry Andric Record::VirtualBaseList VirtBases; 236*a7dea167SDimitry Andric if (auto *CD = dyn_cast<CXXRecordDecl>(RD)) { 237*a7dea167SDimitry Andric for (const CXXBaseSpecifier &Spec : CD->bases()) { 238*a7dea167SDimitry Andric if (Spec.isVirtual()) 239*a7dea167SDimitry Andric continue; 240*a7dea167SDimitry Andric 241*a7dea167SDimitry Andric const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl(); 242*a7dea167SDimitry Andric Record *BR = getOrCreateRecord(BD); 243*a7dea167SDimitry Andric if (Descriptor *Desc = GetBaseDesc(BD, BR)) { 244*a7dea167SDimitry Andric Size += align(sizeof(InlineDescriptor)); 245*a7dea167SDimitry Andric Bases.push_back({BD, Size, Desc, BR}); 246*a7dea167SDimitry Andric Size += align(BR->getSize()); 247*a7dea167SDimitry Andric continue; 248*a7dea167SDimitry Andric } 249*a7dea167SDimitry Andric return nullptr; 250*a7dea167SDimitry Andric } 251*a7dea167SDimitry Andric 252*a7dea167SDimitry Andric for (const CXXBaseSpecifier &Spec : CD->vbases()) { 253*a7dea167SDimitry Andric const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl(); 254*a7dea167SDimitry Andric Record *BR = getOrCreateRecord(BD); 255*a7dea167SDimitry Andric 256*a7dea167SDimitry Andric if (Descriptor *Desc = GetBaseDesc(BD, BR)) { 257*a7dea167SDimitry Andric VirtSize += align(sizeof(InlineDescriptor)); 258*a7dea167SDimitry Andric VirtBases.push_back({BD, VirtSize, Desc, BR}); 259*a7dea167SDimitry Andric VirtSize += align(BR->getSize()); 260*a7dea167SDimitry Andric continue; 261*a7dea167SDimitry Andric } 262*a7dea167SDimitry Andric return nullptr; 263*a7dea167SDimitry Andric } 264*a7dea167SDimitry Andric } 265*a7dea167SDimitry Andric 266*a7dea167SDimitry Andric // Reserve space for fields. 267*a7dea167SDimitry Andric Record::FieldList Fields; 268*a7dea167SDimitry Andric for (const FieldDecl *FD : RD->fields()) { 269*a7dea167SDimitry Andric // Reserve space for the field's descriptor and the offset. 270*a7dea167SDimitry Andric Size += align(sizeof(InlineDescriptor)); 271*a7dea167SDimitry Andric 272*a7dea167SDimitry Andric // Classify the field and add its metadata. 273*a7dea167SDimitry Andric QualType FT = FD->getType(); 274*a7dea167SDimitry Andric const bool IsConst = FT.isConstQualified(); 275*a7dea167SDimitry Andric const bool IsMutable = FD->isMutable(); 276*a7dea167SDimitry Andric Descriptor *Desc; 277*a7dea167SDimitry Andric if (llvm::Optional<PrimType> T = Ctx.classify(FT)) { 278*a7dea167SDimitry Andric Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, 279*a7dea167SDimitry Andric IsMutable); 280*a7dea167SDimitry Andric } else { 281*a7dea167SDimitry Andric Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, 282*a7dea167SDimitry Andric /*isTemporary=*/false, IsMutable); 283*a7dea167SDimitry Andric } 284*a7dea167SDimitry Andric if (!Desc) 285*a7dea167SDimitry Andric return nullptr; 286*a7dea167SDimitry Andric Fields.push_back({FD, Size, Desc}); 287*a7dea167SDimitry Andric Size += align(Desc->getAllocSize()); 288*a7dea167SDimitry Andric } 289*a7dea167SDimitry Andric 290*a7dea167SDimitry Andric Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields), 291*a7dea167SDimitry Andric std::move(VirtBases), VirtSize, Size); 292*a7dea167SDimitry Andric Records.insert({RD, R}); 293*a7dea167SDimitry Andric return R; 294*a7dea167SDimitry Andric } 295*a7dea167SDimitry Andric 296*a7dea167SDimitry Andric Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, 297*a7dea167SDimitry Andric bool IsConst, bool IsTemporary, 298*a7dea167SDimitry Andric bool IsMutable) { 299*a7dea167SDimitry Andric // Classes and structures. 300*a7dea167SDimitry Andric if (auto *RT = Ty->getAs<RecordType>()) { 301*a7dea167SDimitry Andric if (auto *Record = getOrCreateRecord(RT->getDecl())) 302*a7dea167SDimitry Andric return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); 303*a7dea167SDimitry Andric } 304*a7dea167SDimitry Andric 305*a7dea167SDimitry Andric // Arrays. 306*a7dea167SDimitry Andric if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) { 307*a7dea167SDimitry Andric QualType ElemTy = ArrayType->getElementType(); 308*a7dea167SDimitry Andric // Array of well-known bounds. 309*a7dea167SDimitry Andric if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) { 310*a7dea167SDimitry Andric size_t NumElems = CAT->getSize().getZExtValue(); 311*a7dea167SDimitry Andric if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { 312*a7dea167SDimitry Andric // Arrays of primitives. 313*a7dea167SDimitry Andric unsigned ElemSize = primSize(*T); 314*a7dea167SDimitry Andric if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) { 315*a7dea167SDimitry Andric return {}; 316*a7dea167SDimitry Andric } 317*a7dea167SDimitry Andric return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, 318*a7dea167SDimitry Andric IsMutable); 319*a7dea167SDimitry Andric } else { 320*a7dea167SDimitry Andric // Arrays of composites. In this case, the array is a list of pointers, 321*a7dea167SDimitry Andric // followed by the actual elements. 322*a7dea167SDimitry Andric Descriptor *Desc = 323*a7dea167SDimitry Andric createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); 324*a7dea167SDimitry Andric if (!Desc) 325*a7dea167SDimitry Andric return nullptr; 326*a7dea167SDimitry Andric InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); 327*a7dea167SDimitry Andric if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) 328*a7dea167SDimitry Andric return {}; 329*a7dea167SDimitry Andric return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, 330*a7dea167SDimitry Andric IsMutable); 331*a7dea167SDimitry Andric } 332*a7dea167SDimitry Andric } 333*a7dea167SDimitry Andric 334*a7dea167SDimitry Andric // Array of unknown bounds - cannot be accessed and pointer arithmetic 335*a7dea167SDimitry Andric // is forbidden on pointers to such objects. 336*a7dea167SDimitry Andric if (isa<IncompleteArrayType>(ArrayType)) { 337*a7dea167SDimitry Andric if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { 338*a7dea167SDimitry Andric return allocateDescriptor(D, *T, IsTemporary, 339*a7dea167SDimitry Andric Descriptor::UnknownSize{}); 340*a7dea167SDimitry Andric } else { 341*a7dea167SDimitry Andric Descriptor *Desc = 342*a7dea167SDimitry Andric createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); 343*a7dea167SDimitry Andric if (!Desc) 344*a7dea167SDimitry Andric return nullptr; 345*a7dea167SDimitry Andric return allocateDescriptor(D, Desc, IsTemporary, 346*a7dea167SDimitry Andric Descriptor::UnknownSize{}); 347*a7dea167SDimitry Andric } 348*a7dea167SDimitry Andric } 349*a7dea167SDimitry Andric } 350*a7dea167SDimitry Andric 351*a7dea167SDimitry Andric // Atomic types. 352*a7dea167SDimitry Andric if (auto *AT = Ty->getAs<AtomicType>()) { 353*a7dea167SDimitry Andric const Type *InnerTy = AT->getValueType().getTypePtr(); 354*a7dea167SDimitry Andric return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); 355*a7dea167SDimitry Andric } 356*a7dea167SDimitry Andric 357*a7dea167SDimitry Andric // Complex types - represented as arrays of elements. 358*a7dea167SDimitry Andric if (auto *CT = Ty->getAs<ComplexType>()) { 359*a7dea167SDimitry Andric PrimType ElemTy = *Ctx.classify(CT->getElementType()); 360*a7dea167SDimitry Andric return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); 361*a7dea167SDimitry Andric } 362*a7dea167SDimitry Andric 363*a7dea167SDimitry Andric return nullptr; 364*a7dea167SDimitry Andric } 365