// Allow casting a struct by value when all elements in toType correspond to // an element of the same size or larger laid out in the same order in // fromType. The assumption is that if fromType has larger elements, or // additional elements, their presence cannot induce a more compact layout of // the overlapping elements. // // struct {A, B} -> A is castable // struct {A, B, C} -> struct {A, B} is castable // struct { struct {A, B}, C} -> struct {A, B} is castable // struct { A, B, C} -> struct { struct {A, B}, C} is NOT castable static bool canUnsafeCastStruct(SILType fromType, StructDecl *fromStruct, SILType toType, SILModule &M) { auto fromRange = fromStruct->getStoredProperties(); if (fromRange.begin() == fromRange.end()) return false; // Can the first element of fromStruct be cast by value into toType? SILType fromEltTy = fromType.getFieldType(*fromRange.begin(), M); if (SILType::canUnsafeCastValue(fromEltTy, toType, M)) return true; // Otherwise, flatten one level of struct elements on each side. StructDecl *toStruct = toType.getStructOrBoundGenericStruct(); if (!toStruct) return false; auto toRange = toStruct->getStoredProperties(); for (auto toI = toRange.begin(), toE = toRange.end(), fromI = fromRange.begin(), fromE = fromRange.end(); toI != toE; ++toI, ++fromI) { if (fromI == fromE) return false; // fromType is a struct with fewer elements. SILType fromEltTy = fromType.getFieldType(*fromI, M); SILType toEltTy = toType.getFieldType(*toI, M); if (!SILType::canUnsafeCastValue(fromEltTy, toEltTy, M)) return false; } // fromType's overlapping elements are compatible. return true; }
/// Compute the subelement number indicated by the specified pointer (which is /// derived from the root by a series of tuple/struct element addresses) by /// treating the type as a linearized namespace with sequential elements. For /// example, given: /// /// root = alloc { a: { c: i64, d: i64 }, b: (i64, i64) } /// tmp1 = struct_element_addr root, 1 /// tmp2 = tuple_element_addr tmp1, 0 /// /// This will return a subelement number of 2. /// /// If this pointer is to within an existential projection, it returns ~0U. /// static unsigned computeSubelement(SILValue Pointer, SILInstruction *RootInst) { unsigned SubEltNumber = 0; SILModule &M = RootInst->getModule(); while (1) { // If we got to the root, we're done. if (RootInst == Pointer) return SubEltNumber; auto *Inst = cast<SILInstruction>(Pointer); if (auto *PBI = dyn_cast<ProjectBoxInst>(Inst)) { Pointer = PBI->getOperand(); } else if (auto *TEAI = dyn_cast<TupleElementAddrInst>(Inst)) { SILType TT = TEAI->getOperand()->getType(); // Keep track of what subelement is being referenced. for (unsigned i = 0, e = TEAI->getFieldNo(); i != e; ++i) { SubEltNumber += getNumSubElements(TT.getTupleElementType(i), M); } Pointer = TEAI->getOperand(); } else if (auto *SEAI = dyn_cast<StructElementAddrInst>(Inst)) { SILType ST = SEAI->getOperand()->getType(); // Keep track of what subelement is being referenced. StructDecl *SD = SEAI->getStructDecl(); for (auto *D : SD->getStoredProperties()) { if (D == SEAI->getField()) break; SubEltNumber += getNumSubElements(ST.getFieldType(D, M), M); } Pointer = SEAI->getOperand(); } else { assert((isa<InitExistentialAddrInst>(Inst) || isa<InjectEnumAddrInst>(Inst))&& "Unknown access path instruction"); // Cannot promote loads and stores from within an existential projection. return ~0U; } } }
bool swift::ArraySemanticsCall::replaceByAppendingValues( SILModule &M, SILFunction *AppendFn, SILFunction *ReserveFn, const SmallVectorImpl<SILValue> &Vals, SubstitutionMap Subs) { assert(getKind() == ArrayCallKind::kAppendContentsOf && "Must be an append_contentsOf call"); assert(AppendFn && "Must provide an append SILFunction"); // We only handle loadable types. if (any_of(Vals, [&M](SILValue V) -> bool { return !V->getType().isLoadable(M); })) return false; CanSILFunctionType AppendFnTy = AppendFn->getLoweredFunctionType(); SILValue ArrRef = SemanticsCall->getArgument(1); SILBuilderWithScope Builder(SemanticsCall); auto Loc = SemanticsCall->getLoc(); auto *FnRef = Builder.createFunctionRefFor(Loc, AppendFn); if (Vals.size() > 1) { // Create a call to reserveCapacityForAppend() to reserve space for multiple // elements. FunctionRefBaseInst *ReserveFnRef = Builder.createFunctionRefFor(Loc, ReserveFn); SILFunctionType *ReserveFnTy = ReserveFnRef->getType().castTo<SILFunctionType>(); assert(ReserveFnTy->getNumParameters() == 2); StructType *IntType = ReserveFnTy->getParameters()[0].getType()->castTo<StructType>(); StructDecl *IntDecl = IntType->getDecl(); VarDecl *field = *IntDecl->getStoredProperties().begin(); SILType BuiltinIntTy =SILType::getPrimitiveObjectType( field->getInterfaceType()->getCanonicalType()); IntegerLiteralInst *CapacityLiteral = Builder.createIntegerLiteral(Loc, BuiltinIntTy, Vals.size()); StructInst *Capacity = Builder.createStruct(Loc, SILType::getPrimitiveObjectType(CanType(IntType)), {CapacityLiteral}); Builder.createApply(Loc, ReserveFnRef, Subs, {Capacity, ArrRef}, false); } for (SILValue V : Vals) { auto SubTy = V->getType(); auto &ValLowering = Builder.getModule().getTypeLowering(SubTy); auto CopiedVal = ValLowering.emitCopyValue(Builder, Loc, V); auto *AllocStackInst = Builder.createAllocStack(Loc, SubTy); ValLowering.emitStoreOfCopy(Builder, Loc, CopiedVal, AllocStackInst, IsInitialization_t::IsInitialization); SILValue Args[] = {AllocStackInst, ArrRef}; Builder.createApply(Loc, FnRef, Subs, Args, false); Builder.createDeallocStack(Loc, AllocStackInst); if (!isConsumedParameter(AppendFnTy->getParameters()[0].getConvention())) { ValLowering.emitDestroyValue(Builder, Loc, CopiedVal); } } CanSILFunctionType AppendContentsOfFnTy = SemanticsCall->getReferencedFunction()->getLoweredFunctionType(); if (AppendContentsOfFnTy->getParameters()[0].getConvention() == ParameterConvention::Direct_Owned) { SILValue SrcArray = SemanticsCall->getArgument(0); Builder.createReleaseValue(SemanticsCall->getLoc(), SrcArray, Builder.getDefaultAtomicity()); } removeCall(); return true; }