Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions lib/SIL/IR/SILFunctionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/DistributedDecl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/Basic/Assertions.h"
Expand Down Expand Up @@ -407,6 +408,21 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction(
&& "addFunctionAttributes() on ABI-only decl?");
addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration,
constant);
} else if (auto *ce = constant.getAbstractClosureExpr()) {
if (mod.getOptions().EnableGlobalAssemblyVision) {
F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
} else {
// Add the attribute to a closure if the enclosing method has it.
auto decl = ce->getParent()->getInnermostDeclarationDeclContext();
if (decl &&
decl->getAttrs().getAttribute(DeclAttrKind::EmitAssemblyVisionRemarks)) {
F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
}
}
} else {
if (mod.getOptions().EnableGlobalAssemblyVision) {
F->addSemanticsAttr(semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
}
}

return F;
Expand Down
95 changes: 92 additions & 3 deletions lib/SILOptimizer/Transforms/AssemblyVisionRemarkGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,30 @@ static llvm::cl::opt<bool> DecllessDebugValueUseSILDebugInfo(
namespace {

struct ValueToDeclInferrer {

enum class NotePrefix {
of,
fromLocation,
toLocation,
inMemoryLocationOf
};

using Argument = OptRemark::Argument;
using ArgumentKeyKind = OptRemark::ArgumentKeyKind;

SmallVector<std::pair<SILType, Projection>, 32> accessPath;
SmallVector<Operand *, 32> rcIdenticalSecondaryUseSearch;
RCIdentityFunctionInfo &rcfi;
NotePrefix currentNotePrefix = NotePrefix::of;

ValueToDeclInferrer(RCIdentityFunctionInfo &rcfi) : rcfi(rcfi) {}

/// Given a value, attempt to infer a conservative list of decls that the
/// passed in value could be referring to. This is done just using heuristics
bool infer(ArgumentKeyKind keyKind, SILValue value,
SmallVectorImpl<Argument> &resultingInferredDecls,
bool allowSingleRefEltAddrPeek = false);
bool allowSingleRefEltAddrPeek = false,
NotePrefix notePrefix = NotePrefix::of);

/// Print out a note to \p stream that beings at decl and then if
/// useProjectionPath is set to true iterates the accessPath we computed for
Expand Down Expand Up @@ -196,7 +206,20 @@ void ValueToDeclInferrer::printAccessPath(llvm::raw_string_ostream &stream) {
void ValueToDeclInferrer::printNote(llvm::raw_string_ostream &stream,
StringRef name,
bool shouldPrintAccessPath) {
stream << "of '" << name;
switch (currentNotePrefix) {
case NotePrefix::of:
stream << "of '" << name;
break;
case NotePrefix::fromLocation:
stream << "from location '" << name;
break;
case NotePrefix::toLocation:
stream << "to location '" << name;
break;
case NotePrefix::inMemoryLocationOf:
stream << "in memory location of '" << name;
break;
}
if (shouldPrintAccessPath)
printAccessPath(stream);
stream << "'";
Expand Down Expand Up @@ -314,7 +337,11 @@ bool ValueUseToDeclInferrer::findDecls(Operand *use, SILValue value) {
bool ValueToDeclInferrer::infer(
ArgumentKeyKind keyKind, SILValue value,
SmallVectorImpl<Argument> &resultingInferredDecls,
bool allowSingleRefEltAddrPeek) {
bool allowSingleRefEltAddrPeek,
NotePrefix notePrefix) {

currentNotePrefix = notePrefix;

// Clear the stored access path at end of scope.
SWIFT_DEFER { accessPath.clear(); };
ValueUseToDeclInferrer valueUseInferrer{
Expand Down Expand Up @@ -533,10 +560,72 @@ struct AssemblyVisionRemarkGeneratorInstructionVisitor
void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *ccabi);
void visitUnconditionalCheckedCastAddrInst(
UnconditionalCheckedCastAddrInst *uccai);
void visitCopyAddrInst(CopyAddrInst *copy);
void visitDestroyAddrInst(DestroyAddrInst *destroy);
};

} // anonymous namespace

void AssemblyVisionRemarkGeneratorInstructionVisitor::
visitCopyAddrInst(CopyAddrInst *copy) {
ORE.emit([&]() {
using namespace OptRemark;
SmallVector<Argument, 8> inferredArgs;
bool foundArgs = valueToDeclInferrer.infer(
ArgumentKeyKind::Note, copy->getSrc(), inferredArgs,
true /*allow single ref elt peek*/,
ValueToDeclInferrer::NotePrefix::fromLocation);
(void)foundArgs;

// Use the actual source loc of the
auto remark = RemarkMissed("memory", *copy)
<< "Memory copy of value with type '"
<< NV("ValueType", copy->getSrc()->getType()) << "'";
if (foundArgs) {
for (auto arg : inferredArgs) {
remark << arg;
}
}
inferredArgs.clear();
foundArgs = valueToDeclInferrer.infer(
ArgumentKeyKind::Note, copy->getDest(), inferredArgs,
true /*allow single ref elt peek*/,
ValueToDeclInferrer::NotePrefix::toLocation);
if (foundArgs) {
for (auto arg : inferredArgs) {
remark << arg;
}
}

return remark;
});
}

void AssemblyVisionRemarkGeneratorInstructionVisitor::
visitDestroyAddrInst(DestroyAddrInst *destroy) {
ORE.emit([&]() {
using namespace OptRemark;
SmallVector<Argument, 8> inferredArgs;
bool foundArgs = valueToDeclInferrer.infer(
ArgumentKeyKind::Note, destroy->getOperand(), inferredArgs,
true /*allow single ref elt peek*/,
ValueToDeclInferrer::NotePrefix::inMemoryLocationOf);
(void)foundArgs;

// Use the actual source loc of the
auto remark = RemarkMissed("memory", *destroy)
<< "Memory destroy of value with type '"
<< NV("ValueType", destroy->getOperand()->getType()) << "'";
if (foundArgs) {
for (auto arg : inferredArgs) {
remark << arg;
}
}

return remark;
});
}

void AssemblyVisionRemarkGeneratorInstructionVisitor::
visitUnconditionalCheckedCastAddrInst(
UnconditionalCheckedCastAddrInst *uccai) {
Expand Down
32 changes: 32 additions & 0 deletions test/SILOptimizer/assemblyvision_remark/basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,35 @@ func simpleInOut() -> Klass {
// expected-note @-5:9 {{of 'x.next'}}
return x
}


@inline(never)
@_semantics("optimize.sil.specialize.generic.size.never")
public func use<T>(_ t: inout T) { // expected-note @:22 {{from location 't'}}
print(t); // expected-remark @:11 {{heap allocated ref of type}}
// expected-remark @-1:11 {{Memory copy of value with type 'T'}}
// expected-remark @-2:12 {{release of type}}
}

@inline(never)
@_assemblyVision
public func genericFunc<T>(_ t: T) { // expected-note @:30 {{from location 't'}}
var temp: T = t // expected-note @:9 {{to location 'temp'}}
// expected-remark @-1:9 {{Memory destroy of value with type 'T'}}
// expected-note @-2:9 {{in memory location of 'temp'}}
// expected-remark @-3:19 {{Memory copy of value with type 'T'}}
use(&temp)
use(&temp)
}

@_assemblyVision
public func forEach<T>(_ elements: Array<T>, body: (borrowing T) -> Void) {
elements.withUnsafeBufferPointer { buffer in
for i in buffer.indices { // expected-remark @:5 {{Specialized function "Swift.IndexingIterator.next()" with type (@inout IndexingIterator<Range<Int>>) -> Optional<Int>}}
// expected-remark @-1:21 {{Specialized function "Swift.Collection<>.makeIterator()" with type (Range<Int>) -> IndexingIterator<Range<Int>>}}
body(/* copy */ buffer[i]) // expected-remark @:29 {{Memory copy of value with type 'T'}}
// expected-remark @-1:32 {{Memory destroy of value with type 'T'}}
// destroy of T
}
}
}