Files
swift-mirror/include/swift/SIL/SILFunctionConventions.h
Joe Groff 03c7919b4a SIL: Add fields to SILFunctionType for substituted function types.
https://forums.swift.org/t/improving-the-representation-of-polymorphic-interfaces-in-sil-with-substituted-function-types/29711

This prepares SIL to be able to more accurately preserve the calling convention of
polymorphic generic interfaces by letting the type system represent "substituted function types".
We add a couple of fields to SILFunctionType to support this:

- A substitution map, accessed by `getSubstitutions()`, which maps the generic signature
  of the function to its concrete implementation. This will allow, for instance, a protocol
  witness for a requirement of type `<Self: P> (Self, ...) -> ...` for a concrete conforming
  type `Foo` to express its type as `<Self: P> (Self, ...) -> ... for <Foo>`, preserving the relation
  to the protocol interface without relying on the pile of hacks that is the `witness_method`
  protocol.

- A bool for whether the generic signature of the function is "implied" by the substitutions.
  If true, the generic signature isn't really part of the calling convention of the function.
  This will allow closure types to distinguish a closure being passed to a generic function, like
  `<T, U> in (*T, *U) -> T for <Int, String>`, from the concrete type `(*Int, *String) -> Int`,
  which will make it easier for us to differentiate the representation of those as types, for
  instance by giving them different pointer authentication discriminators to harden arm64e
  code.

This patch is currently NFC, it just introduces the new APIs and takes a first pass at updating
code to use them. Much more work will need to be done once we start exercising these new
fields.

This does bifurcate some existing APIs:

- SILFunctionType now has two accessors to get its generic signature.
  `getSubstGenericSignature` gets the generic signature that is used to apply its
  substitution map, if any. `getInvocationGenericSignature` gets the generic signature
  used to invoke the function at apply sites. These differ if the generic signature is
  implied.
- SILParameterInfo and SILResultInfo values carry the unsubstituted types of the parameters
  and results of the function. They now have two APIs to get that type. `getInterfaceType`
  returns the unsubstituted type of the generic interface, and
  `getArgumentType`/`getReturnValueType` produce the substituted type that is used at
  apply sites.
2019-10-25 13:38:51 -07:00

543 lines
19 KiB
C++

//===- SILFunctionConventions.h - Defines SIL func. conventions -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file defines the SILModuleConventions and SILFunctionConventions
/// classes. These interfaces are used to determine when SIL can represent
/// values of a given lowered type by value and when they must be represented by
/// address. This is influenced by a SILModule-wide "lowered address" convention,
/// which reflects whether the current SIL stage requires lowered addresses.
///
/// The primary purpose of this API is mapping the formal SIL parameter and
/// result conventions onto the SIL argument types. The "formal" conventions are
/// immutably associated with a SILFunctionType--a SIL function's type
/// information never changes. The SIL conventions determine how those formal
/// conventions will be represented in the body of SIL functions and at call
/// sites.
///
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_FUNCTIONCONVENTIONS_H
#define SWIFT_SIL_FUNCTIONCONVENTIONS_H
#include "swift/AST/Types.h"
#include "swift/SIL/SILArgumentConvention.h"
#include "swift/SIL/SILType.h"
#include "llvm/Support/ErrorHandling.h"
namespace swift {
template<bool _, template<typename...> class T, typename...Args>
struct delay_template_expansion {
using type = T<Args...>;
};
/// Transient wrapper for SILParameterInfo and SILResultInfo conventions. This
/// abstraction helps handle the transition from canonical SIL conventions to
/// lowered SIL conventions.
class SILModuleConventions {
friend SILParameterInfo;
friend SILResultInfo;
friend SILFunctionConventions;
static bool isIndirectSILParam(SILParameterInfo param,
bool loweredAddresses);
static bool isIndirectSILYield(SILYieldInfo yield,
bool loweredAddresses);
static bool isIndirectSILResult(SILResultInfo result,
bool loweredAddresses);
static SILType getSILParamInterfaceType(
SILParameterInfo yield,
bool loweredAddresses);
static SILType getSILYieldInterfaceType(
SILYieldInfo yield,
bool loweredAddresses);
static SILType getSILResultInterfaceType(
SILResultInfo param,
bool loweredAddresses);
public:
static bool isPassedIndirectlyInSIL(SILType type, SILModule &M);
static bool isReturnedIndirectlyInSIL(SILType type, SILModule &M);
static SILModuleConventions getLoweredAddressConventions(SILModule &M) {
return SILModuleConventions(M, true);
}
private:
SILModule *M;
bool loweredAddresses;
SILModuleConventions(SILModule &M, bool loweredAddresses)
: M(&M), loweredAddresses(loweredAddresses)
{}
public:
SILModuleConventions(SILModule &M);
SILFunctionConventions getFunctionConventions(CanSILFunctionType funcTy);
SILModule &getModule() const { return *M; }
bool useLoweredAddresses() const { return loweredAddresses; }
bool isSILIndirect(SILParameterInfo param) const {
return isIndirectSILParam(param, loweredAddresses);
}
bool isSILIndirect(SILYieldInfo yield) const {
return isIndirectSILYield(yield, loweredAddresses);
}
bool isSILIndirect(SILResultInfo result) const {
return isIndirectSILResult(result, loweredAddresses);
}
SILType getSILType(SILParameterInfo param,
CanSILFunctionType funcTy) const {
auto interfaceTy = getSILParamInterfaceType(param, loweredAddresses);
// TODO: Always require a function type
if (funcTy)
return funcTy->substInterfaceType(*M, interfaceTy);
return interfaceTy;
}
SILType getSILType(SILYieldInfo yield,
CanSILFunctionType funcTy) const {
auto interfaceTy = getSILYieldInterfaceType(yield, loweredAddresses);
// TODO: Always require a function type
if (funcTy)
return funcTy->substInterfaceType(*M, interfaceTy);
return interfaceTy;
}
SILType getSILType(SILResultInfo result,
CanSILFunctionType funcTy) const {
auto interfaceTy = getSILResultInterfaceType(result, loweredAddresses);
// TODO: Always require a function type
if (funcTy)
return funcTy->substInterfaceType(*M, interfaceTy);
return interfaceTy;
}
};
/// Transient wrapper for SIL-level argument conventions. This abstraction helps
/// handle the transition from canonical SIL conventions to lowered SIL
/// conventions.
class SILFunctionConventions {
public:
SILModuleConventions silConv;
CanSILFunctionType funcTy;
SILFunctionConventions(CanSILFunctionType funcTy, SILModule &M)
: silConv(M), funcTy(funcTy) {}
SILFunctionConventions(CanSILFunctionType funcTy,
SILModuleConventions silConv)
: silConv(silConv), funcTy(funcTy) {}
//===--------------------------------------------------------------------===//
// SILModuleConventions API for convenience.
//===--------------------------------------------------------------------===//
bool useLoweredAddresses() const { return silConv.useLoweredAddresses(); }
bool isSILIndirect(SILParameterInfo param) const {
return silConv.isSILIndirect(param);
}
bool isSILIndirect(SILYieldInfo yield) const {
return silConv.isSILIndirect(yield);
}
bool isSILIndirect(SILResultInfo result) const {
return silConv.isSILIndirect(result);
}
SILType getSILType(SILParameterInfo param) const {
return silConv.getSILType(param, funcTy);
}
SILType getSILType(SILYieldInfo yield) const {
return silConv.getSILType(yield, funcTy);
}
SILType getSILType(SILResultInfo result) const {
return silConv.getSILType(result, funcTy);
}
//===--------------------------------------------------------------------===//
// SIL results.
//===--------------------------------------------------------------------===//
/// Get the normal result type of an apply that calls this function.
/// This does not include indirect SIL results.
SILType getSILResultType() {
if (silConv.loweredAddresses)
return funcTy->getDirectFormalResultsType(silConv.getModule());
return funcTy->getAllResultsSubstType(silConv.getModule());
}
/// Get the SIL type for the single result which may be direct or indirect.
SILType getSingleSILResultType() {
return getSILType(funcTy->getSingleResult());
}
/// Get the error result type.
SILType getSILErrorType() { return getSILType(funcTy->getErrorResult()); }
/// Returns an array of result info.
/// Provides convenient access to the underlying SILFunctionType.
ArrayRef<SILResultInfo> getResults() const {
return funcTy->getResults();
}
/// Get the number of SIL results passed as address-typed arguments.
unsigned getNumIndirectSILResults() const {
return silConv.loweredAddresses ? funcTy->getNumIndirectFormalResults() : 0;
}
/// Are any SIL results passed as address-typed arguments?
bool hasIndirectSILResults() const { return getNumIndirectSILResults() != 0; }
using IndirectSILResultIter = SILFunctionType::IndirectFormalResultIter;
using IndirectSILResultRange = SILFunctionType::IndirectFormalResultRange;
/// Return a range of indirect result information for results passed as
/// address-typed SIL arguments.
IndirectSILResultRange getIndirectSILResults() const {
if (silConv.loweredAddresses)
return funcTy->getIndirectFormalResults();
return llvm::make_filter_range(
llvm::make_range((const SILResultInfo *)0, (const SILResultInfo *)0),
SILFunctionType::IndirectFormalResultFilter());
}
struct SILResultTypeFunc;
// Gratuitous template parameter is to delay instantiating `mapped_iterator`
// on the incomplete type SILParameterTypeFunc.
template<bool _ = false>
using IndirectSILResultTypeIter = typename delay_template_expansion<_,
llvm::mapped_iterator, IndirectSILResultIter, SILResultTypeFunc>::type;
template<bool _ = false>
using IndirectSILResultTypeRange = iterator_range<IndirectSILResultTypeIter<_>>;
/// Return a range of SILTypes for each result passed as an address-typed SIL
/// argument.
template<bool _ = false>
IndirectSILResultTypeRange<_> getIndirectSILResultTypes() const;
/// Get the number of SIL results directly returned by SIL value.
unsigned getNumDirectSILResults() const {
return silConv.loweredAddresses ? funcTy->getNumDirectFormalResults()
: funcTy->getNumResults();
}
struct DirectSILResultFilter {
bool loweredAddresses;
DirectSILResultFilter(bool loweredAddresses)
: loweredAddresses(loweredAddresses) {}
bool operator()(SILResultInfo result) const {
return !(loweredAddresses && result.isFormalIndirect());
}
};
using DirectSILResultIter =
llvm::filter_iterator<const SILResultInfo *, DirectSILResultFilter>;
using DirectSILResultRange = iterator_range<DirectSILResultIter>;
/// Return a range of direct result information for results directly returned
/// by SIL value.
DirectSILResultRange getDirectSILResults() const {
return llvm::make_filter_range(
funcTy->getResults(), DirectSILResultFilter(silConv.loweredAddresses));
}
template<bool _ = false>
using DirectSILResultTypeIter = typename delay_template_expansion<_,
llvm::mapped_iterator, DirectSILResultIter, SILResultTypeFunc>::type;
template<bool _ = false>
using DirectSILResultTypeRange = iterator_range<DirectSILResultTypeIter<_>>;
/// Return a range of SILTypes for each result directly returned
/// by SIL value.
template<bool _ = false>
DirectSILResultTypeRange<_> getDirectSILResultTypes() const;
//===--------------------------------------------------------------------===//
// SIL parameters types.
//===--------------------------------------------------------------------===//
/// Returns the number of function parameters, not including any formally
/// indirect results. Provides convenient access to the underlying
/// SILFunctionType.
unsigned getNumParameters() const { return funcTy->getNumParameters(); }
/// Returns an array of parameter info, not including indirect
/// results. Provides convenient access to the underlying SILFunctionType.
ArrayRef<SILParameterInfo> getParameters() const {
return funcTy->getParameters();
}
struct SILParameterTypeFunc;
// Gratuitous template parameter is to delay instantiating `mapped_iterator`
// on the incomplete type SILParameterTypeFunc.
template<bool _ = false>
using SILParameterTypeIter = typename
delay_template_expansion<_, llvm::mapped_iterator,
const SILParameterInfo *, SILParameterTypeFunc>::type;
template<bool _ = false>
using SILParameterTypeRange = iterator_range<SILParameterTypeIter<_>>;
/// Return a range of SILTypes for each function parameter, not including
/// indirect results.
template<bool _ = false>
SILParameterTypeRange<_> getParameterSILTypes() const;
//===--------------------------------------------------------------------===//
// SIL yield types.
//===--------------------------------------------------------------------===//
unsigned getNumYields() const { return funcTy->getNumYields(); }
ArrayRef<SILYieldInfo> getYields() const {
return funcTy->getYields();
}
template<bool _ = false>
using SILYieldTypeIter = typename
delay_template_expansion<_, llvm::mapped_iterator,
const SILYieldInfo *, SILParameterTypeFunc>::type;
template<bool _ = false>
using SILYieldTypeRange = iterator_range<SILYieldTypeIter<_>>;
template<bool _ = false>
SILYieldTypeRange<_> getYieldSILTypes() const;
SILYieldInfo getYieldInfoForOperandIndex(unsigned opIndex) const {
return getYields()[opIndex];
}
//===--------------------------------------------------------------------===//
// SILArgument API, including indirect results and parameters.
//
// The argument indices below relate to full applies in which the caller and
// callee indices match. Partial apply indices are shifted on the caller
// side. See ApplySite::getCalleeArgIndexOfFirstAppliedArg().
//===--------------------------------------------------------------------===//
unsigned getSILArgIndexOfFirstIndirectResult() const { return 0; }
unsigned getSILArgIndexOfFirstParam() const {
return getNumIndirectSILResults();
}
/// Get the index into formal indirect results corresponding to the given SIL
/// indirect result argument index.
unsigned getIndirectFormalResultIndexForSILArg(unsigned argIdx) const {
assert(argIdx <= getNumIndirectSILResults());
unsigned curArgIdx = 0;
unsigned formalIdx = 0;
for (auto formalResult : funcTy->getIndirectFormalResults()) {
if (isSILIndirect(formalResult)) {
if (curArgIdx == argIdx)
return formalIdx;
++curArgIdx;
}
++formalIdx;
}
llvm_unreachable("missing indirect formal result for SIL argument.");
}
/// Get the total number of arguments for a full apply in SIL of
/// this function type. This is also the total number of SILArguments
/// in the entry block.
unsigned getNumSILArguments() const {
return getNumIndirectSILResults() + funcTy->getNumParameters();
}
SILParameterInfo getParamInfoForSILArg(unsigned index) const {
assert(index >= getNumIndirectSILResults()
&& index <= getNumSILArguments());
return funcTy->getParameters()[index - getNumIndirectSILResults()];
}
/// Return the SIL argument convention of apply/entry argument at
/// the given argument index.
SILArgumentConvention getSILArgumentConvention(unsigned index) const;
// See SILArgument.h.
/// Return the SIL type of the apply/entry argument at the given index.
SILType getSILArgumentType(unsigned index) const;
};
struct SILFunctionConventions::SILResultTypeFunc {
SILFunctionConventions silConv;
SILResultTypeFunc(const SILFunctionConventions &silConv)
: silConv(silConv) {}
SILType operator()(SILResultInfo result) const {
return silConv.getSILType(result);
}
};
template<bool _>
SILFunctionConventions::IndirectSILResultTypeRange<_>
SILFunctionConventions::getIndirectSILResultTypes() const {
return llvm::map_range(getIndirectSILResults(), SILResultTypeFunc(*this));
}
template<bool _>
SILFunctionConventions::DirectSILResultTypeRange<_>
SILFunctionConventions::getDirectSILResultTypes() const {
return llvm::map_range(getDirectSILResults(), SILResultTypeFunc(*this));
}
struct SILFunctionConventions::SILParameterTypeFunc {
SILFunctionConventions silConv;
SILParameterTypeFunc(const SILFunctionConventions &silConv)
: silConv(silConv) {}
SILType operator()(SILParameterInfo param) const {
return silConv.getSILType(param);
}
};
template<bool _>
SILFunctionConventions::SILParameterTypeRange<_>
SILFunctionConventions::getParameterSILTypes() const {
return llvm::map_range(funcTy->getParameters(),
SILParameterTypeFunc(*this));
}
template<bool _>
SILFunctionConventions::SILYieldTypeRange<_>
SILFunctionConventions::getYieldSILTypes() const {
return llvm::map_range(funcTy->getYields(),
SILParameterTypeFunc(*this));
}
inline SILType
SILFunctionConventions::getSILArgumentType(unsigned index) const {
assert(index <= getNumSILArguments());
if (index < getNumIndirectSILResults()) {
return *std::next(getIndirectSILResultTypes().begin(), index);
}
return getSILType(
funcTy->getParameters()[index - getNumIndirectSILResults()]);
}
inline SILFunctionConventions
SILModuleConventions::getFunctionConventions(CanSILFunctionType funcTy) {
return SILFunctionConventions(funcTy, *this);
}
inline bool SILModuleConventions::isIndirectSILParam(SILParameterInfo param,
bool loweredAddresses) {
switch (param.getConvention()) {
case ParameterConvention::Direct_Unowned:
case ParameterConvention::Direct_Guaranteed:
case ParameterConvention::Direct_Owned:
return false;
case ParameterConvention::Indirect_In:
case ParameterConvention::Indirect_In_Constant:
case ParameterConvention::Indirect_In_Guaranteed:
return (loweredAddresses ||
param.getInterfaceType()->isOpenedExistentialWithError());
case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable:
return true;
}
llvm_unreachable("covered switch isn't covered?!");
}
inline bool SILModuleConventions::isIndirectSILYield(SILYieldInfo yield,
bool loweredAddresses) {
return isIndirectSILParam(yield, loweredAddresses);
}
inline bool SILModuleConventions::isIndirectSILResult(SILResultInfo result,
bool loweredAddresses) {
switch (result.getConvention()) {
case ResultConvention::Indirect:
return (loweredAddresses ||
result.getInterfaceType()->isOpenedExistentialWithError());
case ResultConvention::Owned:
case ResultConvention::Unowned:
case ResultConvention::UnownedInnerPointer:
case ResultConvention::Autoreleased:
return false;
}
llvm_unreachable("Unhandled ResultConvention in switch.");
}
inline SILType SILModuleConventions::getSILParamInterfaceType(
SILParameterInfo param,
bool loweredAddresses) {
return SILModuleConventions::isIndirectSILParam(param,loweredAddresses)
? SILType::getPrimitiveAddressType(param.getInterfaceType())
: SILType::getPrimitiveObjectType(param.getInterfaceType());
}
inline SILType SILModuleConventions::getSILYieldInterfaceType(
SILYieldInfo yield,
bool loweredAddresses) {
return getSILParamInterfaceType(yield, loweredAddresses);
}
inline SILType SILModuleConventions::getSILResultInterfaceType(
SILResultInfo result,
bool loweredAddresses) {
return SILModuleConventions::isIndirectSILResult(result, loweredAddresses)
? SILType::getPrimitiveAddressType(result.getInterfaceType())
: SILType::getPrimitiveObjectType(result.getInterfaceType());
}
inline SILType
SILParameterInfo::getSILStorageInterfaceType() const {
return SILModuleConventions::getSILParamInterfaceType(*this, true);
}
inline SILType
SILResultInfo::getSILStorageInterfaceType() const {
return SILModuleConventions::getSILResultInterfaceType(*this, true);
}
inline SILType
SILParameterInfo::getSILStorageType(SILModule &M,
const SILFunctionType *funcTy) const {
return funcTy->substInterfaceType(M, getSILStorageInterfaceType());
}
inline SILType
SILResultInfo::getSILStorageType(SILModule &M,
const SILFunctionType *funcTy) const {
return funcTy->substInterfaceType(M, getSILStorageInterfaceType());
}
} // end swift namespace
#endif