mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #19372 from shajrawi/arrayinclass
This commit is contained in:
@@ -654,6 +654,26 @@ void AccessConflictAndMergeAnalysis::visitBeginAccess(
|
||||
// It can potentially be folded
|
||||
// regardless of whether it may conflict with an outer access.
|
||||
addInScopeAccess(info, beginAccess);
|
||||
// We can merge out-of-scope regardless of having a conflict within a scope,
|
||||
// normally, it would have made more sense to add it to out-of-scope set
|
||||
// *only* after encountering the end_access instruction.
|
||||
// However, that will lose us some valid optimization potential:
|
||||
// consider the following pseudo-SIL:
|
||||
// begin_access %x
|
||||
// end_access %x
|
||||
// begin_access %x
|
||||
// conflict
|
||||
// end_access %x
|
||||
// we can merge both of these scopes
|
||||
// but, if we only add the instr. after seeing end_access,
|
||||
// then we would not have the first begin_access in out-of-scope
|
||||
// set when encoutnering the 2nd end_access due to "conflict"
|
||||
// NOTE: What we really want to do here is to check if
|
||||
// we should add the new beginAccess to 'mergePairs' structure
|
||||
// the reason for calling this method is to check for that.
|
||||
// logically, we only need to add an instructio to
|
||||
// out-of-scope conflict-free set when we visit end_access
|
||||
addOutOfScopeAccess(info, beginAccess);
|
||||
}
|
||||
|
||||
void AccessConflictAndMergeAnalysis::visitEndAccess(EndAccessInst *endAccess,
|
||||
@@ -669,10 +689,18 @@ void AccessConflictAndMergeAnalysis::visitEndAccess(EndAccessInst *endAccess,
|
||||
removeInScopeAccess(info, beginAccess);
|
||||
}
|
||||
|
||||
// We can merge out-of-scope regardless of having a conflict within a scope,
|
||||
// when encountering an end access instruction,
|
||||
// regardless of having it in the In scope set,
|
||||
// add it to the out of scope set.
|
||||
// If this exact instruction is already in out-of-scope - skip:
|
||||
if (info.outOfScopeConflictFreeAccesses.conflictFreeAccesses.count(
|
||||
beginAccess) > 0) {
|
||||
return;
|
||||
}
|
||||
// Else we have the opposite situation to the one described in
|
||||
// visitBeginAccess: the first scope is the one conflicting while the second
|
||||
// does not - begin_access %x conflict end_access %x begin_access %x
|
||||
// end_access %x
|
||||
// when seeing the conflict we remove the first begin instruction
|
||||
// but, we can still merge those scopes *UNLESS* there's a conflict
|
||||
// between the first end_access and the second begin_access
|
||||
LLVM_DEBUG(llvm::dbgs() << "Got out of scope from " << *beginAccess << " to "
|
||||
<< *endAccess << "\n");
|
||||
|
||||
|
||||
@@ -48,28 +48,17 @@ bb0(%0 : $@thin X.Type):
|
||||
// }
|
||||
// Preserve begin/end scope for nested conflicts,
|
||||
// after inlining (read|modify)AndPerform.
|
||||
// need to split it into 3 SIL functions else merging would kick in and we would be testing something else
|
||||
//
|
||||
// CHECK-LABEL: sil hidden @testNestedAccess : $@convention(thin) () -> () {
|
||||
// CHECK-LABEL: sil hidden @testNestedAccess1 : $@convention(thin) () -> () {
|
||||
// CHECK: [[F1:%.*]] = function_ref @testNestedAccessClosure1 : $@convention(thin) () -> ()
|
||||
// CHECK: [[C1:%.*]] = convert_function [[F1]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
// CHECK: [[TF1:%.*]] = thin_to_thick_function [[C1]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed ()
|
||||
// CHECK: [[A1:%.*]] = begin_access [read] [dynamic] %0 : $*X
|
||||
// CHECK: apply [[TF1]]() : $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: end_access [[A1]] : $*X
|
||||
// CHECK: [[F2:%.*]] = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
|
||||
// CHECK: [[C2:%.*]] = convert_function [[F2]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
// CHECK: [[TF2:%.*]] = thin_to_thick_function [[C2]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: [[A2:%.*]] = begin_access [modify] [dynamic] %0 : $*X
|
||||
// CHECK: apply [[TF2]]() : $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: end_access [[A2]] : $*X
|
||||
// CHECK: [[F3:%.*]] = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
|
||||
// CHECK: [[C3:%.*]] = convert_function [[F3]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
// CHECK: [[TF3:%.*]] = thin_to_thick_function [[C3]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: [[A3:%.*]] = begin_access [modify] [dynamic] %0 : $*X
|
||||
// CHECK: apply [[TF3]]() : $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: end_access [[A3]] : $*X
|
||||
// CHECK-LABEL: } // end sil function 'testNestedAccess'
|
||||
sil hidden @testNestedAccess : $@convention(thin) () -> () {
|
||||
// CHECK-LABEL: } // end sil function 'testNestedAccess1'
|
||||
sil hidden @testNestedAccess1 : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%2 = global_addr @globalX: $*X
|
||||
%3 = function_ref @testNestedAccessClosure1 : $@convention(thin) () -> ()
|
||||
@@ -78,14 +67,40 @@ bb0:
|
||||
%6 = begin_access [read] [dynamic] %2 : $*X
|
||||
%9 = apply %5() : $@noescape @callee_guaranteed () -> ()
|
||||
end_access %6 : $*X
|
||||
|
||||
%36 = tuple ()
|
||||
return %36 : $()
|
||||
}
|
||||
// CHECK-LABEL: sil hidden @testNestedAccess2 : $@convention(thin) () -> () {
|
||||
// CHECK: [[F2:%.*]] = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
|
||||
// CHECK: [[C2:%.*]] = convert_function [[F2]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
// CHECK: [[TF2:%.*]] = thin_to_thick_function [[C2]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: [[A2:%.*]] = begin_access [modify] [dynamic] %0 : $*X
|
||||
// CHECK: apply [[TF2]]() : $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: end_access [[A2]] : $*X
|
||||
// CHECK-LABEL: } // end sil function 'testNestedAccess2'
|
||||
sil hidden @testNestedAccess2 : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%2 = global_addr @globalX: $*X
|
||||
%15 = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
|
||||
%16 = convert_function %15 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
%17 = thin_to_thick_function %16 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
%18 = begin_access [modify] [dynamic] %2 : $*X
|
||||
%21 = apply %17() : $@noescape @callee_guaranteed () -> ()
|
||||
end_access %18 : $*X
|
||||
|
||||
%36 = tuple ()
|
||||
return %36 : $()
|
||||
}
|
||||
// CHECK-LABEL: sil hidden @testNestedAccess3 : $@convention(thin) () -> () {
|
||||
// CHECK: [[F3:%.*]] = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
|
||||
// CHECK: [[C3:%.*]] = convert_function [[F3]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
// CHECK: [[TF3:%.*]] = thin_to_thick_function [[C3]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: [[A3:%.*]] = begin_access [modify] [dynamic] %0 : $*X
|
||||
// CHECK: apply [[TF3]]() : $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: end_access [[A3]] : $*X
|
||||
// CHECK-LABEL: } // end sil function 'testNestedAccess3'
|
||||
sil hidden @testNestedAccess3 : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%2 = global_addr @globalX: $*X
|
||||
%27 = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
|
||||
%28 = convert_function %27 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
|
||||
%29 = thin_to_thick_function %28 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
@@ -1306,6 +1321,8 @@ class RefElemClass {
|
||||
init()
|
||||
}
|
||||
|
||||
// Checks that we don't crash when unable to create projection paths
|
||||
// we can't merge anything here because of that
|
||||
// CHECK-LABEL: sil @ref_elem_c : $@convention(thin) (RefElemClass) -> () {
|
||||
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
||||
@@ -1314,8 +1331,10 @@ class RefElemClass {
|
||||
// CHECK-NEXT: [[REFX:%.*]] = ref_element_addr %0 : $RefElemClass, #RefElemClass.x
|
||||
// CHECK-NEXT: [[REFY:%.*]] = ref_element_addr %0 : $RefElemClass, #RefElemClass.y
|
||||
// CHECK-NEXT: [[BEGINX:%.*]] = begin_access [modify] [dynamic] [[REFX]] : $*BitfieldOne
|
||||
// CHECK: end_access [[BEGINX]] : $*BitfieldOne
|
||||
// CHECK: [[BEGINY:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFY]] : $*Int32
|
||||
// CHECK-NEXT: end_access [[BEGINX]] : $*BitfieldOne
|
||||
// CHECK: [[BEGINX2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFX]] : $*BitfieldOne
|
||||
// CHECK-NEXT: end_access [[BEGINX2]] : $*BitfieldOne
|
||||
// CHECK-NEXT: end_access [[BEGINY]] : $*Int32
|
||||
// CHECK-LABEL: } // end sil function 'ref_elem_c'
|
||||
|
||||
@@ -1417,3 +1436,55 @@ bb6:
|
||||
%19 = tuple ()
|
||||
return %19 : $()
|
||||
}
|
||||
|
||||
// public func testMergeWithFirstConflict() {
|
||||
// Check that we can merge scopes even if the first one of them has conflicts within it
|
||||
//
|
||||
// CHECK-LABEL: sil @testMergeWithFirstConflict : $@convention(thin) () -> () {
|
||||
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
||||
// CHECK: apply
|
||||
// CHECK-NEXT: load
|
||||
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||
// CHECK-NOT: begin_access
|
||||
// CHECK-LABEL: } // end sil function 'testMergeWithFirstConflict'
|
||||
sil @testMergeWithFirstConflict : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = global_addr @globalX: $*X
|
||||
%4 = begin_access [read] [dynamic] %0 : $*X
|
||||
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
||||
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
||||
end_access %4 : $*X
|
||||
%1 = begin_access [read] [dynamic] %0 : $*X
|
||||
%2 = load %1 : $*X
|
||||
end_access %1 : $*X
|
||||
%10 = tuple ()
|
||||
return %10 : $()
|
||||
}
|
||||
|
||||
// public func testMergeWithSecondConflict() {
|
||||
// Check that we can merge scopes even if the 2nd one of them has conflicts within it
|
||||
//
|
||||
// CHECK-LABEL: sil @testMergeWithSecondConflict : $@convention(thin) () -> () {
|
||||
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
||||
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
||||
// CHECK: load
|
||||
// CHECK: apply
|
||||
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
||||
// CHECK-NOT: begin_access
|
||||
// CHECK-LABEL: } // end sil function 'testMergeWithSecondConflict'
|
||||
sil @testMergeWithSecondConflict : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = global_addr @globalX: $*X
|
||||
%1 = begin_access [read] [dynamic] %0 : $*X
|
||||
%2 = load %1 : $*X
|
||||
end_access %1 : $*X
|
||||
%4 = begin_access [read] [dynamic] %0 : $*X
|
||||
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
||||
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
||||
end_access %4 : $*X
|
||||
%10 = tuple ()
|
||||
return %10 : $()
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user