Skip to content

Commit 0a483a6

Browse files
committed
SILOptimizer: allow function pointers in static globals and static global arrays.
For example, generate a static global for the array literal in: func foo(_ i: Int) -> Int { ... } func bar(_ i: Int) -> Int { ... } func returnFunctionArray() -> [(Int) -> Int] { return [foo, bar] } rdar://73570149 https://bugs.swift.org/browse/SR-14101
1 parent 44f07ae commit 0a483a6

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

lib/IRGen/GenConstant.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,40 @@ llvm::Constant *irgen::emitConstantValue(IRGenModule &IGM, SILValue operand) {
216216
auto *val = emitConstantValue(IGM, VTBI->getOperand());
217217
auto *sTy = IGM.getTypeInfo(VTBI->getType()).getStorageType();
218218
return llvm::ConstantExpr::getIntToPtr(val, sTy);
219+
220+
} else if (auto *CFI = dyn_cast<ConvertFunctionInst>(operand)) {
221+
return emitConstantValue(IGM, CFI->getOperand());
222+
223+
} else if (auto *T2TFI = dyn_cast<ThinToThickFunctionInst>(operand)) {
224+
SILType type = operand->getType();
225+
auto *sTy = cast<llvm::StructType>(IGM.getTypeInfo(type).getStorageType());
226+
227+
auto *function = llvm::ConstantExpr::getBitCast(
228+
emitConstantValue(IGM, T2TFI->getCallee()),
229+
sTy->getTypeAtIndex((unsigned)0));
230+
231+
auto *context = llvm::ConstantExpr::getBitCast(
232+
llvm::ConstantPointerNull::get(IGM.OpaquePtrTy),
233+
sTy->getTypeAtIndex((unsigned)1));
234+
235+
return llvm::ConstantStruct::get(sTy, {function, context});
236+
237+
} else if (auto *FRI = dyn_cast<FunctionRefInst>(operand)) {
238+
SILFunction *fn = FRI->getReferencedFunction();
239+
240+
llvm::Constant *fnPtr = IGM.getAddrOfSILFunction(fn, NotForDefinition);
241+
assert(!fn->isAsync() && "TODO: support async functions");
242+
243+
CanSILFunctionType fnType = FRI->getType().getAs<SILFunctionType>();
244+
auto authInfo = PointerAuthInfo::forFunctionPointer(IGM, fnType);
245+
if (authInfo.isSigned()) {
246+
auto constantDiscriminator =
247+
cast<llvm::Constant>(authInfo.getDiscriminator());
248+
assert(!constantDiscriminator->getType()->isPointerTy());
249+
fnPtr = IGM.getConstantSignedPointer(fnPtr, authInfo.getKey(), nullptr,
250+
constantDiscriminator);
251+
}
252+
return fnPtr;
219253
} else {
220254
llvm_unreachable("Unsupported SILInstruction in static initializer!");
221255
}

lib/SIL/IR/SILGlobalVariable.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,19 @@ bool SILGlobalVariable::isValidStaticInitializerInst(const SILInstruction *I,
170170
return false;
171171
}
172172
return false;
173+
case SILInstructionKind::FunctionRefInst:
174+
// TODO: support async function pointers in static globals.
175+
if (cast<FunctionRefInst>(I)->getReferencedFunction()->isAsync())
176+
return false;
177+
return true;
173178
case SILInstructionKind::StructInst:
174179
case SILInstructionKind::TupleInst:
175180
case SILInstructionKind::IntegerLiteralInst:
176181
case SILInstructionKind::FloatLiteralInst:
177182
case SILInstructionKind::ObjectInst:
178183
case SILInstructionKind::ValueToBridgeObjectInst:
184+
case SILInstructionKind::ConvertFunctionInst:
185+
case SILInstructionKind::ThinToThickFunctionInst:
179186
return true;
180187
default:
181188
return false;
@@ -310,13 +317,7 @@ swift::getVariableOfStaticInitializer(SILFunction *InitFunc,
310317
if (HasStore || SI->getDest() != SGA)
311318
return nullptr;
312319
HasStore = true;
313-
SILValue value = SI->getSrc();
314-
315-
// We only handle StructInst and TupleInst being stored to a
316-
// global variable for now.
317-
if (!isa<StructInst>(value) && !isa<TupleInst>(value))
318-
return nullptr;
319-
InitVal = cast<SingleValueInstruction>(value);
320+
InitVal = cast<SingleValueInstruction>(SI->getSrc());
320321
} else if (!SILGlobalVariable::isValidStaticInitializerInst(&I,
321322
I.getModule())) {
322323
return nullptr;

test/SILOptimizer/static_arrays.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
// Check if the optimizer is able to convert array literals to statically initialized arrays.
1111

12+
// CHECK-LABEL: sil_global @$s4test4FStrV10globalFuncyS2icvpZ : $@callee_guaranteed (Int) -> Int = {
13+
// CHECK: %0 = function_ref @$s4test3fooyS2iF : $@convention(thin) (Int) -> Int
14+
// CHECK-NEXT: %initval = thin_to_thick_function %0
15+
// CHECK-NEXT: }
16+
1217
// CHECK-LABEL: outlined variable #0 of arrayLookup(_:)
1318
// CHECK-NEXT: sil_global private @{{.*}}arrayLookup{{.*}} = {
1419
// CHECK-DAG: integer_literal $Builtin.Int{{[0-9]+}}, 10
@@ -51,6 +56,20 @@
5156
// CHECK: object {{.*}} ({{[^,]*}}, [tail_elems] {{[^,]*}}, {{[^,]*}})
5257
// CHECK-NEXT: }
5358

59+
// CHECK-LABEL: outlined variable #0 of functionArray()
60+
// CHECK-NEXT: sil_global private @{{.*functionArray.*}} = {
61+
// CHECK: function_ref
62+
// CHECK: thin_to_thick_function
63+
// CHECK: convert_function
64+
// CHECK: function_ref
65+
// CHECK: thin_to_thick_function
66+
// CHECK: convert_function
67+
// CHECK: function_ref
68+
// CHECK: thin_to_thick_function
69+
// CHECK: convert_function
70+
// CHECK: object {{.*}} ({{[^,]*}}, [tail_elems]
71+
// CHECK-NEXT: }
72+
5473
// CHECK-LABEL: outlined variable #0 of returnDictionary()
5574
// CHECK-NEXT: sil_global private @{{.*}}returnDictionary{{.*}} = {
5675
// CHECK-DAG: integer_literal $Builtin.Int{{[0-9]+}}, 5
@@ -154,6 +173,22 @@ public func returnStringDictionary() -> [String:String] {
154173
return ["1":"2", "3":"4", "5":"6"]
155174
}
156175

176+
func foo(_ i: Int) -> Int { return i }
177+
178+
// CHECK-LABEL: sil {{.*functionArray.*}} : $@convention(thin) () -> @owned Array<(Int) -> Int> {
179+
// CHECK: global_value @{{.*functionArray.*}}
180+
// CHECK: } // end sil function '{{.*functionArray.*}}'
181+
@inline(never)
182+
func functionArray() -> [(Int) -> Int] {
183+
func bar(_ i: Int) -> Int { return i + 1 }
184+
return [foo, bar, { $0 + 10 }]
185+
}
186+
187+
public struct FStr {
188+
// Not an array, but also tested here.
189+
public static var globalFunc = foo
190+
}
191+
157192
// CHECK-OUTPUT: [100, 101, 102]
158193
print(globalVariable)
159194
// CHECK-OUTPUT-NEXT: 11
@@ -168,6 +203,10 @@ print(gg!)
168203
storeArray()
169204
// CHECK-OUTPUT-NEXT: [227, 228]
170205
print(gg!)
206+
// CHECK-OUTPUT-NEXT: 311
207+
print(functionArray()[0](100) + functionArray()[1](100) + functionArray()[2](100))
208+
// CHECK-OUTPUT-NEXT: 27
209+
print(FStr.globalFunc(27))
171210

172211
let dict = returnDictionary()
173212
// CHECK-OUTPUT-NEXT: dict 3: 2, 4, 6

0 commit comments

Comments
 (0)