mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Exclusivity] Sink may release release instructions out of access scopes
General case: begin_access A ... strong_release / release_value / destroy end_access The release instruction can be sunk below the end_access instruction, This extends the lifetime of the released value, but, might allow us to Mark the access scope as no nested conflict.
This commit is contained in:
@@ -58,6 +58,8 @@ PASS(AccessEnforcementDom, "access-enforcement-dom",
|
|||||||
"Remove dominated access checks with no nested conflict")
|
"Remove dominated access checks with no nested conflict")
|
||||||
PASS(AccessEnforcementOpts, "access-enforcement-opts",
|
PASS(AccessEnforcementOpts, "access-enforcement-opts",
|
||||||
"Access Enforcement Optimization")
|
"Access Enforcement Optimization")
|
||||||
|
PASS(AccessEnforcementReleaseSinking, "access-enforcement-release",
|
||||||
|
"Access Enforcement Release Sinking")
|
||||||
PASS(AccessEnforcementSelection, "access-enforcement-selection",
|
PASS(AccessEnforcementSelection, "access-enforcement-selection",
|
||||||
"Access Enforcement Selection")
|
"Access Enforcement Selection")
|
||||||
PASS(AccessEnforcementWMO, "access-enforcement-wmo",
|
PASS(AccessEnforcementWMO, "access-enforcement-wmo",
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) {
|
|||||||
P.addSimplifyCFG();
|
P.addSimplifyCFG();
|
||||||
// Optimize access markers for better LICM: might merge accesses
|
// Optimize access markers for better LICM: might merge accesses
|
||||||
// It will also set the no_nested_conflict for dynamic accesses
|
// It will also set the no_nested_conflict for dynamic accesses
|
||||||
|
P.addAccessEnforcementReleaseSinking();
|
||||||
P.addAccessEnforcementOpts();
|
P.addAccessEnforcementOpts();
|
||||||
P.addHighLevelLICM();
|
P.addHighLevelLICM();
|
||||||
// Simplify CFG after LICM that creates new exit blocks
|
// Simplify CFG after LICM that creates new exit blocks
|
||||||
@@ -202,6 +203,7 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) {
|
|||||||
// LICM might have added new merging potential by hoisting
|
// LICM might have added new merging potential by hoisting
|
||||||
// we don't want to restart the pipeline - ignore the
|
// we don't want to restart the pipeline - ignore the
|
||||||
// potential of merging out of two loops
|
// potential of merging out of two loops
|
||||||
|
P.addAccessEnforcementReleaseSinking();
|
||||||
P.addAccessEnforcementOpts();
|
P.addAccessEnforcementOpts();
|
||||||
// Start of loop unrolling passes.
|
// Start of loop unrolling passes.
|
||||||
P.addArrayCountPropagation();
|
P.addArrayCountPropagation();
|
||||||
@@ -465,6 +467,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) {
|
|||||||
P.addCodeSinking();
|
P.addCodeSinking();
|
||||||
// Optimize access markers for better LICM: might merge accesses
|
// Optimize access markers for better LICM: might merge accesses
|
||||||
// It will also set the no_nested_conflict for dynamic accesses
|
// It will also set the no_nested_conflict for dynamic accesses
|
||||||
|
P.addAccessEnforcementReleaseSinking();
|
||||||
P.addAccessEnforcementOpts();
|
P.addAccessEnforcementOpts();
|
||||||
P.addLICM();
|
P.addLICM();
|
||||||
// Simplify CFG after LICM that creates new exit blocks
|
// Simplify CFG after LICM that creates new exit blocks
|
||||||
@@ -472,6 +475,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) {
|
|||||||
// LICM might have added new merging potential by hoisting
|
// LICM might have added new merging potential by hoisting
|
||||||
// we don't want to restart the pipeline - ignore the
|
// we don't want to restart the pipeline - ignore the
|
||||||
// potential of merging out of two loops
|
// potential of merging out of two loops
|
||||||
|
P.addAccessEnforcementReleaseSinking();
|
||||||
P.addAccessEnforcementOpts();
|
P.addAccessEnforcementOpts();
|
||||||
|
|
||||||
// Optimize overflow checks.
|
// Optimize overflow checks.
|
||||||
@@ -494,6 +498,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) {
|
|||||||
// - don't require IRGen information.
|
// - don't require IRGen information.
|
||||||
static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) {
|
static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) {
|
||||||
// Optimize access markers for improved IRGen after all other optimizations.
|
// Optimize access markers for improved IRGen after all other optimizations.
|
||||||
|
P.addAccessEnforcementReleaseSinking();
|
||||||
P.addAccessEnforcementOpts();
|
P.addAccessEnforcementOpts();
|
||||||
P.addAccessEnforcementWMO();
|
P.addAccessEnforcementWMO();
|
||||||
P.addAccessEnforcementDom();
|
P.addAccessEnforcementDom();
|
||||||
|
|||||||
124
lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp
Normal file
124
lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
//===--- AccessEnforcementReleaseSinking.cpp - release sinking opt ---===//
|
||||||
|
//
|
||||||
|
// This source file is part of the Swift.org open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||||
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||||
|
//
|
||||||
|
// See https://swift.org/LICENSE.txt for license information
|
||||||
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
///
|
||||||
|
/// This function pass sinks releases out of access scopes.
|
||||||
|
///
|
||||||
|
/// General case:
|
||||||
|
/// begin_access A
|
||||||
|
/// ...
|
||||||
|
/// strong_release / release_value / destroy
|
||||||
|
/// end_access
|
||||||
|
///
|
||||||
|
/// The release instruction can be sunk below the end_access instruction,
|
||||||
|
/// This extends the lifetime of the released value, but, might allow us to
|
||||||
|
/// Mark the access scope as no nested conflict.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#define DEBUG_TYPE "access-enforcement-release"
|
||||||
|
|
||||||
|
#include "swift/SIL/ApplySite.h"
|
||||||
|
#include "swift/SIL/DebugUtils.h"
|
||||||
|
#include "swift/SIL/SILFunction.h"
|
||||||
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
||||||
|
|
||||||
|
using namespace swift;
|
||||||
|
|
||||||
|
// Returns a bool: If this is a "sinkable" instruction type for this opt
|
||||||
|
static bool isSinkable(SILInstruction &inst) {
|
||||||
|
switch (inst.getKind()) {
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case SILInstructionKind::DestroyValueInst:
|
||||||
|
case SILInstructionKind::ReleaseValueInst:
|
||||||
|
case SILInstructionKind::ReleaseValueAddrInst:
|
||||||
|
case SILInstructionKind::StrongReleaseInst:
|
||||||
|
case SILInstructionKind::UnmanagedReleaseValueInst:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a bool: If this is a "barrier" instruction for this opt
|
||||||
|
static bool isBarrier(SILInstruction &inst) {
|
||||||
|
switch (inst.getKind()) {
|
||||||
|
default:
|
||||||
|
return FullApplySite::isa(&inst) != FullApplySite();
|
||||||
|
case SILInstructionKind::BeginAccessInst:
|
||||||
|
case SILInstructionKind::BeginUnpairedAccessInst:
|
||||||
|
case SILInstructionKind::IsUniqueInst:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes a block bottom-up, keeping a lookout for end_access instructions
|
||||||
|
// If we encounter a "barrier" we clear out the current end_access
|
||||||
|
// If we encounter a "release", and we have a current end_access, we sink it
|
||||||
|
static void processBlock(SILBasicBlock &block) {
|
||||||
|
EndAccessInst *bottomEndAccessInst = nullptr;
|
||||||
|
for (auto reverseIt = block.rbegin(); reverseIt != block.rend();
|
||||||
|
++reverseIt) {
|
||||||
|
SILInstruction &currIns = *reverseIt;
|
||||||
|
if (auto *currEAI = dyn_cast<EndAccessInst>(&currIns)) {
|
||||||
|
if (!bottomEndAccessInst) {
|
||||||
|
bottomEndAccessInst = currEAI;
|
||||||
|
}
|
||||||
|
} else if (isBarrier(currIns)) {
|
||||||
|
LLVM_DEBUG(llvm::dbgs() << "Found a barrier " << currIns
|
||||||
|
<< ", clearing last seen end_access\n");
|
||||||
|
bottomEndAccessInst = nullptr;
|
||||||
|
} else if (isSinkable(currIns)) {
|
||||||
|
LLVM_DEBUG(llvm::dbgs()
|
||||||
|
<< "Found a sinkable instruction " << currIns << "\n");
|
||||||
|
if (!bottomEndAccessInst) {
|
||||||
|
LLVM_DEBUG(
|
||||||
|
llvm::dbgs()
|
||||||
|
<< "Cannot be sunk: no open barrier-less end_access found\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LLVM_DEBUG(llvm::dbgs() << "Moving sinkable instruction below "
|
||||||
|
<< *bottomEndAccessInst << "\n");
|
||||||
|
// We need to avoid iterator invalidation:
|
||||||
|
// We know this is not the last instruction of the block:
|
||||||
|
// 1) not a TermInst
|
||||||
|
// 2) bottomEndAccessInst != nil
|
||||||
|
assert(reverseIt != block.rbegin() &&
|
||||||
|
"Did not expect a sinkable instruction at block's end");
|
||||||
|
// Go back to previous iteration
|
||||||
|
auto prevIt = reverseIt;
|
||||||
|
--prevIt;
|
||||||
|
// Move the instruction after the end_access
|
||||||
|
currIns.moveAfter(bottomEndAccessInst);
|
||||||
|
// make reverseIt into a valid iterator again
|
||||||
|
reverseIt = prevIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct AccessEnforcementReleaseSinking : public SILFunctionTransform {
|
||||||
|
void run() override {
|
||||||
|
SILFunction *F = getFunction();
|
||||||
|
if (F->empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LLVM_DEBUG(llvm::dbgs() << "Running AccessEnforcementReleaseSinking on "
|
||||||
|
<< F->getName() << "\n");
|
||||||
|
|
||||||
|
for (SILBasicBlock &currBB : *F) {
|
||||||
|
processBlock(currBB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SILTransform *swift::createAccessEnforcementReleaseSinking() {
|
||||||
|
return new AccessEnforcementReleaseSinking();
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ silopt_register_sources(
|
|||||||
ARCCodeMotion.cpp
|
ARCCodeMotion.cpp
|
||||||
AccessEnforcementDom.cpp
|
AccessEnforcementDom.cpp
|
||||||
AccessEnforcementOpts.cpp
|
AccessEnforcementOpts.cpp
|
||||||
|
AccessEnforcementReleaseSinking.cpp
|
||||||
AccessEnforcementWMO.cpp
|
AccessEnforcementWMO.cpp
|
||||||
AllocBoxToStack.cpp
|
AllocBoxToStack.cpp
|
||||||
ArrayCountPropagation.cpp
|
ArrayCountPropagation.cpp
|
||||||
|
|||||||
217
test/SILOptimizer/access_sink.sil
Normal file
217
test/SILOptimizer/access_sink.sil
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
// RUN: %target-sil-opt -access-enforcement-release -assume-parsing-unqualified-ownership-sil %s -enable-sil-verify-all | %FileCheck %s
|
||||||
|
//
|
||||||
|
// Test the AccessEnforcementReleaseSinking pass in isolation.
|
||||||
|
// This ensures that no upstream passes have removed SIL-level access markers
|
||||||
|
// that are required to ensure the pass is not overly optimistic.
|
||||||
|
|
||||||
|
sil_stage canonical
|
||||||
|
|
||||||
|
import Builtin
|
||||||
|
import Swift
|
||||||
|
import SwiftShims
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
@sil_stored var i: Int64 { get set }
|
||||||
|
init(i: Int64)
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalX: X
|
||||||
|
|
||||||
|
sil_global hidden @globalX : $X
|
||||||
|
|
||||||
|
sil hidden_external [global_init] @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
||||||
|
|
||||||
|
// public func testSimpleRelease() {
|
||||||
|
// Checks the simple case of release sinking
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testSimpleRelease : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-LABEL: } // end sil function 'testSimpleRelease'
|
||||||
|
sil @testSimpleRelease : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testMultiBlocklSimpleRelease() {
|
||||||
|
// Checks the simple case of release sinking with the begin_access in a different block
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testMultiBlocklSimpleRelease : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: br bb1
|
||||||
|
// CHECK: bb1
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-LABEL: } // end sil function 'testMultiBlocklSimpleRelease'
|
||||||
|
sil @testMultiBlocklSimpleRelease : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
br bb1
|
||||||
|
|
||||||
|
bb1:
|
||||||
|
release_value %2 : $X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testMultiBlocklBailOnRelease() {
|
||||||
|
// Checks bailing (for now) on the simple case due to the release being in a different block
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testMultiBlocklBailOnRelease : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-NEXT: br bb1
|
||||||
|
// CHECK: bb1
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-LABEL: } // end sil function 'testMultiBlocklBailOnRelease'
|
||||||
|
sil @testMultiBlocklBailOnRelease : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
br bb1
|
||||||
|
|
||||||
|
bb1:
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testApplyBarrier() {
|
||||||
|
// Checks we don't sink across apply-site barrier
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testApplyBarrier : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK: apply
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-LABEL: } // end sil function 'testApplyBarrier'
|
||||||
|
sil @testApplyBarrier : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
||||||
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testUniquenessBarrier() {
|
||||||
|
// Checks we don't sink across a uniqueness check
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testUniquenessBarrier : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-NEXT: is_unique
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-LABEL: } // end sil function 'testUniquenessBarrier'
|
||||||
|
sil @testUniquenessBarrier : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
is_unique %1 : $*X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testBeginBarrier() {
|
||||||
|
// Checks we don't sink across begin_access barrier
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testBeginBarrier : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-LABEL: } // end sil function 'testBeginBarrier'
|
||||||
|
sil @testBeginBarrier : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
%b0 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
end_access %b0 : $*X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testSinkCrossMultiEnds() {
|
||||||
|
// Checks that we choose the *bottom* end_access when sinking
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testSinkCrossMultiEnds : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-LABEL: } // end sil function 'testSinkCrossMultiEnds'
|
||||||
|
sil @testSinkCrossMultiEnds : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%b0 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
end_access %b0 : $*X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func testSinkAfterBarrierEncounter() {
|
||||||
|
// Checks that we sink after barrier resetting
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil @testSinkAfterBarrierEncounter : $@convention(thin) () -> () {
|
||||||
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||||
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||||
|
// CHECK-NEXT: release_value [[LOADED]]
|
||||||
|
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
||||||
|
// CHECK-LABEL: } // end sil function 'testSinkAfterBarrierEncounter'
|
||||||
|
sil @testSinkAfterBarrierEncounter : $@convention(thin) () -> () {
|
||||||
|
bb0:
|
||||||
|
%0 = global_addr @globalX: $*X
|
||||||
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
%2 = load %1 : $*X
|
||||||
|
release_value %2 : $X
|
||||||
|
end_access %1 : $*X
|
||||||
|
%b0 = begin_access [modify] [dynamic] %0 : $*X
|
||||||
|
end_access %b0 : $*X
|
||||||
|
%ret = tuple ()
|
||||||
|
return %ret : $()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user