From ffd3fef199bae8a451bcfc01b593c123e2140232 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Sat, 19 Oct 2019 13:05:39 -0700 Subject: [PATCH] [OSLogOptimization] Create a SIL test for OSLogOptimization pass to test folding logic, add -enable-ownership-stripping-after-serialization flag to OSLog optimization tests, and update the folding logic and end-of-use discovery logic to handle ownership and non-ownership SIL. --- .../Mandatory/OSLogOptimization.cpp | 244 +++++++---- lib/SILOptimizer/Utils/ConstantFolding.cpp | 7 +- .../OSLogPrototypeCompileTest.sil | 405 ++++++++++++++++++ .../OSLogPrototypeCompileTest.swift | 1 + 4 files changed, 566 insertions(+), 91 deletions(-) create mode 100644 test/SILOptimizer/OSLogPrototypeCompileTest.sil diff --git a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp index 35d8f1570be..7a0f32b88a8 100644 --- a/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp +++ b/lib/SILOptimizer/Mandatory/OSLogOptimization.cpp @@ -76,6 +76,8 @@ #include "swift/Basic/OptimizationMode.h" #include "swift/Demangling/Demangle.h" #include "swift/Demangling/Demangler.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/CFG.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBuilder.h" @@ -86,14 +88,14 @@ #include "swift/SIL/SILModule.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/ConstExpr.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILInliner.h" #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" -#include "llvm/ADT/MapVector.h" -#include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/CFG.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" #include "llvm/ADT/BreadthFirstIterator.h" +#include "llvm/ADT/MapVector.h" using namespace swift; @@ -177,10 +179,7 @@ public: /// Instruction from where folding must begin. SILInstruction *beginInstruction; - /// Instructions that mark the end points of folding. No folded SIL value must - /// be usable beyond these instructions (in the control-flow order). These - /// instructions are also used to emit destory instructions for non-trivial, - /// SIL values emitted during folding. + /// Instructions that mark the end points of constant evaluation. SmallSetVector endInstructions; private: @@ -292,8 +291,14 @@ static bool isSILValueFoldable(SILValue value) { ASTContext &astContext = definingInst->getFunction()->getASTContext(); SILType silType = value->getType(); + // Fold only SIL values of integer or string type that are not one of the + // following: addresses, literals, instructions marking ownership access and + // scope, copy_value (as its operand will be folded), struct creations, or + // call to string literal initializer. return (!silType.isAddress() && !isa(definingInst) && - !isa(definingInst) && + !isa(definingInst) && + !isa(definingInst) && + !isa(definingInst) && !isa(definingInst) && !getStringMakeUTF8Init(definingInst) && isIntegerOrStringType(silType, astContext)); } @@ -428,93 +433,155 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal, } } -/// Collect the end-of-lifetime instructions of the given SILValue. These are -/// either release_value or destroy_value instructions. -/// \param value SIL value whose end-of-lifetime instructions must be collected. -/// \param lifetimeEndInsts buffer for storing the found end-of-lifetime -/// instructions of 'value'. -static void getLifetimeEndInstructionsOfSILValue( - SILValue value, SmallVectorImpl &lifetimeEndInsts) { +/// Collect the end points of the instructions that are data dependent on \c +/// value. A instruction is data dependent on \c value if its result may +/// transitively depends on \c value. Note that data dependencies through +/// addresses are not tracked by this function. +/// +/// \param value SILValue that is not an address. +/// \param fun SILFunction that defines \c value. +/// \param endUsers buffer for storing the found end points of the data +/// dependence chain. +static void +getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, + SmallVectorImpl &endUsers) { + assert(!value->getType().isAddress()); - bool continueLifetimeEndInstructionSearch = true; - SILValue currValue = value; + // Collect the instructions that are data dependent on the value using a + // fix point iteration. + SmallPtrSet visitedUsers; + SmallVector worklist; + worklist.push_back(value); - while (continueLifetimeEndInstructionSearch) { - continueLifetimeEndInstructionSearch = false; - - for (Operand *use : currValue->getUses()) { + while (!worklist.empty()) { + SILValue currVal = worklist.pop_back_val(); + for (Operand *use : currVal->getUses()) { SILInstruction *user = use->getUser(); - - if (isa(user) || isa(user)) { - lifetimeEndInsts.push_back(user); + if (visitedUsers.count(user)) continue; - } - - if (isa(user)) { - auto *copyValueInst = cast(user); - // Continue looking for the end-of-lifetime instruction for the - // result of copy_value. - currValue = copyValueInst; - continueLifetimeEndInstructionSearch = true; - } + visitedUsers.insert(user); + llvm::copy(user->getResults(), std::back_inserter(worklist)); } } + + // At this point, visitedUsers have all the transitive, data-dependent uses. + // Compute the lifetime frontier of all the uses which are the instructions + // following the last uses. Every exit from the last uses will have a + // lifetime frontier. + SILInstruction *valueDefinition = value->getDefiningInstruction(); + SILInstruction *def = + valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); + ValueLifetimeAnalysis lifetimeAnalysis = + ValueLifetimeAnalysis(def, SmallVector( + visitedUsers.begin(), visitedUsers.end())); + ValueLifetimeAnalysis::Frontier frontier; + bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( + frontier, ValueLifetimeAnalysis::DontModifyCFG); + endUsers.append(frontier.begin(), frontier.end()); + if (!hasCriticlEdges) + return; + // If there are some lifetime frontiers on the critical edges, take the + // first instruction of the target of the critical edge as the frontier. This + // will suffice as every exit from the visitedUsers must go through one of + // them. + for (auto edgeIndexPair : lifetimeAnalysis.getCriticalEdges()) { + SILBasicBlock *targetBB = + edgeIndexPair.first->getSuccessors()[edgeIndexPair.second]; + endUsers.push_back(&targetBB->front()); + } } -/// Emit instructions to destroy the folded value at the end of its use, if -/// required. Since this pass folds only integers or strings and since the -/// former is a trivial type, we only have to destroy strings that are folded. -/// For strings, a release_value (or a destory_value instruction in ownership -/// SIL) has to be emitted if it is not already present. +/// Given an instruction \p inst, invoke the given clean-up function \p cleanup +/// on its lifetime frontier, which are instructions that follow the last use of +/// the results of \c inst. E.g. the clean-up function could destory/release +/// the function result. static void -destroyFoldedValueAtEndOfUse(SILValue foldedVal, SILValue originalVal, - ArrayRef endOfUseInsts, - SILFunction *fun) { - // Folded value should have either trivial or owned ownership as it is an - // integer or string constant. - assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::None || - foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned); +cleanupAtEndOfLifetime(SILInstruction *inst, + llvm::function_ref cleanup) { + ValueLifetimeAnalysis lifetimeAnalysis = ValueLifetimeAnalysis(inst); + ValueLifetimeAnalysis::Frontier frontier; + (void)lifetimeAnalysis.computeFrontier( + frontier, ValueLifetimeAnalysis::AllowToModifyCFG); + for (SILInstruction *lifetimeEndInst : frontier) { + cleanup(lifetimeEndInst); + } +} - // If the ownership kinds of folded and original values are both either - // owned or trivial, there is nothing to do. - if (foldedVal.getOwnershipKind() == originalVal.getOwnershipKind()) { +/// Replace all uses of \c originalVal by \c foldedVal and adjust lifetimes of +/// original and folded values by emitting required destory/release instructions +/// at the right places. Note that this function does not remove any +/// instruction. +/// +/// \param originalVal the SIL value that is replaced. +/// \param foldedVal the SIL value that replaces the \c originalVal. +/// \param fun the SIL function containing the \c foldedVal and \c originalVal +static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, + SILValue originalVal, + SILFunction *fun) { + SILInstruction *originalInst = originalVal->getDefiningInstruction(); + SILInstruction *foldedInst = foldedVal->getDefiningInstruction(); + assert(originalInst && + "cannot constant fold function or basic block parameter"); + assert(!isa(originalInst) && + "cannot constant fold a terminator instruction"); + assert(foldedInst && "constant value does not have a defining instruction"); + + // First, replace all uses of originalVal by foldedVal, and then adjust their + // lifetimes if necessary. + originalVal->replaceAllUsesWith(foldedVal); + + if (originalVal->getType().isTrivial(*fun)) { + assert(foldedVal->getType().isTrivial(*fun)); return; } - assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed); + assert(!foldedVal->getType().isTrivial(*fun)); - // Here, the original value may be at +0 and hence may not be released. - // However, the folded value should always be released. - SmallVector lifeTimeEndInstsOfOriginal; - getLifetimeEndInstructionsOfSILValue(originalVal, lifeTimeEndInstsOfOriginal); - - if (!lifeTimeEndInstsOfOriginal.empty()) { - // Here, the original value is released, and so would be the folded value. + if (!fun->hasOwnership()) { + // In non-ownership SIL, handle only folding of struct_extract instruction, + // which is the only important instruction that should be folded by this + // pass. Note that folding an arbitrary instruction in non-ownership SIL + // makes updating reference counts of the original value much harder and + // error prone. + // TODO: this code can be safely removed once ownership SIL becomes the + // default SIL this pass works on. + assert(isa(originalInst)); + cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { + SILBuilderWithScope builder(lifetimeEndInst); + builder.emitReleaseValue(lifetimeEndInst->getLoc(), foldedVal); + }); return; } - // Here, the original value is not released. Release the folded value at the - // 'endOfUse' instructions passed as parameter. - bool hasOwnership = fun->hasOwnership(); - for (SILInstruction *endInst : endOfUseInsts) { - SILBuilderWithScope builder(endInst); - if (hasOwnership) { - builder.createDestroyValue(endInst->getLoc(), foldedVal); - } else { - builder.createReleaseValue(endInst->getLoc(), foldedVal, - builder.getDefaultAtomicity()); - } + assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned && + "constant value must have owned ownership kind"); + + if (originalVal.getOwnershipKind() == ValueOwnershipKind::Owned) { + // Destroy originalVal, which is now unused, immediately after its + // definition. Note that originalVal's destorys are now transferred to + // foldedVal. + SILInstruction *insertionPoint = &(*std::next(originalInst->getIterator())); + SILBuilderWithScope builder(insertionPoint); + SILLocation loc = insertionPoint->getLoc(); + builder.emitDestroyValueOperation(loc, originalVal); + return; } + + // Here, originalVal is not owned. Hence, destroy foldedVal at the end of its + // lifetime. + cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { + SILBuilderWithScope builder(lifetimeEndInst); + builder.emitDestroyValueOperation(lifetimeEndInst->getLoc(), foldedVal); + }); + return; } /// Given a fold state with constant-valued instructions, substitute the /// instructions with the constant values. The constant values could be strings /// or Stdlib integer-struct values or builtin integers. static void substituteConstants(FoldState &foldState) { - ConstExprStepEvaluator &evaluator = foldState.constantEvaluator; - SmallVector deletedInsts; - auto endOfUseInsts = ArrayRef( - foldState.endInstructions.begin(), foldState.endInstructions.end()); + // Instructions that are possibly dead since their results are folded. + SmallVector possiblyDeadInsts; for (SILValue constantSILValue : foldState.getConstantSILValues()) { SymbolicValue constantSymbolicVal = @@ -522,6 +589,12 @@ static void substituteConstants(FoldState &foldState) { SILInstruction *definingInst = constantSILValue->getDefiningInstruction(); assert(definingInst); + SILFunction *fun = definingInst->getFunction(); + + // Do not attempt to fold anything but struct_extract in non-OSSA. + // TODO: this condition should be removed once migration OSSA is complete. + if (!fun->hasOwnership() && !isa(definingInst)) + continue; SILBuilderWithScope builder(definingInst); SILLocation loc = definingInst->getLoc(); @@ -529,19 +602,12 @@ static void substituteConstants(FoldState &foldState) { SILValue foldedSILVal = emitCodeForSymbolicValue( constantSymbolicVal, instType, builder, loc, foldState.stringInfo); - // Add an instruction to end the lifetime of the foldedSILVal, if necessary. - destroyFoldedValueAtEndOfUse(foldedSILVal, constantSILValue, endOfUseInsts, - definingInst->getFunction()); - - constantSILValue->replaceAllUsesWith(foldedSILVal); - - if (isa(definingInst)) { - deletedInsts.push_back(definingInst); - } // Otherwise, be conservative and do not delete the instruction as other - // results of the instruction could be used. + // Replace constantSILValue with foldedSILVal and adjust the lifetime and + // ownership of the values appropriately. + replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun); + possiblyDeadInsts.push_back(definingInst); } - - recursivelyDeleteTriviallyDeadInstructions(deletedInsts, true, + recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false, [&](SILInstruction *DeadI) {}); } @@ -581,8 +647,10 @@ static bool detectAndDiagnoseErrors(Optional errorInfo, return true; } + // The first (and only) property of OSLogMessage is the OSLogInterpolation + // instance. SymbolicValue osLogInterpolationValue = - osLogMessageValueOpt->lookThroughSingleElementAggregates(); + osLogMessageValueOpt->getAggregateValue()[0]; if (!osLogInterpolationValue.isConstant()) { diagnose(astContext, sourceLoc, diag::oslog_non_constant_interpolation); return true; @@ -633,12 +701,14 @@ static bool detectAndDiagnoseErrors(Optional errorInfo, static void constantFold(SILInstruction *start, SingleValueInstruction *oslogMessage, unsigned assertConfig) { + SILFunction *fun = start->getFunction(); // Initialize fold state. - SmallVector lifetimeEndInsts; - getLifetimeEndInstructionsOfSILValue(oslogMessage, lifetimeEndInsts); + SmallVector endUsersOfOSLogMessage; + getEndPointsOfDataDependentChain(oslogMessage, fun, endUsersOfOSLogMessage); + assert(!endUsersOfOSLogMessage.empty()); - FoldState state(start->getFunction(), assertConfig, start, lifetimeEndInsts); + FoldState state(fun, assertConfig, start, endUsersOfOSLogMessage); auto errorInfo = collectConstants(state); diff --git a/lib/SILOptimizer/Utils/ConstantFolding.cpp b/lib/SILOptimizer/Utils/ConstantFolding.cpp index 5758c718192..bda940d25f9 100644 --- a/lib/SILOptimizer/Utils/ConstantFolding.cpp +++ b/lib/SILOptimizer/Utils/ConstantFolding.cpp @@ -1527,10 +1527,9 @@ constantFoldGlobalStringTablePointerBuiltin(BuiltinInst *bi, bool enableDiagnostics) { // Look through string initializer to extract the string_literal instruction. // - // We allow for a single borrow to be stripped here if we are here in - // [ossa]. The begin borrow occurs b/c SILGen treats builtins as having - // arguments with a +0 convention (implying a borrow). - SILValue builtinOperand = stripBorrow(bi->getOperand(0)); + // We can look through ownership instructions to get to the string value that + // is passed to this builtin. + SILValue builtinOperand = stripOwnershipInsts(bi->getOperand(0)); SILFunction *caller = bi->getFunction(); FullApplySite stringInitSite = FullApplySite::isa(builtinOperand); diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.sil b/test/SILOptimizer/OSLogPrototypeCompileTest.sil new file mode 100644 index 00000000000..cce273c92e1 --- /dev/null +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.sil @@ -0,0 +1,405 @@ +// RUN: %target-sil-opt -os-log-optimization -enable-sil-verify-all %s 2>&1 | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize + +// SIL tests for the OSLogOptimization pass which performs compile-time analysis +// and optimization of os log APIs. This test checks specific aspects of the +// OSLogOptimization pass on hand-crafted SIL code. The tests here do not depend +// on the os log overlay. + +import Swift +import Builtin + +/// A type that mimics the OSLogInterpolation struct in the tests in this file. +struct OSLogInterpolationStub { + var formatString: String +} + +/// A type that mimics the OSLogMessage struct in the tests in this file. +struct OSLogMessageStub { + var interpolation: OSLogInterpolationStub +} + +/// A stub for OSLogMessage.init. +sil [Onone] [_semantics "constant_evaluable"] [_semantics "oslog.message.init_stub"] @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub { +bb0(%0 : $String): + %1 = struct $OSLogInterpolationStub(%0 : $String) + %2 = struct $OSLogMessageStub (%1 : $OSLogInterpolationStub) + return %2 : $OSLogMessageStub +} + +// String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) +sil [serialized] [always_inline] [readonly] [_semantics "string.makeUTF8"] @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + +/// A function that models the use of a string in some arbitrary way. +sil @useFormatString: $@convention(thin) (@guaranteed String) -> () + +// CHECK-LABEL: @testConstantFoldingOfStructExtract +sil [ossa] @testConstantFoldingOfStructExtract : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %8 = begin_borrow %7 : $OSLogMessageStub + %9 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%10) : $@convention(thin) (@guaranteed String) -> () + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %13 = tuple () + return %13 : $() + // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +// CHECK-LABEL: @testConstantFoldingOfOwnedValue +sil [ossa] @testConstantFoldingOfOwnedValue : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Extract the formatString property of OSLogMessageStub as a owned value and + // use it. The uses of this owned value should be constant folded. + %8 = function_ref @extractFormatStringAsOwned : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String + %9 = apply %8(%7) : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%9) : $@convention(thin) (@guaranteed String) -> () + destroy_value %7 : $OSLogMessageStub + destroy_value %9 : $String + %13 = tuple () + return %13 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // FIXME: function_ref @extractFormatStringAsOwned will not be removed as of + // now by OSLogOptimization pass even though it is folded as function calls + // are not dead-code eliminated. +} + +sil [ossa] [_semantics "constant_evaluable"] @extractFormatStringAsOwned : $@convention(thin) (@guaranteed OSLogMessageStub) -> @owned String { +bb0(%0 : @guaranteed $OSLogMessageStub): + %1 = struct_extract %0 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %2 = struct_extract %1 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %3 = copy_value %2 : $String + return %3 : $String +} + +// CHECK-LABEL: @testConstantFoldingOfDestructureStruct +sil [ossa] @testConstantFoldingOfDestructureStruct : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Destructure the OSLogMessage instance and use the formatString property + // which should be constant folded by the OSLogOptimization pass. + (%8) = destructure_struct %7 : $OSLogMessageStub + (%9) = destructure_struct %8 : $OSLogInterpolationStub + %10 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %11 = apply %10(%9) : $@convention(thin) (@guaranteed String) -> () + destroy_value %9 : $String + %12 = tuple () + return %12 : $() + // CHECK-NOT: ({{%.*}}) = destructure_struct {{%.*}} : $OSLogInterpolationStub + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +// Test that the OSLogOptimization pass does not fold instructions that define +// ownership scopes like `begin_borrow`, and `copy_value`. +// CHECK-LABEL: @testNonFoldingOfOwnershipScopes +sil [ossa] @testNonFoldingOfOwnershipScopes : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %8 = begin_borrow %7 : $OSLogMessageStub + %9 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = copy_value %10 : $String + %12 = begin_borrow %11 : $String + %13 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %14 = apply %13(%12) : $@convention(thin) (@guaranteed String) -> () + end_borrow %12 : $String + destroy_value %11 : $String + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %15 = tuple () + return %15 : $() + // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[BORROW:%[0-9]+]]) + // CHECK-DAG: [[BORROW]] = begin_borrow [[COPYVAL:%[0-9]+]] + // CHECK-DAG: [[COPYVAL]] = copy_value [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + +// CHECK-LABEL: @testConstantFoldingOfStructExtractNonOSSA +sil @testConstantFoldingOfStructExtractNonOSSA : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %9 = struct_extract %7 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%10) : $@convention(thin) (@guaranteed String) -> () + release_value %7 : $OSLogMessageStub + %13 = tuple () + return %13 : $() + // CHECK-NOT: {{%.*}} = struct_extract {{%.*}} : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: release_value [[STRINGCONST]] : $String +} + +// CHECK-LABEL: @testFoldingWithManyReleaseRetains +sil @testFoldingWithManyReleaseRetains : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + + // Use the formatString property of OSLogMessageStub which will be constant + // folded by the OSLogOptimization pass, as checked below. + %9 = struct_extract %7 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %10 = struct_extract %9 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + retain_value %10 : $String + %11 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %12 = apply %11(%10) : $@convention(thin) (@guaranteed String) -> () + release_value %10 : $String + release_value %7 : $OSLogMessageStub + %13 = tuple () + return %13 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[STRINGCONST:%[0-9]+]]) + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: release_value [[STRINGCONST]] : $String + // FIXME: Here struct_extract is not dead code eliminated by the + // optimization pass. +} + +// CHECK-LABEL: @testPostdominatorComputation +sil [ossa] @testPostdominatorComputation : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %8 = begin_borrow %7 : $OSLogMessageStub + %14 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %9 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + cond_br %2, bb1, bb4 + + // Use the OSLogMessage instance along different branches. The following code + // deliberately uses a borrowed operation like struct_extract so that when it + // is replaced with a folded value, it needs to be destroyed at the post- + // dominating points of its uses. +bb1: + %10 = struct_extract %8 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %11 = struct_extract %10 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + cond_br %2, bb2, bb3 + // Check release of the folded value of %15. + // CHECK-LABEL: bb1: + // CHECK-NEXT: destroy_value [[STRINGCONST1:%[0-9]+]] : $String + +bb2: + %12 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %11. + // CHECK-LABEL: bb2: + // CHECK: destroy_value [[STRINGCONST2:%[0-9]+]] : $String + +bb3: + %13 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %11. + // CHECK-LABEL: bb3: + // CHECK: destroy_value [[STRINGCONST2]] : $String + +bb4: + %16 = apply %9(%15) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %15. + // CHECK-LABEL: bb4: + // CHECK: destroy_value [[STRINGCONST1]] : $String + +exit: + end_borrow %8 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %17 = tuple () + return %17 : $() +} + +// CHECK-LABEL: @testPostdominatorComputationNonOSSA +sil @testPostdominatorComputationNonOSSA : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %14 = struct_extract %7 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %9 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + cond_br %2, bb1, bb4 + + // Use the OSLogMessage instance along different branches. The following code + // deliberately uses a borrowed operation like struct_extract so that when it + // is replaced with a folded value, it needs to be destroyed at the post- + // dominating points of its uses. +bb1: + %10 = struct_extract %7 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %11 = struct_extract %10 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + cond_br %2, bb2, bb3 + // Check release of the folded value of %15. + // CHECK-LABEL: bb1: + // CHECK-NEXT: release_value [[STRINGCONST1:%[0-9]+]] : $String + +bb2: + %12 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %11. + // CHECK-LABEL: bb2: + // CHECK: release_value [[STRINGCONST2:%[0-9]+]] : $String + +bb3: + %13 = apply %9(%11) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %11. + // CHECK-LABEL: bb3: + // CHECK: release_value [[STRINGCONST2]] : $String + +bb4: + %16 = apply %9(%15) : $@convention(thin) (@guaranteed String) -> () + br exit + // Check release of the folded value of %15. + // CHECK-LABEL: bb4: + // CHECK: release_value [[STRINGCONST1]] : $String + +exit: + release_value %7 : $OSLogMessageStub + %17 = tuple () + return %17 : $() +} + +// This test checks whether values that are transitively data dependent on +// an OSLogMessage instance are folded. These can be alive even beyond the +// lifetime of OSLogMessage. +// CHECK-LABEL: @testFoldingOfTransitiveDataDependencies +sil [ossa] @testFoldingOfTransitiveDataDependencies : $@convention(thin) () -> () { +bb0: + // Construct an OSLogMessageStub instance. + %0 = string_literal utf8 "test message: %lld" + %1 = integer_literal $Builtin.Word, 18 + %2 = integer_literal $Builtin.Int1, -1 + %3 = metatype $@thin String.Type + // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) + %4 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %5 = apply %4(%0, %1, %2, %3) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String + %6 = function_ref @oslogMessageInit : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %7 = apply %6(%5) : $@convention(thin) (@owned String) -> @owned OSLogMessageStub + %8 = copy_value %7 : $OSLogMessageStub + destroy_value %7 : $OSLogMessageStub + %10 = begin_borrow %8 : $OSLogMessageStub + %14 = struct_extract %10 : $OSLogMessageStub, #OSLogMessageStub.interpolation + %15 = struct_extract %14 : $OSLogInterpolationStub, #OSLogInterpolationStub.formatString + %12 = copy_value %15 : $String + end_borrow %10 : $OSLogMessageStub + destroy_value %8 : $OSLogMessageStub + %9 = function_ref @useFormatString : $@convention(thin) (@guaranteed String) -> () + %16 = apply %9(%12) : $@convention(thin) (@guaranteed String) -> () + destroy_value %12 : $String + %17 = tuple () + return %17 : $() + // CHECK-DAG: [[STRINGUSE:%[0-9]+]] = function_ref @useFormatString + // CHECK-DAG: {{%.*}} = apply [[STRINGUSE]]([[CONSTCOPY:%[0-9]+]]) + // CHECK-DAG: [[CONSTCOPY]] = copy_value [[STRINGCONST:%[0-9]+]] + // CHECK-DAG: [[STRINGCONST]] = apply [[STRINGINIT:%[0-9]+]]([[LIT:%[0-9]+]], {{%.*}}, {{%.*}}, {{%.*}}) + // CHECK-DAG: [[STRINGINIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK-DAG: [[LIT]] = string_literal utf8 "test message: %lld" + // CHECK-DAG: destroy_value [[STRINGCONST]] : $String +} + diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 1b1dbe3512c..fde045984c4 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -1,4 +1,5 @@ // RUN: %target-swift-frontend -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos // Tests for the OSLogOptimization pass that performs compile-time analysis