Skip to content

Commit 91956a1

Browse files
authored
Merge pull request #70314 from kubamracek/embedded-no-alloc
[embedded] Implement non-allocating embedded Swift mode, under -no-allocations flag
2 parents 957443e + 08053b6 commit 91956a1

File tree

11 files changed

+169
-23
lines changed

11 files changed

+169
-23
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,8 @@ ERROR(wmo_with_embedded,none,
561561
"Whole module optimization (wmo) must be enabled with embedded Swift.", ())
562562
ERROR(objc_with_embedded,none,
563563
"Objective-C interoperability cannot be enabled with embedded Swift.", ())
564+
ERROR(no_allocations_without_embedded,none,
565+
"-no-allocations is only applicable with embedded Swift.", ())
564566

565567
#define UNDEFINE_DIAGNOSTIC_MACROS
566568
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ ERROR(embedded_swift_metatype_type,none,
364364
"cannot use metatype of type %0 in embedded Swift", (Type))
365365
ERROR(embedded_swift_metatype,none,
366366
"cannot use metatype in embedded Swift", ())
367+
ERROR(embedded_swift_allocating_type,none,
368+
"cannot use allocating type %0 in -no-allocations mode", (Type))
369+
ERROR(embedded_swift_allocating,none,
370+
"cannot use allocating type in -no-allocations mode", ())
367371
NOTE(performance_called_from,none,
368372
"called from here", ())
369373

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ class SILOptions {
282282
/// Are we building in embedded Swift mode?
283283
bool EmbeddedSwift = false;
284284

285+
/// Are we building in embedded Swift + -no-allocations?
286+
bool NoAllocations = false;
287+
285288
/// The name of the file to which the backend should save optimization
286289
/// records.
287290
std::string OptRecordFile;

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,10 @@ def save_optimization_record_passes :
872872
"the generated optimization record "
873873
"(by default, include all passes)">, MetaVarName<"<regex>">;
874874

875+
def no_allocations : Flag<["-"], "no-allocations">,
876+
Flags<[FrontendOption, HelpHidden]>,
877+
HelpText<"Diagnose any code that needs to heap allocate (classes, closures, etc.)">;
878+
875879
// Platform options.
876880
def enable_app_extension : Flag<["-"], "application-extension">,
877881
Flags<[FrontendOption, NoInteractiveOption]>,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,8 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
24252425

24262426
Opts.OSSACompleteLifetimes |= Args.hasArg(OPT_enable_ossa_complete_lifetimes);
24272427

2428+
Opts.NoAllocations = Args.hasArg(OPT_no_allocations);
2429+
24282430
return false;
24292431
}
24302432

@@ -3228,6 +3230,11 @@ bool CompilerInvocation::parseArgs(
32283230
SILOpts.SkipFunctionBodies = FunctionBodySkipping::None;
32293231
SILOpts.CMOMode = CrossModuleOptimizationMode::Everything;
32303232
SILOpts.EmbeddedSwift = true;
3233+
} else {
3234+
if (SILOpts.NoAllocations) {
3235+
Diags.diagnose(SourceLoc(), diag::no_allocations_without_embedded);
3236+
return true;
3237+
}
32313238
}
32323239

32333240
return false;

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 132 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class PerformanceDiagnostics {
9494
return visitFunction(function, perfConstr, /*parentLoc*/ nullptr);
9595
}
9696

97+
bool visitFunctionEmbeddedSwift(SILFunction *function) {
98+
return visitFunctionEmbeddedSwift(function, /*parentLoc*/ nullptr);
99+
}
100+
97101
/// Check functions _without_ performance annotations.
98102
///
99103
/// This is need to check closure arguments of called performance-annotated
@@ -104,6 +108,9 @@ class PerformanceDiagnostics {
104108
bool visitFunction(SILFunction *function, PerformanceConstraints perfConstr,
105109
LocWithParent *parentLoc);
106110

111+
bool visitFunctionEmbeddedSwift(SILFunction *function,
112+
LocWithParent *parentLoc);
113+
107114
bool visitInst(SILInstruction *inst, PerformanceConstraints perfConstr,
108115
LocWithParent *parentLoc);
109116

@@ -150,6 +157,75 @@ static bool isEffectFreeArraySemanticCall(SILInstruction *inst) {
150157
}
151158
}
152159

160+
/// Prints Embedded Swift specific performance diagnostics (no existentials,
161+
/// no metatypes, optionally no allocations) for \p function.
162+
bool PerformanceDiagnostics::visitFunctionEmbeddedSwift(
163+
SILFunction *function, LocWithParent *parentLoc) {
164+
// Don't check generic functions in embedded Swift, they're about to be
165+
// removed anyway.
166+
if (function->getLoweredFunctionType()->getSubstGenericSignature())
167+
return false;
168+
169+
if (!function->isDefinition())
170+
return false;
171+
172+
if (visitedFuncs.contains(function))
173+
return false;
174+
visitedFuncs[function] = PerformanceConstraints::None;
175+
176+
NonErrorHandlingBlocks neBlocks(function);
177+
178+
for (SILBasicBlock &block : *function) {
179+
for (SILInstruction &inst : block) {
180+
if (visitInst(&inst, PerformanceConstraints::None, parentLoc)) {
181+
if (inst.getLoc().getSourceLoc().isInvalid()) {
182+
auto demangledName = Demangle::demangleSymbolAsString(
183+
inst.getFunction()->getName(),
184+
Demangle::DemangleOptions::SimplifiedUIDemangleOptions());
185+
llvm::errs() << "in function " << demangledName << "\n";
186+
}
187+
LLVM_DEBUG(llvm::dbgs() << inst << *inst.getFunction());
188+
return true;
189+
}
190+
191+
if (auto as = FullApplySite::isa(&inst)) {
192+
LocWithParent asLoc(inst.getLoc().getSourceLoc(), parentLoc);
193+
LocWithParent *loc = &asLoc;
194+
if (parentLoc &&
195+
asLoc.loc == inst.getFunction()->getLocation().getSourceLoc())
196+
loc = parentLoc;
197+
198+
for (SILFunction *callee : bca->getCalleeList(as)) {
199+
if (visitFunctionEmbeddedSwift(callee, loc))
200+
return true;
201+
}
202+
} else if (auto *bi = dyn_cast<BuiltinInst>(&inst)) {
203+
PrettyStackTracePerformanceDiagnostics stackTrace(
204+
"visitFunction::BuiltinInst (once, once with context)", &inst);
205+
206+
switch (bi->getBuiltinInfo().ID) {
207+
case BuiltinValueKind::Once:
208+
case BuiltinValueKind::OnceWithContext:
209+
if (auto *fri = dyn_cast<FunctionRefInst>(bi->getArguments()[1])) {
210+
LocWithParent asLoc(bi->getLoc().getSourceLoc(), parentLoc);
211+
LocWithParent *loc = &asLoc;
212+
if (parentLoc &&
213+
asLoc.loc == bi->getFunction()->getLocation().getSourceLoc())
214+
loc = parentLoc;
215+
216+
if (visitFunctionEmbeddedSwift(fri->getReferencedFunction(), loc))
217+
return true;
218+
}
219+
break;
220+
default:
221+
break;
222+
}
223+
}
224+
}
225+
}
226+
return false;
227+
}
228+
153229
/// Prints performance diagnostics for \p function.
154230
bool PerformanceDiagnostics::visitFunction(SILFunction *function,
155231
PerformanceConstraints perfConstr,
@@ -438,6 +514,18 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
438514
return true;
439515
}
440516
}
517+
518+
if (module.getOptions().NoAllocations) {
519+
if (impact & RuntimeEffect::Allocating) {
520+
PrettyStackTracePerformanceDiagnostics stackTrace("allocation", inst);
521+
if (impactType) {
522+
diagnose(loc, diag::embedded_swift_allocating_type, impactType.getASTType());
523+
} else {
524+
diagnose(loc, diag::embedded_swift_allocating);
525+
}
526+
return true;
527+
}
528+
}
441529
}
442530

443531
if (perfConstr == PerformanceConstraints::None ||
@@ -585,19 +673,6 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
585673
void PerformanceDiagnostics::checkNonAnnotatedFunction(SILFunction *function) {
586674
for (SILBasicBlock &block : *function) {
587675
for (SILInstruction &inst : block) {
588-
if (function->getModule().getOptions().EmbeddedSwift) {
589-
auto loc = LocWithParent(inst.getLoc().getSourceLoc(), nullptr);
590-
if (visitInst(&inst, PerformanceConstraints::None, &loc)) {
591-
if (inst.getLoc().getSourceLoc().isInvalid()) {
592-
auto demangledName = Demangle::demangleSymbolAsString(
593-
inst.getFunction()->getName(),
594-
Demangle::DemangleOptions::SimplifiedUIDemangleOptions());
595-
llvm::errs() << "in function " << demangledName << "\n";
596-
}
597-
LLVM_DEBUG(llvm::dbgs() << inst << *inst.getFunction());
598-
}
599-
}
600-
601676
auto as = FullApplySite::isa(&inst);
602677
if (!as)
603678
continue;
@@ -701,16 +776,54 @@ class PerformanceDiagnosticsPass : public SILModuleTransform {
701776
if (function.wasDeserializedCanonical())
702777
continue;
703778

704-
// Don't check generic functions in embedded Swift, they're about to be
705-
// removed anyway.
706-
if (getModule()->getOptions().EmbeddedSwift &&
707-
function.getLoweredFunctionType()->getSubstGenericSignature())
708-
continue;
709-
710779
if (function.getPerfConstraints() == PerformanceConstraints::None) {
711780
diagnoser.checkNonAnnotatedFunction(&function);
712781
}
713782
}
783+
784+
if (getModule()->getOptions().EmbeddedSwift) {
785+
// Run embedded Swift SIL checks for metatype/existential use, and
786+
// allocation use (under -no-allocations mode). Try to start with public
787+
// and exported functions to get better call tree information.
788+
SmallVector<SILFunction *, 8> externallyVisibleFunctions;
789+
SmallVector<SILFunction *, 8> vtableMembers;
790+
SmallVector<SILFunction *, 8> others;
791+
SmallVector<SILFunction *, 8> constructorsAndDestructors;
792+
793+
for (SILFunction &function : *module) {
794+
auto func = function.getLocation().getAsASTNode<AbstractFunctionDecl>();
795+
if (func) {
796+
if (isa<DestructorDecl>(func) || isa<ConstructorDecl>(func)) {
797+
constructorsAndDestructors.push_back(&function);
798+
continue;
799+
}
800+
if (getMethodDispatch(func) == MethodDispatch::Class) {
801+
vtableMembers.push_back(&function);
802+
continue;
803+
}
804+
}
805+
806+
if (function.isPossiblyUsedExternally()) {
807+
externallyVisibleFunctions.push_back(&function);
808+
continue;
809+
}
810+
811+
others.push_back(&function);
812+
}
813+
814+
for (SILFunction *function : externallyVisibleFunctions) {
815+
diagnoser.visitFunctionEmbeddedSwift(function);
816+
}
817+
for (SILFunction *function : vtableMembers) {
818+
diagnoser.visitFunctionEmbeddedSwift(function);
819+
}
820+
for (SILFunction *function : others) {
821+
diagnoser.visitFunctionEmbeddedSwift(function);
822+
}
823+
for (SILFunction *function : constructorsAndDestructors) {
824+
diagnoser.visitFunctionEmbeddedSwift(function);
825+
}
826+
}
714827
}
715828
};
716829

test/embedded/anyobject-error-no-stdlib.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,4 @@ precedencegroup AssignmentPrecedence { assignment: true }
1616

1717
public func foo(_ x: AnyObject) {
1818
_ = type(of: x) // expected-error {{cannot use a value of protocol type 'AnyObject' in embedded Swift}}
19-
// expected-note@-1 {{called from here}}
2019
}

test/embedded/basic-errors-no-stdlib.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@ struct Concrete: Player {}
77

88
public func test() -> any Player {
99
Concrete() // expected-error {{cannot use a value of protocol type 'any Player' in embedded Swift}}
10-
// expected-note@-1 {{called from here}}
1110
}

test/embedded/metatypes.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ public func sink<T>(t: T) {}
99
public func test() -> Int {
1010
let metatype = Int.self
1111
sink(t: metatype) // expected-error {{cannot use metatype of type 'Int' in embedded Swift}}
12-
// expected-note@-1 {{called from here}}
1312
return 42
1413
}

test/embedded/no-allocations.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-swift-emit-ir %s -wmo
2+
// RUN: %target-swift-emit-ir %s -enable-experimental-feature Embedded -wmo
3+
// RUN: %target-swift-emit-ir %s -enable-experimental-feature Embedded -no-allocations -wmo -verify -verify-ignore-unknown
4+
5+
// REQUIRES: swift_in_compiler
6+
// REQUIRES: OS=macosx || OS=linux-gnu
7+
8+
public class X {} // expected-error {{cannot use allocating type 'X' in -no-allocations mode}}
9+
public func use_a_class() -> X {
10+
let x = X() // expected-note {{called from here}}
11+
return x
12+
}
13+
14+
public func use_an_array() -> Int {
15+
let a = [1, 2, 3] // expected-error {{cannot use allocating type '_ContiguousArrayStorage<Int>' in -no-allocations mode}}
16+
return a.count
17+
}

test/embedded/typeof.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
public func unsafeWriteArray<T, R>(_ elementType: R.Type, array: inout T, index n: Int, value: R) {
88
precondition(_isPOD(elementType))
99
precondition(_isPOD(type(of: array))) // expected-error {{cannot use metatype of type '(Int, Int, Int, Int)' in embedded Swift}}
10-
// expected-note@-1 {{called from here}}
1110

1211
return withUnsafeMutableBytes(of: &array) { ptr in
1312
let buffer = ptr.bindMemory(to: R.self)

0 commit comments

Comments
 (0)