Skip to content

Commit 65b03f9

Browse files
committed
SILOptimizer: prevent illegal load forwarding of function references in global variables.
We cannot replace a load from a global let-variable with a function_ref, if the referenced function would violate the resilience rules. That means if a non-public function_ref would be inlined into a function which is serialized.
1 parent 9f616e5 commit 65b03f9

File tree

5 files changed

+52
-9
lines changed

5 files changed

+52
-9
lines changed

include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,9 @@ class StaticInitCloner : public SILCloner<StaticInitCloner> {
320320
/// Add \p InitVal and all its operands (transitively) for cloning.
321321
///
322322
/// Note: all init values must are added, before calling clone().
323-
void add(SILInstruction *initVal);
323+
/// Returns false if cloning is not possible, e.g. if we would end up cloning
324+
/// a reference to a private function into a function which is serialized.
325+
bool add(SILInstruction *initVal);
324326

325327
/// Clone \p InitVal and all its operands into the initializer of the
326328
/// SILGlobalVariable.
@@ -332,7 +334,9 @@ class StaticInitCloner : public SILCloner<StaticInitCloner> {
332334
static void appendToInitializer(SILGlobalVariable *gVar,
333335
SingleValueInstruction *initVal) {
334336
StaticInitCloner cloner(gVar);
335-
cloner.add(initVal);
337+
bool success = cloner.add(initVal);
338+
(void)success;
339+
assert(success && "adding initVal cannot fail for a global variable");
336340
cloner.clone(initVal);
337341
}
338342

lib/SILOptimizer/IPO/GlobalOpt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,8 @@ replaceLoadsByKnownValue(SILFunction *InitF, SILGlobalVariable *SILG,
388388
StaticInitCloner cloner(initCall);
389389
SmallVector<SILInstruction *, 8> insertedInsts;
390390
cloner.setTrackingList(&insertedInsts);
391-
cloner.add(initVal);
391+
if (!cloner.add(initVal))
392+
continue;
392393

393394
// Replace all loads from the addressor with the initial value of the global.
394395
replaceLoadsFromGlobal(initCall, initVal, cloner);
@@ -693,7 +694,8 @@ void SILGlobalOpt::optimizeGlobalAccess(SILGlobalVariable *SILG,
693694
continue;
694695

695696
StaticInitCloner cloner(globalAddr);
696-
cloner.add(initVal);
697+
if (!cloner.add(initVal))
698+
continue;
697699

698700
// Replace all loads from the addressor with the initial value of the global.
699701
replaceLoadsFromGlobal(globalAddr, initVal, cloner);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -959,8 +959,9 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
959959
// globals, which can occur with cross-module optimization.
960960
if (SingleValueInstruction *initVal = getValueFromStaticLet(LI->getOperand())) {
961961
StaticInitCloner cloner(LI);
962-
cloner.add(initVal);
963-
return cloner.clone(initVal);
962+
if (cloner.add(initVal)) {
963+
return cloner.clone(initVal);
964+
}
964965
}
965966

966967
// If we have a load [copy] whose only non-debug users are destroy_value, just

lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,19 @@ bool SinkAddressProjections::cloneProjections() {
247247
return true;
248248
}
249249

250-
void StaticInitCloner::add(SILInstruction *initVal) {
250+
bool StaticInitCloner::add(SILInstruction *initVal) {
251251
// Don't schedule an instruction twice for cloning.
252252
if (numOpsToClone.count(initVal) != 0)
253-
return;
253+
return true;
254+
255+
if (auto *funcRef = dyn_cast<FunctionRefInst>(initVal)) {
256+
// We cannot inline non-public functions into functions which are serialized.
257+
if (!getBuilder().isInsertingIntoGlobal() &&
258+
getBuilder().getFunction().isSerialized() &&
259+
!funcRef->getReferencedFunction()->hasValidLinkageForFragileRef()) {
260+
return false;
261+
}
262+
}
254263

255264
ArrayRef<Operand> operands = initVal->getAllOperands();
256265
numOpsToClone[initVal] = operands.size();
@@ -261,9 +270,11 @@ void StaticInitCloner::add(SILInstruction *initVal) {
261270
} else {
262271
// Recursively add all operands.
263272
for (const Operand &operand : operands) {
264-
add(cast<SingleValueInstruction>(operand.get()));
273+
if (!add(cast<SingleValueInstruction>(operand.get())))
274+
return false;
265275
}
266276
}
277+
return true;
267278
}
268279

269280
SingleValueInstruction *

test/SILOptimizer/sil_combine_ossa.sil

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4497,6 +4497,31 @@ bb0:
44974497
return %1 : $Int64
44984498
}
44994499

4500+
sil private [ossa] @somePrivateFunction : $@convention(thin) () -> Int64 {
4501+
bb0:
4502+
%0 = integer_literal $Builtin.Int64, 27
4503+
%1 = struct $Int64 (%0 : $Builtin.Int64)
4504+
return %1 : $Int64
4505+
}
4506+
4507+
sil_global [let] @pointerToSomePrivateFunction : $@callee_guaranteed () -> Int64 = {
4508+
%0 = function_ref @somePrivateFunction: $@convention(thin) () -> Int64
4509+
%initval = thin_to_thick_function %0 : $@convention(thin) () -> Int64 to $@callee_guaranteed () -> Int64
4510+
}
4511+
4512+
// CHECK-LABEL: sil [serialized] [ossa] @dontInlinePrivateFunctionRefIntoSerializedFunction
4513+
// CHECK: [[GA:%[0-9]+]] = global_addr
4514+
// CHECK: [[L:%[0-9]+]] = load [copy] [[GA]]
4515+
// CHECK: return [[L]]
4516+
// CHECK: } // end sil function 'dontInlinePrivateFunctionRefIntoSerializedFunction'
4517+
sil [serialized] [ossa] @dontInlinePrivateFunctionRefIntoSerializedFunction : $@convention(thin) () -> @owned @callee_guaranteed () -> Int64 {
4518+
bb0:
4519+
%0 = global_addr @pointerToSomePrivateFunction : $*@callee_guaranteed () -> Int64
4520+
%1 = load [copy] %0 : $*@callee_guaranteed () -> Int64
4521+
return %1 : $@callee_guaranteed () -> Int64
4522+
}
4523+
4524+
45004525
// Check for disabled optimization of unchecked_bitwise_cast to unchecked_ref_cast in ossa
45014526
// This test can be optimized when ossa is supported in the SILCombine for unchecked_bitwise_cast
45024527
// CHECK-LABEL: sil [ossa] @refcast :

0 commit comments

Comments
 (0)