Skip to content

[6.2] Fix IRGen for @_addressable params which may be "captured". #80709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5452,6 +5452,10 @@ class SILFunctionType final
return getParameters().back();
}

unsigned getSelfParameterIndex() const {
return NumParameters - 1;
}

/// Return SILParameterInfo for the isolated parameter in this SILFunctionType
/// if one exists. Returns None otherwise.
std::optional<SILParameterInfo> maybeGetIsolatedParameter() const {
Expand Down Expand Up @@ -5656,6 +5660,17 @@ class SILFunctionType final
/// Defined in SILType.cpp.
bool isAddressable(unsigned paramIdx, SILFunction *caller);

/// Return true of the specified parameter is addressable based on its type
/// lowering. This includes @_addressableForDependencies parameter types.
///
/// 'genericEnv' may be null.
///
/// Defined in SILType.cpp.
bool isAddressable(unsigned paramIdx, SILModule &module,
GenericEnvironment *genericEnv,
Lowering::TypeConverter &typeConverter,
TypeExpansionContext expansion);

/// Returns true if the function type stores a Clang type that cannot
/// be derived from its Swift type. Returns false otherwise, including if
/// the function type is not @convention(c) or @convention(block).
Expand Down
51 changes: 32 additions & 19 deletions lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,14 @@ static void addDereferenceableAttributeToBuilder(IRGenModule &IGM,
static void addIndirectValueParameterAttributes(IRGenModule &IGM,
llvm::AttributeList &attrs,
const TypeInfo &ti,
unsigned argIndex) {
unsigned argIndex,
bool addressable) {
llvm::AttrBuilder b(IGM.getLLVMContext());
// Value parameter pointers can't alias or be captured.
b.addAttribute(llvm::Attribute::NoAlias);
// Bitwise takable value types are guaranteed not to capture
// a pointer into itself.
if (ti.isBitwiseTakable(ResilienceExpansion::Maximal))
if (!addressable && ti.isBitwiseTakable(ResilienceExpansion::Maximal))
b.addAttribute(llvm::Attribute::NoCapture);
// The parameter must reference dereferenceable memory of the type.
addDereferenceableAttributeToBuilder(IGM, b, ti);
Expand Down Expand Up @@ -340,7 +341,7 @@ static void addPackParameterAttributes(IRGenModule &IGM,
static void addInoutParameterAttributes(IRGenModule &IGM, SILType paramSILType,
llvm::AttributeList &attrs,
const TypeInfo &ti, unsigned argIndex,
bool aliasable) {
bool aliasable, bool addressable) {
llvm::AttrBuilder b(IGM.getLLVMContext());
// Thanks to exclusivity checking, it is not possible to alias inouts except
// those that are inout_aliasable.
Expand All @@ -351,7 +352,7 @@ static void addInoutParameterAttributes(IRGenModule &IGM, SILType paramSILType,
}
// Bitwise takable value types are guaranteed not to capture
// a pointer into itself.
if (ti.isBitwiseTakable(ResilienceExpansion::Maximal))
if (!addressable && ti.isBitwiseTakable(ResilienceExpansion::Maximal))
b.addAttribute(llvm::Attribute::NoCapture);
// The inout must reference dereferenceable memory of the type.
addDereferenceableAttributeToBuilder(IGM, b, ti);
Expand Down Expand Up @@ -600,9 +601,11 @@ namespace {
Signature getSignature();

private:
const TypeInfo &expand(SILParameterInfo param);
const TypeInfo &expand(unsigned paramIdx);
llvm::Type *addIndirectResult(SILType resultType, bool useInReg = false);

bool isAddressableParam(unsigned paramIdx);

SILFunctionConventions getSILFuncConventions() const {
return SILFunctionConventions(FnType, IGM.getSILModule());
}
Expand Down Expand Up @@ -1781,24 +1784,27 @@ static ArrayRef<llvm::Type *> expandScalarOrStructTypeToArray(llvm::Type *&ty) {
return expandedTys;
}

const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) {
const TypeInfo &SignatureExpansion::expand(unsigned paramIdx) {
auto param = FnType->getParameters()[paramIdx];
auto paramSILType = getSILFuncConventions().getSILType(
param, IGM.getMaximalTypeExpansionContext());
auto &ti = IGM.getTypeInfo(paramSILType);
switch (auto conv = param.getConvention()) {
case ParameterConvention::Indirect_In:
case ParameterConvention::Indirect_In_Guaranteed:
case ParameterConvention::Indirect_In_CXX:
addIndirectValueParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size());
addIndirectValueParameterAttributes(IGM, Attrs, ti, ParamIRTypes.size(),
isAddressableParam(paramIdx));
addPointerParameter(IGM.getStorageType(getSILFuncConventions().getSILType(
param, IGM.getMaximalTypeExpansionContext())));
return ti;

case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable:
addInoutParameterAttributes(
IGM, paramSILType, Attrs, ti, ParamIRTypes.size(),
conv == ParameterConvention::Indirect_InoutAliasable);
IGM, paramSILType, Attrs, ti, ParamIRTypes.size(),
conv == ParameterConvention::Indirect_InoutAliasable,
isAddressableParam(paramIdx));
addPointerParameter(IGM.getStorageType(getSILFuncConventions().getSILType(
param, IGM.getMaximalTypeExpansionContext())));
return ti;
Expand All @@ -1822,7 +1828,8 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) {
auto &nativeSchema = ti.nativeParameterValueSchema(IGM);
if (nativeSchema.requiresIndirect()) {
addIndirectValueParameterAttributes(IGM, Attrs, ti,
ParamIRTypes.size());
ParamIRTypes.size(),
/*addressable*/ false);
ParamIRTypes.push_back(ti.getStorageType()->getPointerTo());
return ti;
}
Expand All @@ -1842,6 +1849,13 @@ const TypeInfo &SignatureExpansion::expand(SILParameterInfo param) {
llvm_unreachable("bad parameter convention");
}

bool SignatureExpansion::isAddressableParam(unsigned paramIdx) {
return FnType->isAddressable(paramIdx, IGM.IRGen.SIL,
IGM.getGenericEnvironment(),
IGM.getSILTypes(),
IGM.getMaximalTypeExpansionContext());
}

/// Does the given function type have a self parameter that should be
/// given the special treatment for self parameters?
///
Expand Down Expand Up @@ -1887,7 +1901,6 @@ static void addParamInfo(SignatureExpansionABIDetails *details,
}

void SignatureExpansion::expandKeyPathAccessorParameters() {
auto params = FnType->getParameters();
unsigned numArgsToExpand;
SmallVector<llvm::Type *, 4> tailParams;

Expand Down Expand Up @@ -1933,7 +1946,7 @@ void SignatureExpansion::expandKeyPathAccessorParameters() {
llvm_unreachable("non keypath accessor convention");
}
for (unsigned i = 0; i < numArgsToExpand; i++) {
expand(params[i]);
expand(i);
}
for (auto tailParam : tailParams) {
ParamIRTypes.push_back(tailParam);
Expand Down Expand Up @@ -1987,9 +2000,9 @@ void SignatureExpansion::expandParameters(
params = params.drop_back();
}

for (auto param : params) {
const TypeInfo &ti = expand(param);
addParamInfo(recordedABIDetails, ti, param.getConvention());
for (auto pair : enumerate(params)) {
const TypeInfo &ti = expand(pair.index());
addParamInfo(recordedABIDetails, ti, pair.value().getConvention());
}
if (recordedABIDetails && FnType->hasSelfParam() && !hasSelfContext)
recordedABIDetails->parameters.back().isSelf = true;
Expand All @@ -2015,7 +2028,7 @@ void SignatureExpansion::expandParameters(

if (claimSelf())
IGM.addSwiftSelfAttributes(Attrs, curLength);
expand(FnType->getSelfParameter());
expand(FnType->getSelfParameterIndex());
if (recordedABIDetails)
recordedABIDetails->hasTrailingSelfParam = true;
assert(ParamIRTypes.size() == curLength + 1 &&
Expand Down Expand Up @@ -2260,8 +2273,8 @@ void SignatureExpansion::expandAsyncEntryType() {
params = params.drop_back();
}

for (auto param : params) {
expand(param);
for (unsigned i : range(params.size())) {
expand(i);
}

// Next, the generic signature.
Expand All @@ -2279,7 +2292,7 @@ void SignatureExpansion::expandAsyncEntryType() {
if (hasSelfContext) {
auto curLength = ParamIRTypes.size();
(void)curLength;
expand(FnType->getSelfParameter());
expand(FnType->getSelfParameterIndex());
assert(ParamIRTypes.size() == curLength + 1 &&
"adding 'self' added unexpected number of parameters");
if (claimSelf())
Expand Down
22 changes: 16 additions & 6 deletions lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,17 @@ bool SILFunctionType::isNoReturnFunction(SILModule &M,
}

bool SILFunctionType::isAddressable(unsigned paramIdx, SILFunction *caller) {
return isAddressable(paramIdx, caller->getModule(),
caller->getGenericEnvironment(),
caller->getModule().Types,
caller->getTypeExpansionContext());
}

// 'genericEnv' may be null.
bool SILFunctionType::isAddressable(unsigned paramIdx, SILModule &module,
GenericEnvironment *genericEnv,
Lowering::TypeConverter &typeConverter,
TypeExpansionContext expansion) {
SILParameterInfo paramInfo = getParameters()[paramIdx];
for (auto &depInfo : getLifetimeDependencies()) {
auto *addressableIndices = depInfo.getAddressableIndices();
Expand All @@ -715,13 +726,12 @@ bool SILFunctionType::isAddressable(unsigned paramIdx, SILFunction *caller) {
}
auto *condAddressableIndices = depInfo.getConditionallyAddressableIndices();
if (condAddressableIndices && condAddressableIndices->contains(paramIdx)) {
CanType argType = paramInfo.getArgumentType(
caller->getModule(), this, caller->getTypeExpansionContext());
CanType contextType =
argType->hasTypeParameter()
? caller->mapTypeIntoContext(argType)->getCanonicalType()
CanType argType = paramInfo.getArgumentType(module, this, expansion);
CanType contextType = genericEnv
? genericEnv->mapTypeIntoContext(argType)->getCanonicalType()
: argType;
auto &tl = caller->getTypeLowering(contextType);
assert(!contextType->hasTypeParameter());
auto &tl = typeConverter.getTypeLowering(contextType, expansion);
if (tl.getRecursiveProperties().isAddressableForDependencies())
return true;
}
Expand Down
34 changes: 34 additions & 0 deletions test/IRGen/addressable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-swift-frontend -module-name A -emit-ir -primary-file %s \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: -enable-experimental-feature AddressableTypes \
// RUN: | %FileCheck %s

// REQUIRES: swift_feature_AddressableTypes
// REQUIRES: swift_feature_LifetimeDependence

public struct NE: ~Escapable {}

@_addressableForDependencies
public struct Holder {}

@_silgen_name("holder_NE")
@lifetime(borrow holder)
func getNE(_ holder: Holder) -> NE

@_silgen_name("holder_mut_NE")
@lifetime(&holder)
func getMutNE(_ holder: inout Holder) -> NE

// The parameter cannot be 'nocapture'.
//
// CHECK-LABEL: define{{.*}} swiftcc void @"$s1A17testAddressableInyAA2NEVAA6HolderVF"(ptr noalias %0)
public func testAddressableIn(_ holder: Holder) -> NE {
getNE(holder)
}

// The parameter cannot be 'nocapture'.
//
// CHECK-LABEL: define{{.*}} swiftcc void @"$s1A20testAddressableInoutyAA2NEVAA6HolderVzF"(ptr %0)
public func testAddressableInout(_ holder: inout Holder) -> NE {
getMutNE(&holder)
}