diff --git a/lib/IRGen/GenPack.cpp b/lib/IRGen/GenPack.cpp index 313ad5a92680b..24b991dbde14e 100644 --- a/lib/IRGen/GenPack.cpp +++ b/lib/IRGen/GenPack.cpp @@ -26,6 +26,7 @@ #include "swift/SIL/SILType.h" #include "llvm/IR/DerivedTypes.h" +#include "GenTuple.h" #include "GenType.h" #include "IRGenFunction.h" #include "IRGenModule.h" @@ -1184,3 +1185,227 @@ void irgen::deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType IGF.Builder.CreateLifetimeEnd(addr.getAddress(), elementSize * elementCount); } + +static unsigned getConstantLabelsLength(CanTupleType type) { + unsigned total = 0; + + for (auto elt : type->getElements()) { + if (elt.getType()->is()) { + assert(!elt.hasName()); + continue; + } + + if (elt.hasName()) { + assert(!elt.getType()->is()); + total += elt.getName().getLength(); + } + + ++total; + } + + return total; +} + +/// Emit the dynamic label string for a tuple type containing pack +/// expansions. +/// +/// The basic idea is that the static label string is "stretched out". +/// Pack expansion elements are unlabeled, so they appear as a single +/// blank space in the static label string. We replace this with the +/// appropriate number of blank spaces, given the dynamic length of +/// the pack. +llvm::Optional +irgen::emitDynamicTupleTypeLabels(IRGenFunction &IGF, + CanTupleType type, + CanPackType packType, + llvm::Value *shapeExpression) { + bool hasLabels = false; + for (auto elt : type->getElements()) { + hasLabels |= elt.hasName(); + } + + if (!hasLabels) + return llvm::None; + + // Elements of pack expansion type are unlabeled, so the length of + // the label string is the number of elements in the pack, plus the + // sum of the lengths of the labels. + llvm::Value *labelLength = llvm::ConstantInt::get( + IGF.IGM.SizeTy, getConstantLabelsLength(type)); + labelLength = IGF.Builder.CreateAdd(shapeExpression, labelLength); + + // Leave root for a null byte at the end. + labelLength = IGF.Builder.CreateAdd(labelLength, + llvm::ConstantInt::get(IGF.IGM.SizeTy, 1)); + + // Allocate space for the label string; we fill it in below. + StackAddress labelString = IGF.emitDynamicAlloca( + IGF.IGM.Int8Ty, labelLength, + IGF.IGM.getPointerAlignment(), + /*allowTaskAlloc=*/true); + + // Get the static label string, where each pack expansion is one element. + auto *staticLabelString = getTupleLabelsString(IGF.IGM, type); + + // The position in the static label string for to the current element. + unsigned staticPosition = 0; + + // The position in the dynamic label string for to the current element. + llvm::Value *dynamicPosition = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0); + + // Number of expansions we've seen so far. + unsigned numExpansions = 0; + + // Was there at least one label? + bool sawLabel = false; + + auto visitFn = [&](CanType eltTy, + unsigned scalarIndex, + llvm::Value *dynamicIndex, + llvm::Value *dynamicLength) { + auto elt = type->getElements()[scalarIndex + numExpansions]; + assert(eltTy == CanType(elt.getType())); + + // The destination address, where we put the current element's label. + auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(), + dynamicPosition, Size(1)); + + // If we're looking at a pack expansion, insert the appropriate + // number of blank spaces in the dynamic label string. + if (isa(eltTy)) { + assert(!elt.hasName() && "Pack expansions cannot have labels"); + // Fill the dynamic label string with a blank label for each + // dynamic element. + IGF.Builder.CreateMemSet( + eltAddr, llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '), + dynamicLength); + + // We consumed one static label. + staticPosition += 1; + + // We produced some number of dynamic labels. + dynamicPosition = IGF.Builder.CreateAdd(dynamicPosition, dynamicLength); + + // We consumed an expansion. + numExpansions += 1; + + return; + } + + // Otherwise, we have a single scalar element, which deposits a single + // label in the dynamic label string. + unsigned length = 0; + + // Scalar elements may have labels. + if (elt.hasName()) { + // Index into the static label string. + llvm::Constant *indices[] = { + llvm::ConstantInt::get(IGF.IGM.SizeTy, staticPosition) + }; + + // The source address in the static label string. + Address srcAddr( + llvm::ConstantExpr::getInBoundsGetElementPtr( + IGF.IGM.Int8Ty, staticLabelString, + indices), + IGF.IGM.Int8Ty, Alignment(1)); + + // The number of bytes to copy; add one for the space at the end. + length = elt.getName().getLength() + 1; + + // Desposit the label for this element in the dynamic label string. + IGF.Builder.CreateMemCpy(eltAddr, srcAddr, Size(length)); + + sawLabel = true; + } else { + length = 1; + + // There is no label. The static label string stores a blank space, + // and we need to update the dynamic string for the same. + IGF.Builder.CreateStore( + llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '), + eltAddr); + } + + // We consumed one static label. + staticPosition += length; + + // We produced one dynamic label. + auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, length); + accumulateSum(IGF, dynamicPosition, constant); + }; + + (void) visitPackExplosion(IGF, packType, visitFn); + + // Null-terminate the dynamic label string. + auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(), + dynamicPosition, Size(1)); + IGF.Builder.CreateStore( + llvm::ConstantInt::get(IGF.IGM.Int8Ty, '\0'), + eltAddr); + + assert(sawLabel); + (void) sawLabel; + + return labelString; +} + +StackAddress +irgen::emitDynamicFunctionParameterFlags(IRGenFunction &IGF, + AnyFunctionType::CanParamArrayRef params, + CanPackType packType, + llvm::Value *shapeExpression) { + auto array = + IGF.emitDynamicAlloca(IGF.IGM.Int32Ty, shapeExpression, + Alignment(4), /*allowTaskAlloc=*/true); + + unsigned numExpansions = 0; + + auto visitFn = [&](CanType eltTy, + unsigned scalarIndex, + llvm::Value *dynamicIndex, + llvm::Value *dynamicLength) { + if (scalarIndex != 0 || dynamicIndex == nullptr) { + auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, scalarIndex); + accumulateSum(IGF, dynamicIndex, constant); + } + + auto elt = params[scalarIndex + numExpansions]; + auto flags = getABIParameterFlags(elt.getParameterFlags()); + auto flagsVal = llvm::ConstantInt::get( + IGF.IGM.Int32Ty, flags.getIntValue()); + + assert(eltTy == elt.getPlainType()); + + // If we're looking at a pack expansion, insert the appropriate + // number of flags fields. + if (auto expansionTy = dyn_cast(eltTy)) { + emitPackExpansionPack(IGF, array.getAddress(), expansionTy, + dynamicIndex, dynamicLength, + [&](llvm::Value *) -> llvm::Value * { + return flagsVal; + }); + + // We consumed an expansion. + numExpansions += 1; + + return; + } + + // The destination address, where we put the current element's flags field. + Address eltAddr( + IGF.Builder.CreateInBoundsGEP(array.getAddress().getElementType(), + array.getAddressPointer(), + dynamicIndex), + array.getAddress().getElementType(), + array.getAlignment()); + + // Otherwise, we have a single scalar element, which deposits a single + // flags field. + IGF.Builder.CreateStore(flagsVal, eltAddr); + }; + + (void) visitPackExplosion(IGF, packType, visitFn); + + return array; +} \ No newline at end of file diff --git a/lib/IRGen/GenPack.h b/lib/IRGen/GenPack.h index 0a71e44034b55..504e3c77d4eda 100644 --- a/lib/IRGen/GenPack.h +++ b/lib/IRGen/GenPack.h @@ -114,6 +114,18 @@ StackAddress allocatePack(IRGenFunction &IGF, CanSILPackType packType); void deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType packType); +llvm::Optional +emitDynamicTupleTypeLabels(IRGenFunction &IGF, + CanTupleType tupleType, + CanPackType packType, + llvm::Value *shapeExpression); + +StackAddress +emitDynamicFunctionParameterFlags(IRGenFunction &IGF, + AnyFunctionType::CanParamArrayRef params, + CanPackType packType, + llvm::Value *shapeExpression); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp index b28f47853bf7a..22984f577e8d4 100644 --- a/lib/IRGen/GenTuple.cpp +++ b/lib/IRGen/GenTuple.cpp @@ -640,3 +640,28 @@ irgen::getPhysicalTupleElementStructIndex(IRGenModule &IGM, SILType tupleType, unsigned fieldNo) { FOR_TUPLE_IMPL(IGM, tupleType, getElementStructIndex, fieldNo); } + +/// Emit a string encoding the labels in the given tuple type. +llvm::Constant *irgen::getTupleLabelsString(IRGenModule &IGM, + CanTupleType type) { + bool hasLabels = false; + llvm::SmallString<128> buffer; + for (auto &elt : type->getElements()) { + if (elt.hasName()) { + hasLabels = true; + buffer.append(elt.getName().str()); + } + + // Each label is space-terminated. + buffer += ' '; + } + + // If there are no labels, use a null pointer. + if (!hasLabels) { + return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); + } + + // Otherwise, create a new string literal. + // This method implicitly adds a null terminator. + return IGM.getAddrOfGlobalString(buffer); +} \ No newline at end of file diff --git a/lib/IRGen/GenTuple.h b/lib/IRGen/GenTuple.h index 221b2959f9d3e..74c3de6e2c147 100644 --- a/lib/IRGen/GenTuple.h +++ b/lib/IRGen/GenTuple.h @@ -67,6 +67,11 @@ namespace irgen { llvm::Optional getPhysicalTupleElementStructIndex(IRGenModule &IGM, SILType tupleType, unsigned fieldNo); + + /// Emit a string encoding the labels in the given tuple type. + llvm::Constant *getTupleLabelsString(IRGenModule &IGM, + CanTupleType type); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 79799bc1ccc2a..805a1399b1d99 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -27,6 +27,7 @@ #include "GenPack.h" #include "GenPointerAuth.h" #include "GenProto.h" +#include "GenTuple.h" #include "GenType.h" #include "GenericArguments.h" #include "GenericRequirement.h" @@ -1135,31 +1136,6 @@ MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) { return MetadataAccessStrategy::NonUniqueAccessor; } -/// Emit a string encoding the labels in the given tuple type. -static llvm::Constant *getTupleLabelsString(IRGenModule &IGM, - CanTupleType type) { - bool hasLabels = false; - llvm::SmallString<128> buffer; - for (auto &elt : type->getElements()) { - if (elt.hasName()) { - hasLabels = true; - buffer.append(elt.getName().str()); - } - - // Each label is space-terminated. - buffer += ' '; - } - - // If there are no labels, use a null pointer. - if (!hasLabels) { - return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - } - - // Otherwise, create a new string literal. - // This method implicitly adds a null terminator. - return IGM.getAddrOfGlobalString(buffer); -} - static llvm::Constant *emitEmptyTupleTypeMetadataRef(IRGenModule &IGM) { llvm::Constant *fullMetadata = IGM.getEmptyTupleMetadata(); llvm::Constant *indices[] = { @@ -1188,6 +1164,8 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF, // "vanishes" and gets unwrapped. This behavior is implemented in both // compile-time type substitution, and runtime type metadata instantiation, // ensuring consistent behavior. + // + // FIXME: Inconsistent behavior with one-element labeled tuples. if (type->getNumScalarElements() <= 1) { ConditionalDominanceScope scope(IGF); @@ -1231,6 +1209,9 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF, { ConditionalDominanceScope scope(IGF); + llvm::Optional labelString = emitDynamicTupleTypeLabels( + IGF, type, packType, shapeExpression); + // Otherwise, we know that either statically or dynamically, we have more than // one element. Emit the pack. llvm::Value *shape; @@ -1241,12 +1222,20 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF, auto *pointerToFirst = IGF.Builder.CreatePointerCast( addr.getAddressPointer(), IGF.IGM.TypeMetadataPtrPtrTy); + auto *flags = shapeExpression; + if (labelString) { + flags = IGF.Builder.CreateOr(flags, llvm::ConstantInt::get(IGF.IGM.SizeTy, + TupleTypeFlags().withNonConstantLabels(true).getIntValue())); + } + // Call swift_getTupleMetadata(). llvm::Value *args[] = { request.get(IGF), - shapeExpression, + flags, pointerToFirst, - getTupleLabelsString(IGF.IGM, type), + (labelString + ? labelString->getAddress().getAddress() + : llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy)), llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed }; @@ -1256,6 +1245,9 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF, call->setDoesNotThrow(); cleanupTypeMetadataPack(IGF, addr, shape); + + if (labelString) + IGF.emitDeallocateDynamicAlloca(*labelString); } // Control flow join with the one-element case. @@ -1372,6 +1364,342 @@ static void destroyGenericArgumentsArray(IRGenFunction &IGF, IGF.IGM.getPointerSize() * args.size()); } +static llvm::Value *getFunctionParameterRef(IRGenFunction &IGF, + AnyFunctionType::CanParam param) { + auto type = param.getPlainType()->getCanonicalType(); + return IGF.emitAbstractTypeMetadataRef(type); +} + +/// Mapping type-level parameter flags to ABI parameter flags. +ParameterFlags irgen::getABIParameterFlags(ParameterTypeFlags flags) { + return ParameterFlags() + .withValueOwnership(flags.getValueOwnership()) + .withVariadic(flags.isVariadic()) + .withAutoClosure(flags.isAutoClosure()) + .withNoDerivative(flags.isNoDerivative()) + .withIsolated(flags.isIsolated()); +} + +static FunctionTypeFlags getFunctionTypeFlags(CanFunctionType type) { + bool hasParameterFlags = false; + for (auto param : type.getParams()) { + if (!getABIParameterFlags(param.getParameterFlags()).isNone()) { + hasParameterFlags = true; + break; + } + } + + // Map the convention to a runtime metadata value. + FunctionMetadataConvention metadataConvention; + bool isEscaping = false; + switch (type->getRepresentation()) { + case FunctionTypeRepresentation::Swift: + metadataConvention = FunctionMetadataConvention::Swift; + isEscaping = !type->isNoEscape(); + break; + case FunctionTypeRepresentation::Thin: + metadataConvention = FunctionMetadataConvention::Thin; + break; + case FunctionTypeRepresentation::Block: + metadataConvention = FunctionMetadataConvention::Block; + break; + case FunctionTypeRepresentation::CFunctionPointer: + metadataConvention = FunctionMetadataConvention::CFunctionPointer; + break; + } + + return FunctionTypeFlags() + .withConvention(metadataConvention) + .withAsync(type->isAsync()) + .withConcurrent(type->isSendable()) + .withThrows(type->isThrowing()) + .withParameterFlags(hasParameterFlags) + .withEscaping(isEscaping) + .withDifferentiable(type->isDifferentiable()) + .withGlobalActor(!type->getGlobalActor().isNull()); +} + +namespace { +struct FunctionTypeMetadataParamInfo { + StackAddress parameters; + StackAddress paramFlags; + unsigned numParams; +}; +} + +static FunctionTypeMetadataParamInfo +emitFunctionTypeMetadataParams(IRGenFunction &IGF, + AnyFunctionType::CanParamArrayRef params, + FunctionTypeFlags flags, + DynamicMetadataRequest request, + SmallVectorImpl &arguments) { + FunctionTypeMetadataParamInfo info; + info.numParams = params.size(); + + ConstantInitBuilder paramFlags(IGF.IGM); + auto flagsArr = paramFlags.beginArray(); + + if (!params.empty()) { + auto arrayTy = + llvm::ArrayType::get(IGF.IGM.TypeMetadataPtrTy, info.numParams); + info.parameters = StackAddress(IGF.createAlloca( + arrayTy, IGF.IGM.getTypeMetadataAlignment(), "function-parameters")); + + IGF.Builder.CreateLifetimeStart(info.parameters.getAddress(), + IGF.IGM.getPointerSize() * info.numParams); + + for (unsigned i : indices(params)) { + auto param = params[i]; + auto paramFlags = getABIParameterFlags(param.getParameterFlags()); + + auto argPtr = IGF.Builder.CreateStructGEP(info.parameters.getAddress(), i, + IGF.IGM.getPointerSize()); + auto *typeRef = getFunctionParameterRef(IGF, param); + IGF.Builder.CreateStore(typeRef, argPtr); + if (i == 0) + arguments.push_back(argPtr.getAddress()); + + flagsArr.addInt32(paramFlags.getIntValue()); + } + } else { + auto parametersPtr = + llvm::ConstantPointerNull::get( + IGF.IGM.TypeMetadataPtrTy->getPointerTo()); + arguments.push_back(parametersPtr); + } + + auto *Int32Ptr = IGF.IGM.Int32Ty->getPointerTo(); + if (flags.hasParameterFlags()) { + auto *flagsVar = flagsArr.finishAndCreateGlobal( + "parameter-flags", IGF.IGM.getPointerAlignment(), + /* constant */ true); + arguments.push_back(IGF.Builder.CreateBitCast(flagsVar, Int32Ptr)); + } else { + flagsArr.abandon(); + arguments.push_back(llvm::ConstantPointerNull::get(Int32Ptr)); + } + + return info; +} + +static FunctionTypeMetadataParamInfo +emitDynamicFunctionTypeMetadataParams(IRGenFunction &IGF, + AnyFunctionType::CanParamArrayRef params, + FunctionTypeFlags flags, + CanPackType packType, + DynamicMetadataRequest request, + SmallVectorImpl &arguments) { + assert(!params.empty()); + + FunctionTypeMetadataParamInfo info; + + llvm::Value *shape; + std::tie(info.parameters, shape) = emitTypeMetadataPack( + IGF, packType, MetadataState::Abstract); + + arguments.push_back(info.parameters.getAddress().getAddress()); + + if (flags.hasParameterFlags()) { + info.paramFlags = emitDynamicFunctionParameterFlags( + IGF, params, packType, shape); + + arguments.push_back(info.paramFlags.getAddress().getAddress()); + } else { + arguments.push_back(llvm::ConstantPointerNull::get( + IGF.IGM.Int32Ty->getPointerTo())); + } + + return info; +} + +static void cleanupFunctionTypeMetadataParams(IRGenFunction &IGF, + FunctionTypeMetadataParamInfo info) { + if (info.parameters.isValid()) { + if (info.parameters.getExtraInfo()) { + IGF.emitDeallocateDynamicAlloca(info.parameters); + } else { + IGF.Builder.CreateLifetimeEnd(info.parameters.getAddress(), + IGF.IGM.getPointerSize() * info.numParams); + } + } +} + +static CanPackType getInducedPackType(AnyFunctionType::CanParamArrayRef params, + ASTContext &ctx) { + SmallVector elts; + for (auto param : params) + elts.push_back(param.getPlainType()); + + return CanPackType::get(ctx, elts); +} + +static MetadataResponse emitFunctionTypeMetadataRef(IRGenFunction &IGF, + CanFunctionType type, + DynamicMetadataRequest request) { + auto result = + IGF.emitAbstractTypeMetadataRef(type->getResult()->getCanonicalType()); + + auto params = type.getParams(); + bool hasPackExpansion = type->containsPackExpansionParam(); + + auto flags = getFunctionTypeFlags(type); + llvm::Value *flagsVal = nullptr; + llvm::Value *shapeExpression = nullptr; + CanPackType packType; + + if (!hasPackExpansion) { + flags = flags.withNumParameters(params.size()); + flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy, + flags.getIntValue()); + } else { + packType = getInducedPackType(type.getParams(), type->getASTContext()); + auto *shapeExpression = IGF.emitPackShapeExpression(packType); + + flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy, + flags.getIntValue()); + flagsVal = IGF.Builder.CreateOr(flagsVal, shapeExpression); + } + + auto constructSimpleCall = + [&](llvm::SmallVectorImpl &arguments) + -> FunctionPointer { + assert(!flags.hasParameterFlags()); + assert(!shapeExpression); + + arguments.push_back(flagsVal); + + for (auto param : params) { + arguments.push_back(getFunctionParameterRef(IGF, param)); + } + + arguments.push_back(result); + + switch (params.size()) { + case 0: + return IGF.IGM.getGetFunctionMetadata0FunctionPointer(); + + case 1: + return IGF.IGM.getGetFunctionMetadata1FunctionPointer(); + + case 2: + return IGF.IGM.getGetFunctionMetadata2FunctionPointer(); + + case 3: + return IGF.IGM.getGetFunctionMetadata3FunctionPointer(); + + default: + llvm_unreachable("supports only 1/2/3 parameter functions"); + } + }; + + switch (params.size()) { + case 0: + case 1: + case 2: + case 3: { + if (!flags.hasParameterFlags() && !type->isDifferentiable() && + !type->getGlobalActor() && !hasPackExpansion) { + llvm::SmallVector arguments; + auto metadataFn = constructSimpleCall(arguments); + auto *call = IGF.Builder.CreateCall(metadataFn, arguments); + call->setDoesNotThrow(); + return MetadataResponse::forComplete(call); + } + + // If function type has parameter flags or is differentiable or has a + // global actor, emit the most general function to retrieve them. + LLVM_FALLTHROUGH; + } + + default: + assert((!params.empty() || type->isDifferentiable() || + type->getGlobalActor()) && + "0 parameter case should be specialized unless it is a " + "differentiable function or has a global actor"); + + llvm::SmallVector arguments; + + arguments.push_back(flagsVal); + + llvm::Value *diffKindVal = nullptr; + + { + FunctionMetadataDifferentiabilityKind metadataDifferentiabilityKind; + switch (type->getDifferentiabilityKind()) { + case DifferentiabilityKind::NonDifferentiable: + metadataDifferentiabilityKind = + FunctionMetadataDifferentiabilityKind::NonDifferentiable; + break; + case DifferentiabilityKind::Normal: + metadataDifferentiabilityKind = + FunctionMetadataDifferentiabilityKind::Normal; + break; + case DifferentiabilityKind::Linear: + metadataDifferentiabilityKind = + FunctionMetadataDifferentiabilityKind::Linear; + break; + case DifferentiabilityKind::Forward: + metadataDifferentiabilityKind = + FunctionMetadataDifferentiabilityKind::Forward; + break; + case DifferentiabilityKind::Reverse: + metadataDifferentiabilityKind = + FunctionMetadataDifferentiabilityKind::Reverse; + break; + } + + if (type->isDifferentiable()) { + assert(metadataDifferentiabilityKind.isDifferentiable()); + diffKindVal = llvm::ConstantInt::get( + IGF.IGM.SizeTy, metadataDifferentiabilityKind.getIntValue()); + } else if (type->getGlobalActor()) { + diffKindVal = llvm::ConstantInt::get( + IGF.IGM.SizeTy, + FunctionMetadataDifferentiabilityKind::NonDifferentiable); + } + } + + if (diffKindVal) { + arguments.push_back(diffKindVal); + } + + FunctionTypeMetadataParamInfo info; + if (!hasPackExpansion) { + assert(!shapeExpression); + info = emitFunctionTypeMetadataParams(IGF, params, flags, request, + arguments); + } else { + info = emitDynamicFunctionTypeMetadataParams(IGF, params, flags, packType, + request, arguments); + } + + arguments.push_back(result); + + if (Type globalActor = type->getGlobalActor()) { + arguments.push_back( + IGF.emitAbstractTypeMetadataRef(globalActor->getCanonicalType())); + } + + auto getMetadataFn = + type->getGlobalActor() + ? (IGF.IGM.isConcurrencyAvailable() + ? IGF.IGM + .getGetFunctionMetadataGlobalActorFunctionPointer() + : IGF.IGM + .getGetFunctionMetadataGlobalActorBackDeployFunctionPointer()) + : type->isDifferentiable() + ? IGF.IGM.getGetFunctionMetadataDifferentiableFunctionPointer() + : IGF.IGM.getGetFunctionMetadataFunctionPointer(); + + auto call = IGF.Builder.CreateCall(getMetadataFn, arguments); + call->setDoesNotThrow(); + + cleanupFunctionTypeMetadataParams(IGF, info); + + return MetadataResponse::forComplete(call); + } +} + namespace { /// A visitor class for emitting a reference to a metatype object. /// This implements a "raw" access, useful for implementing cache @@ -1524,256 +1852,14 @@ namespace { return MetadataResponse::getUndef(IGF); } - llvm::Value *getFunctionParameterRef(AnyFunctionType::CanParam ¶m) { - auto type = param.getPlainType()->getCanonicalType(); - return IGF.emitAbstractTypeMetadataRef(type); - } - MetadataResponse visitFunctionType(CanFunctionType type, DynamicMetadataRequest request) { if (auto metatype = tryGetLocal(type, request)) return metatype; - auto result = - IGF.emitAbstractTypeMetadataRef(type->getResult()->getCanonicalType()); - - auto params = type.getParams(); - auto numParams = params.size(); - - // Retrieve the ABI parameter flags from the type-level parameter - // flags. - auto getABIParameterFlags = [](ParameterTypeFlags flags) { - return ParameterFlags() - .withValueOwnership(flags.getValueOwnership()) - .withVariadic(flags.isVariadic()) - .withAutoClosure(flags.isAutoClosure()) - .withNoDerivative(flags.isNoDerivative()) - .withIsolated(flags.isIsolated()); - }; - - bool hasParameterFlags = false; - for (auto param : params) { - if (!getABIParameterFlags(param.getParameterFlags()).isNone()) { - hasParameterFlags = true; - break; - } - } + auto response = emitFunctionTypeMetadataRef(IGF, type, request); - // Map the convention to a runtime metadata value. - FunctionMetadataConvention metadataConvention; - bool isEscaping = false; - switch (type->getRepresentation()) { - case FunctionTypeRepresentation::Swift: - metadataConvention = FunctionMetadataConvention::Swift; - isEscaping = !type->isNoEscape(); - break; - case FunctionTypeRepresentation::Thin: - metadataConvention = FunctionMetadataConvention::Thin; - break; - case FunctionTypeRepresentation::Block: - metadataConvention = FunctionMetadataConvention::Block; - break; - case FunctionTypeRepresentation::CFunctionPointer: - metadataConvention = FunctionMetadataConvention::CFunctionPointer; - break; - } - - FunctionMetadataDifferentiabilityKind metadataDifferentiabilityKind; - switch (type->getDifferentiabilityKind()) { - case DifferentiabilityKind::NonDifferentiable: - metadataDifferentiabilityKind = - FunctionMetadataDifferentiabilityKind::NonDifferentiable; - break; - case DifferentiabilityKind::Normal: - metadataDifferentiabilityKind = - FunctionMetadataDifferentiabilityKind::Normal; - break; - case DifferentiabilityKind::Linear: - metadataDifferentiabilityKind = - FunctionMetadataDifferentiabilityKind::Linear; - break; - case DifferentiabilityKind::Forward: - metadataDifferentiabilityKind = - FunctionMetadataDifferentiabilityKind::Forward; - break; - case DifferentiabilityKind::Reverse: - metadataDifferentiabilityKind = - FunctionMetadataDifferentiabilityKind::Reverse; - break; - } - - auto flags = FunctionTypeFlags() - .withNumParameters(numParams) - .withConvention(metadataConvention) - .withAsync(type->isAsync()) - .withConcurrent(type->isSendable()) - .withThrows(type->isThrowing()) - .withParameterFlags(hasParameterFlags) - .withEscaping(isEscaping) - .withDifferentiable(type->isDifferentiable()) - .withGlobalActor(!type->getGlobalActor().isNull()); - - auto flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy, - flags.getIntValue()); - llvm::Value *diffKindVal = nullptr; - if (type->isDifferentiable()) { - assert(metadataDifferentiabilityKind.isDifferentiable()); - diffKindVal = llvm::ConstantInt::get( - IGF.IGM.SizeTy, metadataDifferentiabilityKind.getIntValue()); - } else if (type->getGlobalActor()) { - diffKindVal = llvm::ConstantInt::get( - IGF.IGM.SizeTy, - FunctionMetadataDifferentiabilityKind::NonDifferentiable); - } - - auto collectParameters = - [&](llvm::function_ref - processor) { - for (auto index : indices(params)) { - auto param = params[index]; - auto flags = param.getParameterFlags(); - - auto parameterFlags = getABIParameterFlags(flags); - processor(index, getFunctionParameterRef(param), parameterFlags); - } - }; - - auto constructSimpleCall = - [&](llvm::SmallVectorImpl &arguments) - -> FunctionPointer { - arguments.push_back(flagsVal); - - collectParameters([&](unsigned i, llvm::Value *typeRef, - ParameterFlags flags) { - arguments.push_back(typeRef); - if (hasParameterFlags) - arguments.push_back( - llvm::ConstantInt::get(IGF.IGM.Int32Ty, flags.getIntValue())); - }); - - arguments.push_back(result); - - switch (params.size()) { - case 0: - return IGF.IGM.getGetFunctionMetadata0FunctionPointer(); - - case 1: - return IGF.IGM.getGetFunctionMetadata1FunctionPointer(); - - case 2: - return IGF.IGM.getGetFunctionMetadata2FunctionPointer(); - - case 3: - return IGF.IGM.getGetFunctionMetadata3FunctionPointer(); - - default: - llvm_unreachable("supports only 1/2/3 parameter functions"); - } - }; - - switch (numParams) { - case 0: - case 1: - case 2: - case 3: { - if (!hasParameterFlags && !type->isDifferentiable() && - !type->getGlobalActor()) { - llvm::SmallVector arguments; - auto metadataFn = constructSimpleCall(arguments); - auto *call = IGF.Builder.CreateCall(metadataFn, arguments); - call->setDoesNotThrow(); - return setLocal(CanType(type), MetadataResponse::forComplete(call)); - } - - // If function type has parameter flags or is differentiable or has a - // global actor, emit the most general function to retrieve them. - LLVM_FALLTHROUGH; - } - - default: - assert((!params.empty() || type->isDifferentiable() || - type->getGlobalActor()) && - "0 parameter case should be specialized unless it is a " - "differentiable function or has a global actor"); - - auto *const Int32Ptr = IGF.IGM.Int32Ty->getPointerTo(); - llvm::SmallVector arguments; - - arguments.push_back(flagsVal); - - if (diffKindVal) { - arguments.push_back(diffKindVal); - } - - ConstantInitBuilder paramFlags(IGF.IGM); - auto flagsArr = paramFlags.beginArray(); - - Address parameters; - if (!params.empty()) { - auto arrayTy = - llvm::ArrayType::get(IGF.IGM.TypeMetadataPtrTy, numParams); - parameters = IGF.createAlloca( - arrayTy, IGF.IGM.getTypeMetadataAlignment(), "function-parameters"); - - IGF.Builder.CreateLifetimeStart(parameters, - IGF.IGM.getPointerSize() * numParams); - - collectParameters([&](unsigned i, llvm::Value *typeRef, - ParameterFlags flags) { - auto argPtr = IGF.Builder.CreateStructGEP(parameters, i, - IGF.IGM.getPointerSize()); - IGF.Builder.CreateStore(typeRef, argPtr); - if (i == 0) - arguments.push_back(argPtr.getAddress()); - - if (hasParameterFlags) - flagsArr.addInt32(flags.getIntValue()); - }); - } else { - auto parametersPtr = - llvm::ConstantPointerNull::get( - IGF.IGM.TypeMetadataPtrTy->getPointerTo()); - arguments.push_back(parametersPtr); - } - - if (hasParameterFlags) { - auto *flagsVar = flagsArr.finishAndCreateGlobal( - "parameter-flags", IGF.IGM.getPointerAlignment(), - /* constant */ true); - arguments.push_back(IGF.Builder.CreateBitCast(flagsVar, Int32Ptr)); - } else { - flagsArr.abandon(); - arguments.push_back(llvm::ConstantPointerNull::get(Int32Ptr)); - } - - arguments.push_back(result); - - if (Type globalActor = type->getGlobalActor()) { - arguments.push_back( - IGF.emitAbstractTypeMetadataRef(globalActor->getCanonicalType())); - } - - auto getMetadataFn = - type->getGlobalActor() - ? (IGF.IGM.isConcurrencyAvailable() - ? IGF.IGM - .getGetFunctionMetadataGlobalActorFunctionPointer() - : IGF.IGM - .getGetFunctionMetadataGlobalActorBackDeployFunctionPointer()) - : type->isDifferentiable() - ? IGF.IGM.getGetFunctionMetadataDifferentiableFunctionPointer() - : IGF.IGM.getGetFunctionMetadataFunctionPointer(); - - auto call = IGF.Builder.CreateCall(getMetadataFn, arguments); - call->setDoesNotThrow(); - - if (parameters.isValid()) - IGF.Builder.CreateLifetimeEnd(parameters, - IGF.IGM.getPointerSize() * numParams); - - return setLocal(type, MetadataResponse::forComplete(call)); - } + return setLocal(type, response); } MetadataResponse visitMetatypeType(CanMetatypeType type, diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index d8e08b78f50e2..f752e399c74ed 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -709,6 +709,8 @@ MetadataResponse emitCheckTypeMetadataState(IRGenFunction &IGF, OperationCost getCheckTypeMetadataStateCost(DynamicMetadataRequest request, MetadataResponse response); +ParameterFlags getABIParameterFlags(ParameterTypeFlags flags); + } // end namespace irgen } // end namespace swift diff --git a/test/Interpreter/variadic_generic_func_types.swift b/test/Interpreter/variadic_generic_func_types.swift new file mode 100644 index 0000000000000..3c7614ba87cd6 --- /dev/null +++ b/test/Interpreter/variadic_generic_func_types.swift @@ -0,0 +1,42 @@ +// RUN: %target-run-simple-swift + +// REQUIRES: executable_test + +import StdlibUnittest + +var funcs = TestSuite("VariadicGenericFuncTypes") + +func makeFunctionType1(_: repeat (each T).Type) -> Any.Type { + return ((repeat each T) -> ()).self +} + +func makeFunctionType2(_: repeat (each T).Type) -> Any.Type { + return ((Character, repeat each T, Bool) -> ()).self +} + +func makeFunctionType3(_: repeat (each T).Type) -> Any.Type { + return ((inout Character, repeat each T, inout Bool) -> ()).self +} + +funcs.test("makeFunctionType1") { + expectEqual("() -> ()", _typeName(makeFunctionType1())) + expectEqual("(Swift.Int) -> ()", _typeName(makeFunctionType1(Int.self))) + expectEqual("(Swift.Int, Swift.String) -> ()", _typeName(makeFunctionType1(Int.self, String.self))) + expectEqual("(Swift.Int, Swift.Float, Swift.String) -> ()", _typeName(makeFunctionType1(Int.self, Float.self, String.self))) +} + +funcs.test("makeFunctionType2") { + expectEqual("(Swift.Character, Swift.Bool) -> ()", _typeName(makeFunctionType2())) + expectEqual("(Swift.Character, Swift.Int, Swift.Bool) -> ()", _typeName(makeFunctionType2(Int.self))) + expectEqual("(Swift.Character, Swift.Int, Swift.String, Swift.Bool) -> ()", _typeName(makeFunctionType2(Int.self, String.self))) + expectEqual("(Swift.Character, Swift.Int, Swift.Float, Swift.String, Swift.Bool) -> ()", _typeName(makeFunctionType2(Int.self, Float.self, String.self))) +} + +funcs.test("makeFunctionType3") { + expectEqual("(inout Swift.Character, inout Swift.Bool) -> ()", _typeName(makeFunctionType3())) + expectEqual("(inout Swift.Character, Swift.Int, inout Swift.Bool) -> ()", _typeName(makeFunctionType3(Int.self))) + expectEqual("(inout Swift.Character, Swift.Int, Swift.String, inout Swift.Bool) -> ()", _typeName(makeFunctionType3(Int.self, String.self))) + expectEqual("(inout Swift.Character, Swift.Int, Swift.Float, Swift.String, inout Swift.Bool) -> ()", _typeName(makeFunctionType3(Int.self, Float.self, String.self))) +} + +runAllTests() diff --git a/test/Interpreter/variadic_generic_tuples.swift b/test/Interpreter/variadic_generic_tuples.swift index 6558faaf325c8..bdbdcd66fefe2 100644 --- a/test/Interpreter/variadic_generic_tuples.swift +++ b/test/Interpreter/variadic_generic_tuples.swift @@ -64,4 +64,26 @@ tuples.test("expandTuple") { expandTupleElements(1, "hello", true) } +func tupleLabelMix(t: repeat (each T).Type, u: repeat (each U).Type) -> Any.Type { + return (Float, hello: Int, repeat each T, swift: String, repeat each U, Bool, world: UInt8).self +} + +func oneElementLabeledTuple(t: repeat (each T).Type) -> Any.Type { + return (label: Int, repeat each T).self +} + +tuples.test("labels") { + expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix())) + expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Double, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(u: Double.self))) + expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Double, Swift.Int32, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(u: Double.self, Int32.self))) + expectEqual("(Swift.Float, hello: Swift.Int, Swift.Character, swift: Swift.String, Swift.Double, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(t: Character.self, u: Double.self))) + expectEqual("(Swift.Float, hello: Swift.Int, Swift.Character, Swift.Substring, swift: Swift.String, Swift.Double, Swift.Int32, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(t: Character.self, Substring.self, u: Double.self, Int32.self))) + + // FIXME: One-element labeled tuples + expectEqual("Swift.Int", _typeName(oneElementLabeledTuple())) + + expectEqual("(label: Swift.Int, Swift.String)", _typeName(oneElementLabeledTuple(t: String.self))) +} + + runAllTests()