Skip to content

Sema: Optimize Constraint layout to save 24 bytes #79882

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 5 commits into from
Mar 11, 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
99 changes: 47 additions & 52 deletions include/swift/Sema/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ class Constraint final : public llvm::ilist_node<Constraint>,
private llvm::TrailingObjects<Constraint,
TypeVariableType *,
ConstraintFix *,
DeclContext *,
ContextualTypeInfo,
OverloadChoice> {
friend TrailingObjects;

Expand All @@ -345,9 +347,17 @@ class Constraint final : public llvm::ilist_node<Constraint>,
/// The kind of restriction placed on this constraint.
ConversionRestrictionKind Restriction : 8;

/// Whether we have a fix.
/// The number of type variables referenced by this constraint.
///
/// The type variables themselves are tail-allocated.
unsigned NumTypeVariables : 11;

/// Whether we have a tail-allocated fix.
unsigned HasFix : 1;

/// Whether we have a tail-allocated DeclContext.
unsigned HasDeclContext : 1;

/// Whether the \c Restriction field is valid.
unsigned HasRestriction : 1;

Expand Down Expand Up @@ -375,14 +385,19 @@ class Constraint final : public llvm::ilist_node<Constraint>,
/// the rest of the constraint system. Currently only applies to conjunctions.
unsigned IsIsolated : 1;

/// The number of type variables referenced by this constraint.
///
/// The type variables themselves are tail-allocated.
unsigned NumTypeVariables : 11;

/// The kind of function reference, for member references.
unsigned TheFunctionRefInfo : 3;

/// The trailing closure matching for an applicable function constraint,
/// if any. 0 = None, 1 = Forward, 2 = Backward.
unsigned trailingClosureMatching : 2;

/// For a SyntacticElement constraint, identify whether the result of this
/// node is unused.
unsigned isDiscarded : 1;

// 22 bits remaining

union {
struct {
/// The first type.
Expand Down Expand Up @@ -414,29 +429,19 @@ class Constraint final : public llvm::ilist_node<Constraint>,
/// Used for ValueWitness constraints.
ValueDecl *Ref;
} Member;

/// The DC in which the use appears.
DeclContext *UseDC;
} Member;

/// The set of constraints for a disjunction.
ArrayRef<Constraint *> Nested;

struct {
/// The first type
/// The first type.
Type First;

/// The DC in which the use appears.
DeclContext *UseDC;
} Overload;

struct {
/// The node itself.
ASTNode Element;
/// Contextual information associated with the element (if any).
ContextualTypeInfo Context;
/// Identifies whether result of this node is unused.
bool IsDiscarded;
} SyntacticElement;

struct {
Expand All @@ -447,11 +452,6 @@ class Constraint final : public llvm::ilist_node<Constraint>,
/// The type being called, primarily a function type, but could
/// be a metatype, a tuple or a nominal type.
Type Callee;
/// The trailing closure matching for an applicable function constraint,
/// if any. 0 = None, 1 = Forward, 2 = Backward.
unsigned TrailingClosureMatching : 2;
/// The declaration context in which the application appears.
DeclContext *UseDC;
} Apply;
};

Expand Down Expand Up @@ -527,6 +527,14 @@ class Constraint final : public llvm::ilist_node<Constraint>,
return HasFix ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<DeclContext *>) const {
return HasDeclContext ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<ContextualTypeInfo>) const {
return Kind == ConstraintKind::SyntacticElement ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<OverloadChoice>) const {
return Kind == ConstraintKind::BindOverload ? 1 : 0;
}
Expand Down Expand Up @@ -604,15 +612,15 @@ class Constraint final : public llvm::ilist_node<Constraint>,
DeclContext *useDC, ConstraintLocator *locator);

static Constraint *createSyntacticElement(ConstraintSystem &cs,
ASTNode node,
ConstraintLocator *locator,
bool isDiscarded = false);
ASTNode node,
ConstraintLocator *locator,
bool isDiscarded = false);

static Constraint *createSyntacticElement(ConstraintSystem &cs,
ASTNode node,
ContextualTypeInfo context,
ConstraintLocator *locator,
bool isDiscarded = false);
ASTNode node,
ContextualTypeInfo context,
ConstraintLocator *locator,
bool isDiscarded = false);

/// Determine the kind of constraint.
ConstraintKind getKind() const { return Kind; }
Expand Down Expand Up @@ -864,24 +872,10 @@ class Constraint final : public llvm::ilist_node<Constraint>,

/// Retrieve the overload choice for an overload-binding constraint.
OverloadChoice getOverloadChoice() const {
assert(Kind == ConstraintKind::BindOverload);
ASSERT(Kind == ConstraintKind::BindOverload);
return *getTrailingObjects<OverloadChoice>();
}

/// Retrieve the DC in which the overload was used.
DeclContext *getOverloadUseDC() const {
assert(Kind == ConstraintKind::BindOverload);
return Overload.UseDC;
}

/// Retrieve the DC in which the member was used.
DeclContext *getMemberUseDC() const {
assert(Kind == ConstraintKind::ValueMember ||
Kind == ConstraintKind::UnresolvedValueMember ||
Kind == ConstraintKind::ValueWitness);
return Member.UseDC;
}

FunctionType *getAppliedFunctionType() const {
assert(Kind == ConstraintKind::ApplicableFunction);
return Apply.AppliedFn;
Expand All @@ -892,24 +886,25 @@ class Constraint final : public llvm::ilist_node<Constraint>,
return Apply.Callee;
}

DeclContext *getApplicationDC() const {
assert(Kind == ConstraintKind::ApplicableFunction);
return Apply.UseDC;
}

ASTNode getSyntacticElement() const {
assert(Kind == ConstraintKind::SyntacticElement);
return SyntacticElement.Element;
}

ContextualTypeInfo getElementContext() const {
assert(Kind == ConstraintKind::SyntacticElement);
return SyntacticElement.Context;
ASSERT(Kind == ConstraintKind::SyntacticElement);
return *getTrailingObjects<ContextualTypeInfo>();
}

bool isDiscardedElement() const {
assert(Kind == ConstraintKind::SyntacticElement);
return SyntacticElement.IsDiscarded;
return isDiscarded;
}

/// Retrieve the DC in which the overload was used.
DeclContext *getDeclContext() const {
ASSERT(HasDeclContext);
return *getTrailingObjects<DeclContext *>();
}

/// For an applicable function constraint, retrieve the trailing closure
Expand Down
1 change: 1 addition & 0 deletions include/swift/Sema/ConstraintSolverStats.def
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# error #define CS_STATISTIC(Name, Description) before including
#endif

CS_STATISTIC(ASTBytesAllocated, "bytes allocated in solver arena")
CS_STATISTIC(NumTypeVariablesBound, "# of type variables bound")
CS_STATISTIC(NumTypeVariableBindings, "# of type variable bindings attempted")
CS_STATISTIC(NumDisjunctions, "# of disjunctions explored")
Expand Down
51 changes: 22 additions & 29 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,6 @@ struct ASTContext::Implementation {
/// The set of function types.
llvm::FoldingSet<FunctionType> FunctionTypes;

/// The set of normal protocol conformances.
llvm::FoldingSet<NormalProtocolConformance> NormalConformances;

// The set of self protocol conformances.
llvm::DenseMap<ProtocolDecl*, SelfProtocolConformance*> SelfConformances;

/// The set of specialized protocol conformances.
llvm::FoldingSet<SpecializedProtocolConformance> SpecializedConformances;

Expand All @@ -597,9 +591,6 @@ struct ASTContext::Implementation {
/// The set of substitution maps (uniqued by their storage).
llvm::FoldingSet<SubstitutionMap::Storage> SubstitutionMaps;

/// The set of unique AvailabilityContexts (uniqued by their storage).
llvm::FoldingSet<AvailabilityContext::Storage> AvailabilityContexts;

~Arena() {
for (auto &conformance : SpecializedConformances)
conformance.~SpecializedProtocolConformance();
Expand All @@ -613,11 +604,6 @@ struct ASTContext::Implementation {
#if SWIFT_COMPILER_IS_MSVC
#pragma warning (default: 4189)
#endif

// Call the normal conformance destructors last since they could be
// referenced by the other conformance types.
for (auto &conformance : NormalConformances)
conformance.~NormalProtocolConformance();
}

size_t getTotalMemory() const;
Expand Down Expand Up @@ -645,6 +631,11 @@ struct ASTContext::Implementation {
AutoDiffDerivativeFunctionIdentifiers;

llvm::FoldingSet<GenericSignatureImpl> GenericSignatures;
llvm::FoldingSet<NormalProtocolConformance> NormalConformances;
llvm::DenseMap<ProtocolDecl*, SelfProtocolConformance*> SelfConformances;

/// The set of unique AvailabilityContexts (uniqued by their storage).
llvm::FoldingSet<AvailabilityContext::Storage> AvailabilityContexts;

/// A cache of information about whether particular nominal types
/// are representable in a foreign language.
Expand Down Expand Up @@ -720,6 +711,9 @@ ASTContext::Implementation::Implementation()
: IdentifierTable(Allocator),
IntrinsicScratchContext(new llvm::LLVMContext()) {}
ASTContext::Implementation::~Implementation() {
for (auto &conformance : NormalConformances)
conformance.~NormalProtocolConformance();

for (auto &cleanup : Cleanups)
cleanup();
}
Expand Down Expand Up @@ -889,6 +883,9 @@ void ASTContext::Implementation::dump(llvm::raw_ostream &os) const {
SIZE_AND_BYTES(SILMoveOnlyWrappedTypes);
SIZE_AND_BYTES(BuiltinIntegerTypes);
SIZE_AND_BYTES(OpenedElementEnvironments);
SIZE(NormalConformances);
SIZE(SelfConformances);
SIZE(AvailabilityContexts);
SIZE_AND_BYTES(ForeignRepresentableCache);
SIZE(SearchPathsSet);

Expand Down Expand Up @@ -2912,15 +2909,14 @@ ASTContext::getNormalConformance(Type conformingType,

// Did we already record the normal conformance?
void *insertPos;
auto &normalConformances =
getImpl().getArena(AllocationArena::Permanent).NormalConformances;
auto &normalConformances = getImpl().NormalConformances;
if (auto result = normalConformances.FindNodeOrInsertPos(id, insertPos))
return result;

// Build a new normal protocol conformance.
auto result = new (*this, AllocationArena::Permanent)
NormalProtocolConformance(conformingType, protocol, loc, dc, state,
options, preconcurrencyLoc);
auto result = new (*this) NormalProtocolConformance(
conformingType, protocol, loc, dc, state,
options, preconcurrencyLoc);
normalConformances.InsertNode(result, insertPos);

return result;
Expand All @@ -2929,12 +2925,11 @@ ASTContext::getNormalConformance(Type conformingType,
/// Produce a self-conformance for the given protocol.
SelfProtocolConformance *
ASTContext::getSelfConformance(ProtocolDecl *protocol) {
auto &selfConformances =
getImpl().getArena(AllocationArena::Permanent).SelfConformances;
auto &selfConformances = getImpl().SelfConformances;
auto &entry = selfConformances[protocol];
if (!entry) {
entry = new (*this, AllocationArena::Permanent)
SelfProtocolConformance(protocol->getDeclaredExistentialType());
entry = new (*this) SelfProtocolConformance(
protocol->getDeclaredExistentialType());
}
return entry;
}
Expand Down Expand Up @@ -3274,6 +3269,9 @@ size_t ASTContext::getTotalMemory() const {
// getImpl().GenericSignatures ?
// getImpl().CompoundNames ?
// getImpl().IntegerTypes ?
// getImpl().NormalConformances ?
// getImpl().SelfConformances ?
// getImpl().AvailabilityContexts
getImpl().Permanent.getTotalMemory();

Size += getSolverMemory();
Expand Down Expand Up @@ -3315,7 +3313,6 @@ size_t ASTContext::Implementation::Arena::getTotalMemory() const {
// FunctionTypes ?
// UnboundGenericTypes ?
// BoundGenericTypes ?
// NormalConformances ?
// SpecializedConformances ?
// InheritedConformances ?
// BuiltinConformances ?
Expand Down Expand Up @@ -3360,14 +3357,11 @@ void ASTContext::Implementation::Arena::dump(llvm::raw_ostream &os) const {
SIZE_AND_BYTES(OpaqueArchetypeEnvironments);
SIZE_AND_BYTES(OpenedExistentialEnvironments);
SIZE(FunctionTypes);
SIZE(NormalConformances);
SIZE(SelfConformances);
SIZE(SpecializedConformances);
SIZE(InheritedConformances);
SIZE_AND_BYTES(BuiltinConformances);
SIZE(PackConformances);
SIZE(SubstitutionMaps);
SIZE(AvailabilityContexts);

#undef SIZE
#undef SIZE_AND_BYTES
Expand Down Expand Up @@ -5720,8 +5714,7 @@ const AvailabilityContext::Storage *AvailabilityContext::Storage::get(
AvailabilityContext::Storage::Profile(id, platformRange, isDeprecated,
domainInfos);

auto &foldingSet =
ctx.getImpl().getArena(AllocationArena::Permanent).AvailabilityContexts;
auto &foldingSet = ctx.getImpl().AvailabilityContexts;
void *insertPos;
auto *existing = foldingSet.FindNodeOrInsertPos(id, insertPos);
if (existing)
Expand Down
10 changes: 5 additions & 5 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13089,7 +13089,7 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl(
// Determine the type that this choice will have.
Type choiceType = getEffectiveOverloadType(
constraint->getLocator(), choice, /*allowMembers=*/true,
constraint->getOverloadUseDC());
constraint->getDeclContext());
if (!choiceType) {
hasUnhandledConstraints = true;
return true;
Expand Down Expand Up @@ -16335,7 +16335,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
return simplifyApplicableFnConstraint(
constraint.getAppliedFunctionType(), constraint.getCalleeType(),
constraint.getTrailingClosureMatching(),
constraint.getApplicationDC(), /*flags=*/std::nullopt,
constraint.getDeclContext(), /*flags=*/std::nullopt,
constraint.getLocator());

case ConstraintKind::DynamicCallableApplicableFunction:
Expand Down Expand Up @@ -16382,7 +16382,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {

resolveOverload(constraint.getLocator(), constraint.getFirstType(),
constraint.getOverloadChoice(),
constraint.getOverloadUseDC());
constraint.getDeclContext());
return SolutionKind::Solved;

case ConstraintKind::SubclassOf:
Expand Down Expand Up @@ -16429,7 +16429,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
case ConstraintKind::UnresolvedValueMember:
return simplifyMemberConstraint(
constraint.getKind(), constraint.getFirstType(), constraint.getMember(),
constraint.getSecondType(), constraint.getMemberUseDC(),
constraint.getSecondType(), constraint.getDeclContext(),
constraint.getFunctionRefInfo(),
/*outerAlternatives=*/{},
/*flags*/ std::nullopt, constraint.getLocator());
Expand All @@ -16438,7 +16438,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
return simplifyValueWitnessConstraint(
constraint.getKind(), constraint.getFirstType(),
constraint.getRequirement(), constraint.getSecondType(),
constraint.getMemberUseDC(), constraint.getFunctionRefInfo(),
constraint.getDeclContext(), constraint.getFunctionRefInfo(),
/*flags*/ std::nullopt, constraint.getLocator());

case ConstraintKind::Defaultable:
Expand Down
Loading