Skip to content

[SIL Opaque Value] Add support for open_existential_box_value in AddressLowering #42247

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 3 commits into from
Apr 8, 2022
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
51 changes: 51 additions & 0 deletions include/swift/SIL/SILValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class DeadEndBlocks;
class ValueBaseUseIterator;
class ConsumingUseIterator;
class NonConsumingUseIterator;
class TypeDependentUseIterator;
class NonTypeDependentUseIterator;
class SILValue;
class SILModuleConventions;
Expand Down Expand Up @@ -376,6 +377,8 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
/// same type as the result of this instruction.
void replaceAllUsesWithUndef();

void replaceAllTypeDependentUsesWith(ValueBase *RHS);

/// Is this value a direct result of the given instruction?
bool isResultOf(SILInstruction *I) const;

Expand All @@ -390,6 +393,8 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
using consuming_use_range = iterator_range<consuming_use_iterator>;
using non_consuming_use_iterator = NonConsumingUseIterator;
using non_consuming_use_range = iterator_range<non_consuming_use_iterator>;
using typedependent_use_iterator = TypeDependentUseIterator;
using typedependent_use_range = iterator_range<typedependent_use_iterator>;
using non_typedependent_use_iterator = NonTypeDependentUseIterator;
using non_typedependent_use_range =
iterator_range<non_typedependent_use_iterator>;
Expand All @@ -403,6 +408,9 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
inline non_consuming_use_iterator non_consuming_use_begin() const;
inline non_consuming_use_iterator non_consuming_use_end() const;

inline typedependent_use_iterator typedependent_use_begin() const;
inline typedependent_use_iterator typedependent_use_end() const;

inline non_typedependent_use_iterator non_typedependent_use_begin() const;
inline non_typedependent_use_iterator non_typedependent_use_end() const;

Expand Down Expand Up @@ -430,6 +438,10 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
/// Returns a range of all non consuming uses
inline non_consuming_use_range getNonConsumingUses() const;

/// Returns a range of uses that are classified as a type dependent
/// operand of the user.
inline typedependent_use_range getTypeDependentUses() const;

/// Returns a range of uses that are not classified as a type dependent
/// operand of the user.
inline non_typedependent_use_range getNonTypeDependentUses() const;
Expand Down Expand Up @@ -1104,6 +1116,7 @@ class Operand {
friend class ValueBaseUseIterator;
friend class ConsumingUseIterator;
friend class NonConsumingUseIterator;
friend class TypeDependentUseIterator;
friend class NonTypeDependentUseIterator;
template <unsigned N> friend class FixedOperandList;
friend class TrailingOperandsList;
Expand Down Expand Up @@ -1231,6 +1244,39 @@ ValueBase::non_consuming_use_end() const {
return ValueBase::non_consuming_use_iterator(nullptr);
}

class TypeDependentUseIterator : public ValueBaseUseIterator {
public:
explicit TypeDependentUseIterator(Operand *cur) : ValueBaseUseIterator(cur) {}
TypeDependentUseIterator &operator++() {
assert(Cur && "incrementing past end()!");
while ((Cur = Cur->NextUse)) {
if (Cur->isTypeDependent())
break;
}
return *this;
}

TypeDependentUseIterator operator++(int unused) {
TypeDependentUseIterator copy = *this;
++*this;
return copy;
}
};

inline ValueBase::typedependent_use_iterator
ValueBase::typedependent_use_begin() const {
auto cur = FirstUse;
while (cur && !cur->isTypeDependent()) {
cur = cur->NextUse;
}
return ValueBase::typedependent_use_iterator(cur);
}

inline ValueBase::typedependent_use_iterator
ValueBase::typedependent_use_end() const {
return ValueBase::typedependent_use_iterator(nullptr);
}

class NonTypeDependentUseIterator : public ValueBaseUseIterator {
public:
explicit NonTypeDependentUseIterator(Operand *cur)
Expand Down Expand Up @@ -1311,6 +1357,11 @@ ValueBase::getNonConsumingUses() const {
return {non_consuming_use_begin(), non_consuming_use_end()};
}

inline ValueBase::typedependent_use_range
ValueBase::getTypeDependentUses() const {
return {typedependent_use_begin(), typedependent_use_end()};
}

inline ValueBase::non_typedependent_use_range
ValueBase::getNonTypeDependentUses() const {
return {non_typedependent_use_begin(), non_typedependent_use_end()};
Expand Down
7 changes: 7 additions & 0 deletions lib/SIL/IR/SILValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ void ValueBase::replaceAllUsesWithUndef() {
}
}

void ValueBase::replaceAllTypeDependentUsesWith(ValueBase *RHS) {
SmallVector<Operand *, 4> typeUses(getTypeDependentUses());
for (Operand *use : typeUses) {
use->set(RHS);
}
}

SILInstruction *ValueBase::getDefiningInstruction() {
if (auto *inst = dyn_cast<SingleValueInstruction>(this))
return inst;
Expand Down
44 changes: 23 additions & 21 deletions lib/SILOptimizer/Mandatory/AddressLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
///
/// 1. Reused-storage: Some operations are guaranteed to reuse their operand's
/// storage. This includes extracting an enum payload and opening an existential
/// value. This is required avoid introducing new copies or moves.
/// value. This is required to avoid introducing new copies or moves.
///
/// // %data's storage must reuse storage allocated for %enum
/// %data = unchecked_enum_data %enum : $Optional<T>, #Optional.some!enumelt
Expand Down Expand Up @@ -2503,12 +2503,6 @@ class UseRewriter : SILInstructionVisitor<UseRewriter> {
// types.
void visitOpenExistentialValueInst(OpenExistentialValueInst *openExistential);

void visitOpenExistentialBoxValueInst(
OpenExistentialBoxValueInst *openExistentialBox) {
// FIXME: Unimplemented
llvm::report_fatal_error("Unimplemented OpenExistentialBox use.");
}

void visitReturnInst(ReturnInst *returnInst) {
// Returns are rewritten for any function with indirect results after
// opaque value rewriting.
Expand Down Expand Up @@ -2625,15 +2619,7 @@ void UseRewriter::visitOpenExistentialValueInst(
openExistential->getType().getAddressType(),
OpenedExistentialAccess::Immutable);

SmallVector<Operand *, 4> typeUses;
for (Operand *use : openExistential->getUses()) {
if (use->isTypeDependent()) {
typeUses.push_back(use);
}
}
for (Operand *use : typeUses) {
use->set(openAddr);
}
openExistential->replaceAllTypeDependentUsesWith(openAddr);
markRewritten(openExistential, openAddr);
}

Expand Down Expand Up @@ -2939,11 +2925,18 @@ class DefRewriter : SILInstructionVisitor<DefRewriter> {
addrMat.initializeComposingUse(&initExistentialValue->getOperandRef());
}

// Project an opaque value out of a box-type existential.
void visitOpenExistentialBoxValueInst(
OpenExistentialBoxValueInst *openExistentialBox) {
// FIXME: Unimplemented
llvm::report_fatal_error("Unimplemented OpenExistentialBoxValue def.");
OpenExistentialBoxValueInst *openExistentialBoxValue) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the operand of open_existential_box_value always a loadable type (a box existential)? If not, then it also needs to be handled in the use rewriter.... just double checking out of paranoia.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Replace the module's openedArchetypesDef
pass.getModule()->willDeleteInstruction(openExistentialBoxValue);

auto *openAddr = builder.createOpenExistentialBox(
openExistentialBoxValue->getLoc(),
openExistentialBoxValue->getOperand(),
openExistentialBoxValue->getType().getAddressType());

openExistentialBoxValue->replaceAllTypeDependentUsesWith(openAddr);
setStorageAddress(openExistentialBoxValue, openAddr);
}

// Load an opaque value.
Expand Down Expand Up @@ -2978,6 +2971,14 @@ class DefRewriter : SILInstructionVisitor<DefRewriter> {
for (Operand &operand : tupleInst->getAllOperands())
addrMat.initializeComposingUse(&operand);
}

void setStorageAddress(SILValue oldValue, SILValue addr) {
auto &storage = pass.valueStorageMap.getStorage(oldValue);
// getReusedStorageOperand() ensures that oldValue does not already have
// separate storage. So there's no need to delete its alloc_stack.
assert(!storage.storageAddress || storage.storageAddress == addr);
storage.storageAddress = addr;
}
};
} // end anonymous namespace

Expand Down Expand Up @@ -3132,7 +3133,8 @@ static void deleteRewrittenInstructions(AddressLoweringState &pass) {
}
}
LLVM_DEBUG(llvm::dbgs() << "DEAD "; deadInst->dump());
if (!isa<OpenExistentialValueInst>(deadInst)) {
if (!isa<OpenExistentialValueInst>(deadInst) &&
!isa<OpenExistentialBoxValueInst>(deadInst)) {
pass.deleter.forceDeleteWithUsers(deadInst);
continue;
}
Expand Down
31 changes: 31 additions & 0 deletions test/SILOptimizer/address_lowering_lib.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %target-sil-opt -address-lowering -enable-sil-opaque-values -sil-verify-all %s | %FileCheck %s
//
import Builtin
import Swift

sil_stage raw

// CHECK-LABEL: sil [ossa] @test_open_existential_box_value : $@convention(thin) (@in Error) -> () {
// CHECK: [[ALLOC:%.*]] = alloc_stack $Any
// CHECK: [[VAL:%.*]] = load [take] %0 : $*Error
// CHECK: [[BORROW:%.*]] = begin_borrow [lexical] [[VAL]] : $Error
// CHECK: [[OPENADDR:%.*]] = open_existential_box [[BORROW]] : $Error to $*@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error
// CHECK: [[INIT:%.*]] = init_existential_addr [[ALLOC]] : $*Any, $@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error
// CHECK: copy_addr [[OPENADDR]] to [initialization] [[INIT]] : $*@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error
// CHECK: destroy_addr [[ALLOC]] : $*Any
// CHECK: end_borrow [[BORROW]] : $Error
// CHECK: destroy_value [[VAL]] : $Error
// CHECK: dealloc_stack [[ALLOC]] : $*Any
// CHECK-LABEL: } // end sil function 'test_open_existential_box_value'
sil [ossa] @test_open_existential_box_value : $@convention(thin) (@in Error) -> () {
bb0(%0 : @owned $Error):
%1 = begin_borrow [lexical] %0 : $Error
%2 = open_existential_box_value %1 : $Error to $@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error
%3 = copy_value %2 : $@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error
%4 = init_existential_value %3 : $@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error, $@opened("169A6848-B636-11EC-83C4-D0817AD59B9D") Error, $Any
destroy_value %4 : $Any
end_borrow %1 : $Error
destroy_value %0 : $Error
%ret = tuple ()
return %ret : $()
}