/// Emit a checked cast of a metatype. void irgen::emitMetatypeDowncast(IRGenFunction &IGF, llvm::Value *metatype, CanMetatypeType toMetatype, CheckedCastMode mode, Explosion &ex) { // Pick a runtime entry point and target metadata based on what kind of // representation we're casting. llvm::Value *castFn; llvm::Value *toMetadata; switch (toMetatype->getRepresentation()) { case MetatypeRepresentation::Thick: { // Get the Swift metadata for the type we're checking. toMetadata = IGF.emitTypeMetadataRef(toMetatype.getInstanceType()); switch (mode) { case CheckedCastMode::Unconditional: castFn = IGF.IGM.getDynamicCastMetatypeUnconditionalFn(); break; case CheckedCastMode::Conditional: castFn = IGF.IGM.getDynamicCastMetatypeFn(); break; } break; } case MetatypeRepresentation::ObjC: { assert(IGF.IGM.ObjCInterop && "should have objc runtime"); // Get the ObjC metadata for the type we're checking. toMetadata = emitClassHeapMetadataRef(IGF, toMetatype.getInstanceType(), MetadataValueType::ObjCClass); switch (mode) { case CheckedCastMode::Unconditional: castFn = IGF.IGM.getDynamicCastObjCClassMetatypeUnconditionalFn(); break; case CheckedCastMode::Conditional: castFn = IGF.IGM.getDynamicCastObjCClassMetatypeFn(); break; } break; } case MetatypeRepresentation::Thin: llvm_unreachable("not implemented"); } auto cc = IGF.IGM.DefaultCC; if (auto fun = dyn_cast<llvm::Function>(castFn)) cc = fun->getCallingConv(); auto call = IGF.Builder.CreateCall(castFn, {metatype, toMetadata}); call->setCallingConv(cc); call->setDoesNotThrow(); ex.add(call); }
void irgen::emitTypeLayoutVerifier(IRGenFunction &IGF, ArrayRef<CanType> formalTypes) { llvm::Type *verifierArgTys[] = { IGF.IGM.TypeMetadataPtrTy, IGF.IGM.Int8PtrTy, IGF.IGM.Int8PtrTy, IGF.IGM.SizeTy, IGF.IGM.Int8PtrTy, }; auto verifierFnTy = llvm::FunctionType::get(IGF.IGM.VoidTy, verifierArgTys, /*var arg*/ false); auto verifierFn = IGF.IGM.Module.getOrInsertFunction( "_swift_debug_verifyTypeLayoutAttribute", verifierFnTy); struct VerifierArgumentBuffers { Address runtimeBuf, staticBuf; }; llvm::DenseMap<llvm::Type *, VerifierArgumentBuffers> verifierArgBufs; auto getSizeConstant = [&](Size sz) -> llvm::Constant * { return llvm::ConstantInt::get(IGF.IGM.SizeTy, sz.getValue()); }; auto getAlignmentMaskConstant = [&](Alignment a) -> llvm::Constant * { return llvm::ConstantInt::get(IGF.IGM.SizeTy, a.getValue() - 1); }; auto getBoolConstant = [&](bool b) -> llvm::Constant * { return llvm::ConstantInt::get(IGF.IGM.Int1Ty, b); }; SmallString<20> numberBuf; for (auto formalType : formalTypes) { // Runtime type metadata always represents the maximal abstraction level of // the type. auto anyTy = ProtocolCompositionType::get(IGF.IGM.Context, {}); auto openedAnyTy = ArchetypeType::getOpened(anyTy); auto maxAbstraction = AbstractionPattern(openedAnyTy); auto &ti = IGF.getTypeInfoForUnlowered(maxAbstraction, formalType); // If there's no fixed type info, we rely on the runtime anyway, so there's // nothing to verify. // TODO: There are some traits of partially-fixed layouts we could check too. auto *fixedTI = dyn_cast<FixedTypeInfo>(&ti); if (!fixedTI) return; auto metadata = IGF.emitTypeMetadataRef(formalType); auto verify = [&](llvm::Value *runtimeVal, llvm::Value *staticVal, const llvm::Twine &description) { assert(runtimeVal->getType() == staticVal->getType()); // Get or create buffers for the arguments. VerifierArgumentBuffers bufs; auto foundBufs = verifierArgBufs.find(runtimeVal->getType()); if (foundBufs != verifierArgBufs.end()) { bufs = foundBufs->second; } else { Address runtimeBuf = IGF.createAlloca(runtimeVal->getType(), IGF.IGM.getPointerAlignment(), "runtime"); Address staticBuf = IGF.createAlloca(staticVal->getType(), IGF.IGM.getPointerAlignment(), "static"); bufs = {runtimeBuf, staticBuf}; verifierArgBufs[runtimeVal->getType()] = bufs; } IGF.Builder.CreateStore(runtimeVal, bufs.runtimeBuf); IGF.Builder.CreateStore(staticVal, bufs.staticBuf); auto runtimePtr = IGF.Builder.CreateBitCast(bufs.runtimeBuf.getAddress(), IGF.IGM.Int8PtrTy); auto staticPtr = IGF.Builder.CreateBitCast(bufs.staticBuf.getAddress(), IGF.IGM.Int8PtrTy); auto count = llvm::ConstantInt::get(IGF.IGM.SizeTy, IGF.IGM.DataLayout.getTypeStoreSize(runtimeVal->getType())); auto msg = IGF.IGM.getAddrOfGlobalString(description.str()); IGF.Builder.CreateCall( verifierFn, {metadata, runtimePtr, staticPtr, count, msg}); }; // Check that the fixed layout matches the runtime layout. SILType layoutType = SILType::getPrimitiveObjectType(formalType); verify(emitLoadOfSize(IGF, layoutType), getSizeConstant(fixedTI->getFixedSize()), "size"); verify(emitLoadOfAlignmentMask(IGF, layoutType), getAlignmentMaskConstant(fixedTI->getFixedAlignment()), "alignment mask"); verify(emitLoadOfStride(IGF, layoutType), getSizeConstant(fixedTI->getFixedStride()), "stride"); verify(emitLoadOfIsInline(IGF, layoutType), getBoolConstant(fixedTI->getFixedPacking(IGF.IGM) == FixedPacking::OffsetZero), "is-inline bit"); verify(emitLoadOfIsPOD(IGF, layoutType), getBoolConstant(fixedTI->isPOD(ResilienceScope::Component)), "is-POD bit"); verify(emitLoadOfIsBitwiseTakable(IGF, layoutType), getBoolConstant(fixedTI->isBitwiseTakable(ResilienceScope::Component)), "is-bitwise-takable bit"); unsigned xiCount = fixedTI->getFixedExtraInhabitantCount(IGF.IGM); verify(emitLoadOfHasExtraInhabitants(IGF, layoutType), getBoolConstant(xiCount != 0), "has-extra-inhabitants bit"); // Check extra inhabitants. if (xiCount > 0) { verify(emitLoadOfExtraInhabitantCount(IGF, layoutType), getSizeConstant(Size(xiCount)), "extra inhabitant count"); // Verify that the extra inhabitant representations are consistent. /* TODO: Update for EnumPayload implementation changes. auto xiBuf = IGF.createAlloca(fixedTI->getStorageType(), fixedTI->getFixedAlignment(), "extra-inhabitant"); auto xiOpaque = IGF.Builder.CreateBitCast(xiBuf, IGF.IGM.OpaquePtrTy); // TODO: Randomize the set of extra inhabitants we check. unsigned bits = fixedTI->getFixedSize().getValueInBits(); for (unsigned i = 0, e = std::min(xiCount, 1024u); i < e; ++i) { // Initialize the buffer with junk, to help ensure we're insensitive to // insignificant bits. // TODO: Randomize the filler. IGF.Builder.CreateMemSet(xiBuf.getAddress(), llvm::ConstantInt::get(IGF.IGM.Int8Ty, 0x5A), fixedTI->getFixedSize().getValue(), fixedTI->getFixedAlignment().getValue()); // Ask the runtime to store an extra inhabitant. auto index = llvm::ConstantInt::get(IGF.IGM.Int32Ty, i); emitStoreExtraInhabitantCall(IGF, layoutType, index, xiOpaque.getAddress()); // Compare the stored extra inhabitant against the fixed extra // inhabitant pattern. auto fixedXI = fixedTI->getFixedExtraInhabitantValue(IGF.IGM, bits, i); auto xiBuf2 = IGF.Builder.CreateBitCast(xiBuf, fixedXI->getType()->getPointerTo()); llvm::Value *runtimeXI = IGF.Builder.CreateLoad(xiBuf2); runtimeXI = fixedTI->maskFixedExtraInhabitant(IGF, runtimeXI); numberBuf.clear(); { llvm::raw_svector_ostream os(numberBuf); os << i; os.flush(); } verify(runtimeXI, fixedXI, llvm::Twine("stored extra inhabitant ") + numberBuf.str()); // Now store the fixed extra inhabitant and ask the runtime to identify // it. // Mask in junk to make sure the runtime correctly ignores it. auto xiMask = fixedTI->getFixedExtraInhabitantMask(IGF.IGM).asAPInt(); auto maskVal = llvm::ConstantInt::get(IGF.IGM.getLLVMContext(), xiMask); auto notMaskVal = llvm::ConstantInt::get(IGF.IGM.getLLVMContext(), ~xiMask); // TODO: Randomize the filler. auto xiFill = llvm::ConstantInt::getAllOnesValue(fixedXI->getType()); llvm::Value *xiFillMask = IGF.Builder.CreateAnd(notMaskVal, xiFill); llvm::Value *xiValMask = IGF.Builder.CreateAnd(maskVal, fixedXI); llvm::Value *filledXI = IGF.Builder.CreateOr(xiFillMask, xiValMask); IGF.Builder.CreateStore(filledXI, xiBuf2); auto runtimeIndex = emitGetExtraInhabitantIndexCall(IGF, layoutType, xiOpaque.getAddress()); verify(runtimeIndex, index, llvm::Twine("extra inhabitant index calculation ") + numberBuf.str()); } */ } // TODO: Verify interesting layout properties specific to the kind of type, // such as struct or class field offsets, enum case tags, vtable entries, // etc. } }
/// Emit a checked cast to a protocol or protocol composition. void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF, llvm::Value *value, SILType srcType, SILType destType, CheckedCastMode mode, Optional<MetatypeRepresentation> metatypeKind, Explosion &ex) { auto srcInstanceType = srcType.getSwiftRValueType(); auto destInstanceType = destType.getSwiftRValueType(); while (auto metatypeType = dyn_cast<ExistentialMetatypeType>( destInstanceType)) { destInstanceType = metatypeType.getInstanceType(); srcInstanceType = cast<AnyMetatypeType>(srcInstanceType).getInstanceType(); } auto layout = destInstanceType.getExistentialLayout(); // Look up witness tables for the protocols that need them and get // references to the ObjC Protocol* values for the objc protocols. SmallVector<llvm::Value*, 4> objcProtos; SmallVector<llvm::Value*, 4> witnessTableProtos; bool hasClassConstraint = layout.requiresClass(); bool hasClassConstraintByProtocol = false; bool hasSuperclassConstraint = bool(layout.superclass); for (auto protoTy : layout.getProtocols()) { auto *protoDecl = protoTy->getDecl(); // If the protocol introduces a class constraint, track whether we need // to check for it independent of protocol witnesses. if (protoDecl->requiresClass()) { assert(hasClassConstraint); hasClassConstraintByProtocol = true; } if (Lowering::TypeConverter::protocolRequiresWitnessTable(protoDecl)) { auto descriptor = emitProtocolDescriptorRef(IGF, protoDecl); witnessTableProtos.push_back(descriptor); } if (protoDecl->isObjC()) objcProtos.push_back(emitReferenceToObjCProtocol(IGF, protoDecl)); } llvm::Type *resultType; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: resultType = IGF.IGM.TypeMetadataPtrTy; break; case MetatypeRepresentation::ObjC: resultType = IGF.IGM.ObjCClassPtrTy; break; } } else { auto schema = IGF.getTypeInfo(destType).getSchema(); resultType = schema[0].getScalarType(); } // The source of a scalar cast is statically known to be a class or a // metatype, so we only have to check the class constraint in two cases: // // 1) The destination type has an explicit superclass constraint that is // more derived than what the source type is known to be. // // 2) We are casting between metatypes, in which case the source might // be a non-class metatype. bool checkClassConstraint = false; if ((bool)metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol && !srcInstanceType->mayHaveSuperclass()) checkClassConstraint = true; // If the source has an equal or more derived superclass constraint than // the destination, we can elide the superclass check. // // Note that destInstanceType is always an existential type, so calling // getSuperclass() returns the superclass constraint of the existential, // not the superclass of some concrete class. bool checkSuperclassConstraint = hasSuperclassConstraint && !destInstanceType->getSuperclass()->isExactSuperclassOf(srcInstanceType); if (checkSuperclassConstraint) checkClassConstraint = true; llvm::Value *resultValue = value; // If we don't have anything we really need to check, then trivially succeed. if (objcProtos.empty() && witnessTableProtos.empty() && !checkClassConstraint) { resultValue = IGF.Builder.CreateBitCast(value, resultType); ex.add(resultValue); return; } // Check the ObjC protocol conformances if there were any. llvm::Value *objcCast = nullptr; if (!objcProtos.empty()) { // Get the ObjC instance or class object to check for these conformances. llvm::Value *objcObject; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: { // The metadata might be for a non-class type, which wouldn't have // an ObjC class object. objcObject = nullptr; break; } case MetatypeRepresentation::ObjC: // Metatype is already an ObjC object. objcObject = value; break; } } else { // Class instance is already an ObjC object. objcObject = value; } if (objcObject) objcObject = IGF.Builder.CreateBitCast(objcObject, IGF.IGM.UnknownRefCountedPtrTy); // Pick the cast function based on the cast mode and on whether we're // casting a Swift metatype or ObjC object. llvm::Constant *castFn; switch (mode) { case CheckedCastMode::Unconditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolUnconditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolUnconditionalFn(); break; case CheckedCastMode::Conditional: castFn = objcObject ? IGF.IGM.getDynamicCastObjCProtocolConditionalFn() : IGF.IGM.getDynamicCastTypeToObjCProtocolConditionalFn(); break; } llvm::Value *objcCastObject = objcObject ? objcObject : value; Address protoRefsBuf = IGF.createAlloca( llvm::ArrayType::get(IGF.IGM.Int8PtrTy, objcProtos.size()), IGF.IGM.getPointerAlignment(), "objc_protocols"); protoRefsBuf = IGF.Builder.CreateBitCast(protoRefsBuf, IGF.IGM.Int8PtrPtrTy); for (unsigned index : indices(objcProtos)) { Address protoRefSlot = IGF.Builder.CreateConstArrayGEP( protoRefsBuf, index, IGF.IGM.getPointerSize()); IGF.Builder.CreateStore(objcProtos[index], protoRefSlot); ++index; } auto cc = IGF.IGM.DefaultCC; if (auto fun = dyn_cast<llvm::Function>(castFn)) cc = fun->getCallingConv(); auto call = IGF.Builder.CreateCall( castFn, {objcCastObject, IGF.IGM.getSize(Size(objcProtos.size())), protoRefsBuf.getAddress()}); call->setCallingConv(cc); objcCast = call; resultValue = IGF.Builder.CreateBitCast(objcCast, resultType); } // If we don't need to look up any witness tables, we're done. if (witnessTableProtos.empty() && !checkClassConstraint) { ex.add(resultValue); return; } // If we're doing a conditional cast, and the ObjC protocol checks failed, // then the cast is done. Optional<ConditionalDominanceScope> condition; llvm::BasicBlock *origBB = nullptr, *successBB = nullptr, *contBB = nullptr; if (!objcProtos.empty()) { switch (mode) { case CheckedCastMode::Unconditional: break; case CheckedCastMode::Conditional: { origBB = IGF.Builder.GetInsertBlock(); successBB = IGF.createBasicBlock("success"); contBB = IGF.createBasicBlock("cont"); auto isNull = IGF.Builder.CreateICmpEQ(objcCast, llvm::ConstantPointerNull::get( cast<llvm::PointerType>(objcCast->getType()))); IGF.Builder.CreateCondBr(isNull, contBB, successBB); IGF.Builder.emitBlock(successBB); condition.emplace(IGF); } } } // Get the Swift type metadata for the type. llvm::Value *metadataValue; if (metatypeKind) { switch (*metatypeKind) { case MetatypeRepresentation::Thin: llvm_unreachable("can't cast to thin metatype"); case MetatypeRepresentation::Thick: // The value is already a native metatype. metadataValue = value; break; case MetatypeRepresentation::ObjC: // Get the type metadata from the ObjC class, which may be a wrapper. metadataValue = emitObjCMetadataRefForMetadata(IGF, value); } } else { // Get the type metadata for the instance. metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType); } // Look up witness tables for the protocols that need them. auto fn = emitExistentialScalarCastFn(IGF.IGM, witnessTableProtos.size(), mode, checkClassConstraint, checkSuperclassConstraint); llvm::SmallVector<llvm::Value *, 4> args; if (resultValue->getType() != IGF.IGM.Int8PtrTy) resultValue = IGF.Builder.CreateBitCast(resultValue, IGF.IGM.Int8PtrTy); args.push_back(resultValue); args.push_back(metadataValue); if (checkSuperclassConstraint) args.push_back(IGF.emitTypeMetadataRef(CanType(layout.superclass))); for (auto proto : witnessTableProtos) args.push_back(proto); auto valueAndWitnessTables = IGF.Builder.CreateCall(fn, args); resultValue = IGF.Builder.CreateExtractValue(valueAndWitnessTables, 0); if (resultValue->getType() != resultType) resultValue = IGF.Builder.CreateBitCast(resultValue, resultType); ex.add(resultValue); for (unsigned i = 0, e = witnessTableProtos.size(); i < e; ++i) { auto wt = IGF.Builder.CreateExtractValue(valueAndWitnessTables, i + 1); ex.add(wt); } // If we had conditional ObjC checks, join the failure paths. if (contBB) { condition.reset(); IGF.Builder.CreateBr(contBB); IGF.Builder.emitBlock(contBB); // Return null on the failure path. Explosion successEx = std::move(ex); ex.reset(); while (!successEx.empty()) { auto successVal = successEx.claimNext(); auto failureVal = llvm::Constant::getNullValue(successVal->getType()); auto phi = IGF.Builder.CreatePHI(successVal->getType(), 2); phi->addIncoming(successVal, successBB); phi->addIncoming(failureVal, origBB); ex.add(phi); } } }