Skip to content

Commit b0389af

Browse files
authored
Merge pull request #19894 from shajrawi/inline_exclusivity
2 parents c1cabae + 2dc1015 commit b0389af

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#define DEBUG_TYPE "sil-inliner"
1414
#include "swift/AST/Module.h"
15+
#include "swift/SIL/MemAccessUtils.h"
1516
#include "swift/SIL/OptimizationRemark.h"
1617
#include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h"
1718
#include "swift/SILOptimizer/PassManager/Passes.h"
@@ -100,6 +101,11 @@ class SILPerformanceInliner {
100101
/// specialization for a call.
101102
GenericSpecializationBenefit = RemovedCallBenefit + 300,
102103

104+
/// The benefit of inlining an exclusivity-containing callee.
105+
/// The exclusivity needs to be: dynamic,
106+
/// has no nested conflict and addresses known storage
107+
ExclusivityBenefit = RemovedCallBenefit + 300,
108+
103109
/// The benefit of inlining class methods with -Osize.
104110
/// We only inline very small class methods with -Osize.
105111
OSizeClassMethodBenefit = 5,
@@ -108,8 +114,8 @@ class SILPerformanceInliner {
108114
/// increasing the code size.
109115
TrivialFunctionThreshold = 18,
110116

111-
/// Configuration for the "soft" caller block limit. When changing, make sure
112-
/// you update BlockLimitMaxIntNumerator.
117+
/// Configuration for the "soft" caller block limit. When changing, make
118+
/// sure you update BlockLimitMaxIntNumerator.
113119
BlockLimitDenominator = 3000,
114120

115121
/// Computations with BlockLimitDenominator will overflow with numerators
@@ -298,9 +304,18 @@ bool SILPerformanceInliner::isProfitableToInline(
298304
SILBasicBlock *CalleeEntry = &Callee->front();
299305
DominanceOrder domOrder(CalleeEntry, DT, Callee->size());
300306

307+
// We don't want to blow up code-size
308+
// We will only inline if *ALL* dynamic accesses are
309+
// known and have no nested conflict
310+
bool AllAccessesBeneficialToInline = true;
311+
301312
// Calculate the inlining cost of the callee.
302313
int CalleeCost = 0;
303314
int Benefit = 0;
315+
// We don’t know if we want to update the benefit with
316+
// the exclusivity heuristic or not. We can *only* do that
317+
// if AllAccessesBeneficialToInline is true
318+
int ExclusivityBenefitWeight = 0;
304319

305320
SubstitutionMap CalleeSubstMap = AI.getSubstitutionMap();
306321

@@ -392,6 +407,21 @@ bool SILPerformanceInliner::isProfitableToInline(
392407
} else if (auto *BI = dyn_cast<BuiltinInst>(&I)) {
393408
if (BI->getBuiltinInfo().ID == BuiltinValueKind::OnFastPath)
394409
BlockW.updateBenefit(Benefit, FastPathBuiltinBenefit);
410+
} else if (auto *BAI = dyn_cast<BeginAccessInst>(&I)) {
411+
if (BAI->getEnforcement() == SILAccessEnforcement::Dynamic) {
412+
// The access is dynamic and has no nested conflict
413+
// See if the storage location is considered by
414+
// access enforcement optimizations
415+
AccessedStorage storage =
416+
findAccessedStorageNonNested(BAI->getSource());
417+
if (BAI->hasNoNestedConflict() &&
418+
(storage.isUniquelyIdentified() ||
419+
storage.getKind() == AccessedStorage::Class)) {
420+
BlockW.updateBenefit(ExclusivityBenefitWeight, ExclusivityBenefit);
421+
} else {
422+
AllAccessesBeneficialToInline = false;
423+
}
424+
}
395425
}
396426
}
397427
// Don't count costs in blocks which are dead after inlining.
@@ -407,6 +437,10 @@ bool SILPerformanceInliner::isProfitableToInline(
407437
}
408438
}
409439

440+
if (AllAccessesBeneficialToInline) {
441+
Benefit = std::max(Benefit, ExclusivityBenefitWeight);
442+
}
443+
410444
if (AI.getFunction()->isThunk()) {
411445
// Only inline trivial functions into thunks (which will not increase the
412446
// code size).

test/SILOptimizer/inline_heuristics.sil

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,42 @@ bb0(%0 : $*U):
429429
return %13 : $()
430430
} // end sil function 'testSpecializationAfterGenericInlining'
431431

432+
// Tests of exclusivity inlining.
433+
434+
// CHECK-LABEL: sil @testExclusivity : $@convention(thin) () -> () {
435+
// CHECK-NOT: apply
436+
// CHECK: begin_access
437+
// CHECK: return
438+
// CHECK: end sil function 'testExclusivity'
439+
440+
struct X {
441+
@sil_stored var i: Int64 { get set }
442+
init(i: Int64)
443+
init()
444+
}
445+
446+
var globalX: X
447+
448+
sil_global hidden @globalX : $X
449+
450+
sil @exclusivityCallee : $@convention(thin) () -> () {
451+
bb0:
452+
%0 = global_addr @globalX: $*X
453+
%1 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
454+
%2 = load %1 : $*X
455+
end_access %1 : $*X
456+
%4 = tuple ()
457+
return %4 : $()
458+
}
459+
460+
sil @testExclusivity : $@convention(thin) () -> () {
461+
bb0:
462+
%0 = function_ref @exclusivityCallee : $@convention(thin) () -> ()
463+
%1 = apply %0() : $@convention(thin) () -> ()
464+
%2 = tuple ()
465+
return %2 : $()
466+
}
467+
432468
// TODO:
433469
// Check that the inlining heuristic takes into account the possibility
434470
// of performing a devirtualization after inlining.

0 commit comments

Comments
 (0)