Skip to content

Commit 69f6ae5

Browse files
authored
Merge pull request swiftlang#20923 from gottesmm/pr-f63866d54df4db5291264283fe900d1af78ac87b
[semantic-arc-opts] Convert exclusive accessed load [copy] that are n…
2 parents cb20bf3 + 8f7620f commit 69f6ae5

File tree

5 files changed

+220
-25
lines changed

5 files changed

+220
-25
lines changed

include/swift/SIL/AccessedStorage.def

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===--- AccessedStorage.def ----------------------------*- c++ -*---------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file
14+
///
15+
/// A file used for metaprogramming with accessed storage. Used to enable
16+
/// easily updateable visitors.
17+
///
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef ACCESSED_STORAGE
21+
#error "Must define accesed storage before including this?!"
22+
#endif
23+
24+
#ifndef ACCESSED_STORAGE_RANGE
25+
#define ACCESSED_STORAGE_RANGE(Name, Start, End)
26+
#endif
27+
28+
ACCESSED_STORAGE(Box)
29+
ACCESSED_STORAGE(Stack)
30+
ACCESSED_STORAGE(Global)
31+
ACCESSED_STORAGE(Class)
32+
ACCESSED_STORAGE(Argument)
33+
ACCESSED_STORAGE(Yield)
34+
ACCESSED_STORAGE(Nested)
35+
ACCESSED_STORAGE(Unidentified)
36+
ACCESSED_STORAGE_RANGE(AccessedStorageKind, Box, Unidentified)
37+
38+
#undef ACCESSED_STORAGE_RANGE
39+
#undef ACCESSED_STORAGE

include/swift/SIL/MemAccessUtils.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,11 @@ class AccessedStorage {
106106
/// Enumerate over all valid begin_access bases. Clients can use a covered
107107
/// switch to warn if findAccessedAddressBase ever adds a case.
108108
enum Kind : uint8_t {
109-
Box,
110-
Stack,
111-
Global,
112-
Class,
113-
Argument,
114-
Yield,
115-
Nested,
116-
Unidentified,
117-
NumKindBits = countBitsUsed(static_cast<unsigned>(Unidentified))
109+
#define ACCESSED_STORAGE(Name) Name,
110+
#define ACCESSED_STORAGE_RANGE(Name, Start, End) \
111+
First_##Name = Start, Last_##Name = End,
112+
#include "swift/SIL/AccessedStorage.def"
113+
NumKindBits = countBitsUsed(unsigned(Last_AccessedStorageKind))
118114
};
119115

120116
static const char *getKindName(Kind k);
@@ -339,6 +335,26 @@ inline bool accessingIdenticalLocations(AccessedStorage LHS,
339335
return LHS.getObjectProjection() == RHS.getObjectProjection();
340336
}
341337
}
338+
339+
template <class ImplTy, class ResultTy = void, typename... ArgTys>
340+
class AccessedStorageVisitor {
341+
ImplTy &asImpl() { return static_cast<ImplTy &>(*this); }
342+
343+
public:
344+
#define ACCESSED_STORAGE(Name) \
345+
ResultTy visit##Name(const AccessedStorage &storage, ArgTys &&... args);
346+
#include "swift/SIL/AccessedStorage.def"
347+
348+
ResultTy visit(const AccessedStorage &storage, ArgTys &&... args) {
349+
switch (storage.getKind()) {
350+
#define ACCESSED_STORAGE(Name) \
351+
case AccessedStorage::Name: \
352+
return asImpl().visit##Name(storage, std::forward<ArgTys>(args)...);
353+
#include "swift/SIL/AccessedStorage.def"
354+
}
355+
}
356+
};
357+
342358
} // end namespace swift
343359

344360
namespace llvm {

lib/SIL/MemAccessUtils.cpp

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,22 +140,10 @@ const ValueDecl *AccessedStorage::getDecl(SILFunction *F) const {
140140

141141
const char *AccessedStorage::getKindName(AccessedStorage::Kind k) {
142142
switch (k) {
143-
case Box:
144-
return "Box";
145-
case Stack:
146-
return "Stack";
147-
case Nested:
148-
return "Nested";
149-
case Unidentified:
150-
return "Unidentified";
151-
case Argument:
152-
return "Argument";
153-
case Yield:
154-
return "Yield";
155-
case Global:
156-
return "Global";
157-
case Class:
158-
return "Class";
143+
#define ACCESSED_STORAGE(NAME) \
144+
case AccessedStorage::NAME: \
145+
return #NAME;
146+
#include "swift/SIL/AccessedStorage.def"
159147
}
160148
llvm_unreachable("unhandled kind");
161149
}

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#define DEBUG_TYPE "sil-semantic-arc-opts"
1414
#include "swift/Basic/STLExtras.h"
1515
#include "swift/SIL/BasicBlockUtils.h"
16+
#include "swift/SIL/MemAccessUtils.h"
1617
#include "swift/SIL/OwnershipUtils.h"
1718
#include "swift/SIL/SILArgument.h"
19+
#include "swift/SIL/SILBuilder.h"
1820
#include "swift/SIL/SILInstruction.h"
1921
#include "swift/SIL/SILVisitor.h"
2022
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
@@ -27,6 +29,8 @@
2729
using namespace swift;
2830

2931
STATISTIC(NumEliminatedInsts, "number of removed instructions");
32+
STATISTIC(NumLoadCopyConvertedToLoadBorrow,
33+
"number of load_copy converted to load_borrow");
3034

3135
//===----------------------------------------------------------------------===//
3236
// Utility
@@ -79,6 +83,7 @@ struct SemanticARCOptVisitor
7983
bool visitSILInstruction(SILInstruction *i) { return false; }
8084
bool visitCopyValueInst(CopyValueInst *cvi);
8185
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
86+
bool visitLoadInst(LoadInst *li);
8287
};
8388

8489
} // end anonymous namespace
@@ -249,6 +254,116 @@ bool SemanticARCOptVisitor::visitCopyValueInst(CopyValueInst *cvi) {
249254
return false;
250255
}
251256

257+
//===----------------------------------------------------------------------===//
258+
// load [copy] Optimizations
259+
//===----------------------------------------------------------------------===//
260+
261+
/// A flow insensitive analysis that tells the load [copy] analysis if the
262+
/// storage has 0, 1, >1 writes to it.
263+
///
264+
/// In the case of 0 writes, we return CanOptimizeLoadCopyResult::Always.
265+
///
266+
/// In the case of 1 write, we return OnlyIfStorageIsLocal. We are taking
267+
/// advantage of definite initialization implying that an alloc_stack must be
268+
/// written to once before any loads from the memory location. Thus if we are
269+
/// local and see 1 write, we can still change to load_borrow if all other uses
270+
/// check out.
271+
///
272+
/// If there is 2+ writes, we can not optimize = (.
273+
namespace {
274+
275+
struct CanOptimizeLoadCopyFromAccessVisitor
276+
: AccessedStorageVisitor<CanOptimizeLoadCopyFromAccessVisitor, bool> {
277+
SILFunction &f;
278+
279+
CanOptimizeLoadCopyFromAccessVisitor(SILFunction &f) : f(f) {}
280+
281+
// Stubs
282+
bool visitBox(const AccessedStorage &boxStorage) { return false; }
283+
bool visitStack(const AccessedStorage &stackStorage) { return false; }
284+
bool visitGlobal(const AccessedStorage &globalStorage) { return false; }
285+
bool visitClass(const AccessedStorage &classStorage) { return false; }
286+
bool visitYield(const AccessedStorage &yieldStorage) { return false; }
287+
bool visitUnidentified(const AccessedStorage &unidentifiedStorage) {
288+
return false;
289+
}
290+
bool visitNested(const AccessedStorage &nested) {
291+
llvm_unreachable("Visitor should never see nested since we lookup our "
292+
"address storage using lookup non nested");
293+
}
294+
295+
bool visitArgument(const AccessedStorage &argumentStorage);
296+
};
297+
298+
} // namespace
299+
300+
bool CanOptimizeLoadCopyFromAccessVisitor::visitArgument(
301+
const AccessedStorage &storage) {
302+
auto *arg = cast<SILFunctionArgument>(storage.getArgument(&f));
303+
304+
// Then check if we have an in_guaranteed argument. In this case, we can
305+
// always optimize load [copy] from this.
306+
if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed))
307+
return true;
308+
309+
// For now just return false.
310+
return false;
311+
}
312+
313+
static bool isWrittenTo(SILFunction &f, SILValue value) {
314+
// Then find our accessed storage. If we can not find anything, be
315+
// conservative and assume that the value is written to.
316+
const auto &storage = findAccessedStorageNonNested(value);
317+
if (!storage)
318+
return false;
319+
320+
// Then see if we ever write to this address in a flow insensitive
321+
// way (ignoring stores that are obviously the only initializer to
322+
// memory). We have to do this since load_borrow assumes that the
323+
// underlying memory is never written to.
324+
return !CanOptimizeLoadCopyFromAccessVisitor(f).visit(storage);
325+
}
326+
327+
// Convert a load [copy] from unique storage [read] that has all uses that can
328+
// accept a guaranteed parameter to a load_borrow.
329+
bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
330+
if (li->getOwnershipQualifier() != LoadOwnershipQualifier::Copy)
331+
return false;
332+
333+
// Ok, we have our load [copy]. Make sure its value is never
334+
// consumed. If it is consumed, we need to pass off a +1 value, so
335+
// bail.
336+
//
337+
// FIXME: We should consider if it is worth promoting a load [copy]
338+
// -> load_borrow if we can put a copy_value on a cold path and thus
339+
// eliminate RR traffic on a hot path.
340+
SmallVector<DestroyValueInst *, 32> destroyValues;
341+
if (isConsumed(li, destroyValues))
342+
return false;
343+
344+
// Then check if our address is ever written to. If it is, then we
345+
// can not use the load_borrow.
346+
if (isWrittenTo(*li->getFunction(), li->getOperand()))
347+
return false;
348+
349+
// Ok, we can perform our optimization. Convert the load [copy] into a
350+
// load_borrow.
351+
auto *lbi =
352+
SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());
353+
while (!destroyValues.empty()) {
354+
auto *dvi = destroyValues.pop_back_val();
355+
SILBuilderWithScope(dvi).createEndBorrow(dvi->getLoc(), lbi);
356+
dvi->eraseFromParent();
357+
++NumEliminatedInsts;
358+
}
359+
360+
li->replaceAllUsesWith(lbi);
361+
li->eraseFromParent();
362+
++NumEliminatedInsts;
363+
++NumLoadCopyConvertedToLoadBorrow;
364+
return true;
365+
}
366+
252367
//===----------------------------------------------------------------------===//
253368
// Top Level Entrypoint
254369
//===----------------------------------------------------------------------===//

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,40 @@ bb3:
239239
%9999 = tuple()
240240
return %9999 : $()
241241
}
242+
243+
// Simple in_guaranteed argument load_copy.
244+
// CHECK-LABEL: sil @load_copy_from_in_guaranteed : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
245+
// CHECK: bb0([[ARG:%.*]] :
246+
// CHECK: load_borrow
247+
// CHECK: load_borrow
248+
// CHECK: load [copy]
249+
// CHECK: } // end sil function 'load_copy_from_in_guaranteed'
250+
sil @load_copy_from_in_guaranteed : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
251+
bb0(%0 : $*Builtin.NativeObject):
252+
%g = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
253+
// Simple same bb.
254+
%1 = load [copy] %0 : $*Builtin.NativeObject
255+
apply %g(%1) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
256+
destroy_value %1 : $Builtin.NativeObject
257+
258+
// Diamond.
259+
%2 = load [copy] %0 : $*Builtin.NativeObject
260+
cond_br undef, bb1, bb2
261+
262+
bb1:
263+
apply %g(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
264+
destroy_value %2 : $Builtin.NativeObject
265+
br bb3
266+
267+
bb2:
268+
destroy_value %2 : $Builtin.NativeObject
269+
br bb3
270+
271+
bb3:
272+
// Consuming use blocks.
273+
%3 = load [copy] %0 : $*Builtin.NativeObject
274+
%4 = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
275+
apply %4(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
276+
%9999 = tuple()
277+
return %9999 : $()
278+
}

0 commit comments

Comments
 (0)