Skip to content

Commit fc3e68a

Browse files
committed
SILCombine: optimize a load from global static let
Propagate a value from a static "let" global variable. This optimization is also done by GlobalOpt, but not with de-serialized globals, which can occur with cross-module optimization.
1 parent 4aa99a2 commit fc3e68a

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
2727
#include "swift/SILOptimizer/Utils/Devirtualize.h"
2828
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
29+
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
2930
#include "llvm/ADT/DenseMap.h"
3031
#include "llvm/ADT/SmallPtrSet.h"
3132
#include "llvm/ADT/SmallVector.h"
@@ -807,6 +808,31 @@ static bool isZeroLoadFromEmptyCollection(LoadInst *LI) {
807808
}
808809
}
809810

811+
static SingleValueInstruction *getValueFromStaticLet(SILValue v) {
812+
if (auto *globalAddr = dyn_cast<GlobalAddrInst>(v)) {
813+
SILGlobalVariable *global = globalAddr->getReferencedGlobal();
814+
if (!global->isLet())
815+
return nullptr;
816+
return dyn_cast_or_null<SingleValueInstruction>(
817+
global->getStaticInitializerValue());
818+
}
819+
if (auto *seai = dyn_cast<StructElementAddrInst>(v)) {
820+
auto *structVal = getValueFromStaticLet(seai->getOperand());
821+
if (!structVal)
822+
return nullptr;
823+
return cast<SingleValueInstruction>(
824+
cast<StructInst>(structVal)->getOperandForField(seai->getField())->get());
825+
}
826+
if (auto *teai = dyn_cast<TupleElementAddrInst>(v)) {
827+
auto *tupleVal = getValueFromStaticLet(teai->getOperand());
828+
if (!tupleVal)
829+
return nullptr;
830+
return cast<SingleValueInstruction>(
831+
cast<TupleInst>(tupleVal)->getElement(teai->getFieldNo()));
832+
}
833+
return nullptr;
834+
}
835+
810836
SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
811837
if (LI->getFunction()->hasOwnership())
812838
return nullptr;
@@ -833,6 +859,15 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
833859
if (isZeroLoadFromEmptyCollection(LI))
834860
return Builder.createIntegerLiteral(LI->getLoc(), LI->getType(), 0);
835861

862+
// Propagate a value from a static "let" global variable.
863+
// This optimization is also done by GlobalOpt, but not with de-serialized
864+
// globals, which can occur with cross-module optimization.
865+
if (SingleValueInstruction *initVal = getValueFromStaticLet(LI->getOperand())) {
866+
StaticInitCloner cloner(LI);
867+
cloner.add(initVal);
868+
return cloner.clone(initVal);
869+
}
870+
836871
return nullptr;
837872
}
838873

test/SILOptimizer/sil_combine.sil

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3739,3 +3739,80 @@ bb0(%0 : $@thick SpecialEnum.Type):
37393739
%4 = struct $Bool (%3 : $Builtin.Int1)
37403740
return %4 : $Bool
37413741
}
3742+
3743+
struct IntTuple {
3744+
@_hasStorage var t: (Int64, Int64)
3745+
}
3746+
3747+
sil_global [let] @initialized_let_global : $Int64 = {
3748+
%0 = integer_literal $Builtin.Int64, 27
3749+
%initval = struct $Int64 (%0 : $Builtin.Int64)
3750+
}
3751+
3752+
sil_global @initialized_var_global : $Int64 = {
3753+
%0 = integer_literal $Builtin.Int64, 27
3754+
%initval = struct $Int64 (%0 : $Builtin.Int64)
3755+
}
3756+
3757+
sil_global [let] @initialized_tuple_global : $IntTuple = {
3758+
%0 = integer_literal $Builtin.Int64, 27
3759+
%1 = integer_literal $Builtin.Int64, 28
3760+
%2 = struct $Int64 (%0 : $Builtin.Int64)
3761+
%3 = struct $Int64 (%1 : $Builtin.Int64)
3762+
%4 = tuple (%2 : $Int64, %3 : $Int64)
3763+
%initval = struct $IntTuple (%4 : $(Int64, Int64))
3764+
}
3765+
3766+
// CHECK-LABEL: sil @load_from_global_let
3767+
// CHECK: [[I:%.*]] = integer_literal $Builtin.Int64, 27
3768+
// CHECK: [[R:%.*]] = struct $Int64 ([[I]] : $Builtin.Int64)
3769+
// CHECK: return [[R]]
3770+
// CHECK: } // end sil function 'load_from_global_let'
3771+
sil @load_from_global_let : $@convention(thin) () -> Int64 {
3772+
bb0:
3773+
%0 = global_addr @initialized_let_global : $*Int64
3774+
%1 = load %0 : $*Int64
3775+
return %1 : $Int64
3776+
}
3777+
3778+
// CHECK-LABEL: sil @load_from_global_with_projections
3779+
// CHECK: [[I:%.*]] = integer_literal $Builtin.Int64, 28
3780+
// CHECK: return [[I]]
3781+
// CHECK: } // end sil function 'load_from_global_with_projections'
3782+
sil @load_from_global_with_projections : $@convention(thin) () -> Builtin.Int64 {
3783+
bb0:
3784+
%0 = global_addr @initialized_tuple_global : $*IntTuple
3785+
%1 = struct_element_addr %0 : $*IntTuple, #IntTuple.t
3786+
%2 = tuple_element_addr %1 : $*(Int64, Int64), 1
3787+
%3 = struct_element_addr %2 : $*Int64, #Int64._value
3788+
%4 = load %3 : $*Builtin.Int64
3789+
return %4 : $Builtin.Int64
3790+
}
3791+
3792+
// CHECK-LABEL: sil @load_from_global_without_projections
3793+
// CHECK-DAG: [[I1:%.*]] = integer_literal $Builtin.Int64, 27
3794+
// CHECK-DAG: [[I2:%.*]] = integer_literal $Builtin.Int64, 28
3795+
// CHECK-DAG: [[S1:%.*]] = struct $Int64 ([[I1]] : $Builtin.Int64)
3796+
// CHECK-DAG: [[S2:%.*]] = struct $Int64 ([[I2]] : $Builtin.Int64)
3797+
// CHECK-DAG: [[T:%.*]] = tuple ([[S1]] : $Int64, [[S2]] : $Int64)
3798+
// CHECK-DAG: [[IT:%.*]] = struct $IntTuple ([[T]] : $(Int64, Int64))
3799+
// CHECK: return [[IT]]
3800+
// CHECK: } // end sil function 'load_from_global_without_projections'
3801+
sil @load_from_global_without_projections : $@convention(thin) () -> IntTuple {
3802+
bb0:
3803+
%0 = global_addr @initialized_tuple_global : $*IntTuple
3804+
%1 = load %0 : $*IntTuple
3805+
return %1 : $IntTuple
3806+
}
3807+
3808+
// CHECK-LABEL: sil @load_from_global_var
3809+
// CHECK: global_addr
3810+
// CHECK-NEXT: load
3811+
// CHECK-NEXT: return
3812+
// CHECK: } // end sil function 'load_from_global_var'
3813+
sil @load_from_global_var : $@convention(thin) () -> Int64 {
3814+
bb0:
3815+
%0 = global_addr @initialized_var_global : $*Int64
3816+
%1 = load %0 : $*Int64
3817+
return %1 : $Int64
3818+
}

0 commit comments

Comments
 (0)