-
Notifications
You must be signed in to change notification settings - Fork 10.5k
const evaluator: array values and initialization #22772
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,27 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const { | |
os << "\n"; | ||
break; | ||
} | ||
case RK_Array: | ||
case RK_ArrayAddress: { | ||
CanType elementType; | ||
ArrayRef<SymbolicValue> elements = getArrayValue(elementType); | ||
os << "array<" << elementType << ">: " << elements.size(); | ||
switch (elements.size()) { | ||
case 0: | ||
os << " elements []\n"; | ||
return; | ||
case 1: | ||
os << " elt: "; | ||
elements[0].print(os, indent + 2); | ||
return; | ||
default: | ||
os << " elements [\n"; | ||
for (auto elt : elements) | ||
elt.print(os, indent + 2); | ||
os.indent(indent) << "]\n"; | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
@@ -137,6 +158,9 @@ SymbolicValue::Kind SymbolicValue::getKind() const { | |
case RK_DirectAddress: | ||
case RK_DerivedAddress: | ||
return Address; | ||
case RK_Array: | ||
case RK_ArrayAddress: | ||
return Array; | ||
} | ||
} | ||
|
||
|
@@ -177,6 +201,16 @@ SymbolicValue::cloneInto(ASTContext &astContext) const { | |
memObject->getType(), memObject->getValue(), astContext); | ||
return getAddress(newMemObject, accessPath, astContext); | ||
} | ||
case RK_Array: | ||
case RK_ArrayAddress: { | ||
CanType elementType; | ||
auto elts = getArrayValue(elementType); | ||
SmallVector<SymbolicValue, 4> results; | ||
results.reserve(elts.size()); | ||
for (auto elt : elts) | ||
results.push_back(elt.cloneInto(astContext)); | ||
return getArray(results, elementType, astContext); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems clone is collapsing the representation of RK_Array and RK_ArrayAddress. It ignores the differences. Is this intended? Can we add a line to document this behavior, |
||
} | ||
|
||
|
@@ -520,6 +554,81 @@ SymbolicValueMemoryObject *SymbolicValue::getAddressValueMemoryObject() const { | |
return value.derivedAddress->memoryObject; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Arrays | ||
//===----------------------------------------------------------------------===// | ||
|
||
namespace swift { | ||
|
||
/// This is the representation of an array. | ||
struct ArraySymbolicValue final | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment should be updated for arrays. |
||
: private llvm::TrailingObjects<ArraySymbolicValue, SymbolicValue> { | ||
friend class llvm::TrailingObjects<ArraySymbolicValue, SymbolicValue>; | ||
|
||
const CanType elementType; | ||
|
||
/// This is the number of indices in the derived address. | ||
const unsigned numElements; | ||
|
||
static ArraySymbolicValue *create(ArrayRef<SymbolicValue> elements, | ||
CanType elementType, | ||
ASTContext &astContext) { | ||
auto byteSize = | ||
ArraySymbolicValue::totalSizeToAlloc<SymbolicValue>(elements.size()); | ||
auto rawMem = astContext.Allocate(byteSize, alignof(ArraySymbolicValue)); | ||
|
||
// Placement initialize the object. | ||
auto asv = ::new (rawMem) ArraySymbolicValue(elementType, elements.size()); | ||
std::uninitialized_copy(elements.begin(), elements.end(), | ||
asv->getTrailingObjects<SymbolicValue>()); | ||
return asv; | ||
} | ||
|
||
/// Return the element constants for this aggregate constant. These are | ||
/// known to all be constants. | ||
ArrayRef<SymbolicValue> getElements() const { | ||
return {getTrailingObjects<SymbolicValue>(), numElements}; | ||
} | ||
|
||
// This is used by the llvm::TrailingObjects base class. | ||
size_t numTrailingObjects(OverloadToken<SymbolicValue>) const { | ||
return numElements; | ||
} | ||
|
||
private: | ||
ArraySymbolicValue() = delete; | ||
ArraySymbolicValue(const ArraySymbolicValue &) = delete; | ||
ArraySymbolicValue(CanType elementType, unsigned numElements) | ||
: elementType(elementType), numElements(numElements) {} | ||
}; | ||
} // end namespace swift | ||
|
||
/// Produce an array of elements. | ||
SymbolicValue SymbolicValue::getArray(ArrayRef<SymbolicValue> elements, | ||
CanType elementType, | ||
ASTContext &astContext) { | ||
// TODO: Could compress the empty array representation if there were a reason | ||
// to. | ||
auto asv = ArraySymbolicValue::create(elements, elementType, astContext); | ||
SymbolicValue result; | ||
result.representationKind = RK_Array; | ||
result.value.array = asv; | ||
return result; | ||
} | ||
|
||
ArrayRef<SymbolicValue> | ||
SymbolicValue::getArrayValue(CanType &elementType) const { | ||
assert(getKind() == Array); | ||
auto val = *this; | ||
if (representationKind == RK_ArrayAddress) | ||
val = value.arrayAddress->getValue(); | ||
|
||
assert(val.representationKind == RK_Array); | ||
|
||
elementType = val.value.array->elementType; | ||
return val.value.array->getElements(); | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// Higher level code | ||
//===----------------------------------------------------------------------===// | ||
|
@@ -653,22 +762,32 @@ static SymbolicValue getIndexedElement(SymbolicValue aggregate, | |
if (aggregate.getKind() == SymbolicValue::UninitMemory) | ||
return SymbolicValue::getUninitMemory(); | ||
|
||
assert(aggregate.getKind() == SymbolicValue::Aggregate && | ||
assert((aggregate.getKind() == SymbolicValue::Aggregate || | ||
aggregate.getKind() == SymbolicValue::Array) && | ||
"the accessPath is invalid for this type"); | ||
|
||
unsigned elementNo = accessPath.front(); | ||
|
||
SymbolicValue elt = aggregate.getAggregateValue()[elementNo]; | ||
SymbolicValue elt; | ||
Type eltType; | ||
if (auto *decl = type->getStructOrBoundGenericStruct()) { | ||
auto it = decl->getStoredProperties().begin(); | ||
std::advance(it, elementNo); | ||
eltType = (*it)->getType(); | ||
} else if (auto tuple = type->getAs<TupleType>()) { | ||
assert(elementNo < tuple->getNumElements() && "invalid index"); | ||
eltType = tuple->getElement(elementNo).getType(); | ||
|
||
// We need to have an array, struct or a tuple type. | ||
if (aggregate.getKind() == SymbolicValue::Array) { | ||
CanType arrayEltTy; | ||
elt = aggregate.getArrayValue(arrayEltTy)[elementNo]; | ||
eltType = arrayEltTy; | ||
} else { | ||
llvm_unreachable("the accessPath is invalid for this type"); | ||
elt = aggregate.getAggregateValue()[elementNo]; | ||
if (auto *decl = type->getStructOrBoundGenericStruct()) { | ||
auto it = decl->getStoredProperties().begin(); | ||
std::advance(it, elementNo); | ||
eltType = (*it)->getType(); | ||
} else if (auto tuple = type->getAs<TupleType>()) { | ||
assert(elementNo < tuple->getNumElements() && "invalid index"); | ||
eltType = tuple->getElement(elementNo).getType(); | ||
} else { | ||
llvm_unreachable("the accessPath is invalid for this type"); | ||
} | ||
} | ||
|
||
return getIndexedElement(elt, accessPath.drop_front(), eltType); | ||
|
@@ -718,22 +837,33 @@ static SymbolicValue setIndexedElement(SymbolicValue aggregate, | |
aggregate = SymbolicValue::getAggregate(newElts, astCtx); | ||
} | ||
|
||
assert(aggregate.getKind() == SymbolicValue::Aggregate && | ||
assert((aggregate.getKind() == SymbolicValue::Aggregate || | ||
aggregate.getKind() == SymbolicValue::Array) && | ||
"the accessPath is invalid for this type"); | ||
|
||
unsigned elementNo = accessPath.front(); | ||
|
||
ArrayRef<SymbolicValue> oldElts = aggregate.getAggregateValue(); | ||
ArrayRef<SymbolicValue> oldElts; | ||
Type eltType; | ||
if (auto *decl = type->getStructOrBoundGenericStruct()) { | ||
auto it = decl->getStoredProperties().begin(); | ||
std::advance(it, elementNo); | ||
eltType = (*it)->getType(); | ||
} else if (auto tuple = type->getAs<TupleType>()) { | ||
assert(elementNo < tuple->getNumElements() && "invalid index"); | ||
eltType = tuple->getElement(elementNo).getType(); | ||
|
||
// We need to have an array, struct or a tuple type. | ||
if (aggregate.getKind() == SymbolicValue::Array) { | ||
CanType arrayEltTy; | ||
oldElts = aggregate.getArrayValue(arrayEltTy); | ||
eltType = arrayEltTy; | ||
} else { | ||
llvm_unreachable("the accessPath is invalid for this type"); | ||
oldElts = aggregate.getAggregateValue(); | ||
|
||
if (auto *decl = type->getStructOrBoundGenericStruct()) { | ||
auto it = decl->getStoredProperties().begin(); | ||
std::advance(it, elementNo); | ||
eltType = (*it)->getType(); | ||
} else if (auto tuple = type->getAs<TupleType>()) { | ||
assert(elementNo < tuple->getNumElements() && "invalid index"); | ||
eltType = tuple->getElement(elementNo).getType(); | ||
} else { | ||
llvm_unreachable("the accessPath is invalid for this type"); | ||
} | ||
} | ||
|
||
// Update the indexed element of the aggregate. | ||
|
@@ -742,7 +872,11 @@ static SymbolicValue setIndexedElement(SymbolicValue aggregate, | |
accessPath.drop_front(), newElement, | ||
eltType, astCtx); | ||
|
||
aggregate = SymbolicValue::getAggregate(newElts, astCtx); | ||
if (aggregate.getKind() == SymbolicValue::Aggregate) | ||
aggregate = SymbolicValue::getAggregate(newElts, astCtx); | ||
else | ||
aggregate = SymbolicValue::getArray(newElts, eltType->getCanonicalType(), | ||
astCtx); | ||
|
||
return aggregate; | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcrasi Sorry for the delay in the responding to this PR. Based on discussions with @devincoughlin, we have a few questions/comments about this PR. We are trying to understand the motivation for the two representations for an array:
RK_Array
andRK_ArrayAddress
. Swift's representation of an Array, in principle, is astruct
which has a pointer to a backing storage that contains the elements. It seems to us that RK_Array (i.e, ArraySymbolicValue) models the backing storage which stores the elements. RK_ArrayAddress is modeling the more accurate view of the array as a struct with a pointer to the backing storage namely RK_Array. It seems this distinction between an Array and its backing storage becomes important for modeling_allocateUninitializedArray
function which returns them separately and initializes the backing storage directly through the pointer. Our first question is, is our understanding right?If that is the case, it seems to us that RK_ArrayAddress is possibly the more accurate view of an array and RK_Array can be seen as a more optimal representation when the distinction between the array and the backing storage can be safely discarded. Therefore, the name
RK_ArrayAddress
seems a bit misleading as it is not representing an address of an Array, but rather a more accurate view of an Array. Would it be possible to give RK_Array, RK_ArrayAddress, ArraySymbolicValue another name and also add some documentation? For instance, what do you think about,SymbolicArrayBuffer
for ArraySymbolicValue,RK_ArrayBuffer
for RK_Array andRK_ArrayStruct
for RK_ArrayAddress.Ideally, I think we can create a new symbolic value for Array that will manage the differences in the representation. But, I think I can do that in another PR, after this lands.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reflection, I think
SymbolicArrayStorage
andRK_ArrayStorage
are better alternative names forSymbolicArrayBuffer
andRK_ArrayBuffer
I proposed above. This is inline with_ContiguousArrayStorage
class used in by struct Array.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like these name suggestions, thanks! Your comments and name suggestions are making it clear to me that these RKs are two fundamentally different things that should actually have different
Kind
s.Also, I'm pretty sure that the place below where you noticed the collapsing is a bug. (Probably not exposed by anything yet, but likely to become exposed when there are more array features.)
Finally, now that it's clear that RK_ArrayAddress is just a struct containing a pointer, why not make it a RK_Aggregate, like all the normal structs? This should simplify things!
So in summary, the total changes from the current state of this PR are:
RK_Array
=>RK_ArrayStorage
ArraySymbolicValue
=>SymbolicArrayStorage
RK_ArrayAddress
=> eliminated, replaced by the existingRK_Aggregate
I haven't tried this out yet so there may be some difficulty I'm not seeing. So I'll try it out soon and let you know how it goes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great. :-)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reflection, I think that it may be a good idea to have a special kind for "Array" aggregates (in addition to
ArrayStorage
kind) and not treat them as general aggregates. In fact, I even think we should have specialArraySymbolicValue
(in addition toSymbolicArrayStorage
). The following are some reasons for that:We don't want to make it appear that the interpreter supports arbitrary aggregates with pointers. Having a special "kind" for such an aggregate, makes this clear.
get/setIndexedElement
would need to handleaddresses
(which could now appear inside aggregates). Normally,setIndexedElement
would be expected to update the underlying memory object in place, as we want everything that aliases with the pointer field to also see the changes whensetIndexedElement
completes. But, arrays are COW and provide a value semantics. This means we have to special case theaddress
case for arrays, like creating a new memory object and not in-place updating the underlying memory object. This would appear confusing, as it is only correct for a type like Array that uses pointers underneath but provides a value semantics.To address both of this, we may rather have a new symbolic value for "array" which just stores a single
DirectAddressValue
or even just aSymbolicMemoryObject
containing aSymbolicArrayStorage
. ThisArraySymbolicValue
may support operations likegetAddressOfStorage()
which creates a direct address from the underlying SymbolicMemoryObject, andgetStorage()
which returns the storage.setIndexedElement
can just retrieve and (immutably) update the array storage and create a new ArraySymbolicValue with the new storage and return it.As usual, we can model mutable array update methods like
append
andsubscript
which take an address to an ArraySymbolicValue, by creating a new ArraySymbolicValue with the necessary changes and replacing it in the SymbolicMemoryObject corresponding to the address. So the ArraySymbolicValue will remain mostly immutable much like other Symbolic values (despite containing an address), and the inner address of the buffer is only exposed bygetAddressOfStorage
which is used only during initialization.