Skip to content

Add attr to enable lexical lifetime per function. #63766

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
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
11 changes: 11 additions & 0 deletions docs/ReferenceGuides/UnderscoredAttributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,17 @@ recursive function, the attribute is ignored.

This attribute has no effect in debug builds.

## `@_lexicalLifetimes`

Applies lexical lifetime rules within a module built with lexical lifetimes
disabled. Facilitates gradual migration.

In modules built with lexical lifetimes disabled but lexical borrow scopes
enabled--the behavior of `-enable-lexical-lifetimes=false`--all lexical markers
are stripped by the LexicalLifetimeEliminator pass. Functions annotated with
this attribute keep their lexical markers, affecting the optimizations that run
on the function subsequently.

## `@_noEagerMove`

When applied to a value, indicates that the value's lifetime is lexical, that
Expand Down
16 changes: 16 additions & 0 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ enum IsRuntimeAccessible_t {
IsRuntimeAccessible
};

enum ForceEnableLexicalLifetimes_t {
DoNotForceEnableLexicalLifetimes,
DoForceEnableLexicalLifetimes
};

enum class PerformanceConstraints : uint8_t {
None = 0,
NoAllocation = 1,
Expand Down Expand Up @@ -403,6 +408,9 @@ class SILFunction
/// The function is in a statically linked module.
unsigned IsStaticallyLinked : 1;

/// If true, the function has lexical lifetimes even if the module does not.
unsigned ForceEnableLexicalLifetimes : 1;

static void
validateSubclassScope(SubclassScope scope, IsThunk_t isThunk,
const GenericSpecializationInformation *genericInfo) {
Expand Down Expand Up @@ -670,6 +678,14 @@ class SILFunction
IsStaticallyLinked = value;
}

ForceEnableLexicalLifetimes_t forceEnableLexicalLifetimes() const {
return ForceEnableLexicalLifetimes_t(ForceEnableLexicalLifetimes);
}

void setForceEnableLexicalLifetimes(ForceEnableLexicalLifetimes_t value) {
ForceEnableLexicalLifetimes = value;
}

/// Returns true if this is a reabstraction thunk of escaping function type
/// whose single argument is a potentially non-escaping closure. i.e. the
/// thunks' function argument may itself have @inout_aliasable parameters.
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ void SILFunction::init(
this->ExactSelfClass = isExactSelfClass;
this->IsDistributed = isDistributed;
this->IsRuntimeAccessible = isRuntimeAccessible;
this->ForceEnableLexicalLifetimes = DoNotForceEnableLexicalLifetimes;
this->stackProtection = false;
this->Inlined = false;
this->Zombie = false;
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/SILFunctionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ void SILFunctionBuilder::addFunctionAttributes(
F->setPerfConstraints(PerformanceConstraints::NoAllocation);
}

if (Attrs.hasAttribute<LexicalLifetimesAttr>()) {
F->setForceEnableLexicalLifetimes(DoForceEnableLexicalLifetimes);
}

// Validate `@differentiable` attributes by calling `getParameterIndices`.
// This is important for:
// - Skipping invalid `@differentiable` attributes in non-primary files.
Expand Down
3 changes: 3 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,9 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
if (isRuntimeAccessible()) {
OS << "[runtime_accessible] ";
}
if (forceEnableLexicalLifetimes()) {
OS << "[lexical_lifetimes] ";
}

if (isExactSelfClass()) {
OS << "[exact_self_class] ";
Expand Down
31 changes: 20 additions & 11 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ static bool parseDeclSILOptional(bool *isTransparent,
IsDynamicallyReplaceable_t *isDynamic,
IsDistributed_t *isDistributed,
IsRuntimeAccessible_t *isRuntimeAccessible,
ForceEnableLexicalLifetimes_t *forceEnableLexicalLifetimes,
IsExactSelfClass_t *isExactSelfClass,
SILFunction **dynamicallyReplacedFunction,
SILFunction **usedAdHocRequirementWitness,
Expand Down Expand Up @@ -1042,6 +1043,9 @@ static bool parseDeclSILOptional(bool *isTransparent,
*isDistributed = IsDistributed;
else if (isRuntimeAccessible && SP.P.Tok.getText() == "runtime_accessible")
*isRuntimeAccessible = IsRuntimeAccessible;
else if (forceEnableLexicalLifetimes &&
SP.P.Tok.getText() == "lexical_lifetimes")
*forceEnableLexicalLifetimes = DoForceEnableLexicalLifetimes;
else if (isExactSelfClass && SP.P.Tok.getText() == "exact_self_class")
*isExactSelfClass = IsExactSelfClass;
else if (isCanonical && SP.P.Tok.getText() == "canonical")
Expand Down Expand Up @@ -6793,6 +6797,8 @@ bool SILParserState::parseDeclSIL(Parser &P) {
IsDynamicallyReplaceable_t isDynamic = IsNotDynamic;
IsDistributed_t isDistributed = IsNotDistributed;
IsRuntimeAccessible_t isRuntimeAccessible = IsNotRuntimeAccessible;
ForceEnableLexicalLifetimes_t forceEnableLexicalLifetimes =
DoNotForceEnableLexicalLifetimes;
IsExactSelfClass_t isExactSelfClass = IsNotExactSelfClass;
bool hasOwnershipSSA = false;
IsThunk_t isThunk = IsNotThunk;
Expand All @@ -6815,12 +6821,12 @@ bool SILParserState::parseDeclSIL(Parser &P) {
parseDeclSILOptional(
&isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA,
&isThunk, &isDynamic, &isDistributed, &isRuntimeAccessible,
&isExactSelfClass,
&DynamicallyReplacedFunction, &AdHocWitnessFunction, &objCReplacementFor, &specialPurpose,
&inlineStrategy, &optimizationMode, &perfConstr, nullptr,
&isWeakImported, &needStackProtection, &availability,
&isWithoutActuallyEscapingThunk, &Semantics,
&SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
&forceEnableLexicalLifetimes, &isExactSelfClass,
&DynamicallyReplacedFunction, &AdHocWitnessFunction,
&objCReplacementFor, &specialPurpose, &inlineStrategy,
&optimizationMode, &perfConstr, nullptr, &isWeakImported,
&needStackProtection, &availability, &isWithoutActuallyEscapingThunk,
&Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
P.parseIdentifier(FnName, FnNameLoc, /*diagnoseDollarPrefix=*/false,
diag::expected_sil_function_name) ||
Expand Down Expand Up @@ -6852,6 +6858,8 @@ bool SILParserState::parseDeclSIL(Parser &P) {
FunctionState.F->setIsDynamic(isDynamic);
FunctionState.F->setIsDistributed(isDistributed);
FunctionState.F->setIsRuntimeAccessible(isRuntimeAccessible);
FunctionState.F->setForceEnableLexicalLifetimes(
forceEnableLexicalLifetimes);
FunctionState.F->setIsExactSelfClass(isExactSelfClass);
FunctionState.F->setDynamicallyReplacedFunction(
DynamicallyReplacedFunction);
Expand Down Expand Up @@ -7058,7 +7066,7 @@ bool SILParserState::parseSILGlobal(Parser &P) {
if (parseSILLinkage(GlobalLinkage, P) ||
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, State, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
Expand Down Expand Up @@ -7111,7 +7119,7 @@ bool SILParserState::parseSILProperty(Parser &P) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, SP, M))
nullptr, nullptr, nullptr, SP, M))
return true;

ValueDecl *VD;
Expand Down Expand Up @@ -7179,7 +7187,7 @@ bool SILParserState::parseSILVTable(Parser &P) {
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, VTableState, M))
return true;

Expand Down Expand Up @@ -7290,7 +7298,8 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, moveOnlyDeinitTableState, M))
nullptr, nullptr, nullptr, moveOnlyDeinitTableState,
M))
return true;

// Parse the class name.
Expand Down Expand Up @@ -7775,7 +7784,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) {
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, WitnessState, M))
return true;

Expand Down
3 changes: 3 additions & 0 deletions lib/SILOptimizer/Mandatory/LexicalLifetimeEliminator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class LexicalLifetimeEliminatorPass : public SILFunctionTransform {
void run() override {
auto *fn = getFunction();

if (fn->forceEnableLexicalLifetimes())
return;

// If we are already canonical, we do not have any diagnostics to emit.
if (fn->wasDeserializedCanonical())
return;
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
IGNORED_ATTR(BackDeployed)
IGNORED_ATTR(Documentation)
IGNORED_ATTR(MacroRole)
IGNORED_ATTR(LexicalLifetimes)
#undef IGNORED_ATTR

void visitAlignmentAttr(AlignmentAttr *attr) {
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,7 @@ namespace {
UNINTERESTING_ATTR(RuntimeMetadata)

UNINTERESTING_ATTR(MacroRole)
UNINTERESTING_ATTR(LexicalLifetimes)
#undef UNINTERESTING_ATTR

void visitAvailableAttr(AvailableAttr *attr) {
Expand Down
42 changes: 22 additions & 20 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,20 +534,20 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
GenericSignatureID genericSigID;
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
optimizationMode, perfConstr,
subclassScope, hasCReferences, effect, numAttrs,
hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible;
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
optimizationMode, perfConstr,
subclassScope, hasCReferences, effect, numAttrs,
hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible, funcTyID,
replacedFunctionID, usedAdHocWitnessFunctionID,
genericSigID, clangNodeOwnerID, parentModuleID, SemanticsIDs);
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes, funcTyID,
replacedFunctionID, usedAdHocWitnessFunctionID, genericSigID,
clangNodeOwnerID, parentModuleID, SemanticsIDs);

if (funcTyID == 0)
return MF->diagnoseFatal("SILFunction typeID is 0");
Expand Down Expand Up @@ -680,6 +680,8 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
fn->setIsExactSelfClass(IsExactSelfClass_t(isExactSelfClass));
fn->setIsDistributed(IsDistributed_t(isDistributed));
fn->setIsRuntimeAccessible(IsRuntimeAccessible_t(isRuntimeAccessible));
fn->setForceEnableLexicalLifetimes(
ForceEnableLexicalLifetimes_t(forceEnableLexicalLifetimes));
if (replacedFunction)
fn->setDynamicallyReplacedFunction(replacedFunction);
if (!replacedObjectiveCFunc.empty())
Expand Down Expand Up @@ -3204,20 +3206,20 @@ bool SILDeserializer::hasSILFunction(StringRef Name,
GenericSignatureID genericSigID;
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, perfConstr,
subclassScope, hasCReferences, effect, numSpecAttrs,
hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible;
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numSpecAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, perfConstr,
subclassScope, hasCReferences, effect, numSpecAttrs,
hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available),
isDynamic, isExactSelfClass, isDistributed, isRuntimeAccessible, funcTyID,
replacedFunctionID, usedAdHocWitnessFunctionID,
genericSigID, clangOwnerID, parentModuleID, SemanticsIDs);
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numSpecAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes, funcTyID,
replacedFunctionID, usedAdHocWitnessFunctionID, genericSigID,
clangOwnerID, parentModuleID, SemanticsIDs);
auto linkage = fromStableSILLinkage(rawLinkage);
if (!linkage) {
LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage
Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 744; // nonescaping closures are move-only in SIL
const uint16_t SWIFTMODULE_VERSION_MINOR = 745; // _lexicalLifetimes attribute

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SILFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ namespace sil_block {
BCFixed<1>, // exact self class
BCFixed<1>, // is distributed
BCFixed<1>, // is runtime accessible
BCFixed<1>, // are lexical lifetimes force-enabled
TypeIDField, // SILFunctionType
DeclIDField, // SILFunction name or 0 (replaced function)
DeclIDField, // SILFunction name or 0 (used ad-hoc requirement witness function)
Expand Down
17 changes: 8 additions & 9 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,16 +511,15 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) {
(unsigned)F.isThunk(), (unsigned)F.isWithoutActuallyEscapingThunk(),
(unsigned)F.getSpecialPurpose(), (unsigned)F.getInlineStrategy(),
(unsigned)F.getOptimizationMode(), (unsigned)F.getPerfConstraints(),
(unsigned)F.getClassSubclassScope(),
(unsigned)F.hasCReferences(), (unsigned)F.getEffectsKind(),
(unsigned)numAttrs, (unsigned)F.hasOwnership(),
F.isAlwaysWeakImported(), LIST_VER_TUPLE_PIECES(available),
(unsigned)F.isDynamicallyReplaceable(),
(unsigned)F.isExactSelfClass(),
(unsigned)F.isDistributed(),
(unsigned)F.getClassSubclassScope(), (unsigned)F.hasCReferences(),
(unsigned)F.getEffectsKind(), (unsigned)numAttrs,
(unsigned)F.hasOwnership(), F.isAlwaysWeakImported(),
LIST_VER_TUPLE_PIECES(available), (unsigned)F.isDynamicallyReplaceable(),
(unsigned)F.isExactSelfClass(), (unsigned)F.isDistributed(),
(unsigned)F.isRuntimeAccessible(),
FnID, replacedFunctionID, usedAdHocWitnessFunctionID,
genericSigID, clangNodeOwnerID, parentModuleID, SemanticsIDs);
(unsigned)F.forceEnableLexicalLifetimes(), FnID, replacedFunctionID,
usedAdHocWitnessFunctionID, genericSigID, clangNodeOwnerID,
parentModuleID, SemanticsIDs);

F.visitArgEffects(
[&](int effectIdx, int argumentIndex, bool isDerived) {
Expand Down
6 changes: 6 additions & 0 deletions test/SIL/Parser/function_argument_lifetime_annotation.sil
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ bb0(%instance : @noImplicitCopy @_lexical @owned $C):
return %retval : $()
}

// CHECK-LABEL: sil [lexical_lifetimes] [ossa] @force_lexical_function : {{.*}} {
// CHECK-LABEL: } // end sil function 'force_lexical_function'
sil [lexical_lifetimes] [ossa] @force_lexical_function : $@convention(thin) () -> () {
%retval = tuple()
return %retval : $()
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ bb0(%instance : @_lexical @owned $C):
return %retval : $()
}

// CHECK-LABEL: sil [serialized] [lexical_lifetimes] [ossa] @force_lexical_function : {{.*}} {
// CHECK-LABEL: } // end sil function 'force_lexical_function'
sil [serialized] [lexical_lifetimes] [ossa] @force_lexical_function : $@convention(thin) () -> () {
%retval = tuple()
return %retval : $()
}
7 changes: 7 additions & 0 deletions test/SILGen/lexical_lifetime_forced.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: %target-swift-emit-silgen -enable-lexical-lifetimes=false -module-name borrow -parse-stdlib %s | %FileCheck %s

// CHECK-LABEL: sil {{.*}}[lexical_lifetimes] [ossa] @funky : {{.*}} {
// CHECK-LABEL: } // end sil function 'funky'
@_silgen_name("funky")
@_lexicalLifetimes func funky() {}

25 changes: 25 additions & 0 deletions test/SILOptimizer/lexical_lifetime_force.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %target-swift-frontend -emit-sil -enable-lexical-lifetimes=false -O -module-name=main %s | %FileCheck %s

class C {}

@_silgen_name("borrow")
func borrow(_ c: __shared C)

@_silgen_name("barrier")
func barrier()

// CHECK-LABEL: sil {{.*}} [lexical_lifetimes] @funky : {{.*}} {
// CHECK: [[INSTANCE:%[^,]+]] = alloc_ref $C
// CHECK: [[BORROW:%[^,]+]] = function_ref @borrow
// CHECK: apply [[BORROW]]([[INSTANCE]])
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
// CHECK: apply [[BARRIER]]()
// CHECK: strong_release [[INSTANCE]]
// CHECK-LABEL: } // end sil function 'funky'
@_silgen_name("funky")
@_lexicalLifetimes
func funky() {
let c = C()
borrow(c)
barrier()
}
6 changes: 6 additions & 0 deletions utils/gyb_syntax_support/AttributeKinds.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,12 @@ def __init__(self, name, swift_name=None):
OnFunc, OnParam, OnVar, OnNominalType,
code=117),

SimpleDeclAttribute('_lexicalLifetimes', 'LexicalLifetimes',
UserInaccessible,
ABIStableToAdd, ABIStableToRemove,
APIStableToAdd, APIStableToRemove,
OnFunc, code=36),

SimpleDeclAttribute('_noEagerMove', 'NoEagerMove',
UserInaccessible,
ABIStableToAdd, ABIStableToRemove,
Expand Down