diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 388de96f911..4254b0c1575 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -86,6 +86,10 @@ llvm::cl::opt SILPrintTypes("sil-print-types", llvm::cl::init(false), llvm::cl::desc("always print type annotations for instruction operands in SIL output")); +llvm::cl::opt +SILPrintOwnership("sil-print-ownership", llvm::cl::init(false), + llvm::cl::desc("print ownership of instruction results in SIL output")); + llvm::cl::opt SILPrintNoUses("sil-print-no-uses", llvm::cl::init(false), llvm::cl::desc("omit use comments in SIL output")); @@ -693,6 +697,35 @@ protected: } }; +static bool hasNonAddressResults(const SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + if (result->getType().isObject()) + return true; + } + return false; +} + +/// Returns true if the ownership of a result of `inst` mismatches with its type. +/// That can happen e.g. for non-trivial enums which are constructed with a trivial case: +/// ``` +/// enum E { +/// case A +/// case B(AnyObject) +/// } +/// +/// %1 = enum $E, #E.A!enumelt // type of %1 is non trivial, but ownership is "none" +/// ``` +static bool hasUnusualResultOwnership(const SILInstruction *inst) { + for (SILValue result : inst->getResults()) { + if (result->getType().isObject() && + result->getOwnershipKind() == OwnershipKind::None && + !result->getType().isTrivial(*inst->getFunction())) { + return true; + } + } + return false; +} + } // namespace namespace swift { @@ -1092,6 +1125,28 @@ public: printBranchTargets(inst); } + void printOwnershipOfInstruction(const SILInstruction *inst) { + + if (!inst->isStaticInitializerInst() && + inst->getFunction()->hasOwnership() && + hasNonAddressResults(inst) && + (SILPrintOwnership || hasUnusualResultOwnership(inst))) + { + lineComments.delim(); + + *this << "ownership: "; + llvm::interleave(inst->getResults(), + [&](SILValue result) { + if (result->getType().isAddress()) { + *this << '-'; + } else { + *this << result->getOwnershipKind(); + } + }, + [&] { *this << ", "; }); + } + } + void printUserList(ArrayRef values, SILNodePointer node) { if (SILPrintNoUses) return; @@ -1364,6 +1419,8 @@ public: // Print users, or id for valueless instructions. printUsersOfInstruction(I); + printOwnershipOfInstruction(I); + // Print SIL location. if (Ctx.printVerbose()) { printSILLocation(I->getLoc(), I->getModule(), I->getDebugScope()); diff --git a/test/SIL/print_ownership.sil b/test/SIL/print_ownership.sil new file mode 100644 index 00000000000..2ac7df3decc --- /dev/null +++ b/test/SIL/print_ownership.sil @@ -0,0 +1,56 @@ +// RUN: %target-sil-opt -sil-print-ownership %s | %FileCheck %s --check-prefix=CHECK --check-prefix=ENABLE +// RUN: %target-sil-opt %s | %FileCheck %s --check-prefix=CHECK --check-prefix=DISABLE + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +enum E { + case A + case B(AnyObject) +} + +struct S { + var e: E + var o: AnyObject +} + +struct T { + var s: S +} + +// CHECK-LABEL: sil [ossa] @ossa_function : +// CHECK: enum $E{{.*}} ownership: none +// ENABLE: struct $S{{.*}} ownership: owned +// ENABLE: begin_apply {{.*}} ownership: -, guaranteed +// DISABLE-NOT: ownership +// CHECK: } // end sil function 'ossa_function' +sil [ossa] @ossa_function : $@convention(thin) (@inout T, @owned AnyObject) -> () { +bb0(%0 : $*T, %1 : @owned $AnyObject): + %2 = enum $E, #E.A!enumelt + %3 = struct $S (%2, %1) + %4 = struct_element_addr %0, #T.s + store %3 to [assign] %4 + (%6, %7) = begin_apply undef() : $@yield_once () -> (@yields @in_guaranteed AnyObject) + end_apply %7 as $() + %9 = tuple () + return %9 +} + +// CHECK-LABEL: sil @non_ossa_function : +// CHECK-NOT: ownership +// CHECK: } // end sil function 'non_ossa_function' +sil @non_ossa_function : $@convention(thin) (@inout T, @owned AnyObject) -> () { +bb0(%0 : $*T, %1 : $AnyObject): + %2 = enum $E, #E.A!enumelt + %3 = struct $S (%2, %1) + %4 = struct_element_addr %0, #T.s + store %3 to %4 + (%6, %7) = begin_apply undef() : $@yield_once () -> (@yields @in_guaranteed AnyObject) + end_apply %7 as $() + %9 = tuple () + return %9 +} +