SIL Optimizations
AllocBoxToStack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Hi, I'm Yusuke
@kitasuke
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Why SIL? 
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Why SIL?
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Why SIL?
→ Better idea of Swift type system !
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Why SIL?
→ Better idea of Swift type system !
→ Optimizations magic "✨
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Why SIL?
→ Better idea of Swift type system !
→ Optimizations magic "✨
→ Just for fun $
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
SIL  
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Swift
Intermediate
LanguageSIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
SIL is a language specific
Intermediate
Representation
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Swift 
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Swift Compiler
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Swift Compiler
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Swift Compiler
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
SIL
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
raw SIL
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
canonical SIL
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Objective-C 
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Clang   
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Clang
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
SIL  
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Optimizations
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Optimization Passes
→ CapturePromotion
→ AllocBoxToStack
→ MandatoryInlining
→ DeadCodeElimination
→ Mem2Reg
...
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
AllocBoxToStack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack Promotion of
Box Objects
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack vs Heap
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack
→ Very fast
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack
→ Very fast
→ No explicit memory management
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack
→ Very fast
→ No explicit memory management
→ Size limits
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Heap
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Heap
→ Slow
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Heap
→ Slow
→ Explicit memory management
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Heap
→ Slow
→ Explicit memory management
→ No size limits
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack vs Heap
Stack Heap
Value types Reference types
Pointers to reference
type
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
How AllocBoxToStack
works?
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_box ➡ alloc_stack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
stack.swift
func number() -> Int {
var x: Int
x = 1
return x
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
raw SIL
$swiftc -emit-silgen stack.swift > stack.sil
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
stack.sil
%0 = alloc_box ${ var Int }, var, name "x"
%1 = mark_uninitialized [var] %0 : ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
...
%10 = begin_access [read] [unknown] %2 : $*Int
%11 = load [trivial] %10 : $*Int
end_access %10 : $*Int
destroy_value %1 : ${ var Int }
return %11 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
stack.sil
%0 = alloc_box ${ var Int }, var, name "x"
%1 = mark_uninitialized [var] %0 : ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
...
%10 = begin_access [read] [unknown] %2 : $*Int
%11 = load [trivial] %10 : $*Int
end_access %10 : $*Int
destroy_value %1 : ${ var Int }
return %11 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
canonical SIL
$swiftc -emit-sil stack.swift > stack.sil
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
stack.sil
%0 = alloc_stack $Int, var, name "x"
%1 = integer_literal $Builtin.Int64, 1
%2 = struct $Int (%1 : $Builtin.Int64)
...
return %2 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
stack.sil
%0 = alloc_stack $Int, var, name "x"
%1 = integer_literal $Builtin.Int64, 1
%2 = struct $Int (%1 : $Builtin.Int64)
...
return %2 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_box vs alloc_stack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_box
alloc_box allocates a reference counted box on
heap.
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_stack
alloc_stack allocates a value on stack. All
allocations must be deallocated prior to returning
from a function.
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Stack Promotion of
Box Objects
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_box ➡ alloc_stack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
alloc_box ➡ alloc_box
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.swift
func closure(_ completion: @escaping () -> Void) {
completion()
}
func number() -> Int {
var i: Int
i = 0
closure { i = 10 }
return i
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.swift
func closure(_ completion: @escaping () -> Void) {
completion()
}
func number() -> Int {
var i: Int
i = 0
closure { i = 10 }
return i
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.swift
func closure(_ completion: @escaping () -> Void) {
completion()
}
func number() -> Int {
var i: Int
i = 0
closure { i = 10 }
return i
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
raw SIL
$swiftc -emit-silgen box.swift > box.sil
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.sil
%0 = alloc_box ${ var Int }, var, name "i"
%1 = mark_uninitialized [var] %0 : ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
...
%16 = begin_access [read] [unknown] %2 : $*Int
%17 = load [trivial] %16 : $*Int
end_access %16 : $*Int
destroy_value %1 : ${ var Int }
return %17 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.sil
%0 = alloc_box ${ var Int }, var, name "i"
%1 = mark_uninitialized [var] %0 : ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
...
%16 = begin_access [read] [unknown] %2 : $*Int
%17 = load [trivial] %16 : $*Int
end_access %16 : $*Int
destroy_value %1 : ${ var Int }
return %17 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
canonical SIL
$swiftc -emit-sil box.swift > box.sil
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.sil
%0 = alloc_box ${ var Int }, var, name "i"
%1 = project_box %0 : ${ var Int }, 0
%2 = integer_literal $Builtin.Int64, 0
%3 = struct $Int (%2 : $Builtin.Int64)
...
%12 = begin_access [read] [dynamic] %1 : $*Int
%13 = load %12 : $*Int
end_access %12 : $*Int
strong_release %0 : ${ var Int }
return %13 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
box.sil
%0 = alloc_box ${ var Int }, var, name "i"
%1 = project_box %0 : ${ var Int }, 0
%2 = integer_literal $Builtin.Int64, 0
%3 = struct $Int (%2 : $Builtin.Int64)
...
%12 = begin_access [read] [dynamic] %1 : $*Int
%13 = load %12 : $*Int
end_access %12 : $*Int
strong_release %0 : ${ var Int }
return %13 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
How can promote
alloc_box?
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class AllocBoxToStack : public SILFunctionTransform {
void run() override {
...
AllocBoxToStackState pass(this);
for (auto &BB : *getFunction()) {
for (auto &I : BB)
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
if (canPromoteAllocBox(ABI, pass.PromotedOperands))
pass.Promotable.push_back(ABI);
}
if (!pass.Promotable.empty()) {
auto Count = rewritePromotedBoxes(pass);
...
}
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class AllocBoxToStack : public SILFunctionTransform {
void run() override {
...
AllocBoxToStackState pass(this);
for (auto &BB : *getFunction()) {
for (auto &I : BB)
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
if (canPromoteAllocBox(ABI, pass.PromotedOperands))
pass.Promotable.push_back(ABI);
}
if (!pass.Promotable.empty()) {
auto Count = rewritePromotedBoxes(pass);
...
}
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class AllocBoxToStack : public SILFunctionTransform {
void run() override {
...
AllocBoxToStackState pass(this);
for (auto &BB : *getFunction()) {
for (auto &I : BB)
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
if (canPromoteAllocBox(ABI, pass.PromotedOperands))
pass.Promotable.push_back(ABI);
}
if (!pass.Promotable.empty()) {
auto Count = rewritePromotedBoxes(pass);
...
}
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class AllocBoxToStack : public SILFunctionTransform {
void run() override {
...
AllocBoxToStackState pass(this);
for (auto &BB : *getFunction()) {
for (auto &I : BB)
if (auto *ABI = dyn_cast<AllocBoxInst>(&I))
if (canPromoteAllocBox(ABI, pass.PromotedOperands))
pass.Promotable.push_back(ABI);
}
if (!pass.Promotable.empty()) {
auto Count = rewritePromotedBoxes(pass);
...
}
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply,
bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) {
llvm::SmallVector<Operand *, 4> LocalPromotedOperands;
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist));
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(Op);
continue;
}
return User;
}
...
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply,
bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) {
llvm::SmallVector<Operand *, 4> LocalPromotedOperands;
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist));
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(Op);
continue;
}
return User;
}
...
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply,
bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) {
llvm::SmallVector<Operand *, 4> LocalPromotedOperands;
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist));
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(Op);
continue;
}
return User;
}
...
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply,
bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) {
llvm::SmallVector<Operand *, 4> LocalPromotedOperands;
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist));
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(Op);
continue;
}
return User;
}
...
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
static SILInstruction * findUnexpectedBoxUse(SILValue Box, bool examinePartialApply,
bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) {
llvm::SmallVector<Operand *, 4> LocalPromotedOperands;
llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end());
while (!Worklist.empty()) {
auto *Op = Worklist.pop_back_val();
auto *User = Op->getUser();
if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) ||
isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) ||
(!inAppliedFunction && isa<DeallocBoxInst>(User)))
continue;
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) {
copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist));
continue;
}
if (auto *PAI = dyn_cast<PartialApplyInst>(User))
if (examinePartialApply && checkPartialApplyBody(Op) &&
!partialApplyEscapes(PAI, /* examineApply = */ true)) {
LocalPromotedOperands.push_back(Op);
continue;
}
return User;
}
...
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Recap
→ Promotes @noescape values to stack
→ @escaping values with a reference count
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Optimization Flags
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
optimized.swift
func closure(_ completion: @escaping () -> Void) {
completion()
}
func number() -> Int {
var i: Int
i = 0
closure { i = 10 }
return i
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
canonical SIL with -O
$swiftc -emit-sil -O optimized.swift > optimized.sil
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
optimized.sil
%0 = integer_literal $Builtin.Int64, 10
%1 = struct $Int (%0 : $Builtin.Int64)
return %1 : $Int
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
No alloc_box or
alloc_stack
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Mem2Reg
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Register Promotion of
Stack Allocations
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
How can promote stack
allocation?
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class SILMem2Reg : public SILFunctionTransform {
void run() override {
SILFunction *F = getFunction();
DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>();
bool Changed = MemoryToRegisters(*F, DA->get(F)).run();
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class SILMem2Reg : public SILFunctionTransform {
void run() override {
SILFunction *F = getFunction();
DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>();
bool Changed = MemoryToRegisters(*F, DA->get(F)).run();
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class SILMem2Reg : public SILFunctionTransform {
void run() override {
SILFunction *F = getFunction();
DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>();
bool Changed = MemoryToRegisters(*F, DA->get(F)).run();
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
class SILMem2Reg : public SILFunctionTransform {
void run() override {
SILFunction *F = getFunction();
DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>();
bool Changed = MemoryToRegisters(*F, DA->get(F)).run();
...
}
};
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc, DomTreeLevelMap &DomTreeLevels){
NumAllocStackFound++;
bool inSingleBlock = false;
if (isCaptured(alloc, inSingleBlock)) {
NumAllocStackCaptured++;
return false;
}
if (isWriteOnlyAllocation(alloc)) {
eraseUsesOfInstruction(alloc);
return true;
}
if (inSingleBlock) {
removeSingleBlockAllocation(alloc);
if (!alloc->use_empty()) {
B.setInsertionPoint(std::next(alloc->getIterator()));
B.createDeallocStack(alloc->getLoc(), alloc);
}
return true;
}
StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run();
eraseUsesOfInstruction(alloc);
return true;
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc, DomTreeLevelMap &DomTreeLevels){
NumAllocStackFound++;
bool inSingleBlock = false;
if (isCaptured(alloc, inSingleBlock)) {
NumAllocStackCaptured++;
return false;
}
if (isWriteOnlyAllocation(alloc)) {
eraseUsesOfInstruction(alloc);
return true;
}
if (inSingleBlock) {
removeSingleBlockAllocation(alloc);
if (!alloc->use_empty()) {
B.setInsertionPoint(std::next(alloc->getIterator()));
B.createDeallocStack(alloc->getLoc(), alloc);
}
return true;
}
StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run();
eraseUsesOfInstruction(alloc);
return true;
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc, DomTreeLevelMap &DomTreeLevels){
NumAllocStackFound++;
bool inSingleBlock = false;
if (isCaptured(alloc, inSingleBlock)) {
NumAllocStackCaptured++;
return false;
}
if (isWriteOnlyAllocation(alloc)) {
eraseUsesOfInstruction(alloc);
return true;
}
if (inSingleBlock) {
removeSingleBlockAllocation(alloc);
if (!alloc->use_empty()) {
B.setInsertionPoint(std::next(alloc->getIterator()));
B.createDeallocStack(alloc->getLoc(), alloc);
}
return true;
}
StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run();
eraseUsesOfInstruction(alloc);
return true;
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc, DomTreeLevelMap &DomTreeLevels){
NumAllocStackFound++;
bool inSingleBlock = false;
if (isCaptured(alloc, inSingleBlock)) {
NumAllocStackCaptured++;
return false;
}
if (isWriteOnlyAllocation(alloc)) {
eraseUsesOfInstruction(alloc);
return true;
}
if (inSingleBlock) {
removeSingleBlockAllocation(alloc);
if (!alloc->use_empty()) {
B.setInsertionPoint(std::next(alloc->getIterator()));
B.createDeallocStack(alloc->getLoc(), alloc);
}
return true;
}
StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run();
eraseUsesOfInstruction(alloc);
return true;
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc, DomTreeLevelMap &DomTreeLevels){
NumAllocStackFound++;
bool inSingleBlock = false;
if (isCaptured(alloc, inSingleBlock)) {
NumAllocStackCaptured++;
return false;
}
if (isWriteOnlyAllocation(alloc)) {
eraseUsesOfInstruction(alloc);
return true;
}
if (inSingleBlock) {
removeSingleBlockAllocation(alloc);
if (!alloc->use_empty()) {
B.setInsertionPoint(std::next(alloc->getIterator()));
B.createDeallocStack(alloc->getLoc(), alloc);
}
return true;
}
StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run();
eraseUsesOfInstruction(alloc);
return true;
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
optimized.swift
func closure(_ completion: @escaping () -> Void) {
completion()
}
func number() -> Int {
var i: Int
i = 0
closure { i = 10 }
return i
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
optimized.swift
func number() -> Int {
return 10
}
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Optimization Flags
→ -Onone
→ -O
→ -Ounchecked
→ -Osize
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Tips for Debug
→ -Xllvm -sil-print-all
→ -Xllvm -sil-print-only-functions
→ -Xllvm -sil-print-before/after/around
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Summary
→ Optimize, optimize and optimize !
→ Better ideas of how Swift Compiler works "
→ Definitely worth learning #
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
References
→ var vs let in SIL
→ swift/docs/SIL.rst
→ Debugging the Swift Compiler
→ Swift's High-Level IR: A Case Study of
Complementing LLVM IR with Language-Specific
Optimization
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
Thank you!
SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018

SIL Optimizations - AllocBoxToStack

  • 1.
    SIL Optimizations AllocBoxToStack SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 2.
    Hi, I'm Yusuke @kitasuke SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 3.
    Why SIL?  SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 4.
    Why SIL? SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 5.
    Why SIL? → Betteridea of Swift type system ! SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 6.
    Why SIL? → Betteridea of Swift type system ! → Optimizations magic "✨ SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 7.
    Why SIL? → Betteridea of Swift type system ! → Optimizations magic "✨ → Just for fun $ SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 8.
    SIL   SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 9.
    Swift Intermediate LanguageSIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 10.
    SIL is alanguage specific Intermediate Representation SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 11.
    Swift  SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 12.
    Swift Compiler SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 13.
    Swift Compiler SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 14.
    Swift Compiler SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 15.
    SIL SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 16.
    raw SIL SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 17.
    canonical SIL SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 18.
    Objective-C  SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 19.
    Clang    SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 20.
    Clang SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 21.
    SIL   SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 22.
    Optimizations SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 23.
    Optimization Passes → CapturePromotion →AllocBoxToStack → MandatoryInlining → DeadCodeElimination → Mem2Reg ... SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 24.
    AllocBoxToStack SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 25.
    Stack Promotion of BoxObjects SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 26.
    Stack vs Heap SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 27.
    Stack SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 28.
    Stack → Very fast SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 29.
    Stack → Very fast →No explicit memory management SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 30.
    Stack → Very fast →No explicit memory management → Size limits SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 31.
    Heap SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 32.
    Heap → Slow SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 33.
    Heap → Slow → Explicitmemory management SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 34.
    Heap → Slow → Explicitmemory management → No size limits SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 35.
    Stack vs Heap StackHeap Value types Reference types Pointers to reference type SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 36.
    How AllocBoxToStack works? SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 37.
    alloc_box ➡ alloc_stack SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 38.
    stack.swift func number() ->Int { var x: Int x = 1 return x } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 39.
    raw SIL $swiftc -emit-silgenstack.swift > stack.sil SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 40.
    stack.sil %0 = alloc_box${ var Int }, var, name "x" %1 = mark_uninitialized [var] %0 : ${ var Int } %2 = project_box %1 : ${ var Int }, 0 ... %10 = begin_access [read] [unknown] %2 : $*Int %11 = load [trivial] %10 : $*Int end_access %10 : $*Int destroy_value %1 : ${ var Int } return %11 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 41.
    stack.sil %0 = alloc_box${ var Int }, var, name "x" %1 = mark_uninitialized [var] %0 : ${ var Int } %2 = project_box %1 : ${ var Int }, 0 ... %10 = begin_access [read] [unknown] %2 : $*Int %11 = load [trivial] %10 : $*Int end_access %10 : $*Int destroy_value %1 : ${ var Int } return %11 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 42.
    canonical SIL $swiftc -emit-silstack.swift > stack.sil SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 43.
    stack.sil %0 = alloc_stack$Int, var, name "x" %1 = integer_literal $Builtin.Int64, 1 %2 = struct $Int (%1 : $Builtin.Int64) ... return %2 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 44.
    stack.sil %0 = alloc_stack$Int, var, name "x" %1 = integer_literal $Builtin.Int64, 1 %2 = struct $Int (%1 : $Builtin.Int64) ... return %2 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 45.
    alloc_box vs alloc_stack SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 46.
    alloc_box alloc_box allocates areference counted box on heap. SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 47.
    alloc_stack alloc_stack allocates avalue on stack. All allocations must be deallocated prior to returning from a function. SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 48.
    Stack Promotion of BoxObjects SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 49.
    alloc_box ➡ alloc_stack SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 50.
    alloc_box ➡ alloc_box SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 51.
    box.swift func closure(_ completion:@escaping () -> Void) { completion() } func number() -> Int { var i: Int i = 0 closure { i = 10 } return i } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 52.
    box.swift func closure(_ completion:@escaping () -> Void) { completion() } func number() -> Int { var i: Int i = 0 closure { i = 10 } return i } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 53.
    box.swift func closure(_ completion:@escaping () -> Void) { completion() } func number() -> Int { var i: Int i = 0 closure { i = 10 } return i } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 54.
    raw SIL $swiftc -emit-silgenbox.swift > box.sil SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 55.
    box.sil %0 = alloc_box${ var Int }, var, name "i" %1 = mark_uninitialized [var] %0 : ${ var Int } %2 = project_box %1 : ${ var Int }, 0 ... %16 = begin_access [read] [unknown] %2 : $*Int %17 = load [trivial] %16 : $*Int end_access %16 : $*Int destroy_value %1 : ${ var Int } return %17 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 56.
    box.sil %0 = alloc_box${ var Int }, var, name "i" %1 = mark_uninitialized [var] %0 : ${ var Int } %2 = project_box %1 : ${ var Int }, 0 ... %16 = begin_access [read] [unknown] %2 : $*Int %17 = load [trivial] %16 : $*Int end_access %16 : $*Int destroy_value %1 : ${ var Int } return %17 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 57.
    canonical SIL $swiftc -emit-silbox.swift > box.sil SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 58.
    box.sil %0 = alloc_box${ var Int }, var, name "i" %1 = project_box %0 : ${ var Int }, 0 %2 = integer_literal $Builtin.Int64, 0 %3 = struct $Int (%2 : $Builtin.Int64) ... %12 = begin_access [read] [dynamic] %1 : $*Int %13 = load %12 : $*Int end_access %12 : $*Int strong_release %0 : ${ var Int } return %13 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 59.
    box.sil %0 = alloc_box${ var Int }, var, name "i" %1 = project_box %0 : ${ var Int }, 0 %2 = integer_literal $Builtin.Int64, 0 %3 = struct $Int (%2 : $Builtin.Int64) ... %12 = begin_access [read] [dynamic] %1 : $*Int %13 = load %12 : $*Int end_access %12 : $*Int strong_release %0 : ${ var Int } return %13 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 60.
    How can promote alloc_box? SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 61.
    class AllocBoxToStack :public SILFunctionTransform { void run() override { ... AllocBoxToStackState pass(this); for (auto &BB : *getFunction()) { for (auto &I : BB) if (auto *ABI = dyn_cast<AllocBoxInst>(&I)) if (canPromoteAllocBox(ABI, pass.PromotedOperands)) pass.Promotable.push_back(ABI); } if (!pass.Promotable.empty()) { auto Count = rewritePromotedBoxes(pass); ... } ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 62.
    class AllocBoxToStack :public SILFunctionTransform { void run() override { ... AllocBoxToStackState pass(this); for (auto &BB : *getFunction()) { for (auto &I : BB) if (auto *ABI = dyn_cast<AllocBoxInst>(&I)) if (canPromoteAllocBox(ABI, pass.PromotedOperands)) pass.Promotable.push_back(ABI); } if (!pass.Promotable.empty()) { auto Count = rewritePromotedBoxes(pass); ... } ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 63.
    class AllocBoxToStack :public SILFunctionTransform { void run() override { ... AllocBoxToStackState pass(this); for (auto &BB : *getFunction()) { for (auto &I : BB) if (auto *ABI = dyn_cast<AllocBoxInst>(&I)) if (canPromoteAllocBox(ABI, pass.PromotedOperands)) pass.Promotable.push_back(ABI); } if (!pass.Promotable.empty()) { auto Count = rewritePromotedBoxes(pass); ... } ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 64.
    class AllocBoxToStack :public SILFunctionTransform { void run() override { ... AllocBoxToStackState pass(this); for (auto &BB : *getFunction()) { for (auto &I : BB) if (auto *ABI = dyn_cast<AllocBoxInst>(&I)) if (canPromoteAllocBox(ABI, pass.PromotedOperands)) pass.Promotable.push_back(ABI); } if (!pass.Promotable.empty()) { auto Count = rewritePromotedBoxes(pass); ... } ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 65.
    static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) { llvm::SmallVector<Operand *, 4> LocalPromotedOperands; llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) || isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) || (!inAppliedFunction && isa<DeallocBoxInst>(User))) continue; if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(User)) if (examinePartialApply && checkPartialApplyBody(Op) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(Op); continue; } return User; } ... } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 66.
    static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) { llvm::SmallVector<Operand *, 4> LocalPromotedOperands; llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) || isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) || (!inAppliedFunction && isa<DeallocBoxInst>(User))) continue; if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(User)) if (examinePartialApply && checkPartialApplyBody(Op) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(Op); continue; } return User; } ... } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 67.
    static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) { llvm::SmallVector<Operand *, 4> LocalPromotedOperands; llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) || isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) || (!inAppliedFunction && isa<DeallocBoxInst>(User))) continue; if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(User)) if (examinePartialApply && checkPartialApplyBody(Op) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(Op); continue; } return User; } ... } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 68.
    static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) { llvm::SmallVector<Operand *, 4> LocalPromotedOperands; llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) || isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) || (!inAppliedFunction && isa<DeallocBoxInst>(User))) continue; if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(User)) if (examinePartialApply && checkPartialApplyBody(Op) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(Op); continue; } return User; } ... } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 69.
    static SILInstruction *findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, bool inAppliedFunction, llvm::SmallVectorImpl<Operand *> &PromotedOperands) { llvm::SmallVector<Operand *, 4> LocalPromotedOperands; llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); if (isa<StrongRetainInst>(User) || isa<StrongReleaseInst>(User) || isa<ProjectBoxInst>(User) || isa<DestroyValueInst>(User) || (!inAppliedFunction && isa<DeallocBoxInst>(User))) continue; if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(User)) if (examinePartialApply && checkPartialApplyBody(Op) && !partialApplyEscapes(PAI, /* examineApply = */ true)) { LocalPromotedOperands.push_back(Op); continue; } return User; } ... } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 70.
    Recap → Promotes @noescapevalues to stack → @escaping values with a reference count SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 71.
    Optimization Flags SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 72.
    optimized.swift func closure(_ completion:@escaping () -> Void) { completion() } func number() -> Int { var i: Int i = 0 closure { i = 10 } return i } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 73.
    canonical SIL with-O $swiftc -emit-sil -O optimized.swift > optimized.sil SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 74.
    optimized.sil %0 = integer_literal$Builtin.Int64, 10 %1 = struct $Int (%0 : $Builtin.Int64) return %1 : $Int SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 75.
    No alloc_box or alloc_stack SILOptimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 76.
    Mem2Reg SIL Optimizations -AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 77.
    Register Promotion of StackAllocations SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 78.
    How can promotestack allocation? SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 79.
    class SILMem2Reg :public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>(); bool Changed = MemoryToRegisters(*F, DA->get(F)).run(); ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 80.
    class SILMem2Reg :public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>(); bool Changed = MemoryToRegisters(*F, DA->get(F)).run(); ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 81.
    class SILMem2Reg :public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>(); bool Changed = MemoryToRegisters(*F, DA->get(F)).run(); ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 82.
    class SILMem2Reg :public SILFunctionTransform { void run() override { SILFunction *F = getFunction(); DominanceAnalysis* DA = PM->getAnalysis<DominanceAnalysis>(); bool Changed = MemoryToRegisters(*F, DA->get(F)).run(); ... } }; SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 83.
    bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,DomTreeLevelMap &DomTreeLevels){ NumAllocStackFound++; bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { NumAllocStackCaptured++; return false; } if (isWriteOnlyAllocation(alloc)) { eraseUsesOfInstruction(alloc); return true; } if (inSingleBlock) { removeSingleBlockAllocation(alloc); if (!alloc->use_empty()) { B.setInsertionPoint(std::next(alloc->getIterator())); B.createDeallocStack(alloc->getLoc(), alloc); } return true; } StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run(); eraseUsesOfInstruction(alloc); return true; } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 84.
    bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,DomTreeLevelMap &DomTreeLevels){ NumAllocStackFound++; bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { NumAllocStackCaptured++; return false; } if (isWriteOnlyAllocation(alloc)) { eraseUsesOfInstruction(alloc); return true; } if (inSingleBlock) { removeSingleBlockAllocation(alloc); if (!alloc->use_empty()) { B.setInsertionPoint(std::next(alloc->getIterator())); B.createDeallocStack(alloc->getLoc(), alloc); } return true; } StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run(); eraseUsesOfInstruction(alloc); return true; } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 85.
    bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,DomTreeLevelMap &DomTreeLevels){ NumAllocStackFound++; bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { NumAllocStackCaptured++; return false; } if (isWriteOnlyAllocation(alloc)) { eraseUsesOfInstruction(alloc); return true; } if (inSingleBlock) { removeSingleBlockAllocation(alloc); if (!alloc->use_empty()) { B.setInsertionPoint(std::next(alloc->getIterator())); B.createDeallocStack(alloc->getLoc(), alloc); } return true; } StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run(); eraseUsesOfInstruction(alloc); return true; } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 86.
    bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,DomTreeLevelMap &DomTreeLevels){ NumAllocStackFound++; bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { NumAllocStackCaptured++; return false; } if (isWriteOnlyAllocation(alloc)) { eraseUsesOfInstruction(alloc); return true; } if (inSingleBlock) { removeSingleBlockAllocation(alloc); if (!alloc->use_empty()) { B.setInsertionPoint(std::next(alloc->getIterator())); B.createDeallocStack(alloc->getLoc(), alloc); } return true; } StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run(); eraseUsesOfInstruction(alloc); return true; } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 87.
    bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc,DomTreeLevelMap &DomTreeLevels){ NumAllocStackFound++; bool inSingleBlock = false; if (isCaptured(alloc, inSingleBlock)) { NumAllocStackCaptured++; return false; } if (isWriteOnlyAllocation(alloc)) { eraseUsesOfInstruction(alloc); return true; } if (inSingleBlock) { removeSingleBlockAllocation(alloc); if (!alloc->use_empty()) { B.setInsertionPoint(std::next(alloc->getIterator())); B.createDeallocStack(alloc->getLoc(), alloc); } return true; } StackAllocationPromoter(alloc, DT, DomTreeLevels, B).run(); eraseUsesOfInstruction(alloc); return true; } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 88.
    optimized.swift func closure(_ completion:@escaping () -> Void) { completion() } func number() -> Int { var i: Int i = 0 closure { i = 10 } return i } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 89.
    optimized.swift func number() ->Int { return 10 } SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 90.
    Optimization Flags → -Onone →-O → -Ounchecked → -Osize SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 91.
    Tips for Debug →-Xllvm -sil-print-all → -Xllvm -sil-print-only-functions → -Xllvm -sil-print-before/after/around SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 92.
    Summary → Optimize, optimizeand optimize ! → Better ideas of how Swift Compiler works " → Definitely worth learning # SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 93.
    References → var vslet in SIL → swift/docs/SIL.rst → Debugging the Swift Compiler → Swift's High-Level IR: A Case Study of Complementing LLVM IR with Language-Specific Optimization SIL Optimizations - AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018
  • 94.
    Thank you! SIL Optimizations- AllocBoxToStack, Yusuke Kita (@kitasuke), iOSDC 2018