//===--- GenKeyPath.cpp - IRGen support for key path objects --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file contains code for emitting key path patterns, which can be used // by the standard library to instantiate key path objects. // //===----------------------------------------------------------------------===// #include "ConstantBuilder.h" #include "Explosion.h" #include "GenClass.h" #include "GenMeta.h" #include "GenStruct.h" #include "GenericRequirement.h" #include "IRGenDebugInfo.h" #include "IRGenFunction.h" #include "IRGenModule.h" #include "ProtocolInfo.h" #include "llvm/ADT/SetVector.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILLocation.h" #include "swift/SIL/TypeLowering.h" #include "swift/ABI/KeyPath.h" #include "swift/AST/ASTContext.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsIRGen.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Types.h" using namespace swift; using namespace irgen; llvm::Constant * IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern, SILLocation diagLoc) { // TODO: Landing 32-bit key paths requires some runtime changes to get the // 8-byte object header. if (getPointerSize() != Size(8)) { Context.Diags.diagnose(diagLoc.getSourceLoc(), diag::not_implemented, "32-bit key paths"); return llvm::UndefValue::get(Int8PtrTy); } // See if we already emitted this. auto found = KeyPathPatterns.find(pattern); if (found != KeyPathPatterns.end()) return found->second; // Gather type arguments from the root and leaf types of the key path. auto rootTy = pattern->getRootType(); auto valueTy = pattern->getValueType(); // Check for parameterization, whether by subscript indexes or by the generic // environment. If there isn't any, we can instantiate the pattern in-place. bool isInstantiableInPlace = pattern->getNumOperands() == 0 && !pattern->getGenericSignature(); // Collect the required parameters for the keypath's generic environment. SmallVector requirements; GenericEnvironment *genericEnv = nullptr; if (auto sig = pattern->getGenericSignature()) { genericEnv = sig->createGenericEnvironment(*getSwiftModule()); enumerateGenericSignatureRequirements(pattern->getGenericSignature(), [&](GenericRequirement reqt) { requirements.push_back(reqt); }); } /// Generate a metadata accessor that produces metadata for the given type /// using arguments from the generic context of the key path. auto emitMetadataGenerator = [&](CanType type) -> llvm::Function * { // TODO: Use the standard metadata accessor when there are no arguments // and the metadata accessor is defined. // Build a stub that loads the necessary bindings from the key path's // argument buffer then fetches the metadata. auto fnTy = llvm::FunctionType::get(TypeMetadataPtrTy, {Int8PtrTy}, /*vararg*/ false); auto accessorThunk = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage, "keypath_get_type", getModule()); accessorThunk->setAttributes(constructInitialAttributes()); { IRGenFunction IGF(*this, accessorThunk); if (DebugInfo) DebugInfo->emitArtificialFunction(IGF, accessorThunk); if (type->hasTypeParameter()) { auto bindingsBufPtr = IGF.collectParameters().claimNext(); bindFromGenericRequirementsBuffer(IGF, requirements, Address(bindingsBufPtr, getPointerAlignment()), [&](CanType t) { if (!genericEnv) return t; return genericEnv->mapTypeIntoContext(t)->getCanonicalType(); }); type = genericEnv->mapTypeIntoContext(type)->getCanonicalType(); } auto ret = IGF.emitTypeMetadataRef(type); IGF.Builder.CreateRet(ret); } return accessorThunk; }; // Start building the key path pattern. ConstantInitBuilder builder(*this); ConstantStructBuilder fields = builder.beginStruct(); fields.setPacked(true); // Add a zero-initialized header we can use for lazy initialization. fields.add(llvm::ConstantInt::get(SizeTy, 0)); // Store references to metadata generator functions to generate the metadata // for the root and leaf. These sit in the "isa" and object header parts of // the final object. fields.add(emitMetadataGenerator(rootTy)); fields.add(emitMetadataGenerator(valueTy)); // Add a pointer to the ObjC KVC compatibility string, if there is one, or // null otherwise. llvm::Constant *objcString; if (!pattern->getObjCString().empty()) { objcString = getAddrOfGlobalString(pattern->getObjCString()); } else { objcString = llvm::ConstantPointerNull::get(Int8PtrTy); } fields.add(objcString); // Leave a placeholder for the buffer header, since we need to know the full // buffer size to fill it in. auto headerPlaceholder = fields.addPlaceholderWithSize(Int32Ty); auto startOfKeyPathBuffer = fields.getNextOffsetFromGlobal(); // Build out the components. auto baseTy = rootTy; auto getPropertyOffsetOrIndirectOffset = [&](SILType loweredBaseTy, VarDecl *property) -> std::pair { llvm::Constant *offset; bool isResolved; bool isStruct; if (auto structTy = loweredBaseTy.getStructOrBoundGenericStruct()) { offset = emitPhysicalStructMemberFixedOffset(*this, loweredBaseTy, property); isStruct = true; } else if (auto classTy = loweredBaseTy.getClassOrBoundGenericClass()) { offset = tryEmitConstantClassFragilePhysicalMemberOffset(*this, loweredBaseTy, property); isStruct = false; } else { llvm_unreachable("property of non-struct, non-class?!"); } // If the offset isn't fixed, try instead to get the field offset vector // offset for the field to look it up dynamically. isResolved = offset != nullptr; if (!isResolved) { if (isStruct) { offset = emitPhysicalStructMemberOffsetOfFieldOffset( *this, loweredBaseTy, property); assert(offset && "field is neither fixed-offset nor in offset vector"); } else { auto offsetValue = getClassFieldOffset(*this, loweredBaseTy.getClassOrBoundGenericClass(), property); offset = llvm::ConstantInt::get(Int32Ty, offsetValue.getValue()); } } return {offset, isResolved}; }; for (unsigned i : indices(pattern->getComponents())) { SILType loweredBaseTy; Lowering::GenericContextScope scope(getSILTypes(), pattern->getGenericSignature()); loweredBaseTy = getLoweredType(AbstractionPattern::getOpaque(), baseTy->getLValueOrInOutObjectType()); auto &component = pattern->getComponents()[i]; switch (auto kind = component.getKind()) { case KeyPathPatternComponent::Kind::StoredProperty: { // Try to get a constant offset if we can. auto property = cast(component.getStoredPropertyDecl()); llvm::Constant *offset; bool isResolved; std::tie(offset, isResolved) = getPropertyOffsetOrIndirectOffset(loweredBaseTy, property); offset = llvm::ConstantExpr::getTruncOrBitCast(offset, Int32Ty); bool isStruct = (bool)loweredBaseTy.getStructOrBoundGenericStruct(); // If the projection is a statically known integer, try to pack it into // the key path payload. if (isResolved) { if (auto offsetInt = dyn_cast_or_null(offset)) { auto offsetValue = offsetInt->getValue().getZExtValue(); if (KeyPathComponentHeader::offsetCanBeInline(offsetValue)) { auto header = isStruct ? KeyPathComponentHeader::forStructComponentWithInlineOffset(offsetValue) : KeyPathComponentHeader::forClassComponentWithInlineOffset(offsetValue); fields.addInt32(header.getData()); break; } } auto header = isStruct ? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset() : KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(); fields.addInt32(header.getData()); fields.add(offset); } else { // Otherwise, stash the offset of the field offset within the metadata // object, so we can pull it out at instantiation time. // TODO: We'll also need a way to handle resilient field offsets, once // field offset vectors no longer cover all fields in the type. KeyPathComponentHeader header = isStruct ? KeyPathComponentHeader::forStructComponentWithUnresolvedOffset() : KeyPathComponentHeader::forClassComponentWithUnresolvedOffset(); fields.addInt32(header.getData()); fields.add(offset); } break; } case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: { // Encode the settability. bool settable = kind == KeyPathPatternComponent::Kind::SettableProperty; KeyPathComponentHeader::ComputedPropertyKind componentKind; if (settable) { componentKind = component.isComputedSettablePropertyMutating() ? KeyPathComponentHeader::SettableMutating : KeyPathComponentHeader::SettableNonmutating; } else { componentKind = KeyPathComponentHeader::GetOnly; } // Lower the id reference. auto id = component.getComputedPropertyId(); KeyPathComponentHeader::ComputedPropertyIDKind idKind; llvm::Constant *idValue; bool idResolved; switch (id.getKind()) { case KeyPathPatternComponent::ComputedPropertyId::Function: idKind = KeyPathComponentHeader::Getter; idValue = getAddrOfSILFunction(id.getFunction(), NotForDefinition); idResolved = true; break; case KeyPathPatternComponent::ComputedPropertyId::DeclRef: { idKind = KeyPathComponentHeader::VTableOffset; auto declRef = id.getDeclRef(); auto dc = declRef.getDecl()->getDeclContext(); if (auto methodClass = dyn_cast(dc)) { auto index = getVirtualMethodIndex(*this, declRef); idValue = llvm::ConstantInt::get(SizeTy, index); idResolved = true; } else if (auto methodProto = dyn_cast(dc)) { auto &protoInfo = getProtocolInfo(methodProto); auto index = protoInfo.getFunctionIndex( cast(declRef.getDecl())); idValue = llvm::ConstantInt::get(SizeTy, -index.getValue()); idResolved = true; } else { llvm_unreachable("neither a class nor protocol dynamic method?"); } break; } case KeyPathPatternComponent::ComputedPropertyId::Property: idKind = KeyPathComponentHeader::StoredPropertyOffset; std::tie(idValue, idResolved) = getPropertyOffsetOrIndirectOffset(loweredBaseTy, id.getProperty()); idValue = llvm::ConstantExpr::getZExtOrBitCast(idValue, SizeTy); break; } auto header = KeyPathComponentHeader::forComputedProperty(componentKind, idKind, !isInstantiableInPlace, idResolved); fields.addInt32(header.getData()); fields.add(idValue); if (isInstantiableInPlace) { // No generic arguments, so we can invoke the getter/setter as is. fields.add(getAddrOfSILFunction(component.getComputedPropertyGetter(), NotForDefinition)); if (settable) fields.add(getAddrOfSILFunction(component.getComputedPropertySetter(), NotForDefinition)); } else { // If there's generic context (TODO: or subscript indexes), embed as // arguments in the component. Thunk the SIL-level accessors to give the // runtime implementation a polymorphically-callable interface. Context.Diags.diagnose(diagLoc.getSourceLoc(), diag::not_implemented, "generic computed key paths"); return llvm::UndefValue::get(Int8PtrTy); } } } // For all but the last component, we pack in the type of the component. if (i + 1 != pattern->getComponents().size()) { fields.add(emitMetadataGenerator(component.getComponentType())); } baseTy = component.getComponentType(); } // Save the total size of the buffer. Size componentSize = fields.getNextOffsetFromGlobal() - startOfKeyPathBuffer; // We now have enough info to build the header. KeyPathBufferHeader header(componentSize.getValue(), isInstantiableInPlace, /*reference prefix*/ false); // Add the header, followed by the components. fields.fillPlaceholder(headerPlaceholder, llvm::ConstantInt::get(Int32Ty, header.getData())); // Create the global variable. // TODO: The pattern could be immutable if // it isn't instantiable in place, and if we made the type metadata accessor // references private, it could go in true-const memory. auto patternVar = fields.finishAndCreateGlobal("keypath", getPointerAlignment(), /*constant*/ false, llvm::GlobalVariable::PrivateLinkage); KeyPathPatterns.insert({pattern, patternVar}); return patternVar; }