Skip to content

Fix SILInliner to create move_value in OSSA only #63921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 26, 2023
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
3 changes: 3 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,7 @@ class SILBuilder {
}

CopyValueInst *createCopyValue(SILLocation Loc, SILValue operand) {
assert(getFunction().hasOwnership());
assert(!operand->getType().isTrivial(getFunction()) &&
"Should not be passing trivial values to this api. Use instead "
"emitCopyValueOperation");
Expand All @@ -1296,6 +1297,7 @@ class SILBuilder {

DestroyValueInst *createDestroyValue(SILLocation Loc, SILValue operand,
bool poisonRefs = false) {
assert(getFunction().hasOwnership());
assert(isLoadableOrOpaque(operand->getType()));
assert(!operand->getType().isTrivial(getFunction()) &&
"Should not be passing trivial values to this api. Use instead "
Expand All @@ -1306,6 +1308,7 @@ class SILBuilder {

MoveValueInst *createMoveValue(SILLocation loc, SILValue operand,
bool isLexical = false) {
assert(getFunction().hasOwnership());
assert(!operand->getType().isTrivial(getFunction()) &&
"Should not be passing trivial values to this api. Use instead "
"emitMoveValueOperation");
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1870,6 +1870,9 @@ void SILCloner<ImplClass>::visitExplicitCopyValueInst(
template <typename ImplClass>
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
if (!getBuilder().hasOwnership()) {
return recordFoldedValue(Inst, getOpValue(Inst->getOperand()));
}
auto *MVI = getBuilder().createMoveValue(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand()),
Inst->isLexical());
Expand Down
90 changes: 49 additions & 41 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,50 +449,58 @@ void SILInlineCloner::cloneInline(ArrayRef<SILValue> AppliedArgs) {

SmallVector<SILValue, 4> entryArgs;
entryArgs.reserve(AppliedArgs.size());

auto calleeConv = getCalleeFunction()->getConventions();
SmallBitVector borrowedArgs(AppliedArgs.size());
SmallBitVector copiedArgs(AppliedArgs.size());
if (!Apply->getFunction()->hasOwnership()) {

auto calleeConv = getCalleeFunction()->getConventions();
for (auto p : llvm::enumerate(AppliedArgs)) {
SILValue callArg = p.value();
SWIFT_DEFER { entryArgs.push_back(callArg); };
unsigned idx = p.index();
if (idx >= calleeConv.getSILArgIndexOfFirstParam()) {
auto paramInfo = calleeConv.getParamInfoForSILArg(idx);
if (callArg->getType().isAddress()) {
// If lexical lifetimes are enabled, any alloc_stacks in the caller that
// are passed to the callee being inlined (except mutating exclusive
// accesses) need to be promoted to be lexical. Otherwise,
// destroy_addrs could be hoisted through the body of the newly inlined
// function without regard to the deinit barriers it contains.
//
// TODO: [begin_borrow_addr] Instead of marking the alloc_stack as a
// whole lexical, just mark the inlined range lexical via
// begin_borrow_addr [lexical]/end_borrow_addr just as is done
// with values.
auto &module = Apply.getFunction()->getModule();
auto enableLexicalLifetimes =
module.getASTContext().SILOpts.supportsLexicalLifetimes(module);
if (!enableLexicalLifetimes)
continue;

// Exclusive mutating accesses don't entail a lexical scope.
if (paramInfo.getConvention() == ParameterConvention::Indirect_Inout)
continue;

auto storage = AccessStorageWithBase::compute(callArg);
if (auto *asi = dyn_cast_or_null<AllocStackInst>(storage.base))
asi->setIsLexical();
} else {
// Insert begin/end borrow for guaranteed arguments.
if (paramInfo.isGuaranteed()) {
if (SILValue newValue = borrowFunctionArgument(callArg, idx)) {
callArg = newValue;
borrowedArgs[idx] = true;
}
} else if (paramInfo.isConsumed()) {
if (SILValue newValue = moveFunctionArgument(callArg, idx)) {
callArg = newValue;
for (auto p : llvm::enumerate(AppliedArgs)) {
SILValue callArg = p.value();
entryArgs.push_back(callArg);
}
} else {
for (auto p : llvm::enumerate(AppliedArgs)) {
SILValue callArg = p.value();
SWIFT_DEFER { entryArgs.push_back(callArg); };
unsigned idx = p.index();
if (idx >= calleeConv.getSILArgIndexOfFirstParam()) {
auto paramInfo = calleeConv.getParamInfoForSILArg(idx);
if (callArg->getType().isAddress()) {
// If lexical lifetimes are enabled, any alloc_stacks in the caller
// that are passed to the callee being inlined (except mutating
// exclusive accesses) need to be promoted to be lexical. Otherwise,
// destroy_addrs could be hoisted through the body of the newly
// inlined function without regard to the deinit barriers it contains.
//
// TODO: [begin_borrow_addr] Instead of marking the alloc_stack as a
// whole lexical, just mark the inlined range lexical via
// begin_borrow_addr [lexical]/end_borrow_addr just as is done
// with values.
auto &module = Apply.getFunction()->getModule();
auto enableLexicalLifetimes =
module.getASTContext().SILOpts.supportsLexicalLifetimes(module);
if (!enableLexicalLifetimes)
continue;

// Exclusive mutating accesses don't entail a lexical scope.
if (paramInfo.getConvention() == ParameterConvention::Indirect_Inout)
continue;

auto storage = AccessStorageWithBase::compute(callArg);
if (auto *asi = dyn_cast_or_null<AllocStackInst>(storage.base))
asi->setIsLexical();
} else {
// Insert begin/end borrow for guaranteed arguments.
if (paramInfo.isGuaranteed()) {
if (SILValue newValue = borrowFunctionArgument(callArg, idx)) {
callArg = newValue;
borrowedArgs[idx] = true;
}
} else if (paramInfo.isConsumed()) {
if (SILValue newValue = moveFunctionArgument(callArg, idx)) {
callArg = newValue;
}
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions test/IRGen/move_value.sil
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import Builtin
// CHECK-NEXT: entry:
// CHECK-NEXT: ret
// CHECK-NEXT: }
sil @move_value_test : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
sil [ossa] @move_value_test : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}
}

4 changes: 1 addition & 3 deletions test/Profiler/rdar39146527.sil
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,9 @@ bb9(%31 : $(Character, allowingJoined: Bool)):
%35 = struct_extract %34 : $_StringGuts, #_StringGuts._object
%36 = struct_extract %35 : $_StringObject, #_StringObject._object
strong_retain %36 : $Builtin.BridgeObject
%38 = move_value [lexical] %32 : $Character

// CHECK: increment_profiler_counter 2, "$s4Test4NameO11valueStringSSvg", num_counters 4, hash 0
increment_profiler_counter 2, "$s4Test4NameO11valueStringSSvg", num_counters 4, hash 0
%40 = struct_extract %38 : $Character, #Character._str
%40 = struct_extract %32 : $Character, #Character._str
release_value %25 : $Name
br bb11(%40 : $String)

Expand Down
15 changes: 1 addition & 14 deletions test/SIL/Parser/basic2.sil
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,6 @@ bb0(%0 : @owned $Builtin.NativeObject):
return %5 : $Builtin.NativeObject
}

// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: %2 = move_value [allows_diagnostics] %1 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
%2 = move_value [allows_diagnostics] %1 : $Builtin.NativeObject
return %2 : $Builtin.NativeObject
}

// CHECK-LABEL: sil [ossa] @test_debug_value_alloc_stack_moved : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: debug_value [moved] %0 : $Builtin.NativeObject
// CHECK: debug_value [poison] %0 : $Builtin.NativeObject
Expand Down Expand Up @@ -142,4 +129,4 @@ bb0(%0 : @owned $Builtin.NativeObject):
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
}
11 changes: 0 additions & 11 deletions test/SIL/Serialization/basic.sil
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,6 @@ bb0(%0 : @owned $Builtin.NativeObject):
return %5 : $Builtin.NativeObject
}

// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}

// CHECK-LABEL: sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () {
sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () {
bb0:
Expand Down
13 changes: 6 additions & 7 deletions test/SIL/opaque-verify.sil
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ protocol P {
}

// Ensure that open_existential_addr can be used by an unqualified load.
sil @loadFromExis : $@convention(thin) (@in P) -> () {
bb(%0 : $P):
sil [ossa] @loadFromExis : $@convention(thin) (@in P) -> () {
bb(%0 : @owned $P):
%tempP = alloc_stack $P, var
store %0 to %tempP : $*P
%opened = open_existential_addr immutable_access %tempP : $*P to $*@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
%val = load %opened : $*@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
%copy = copy_value %val : $@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
store %0 to [init] %tempP : $*P
%opened = open_existential_addr mutable_access %tempP : $*P to $*@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
%val = load [take] %opened : $*@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
%wm = witness_method $@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self, #P.foo : <Self where Self : P> (Self) -> () -> (), %opened : $*@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
%call = apply %wm<@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self>(%copy) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
%call = apply %wm<@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self>(%val) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
destroy_value %val : $@opened("295A5344-9728-11E7-B89E-38C9860EC692", P) Self
dealloc_stack %tempP : $*P
%void = tuple ()
Expand Down
8 changes: 2 additions & 6 deletions test/SILOptimizer/funcsig_opaque.sil
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,16 @@ sil @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
//
// CHECK-LABEL: sil shared @$s16testAggregateArgTf4g_n : $@convention(thin) (@in_guaranteed R<S>) -> @out () {
// CHECK: bb0(%0 : $R<S>):
// CHECK: [[COPY:%.*]] = copy_value %0 : $R<S>
// CHECK: retain_value %0 : $R<S>
// CHECK: [[F:%.*]] = function_ref @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
// CHECK: [[CALL:%.*]] = apply [[F]]([[COPY]]) : $@convention(thin) (@owned R<S>) -> ()
// CHECK: destroy_value %0 : $R<S>
// CHECK: [[CALL:%.*]] = apply [[F]](%0) : $@convention(thin) (@owned R<S>) -> ()
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function '$s16testAggregateArgTf4g_n'
sil @testAggregateArg : $@convention(thin) (@in R<S>) -> @out () {
bb0(%0 : $R<S>):
%9 = copy_value %0 : $R<S>
retain_value %0 : $R<S>
%10 = function_ref @testAggregateArgHelper : $@convention(thin) (@owned R<S>) -> ()
%12 = apply %10(%9) : $@convention(thin) (@owned R<S>) -> ()
destroy_value %0 : $R<S>
%12 = apply %10(%0) : $@convention(thin) (@owned R<S>) -> ()
release_value %0 : $R<S>
%1284 = tuple ()
return %1284 : $()
Expand Down
2 changes: 1 addition & 1 deletion test/SILOptimizer/specialize_dynamic_self.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension P {

class C<T> : P {
// CHECK-LABEL: sil shared [noinline] @$s23specialize_dynamic_self1CC11returnsSelfACyxGXDyFSi_Tg5 : $@convention(method) (@guaranteed C<Int>) -> @owned C<Int>
// CHECK: [[RESULT:%.*]] = alloc_stack [lexical] $C<Int>
// CHECK: [[RESULT:%.*]] = alloc_stack $C<Int>
// CHECK: [[FN:%.*]] = function_ref @$s23specialize_dynamic_self1PPAAE7method1yyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
// CHECK: apply [[FN]]<@dynamic_self C<Int>>([[RESULT]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
// CHECK: return %0 : $C<Int>
Expand Down
6 changes: 3 additions & 3 deletions test/SILOptimizer/specialize_opaque.sil
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import Builtin

// Test that foo is specialized on Builtin.Int64 and the copy_values and destroy_values are dropped.
//
// CHECK-LABEL: sil shared @$s3fooBi64__Tg5 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> () {
// CHECK-LABEL: sil shared [ossa] @$s3fooBi64__Tg5 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> () {
// CHECK: bb0(%0 : $Builtin.Int64, %1 : $Builtin.Int64):
// CHECK: [[F:%.*]] = function_ref @$s3fooBi64__Tg5 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
// CHECK: %{{.*}} = apply [[F]](%0, %1) : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function '$s3fooBi64__Tg5'
sil hidden @foo : $@convention(thin) <T> (@in T, @in T) -> () {
bb0(%0 : $T, %1 : $T):
sil hidden [ossa] @foo : $@convention(thin) <T> (@in T, @in T) -> () {
bb0(%0 : @owned $T, %1 : @owned $T):
%f = function_ref @foo : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
%cp0 = copy_value %0 : $T
%cp1 = copy_value %1 : $T
Expand Down