Skip to content

constant interpreter: addresses and memory #19657

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
Dec 15, 2018
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
77 changes: 76 additions & 1 deletion include/swift/SIL/SILConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class SerializedSILLoader;

struct APIntSymbolicValue;
struct ArraySymbolicValue;
struct DerivedAddressValue;
struct EnumWithPayloadSymbolicValue;
struct SymbolicValueMemoryObject;
struct UnknownSymbolicValue;

extern llvm::cl::opt<unsigned> ConstExprLimit;
Expand Down Expand Up @@ -69,6 +71,10 @@ enum class UnknownReason {
class SymbolicValue {
private:
enum RepresentationKind {
/// This value is an alloc stack that has not (yet) been initialized
/// by flow-sensitive analysis.
Copy link
Contributor

Choose a reason for hiding this comment

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

@marcrasi "that is has" -> that has

RK_UninitMemory,

/// This symbolic value cannot be determined, carries multiple values
/// (i.e., varies dynamically at the top level), or is of some type that
/// we cannot analyze and propagate (e.g. NSObject).
Expand All @@ -92,6 +98,12 @@ class SymbolicValue {
/// This value is a struct or tuple of constants. This is tracked by the
/// "aggregate" member of the value union.
RK_Aggregate,

/// This represents the address of a memory object.
RK_DirectAddress,
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 phrase "direct reference to" needed in the comment? Could we instead say "This represents the address of a memory object". Is it missing something?


/// This represents an index *into* a memory object.
RK_DerivedAddress,
};

union {
Expand All @@ -115,6 +127,14 @@ class SymbolicValue {
/// When this SymbolicValue is of "Aggregate" kind, this pointer stores
/// information about the array elements and count.
const SymbolicValue *aggregate;

/// When the representationKind is "DirectAddress", this pointer is the
/// memory object referenced.
SymbolicValueMemoryObject *directAddress;

/// When this SymbolicValue is of "DerivedAddress" kind, this pointer stores
/// information about the memory object and access path of the access.
DerivedAddressValue *derivedAddress;
} value;

RepresentationKind representationKind : 8;
Expand Down Expand Up @@ -150,6 +170,13 @@ class SymbolicValue {

/// This can be an array, struct, tuple, etc.
Aggregate,

/// This value represents the address of, or into, a memory object.
Address,

/// These values are generally only seen internally to the system, external
/// clients shouldn't have to deal with them.
UninitMemory
};

/// For constant values, return the type classification of this value.
Expand All @@ -158,7 +185,7 @@ class SymbolicValue {
/// Return true if this represents a constant value.
bool isConstant() const {
auto kind = getKind();
return kind != Unknown;
return kind != Unknown && kind != UninitMemory;
}

static SymbolicValue getUnknown(SILNode *node, UnknownReason reason,
Expand All @@ -177,6 +204,12 @@ class SymbolicValue {
/// Return the reason an unknown result was generated.
UnknownReason getUnknownReason() const;

static SymbolicValue getUninitMemory() {
SymbolicValue result;
result.representationKind = RK_UninitMemory;
return result;
}

static SymbolicValue getMetatype(CanType type) {
SymbolicValue result;
result.representationKind = RK_Metatype;
Expand Down Expand Up @@ -216,6 +249,25 @@ class SymbolicValue {

ArrayRef<SymbolicValue> getAggregateValue() const;

/// Return a symbolic value that represents the address of a memory object.
static SymbolicValue getAddress(SymbolicValueMemoryObject *memoryObject) {
SymbolicValue result;
result.representationKind = RK_DirectAddress;
result.value.directAddress = memoryObject;
return result;
}

/// Return a symbolic value that represents the address of a memory object
/// indexed by a path.
static SymbolicValue getAddress(SymbolicValueMemoryObject *memoryObject,
ArrayRef<unsigned> indices,
ASTContext &astContext);

/// Return the memory object of this reference along with any access path
/// indices involved.
SymbolicValueMemoryObject *
getAddressValue(SmallVectorImpl<unsigned> &accessPath) const;

//===--------------------------------------------------------------------===//
// Helpers

Expand Down Expand Up @@ -247,6 +299,29 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, SymbolicValue val) {
return os;
}

/// This is a representation of a memory object referred to by an address.
/// Memory objects may be mutated over their lifetime, but their overall type
/// remains the same.
struct SymbolicValueMemoryObject {
Type getType() const { return type; }

SymbolicValue getValue() const { return value; }
void setValue(SymbolicValue newValue) { value = newValue; }

/// Create a new memory object whose overall type is as specified.
static SymbolicValueMemoryObject *create(Type type, SymbolicValue value,
ASTContext &astContext);

private:
const Type type;
SymbolicValue value;

SymbolicValueMemoryObject(Type type, SymbolicValue value)
: type(type), value(value) {}
SymbolicValueMemoryObject(const SymbolicValueMemoryObject &) = delete;
void operator=(const SymbolicValueMemoryObject &) = delete;
};

} // end namespace swift

#endif
127 changes: 126 additions & 1 deletion lib/SIL/SILConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc,
void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const {
os.indent(indent);
switch (representationKind) {
case RK_UninitMemory:
os << "uninit\n";
return;
case RK_Unknown: {
os << "unknown(" << (int)getUnknownReason() << "): ";
getUnknownNode()->dump();
Expand Down Expand Up @@ -76,6 +79,16 @@ void SymbolicValue::print(llvm::raw_ostream &os, unsigned indent) const {
return;
}
}
case RK_DirectAddress:
case RK_DerivedAddress: {
SmallVector<unsigned, 4> accessPath;
SymbolicValueMemoryObject *memObject = getAddressValue(accessPath);
os << "Address[" << memObject->getType() << "] ";
interleave(accessPath.begin(), accessPath.end(),
[&](unsigned idx) { os << idx; }, [&]() { os << ", "; });
os << "\n";
break;
}
}
}

Expand All @@ -85,6 +98,8 @@ void SymbolicValue::dump() const { print(llvm::errs()); }
/// multiple forms for efficiency, but provide a simpler interface to clients.
SymbolicValue::Kind SymbolicValue::getKind() const {
switch (representationKind) {
case RK_UninitMemory:
return UninitMemory;
case RK_Unknown:
return Unknown;
case RK_Metatype:
Expand All @@ -96,6 +111,9 @@ SymbolicValue::Kind SymbolicValue::getKind() const {
case RK_Integer:
case RK_IntegerInline:
return Integer;
case RK_DirectAddress:
case RK_DerivedAddress:
return Address;
}
}

Expand All @@ -105,6 +123,7 @@ SymbolicValue
SymbolicValue::cloneInto(ASTContext &astContext) const {
auto thisRK = representationKind;
switch (thisRK) {
case RK_UninitMemory:
case RK_Unknown:
case RK_Metatype:
case RK_Function:
Expand All @@ -120,9 +139,30 @@ SymbolicValue::cloneInto(ASTContext &astContext) const {
results.push_back(elt.cloneInto(astContext));
return getAggregate(results, astContext);
}
case RK_DirectAddress:
case RK_DerivedAddress: {
SmallVector<unsigned, 4> accessPath;
auto *memObject = getAddressValue(accessPath);
auto *newMemObject = SymbolicValueMemoryObject::create(
memObject->getType(), memObject->getValue(), astContext);
return getAddress(newMemObject, accessPath, astContext);
}
}
}

//===----------------------------------------------------------------------===//
// SymbolicValueMemoryObject implementation
//===----------------------------------------------------------------------===//

SymbolicValueMemoryObject *
SymbolicValueMemoryObject::create(Type type, SymbolicValue value,
ASTContext &astContext) {
auto *result = astContext.Allocate(sizeof(SymbolicValueMemoryObject),
alignof(SymbolicValueMemoryObject));
new (result) SymbolicValueMemoryObject(type, value);
return (SymbolicValueMemoryObject *)result;
}

//===----------------------------------------------------------------------===//
// Integers
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -225,7 +265,7 @@ struct alignas(SourceLoc) UnknownSymbolicValue final
ASTContext &astContext) {
auto byteSize =
UnknownSymbolicValue::totalSizeToAlloc<SourceLoc>(elements.size());
auto rawMem = astContext.Allocate(byteSize, alignof(UnknownSymbolicValue));
auto *rawMem = astContext.Allocate(byteSize, alignof(UnknownSymbolicValue));

// Placement-new the value inside the memory we just allocated.
auto value = ::new (rawMem) UnknownSymbolicValue(
Expand Down Expand Up @@ -279,6 +319,91 @@ UnknownReason SymbolicValue::getUnknownReason() const {
return value.unknown->reason;
}

//===----------------------------------------------------------------------===//
// Addresses
//===----------------------------------------------------------------------===//

namespace swift {

/// This is the representation of a derived address. A derived address refers
/// to a memory object along with an access path that drills into it.
struct DerivedAddressValue final
: private llvm::TrailingObjects<DerivedAddressValue, unsigned> {
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't need to happen in this PR, but would be better to use IndexTrieNode from SILOptimizer/Utils/IndexTrie for the access path instead. With that you can share common access paths between different values to save memory and copying.

friend class llvm::TrailingObjects<DerivedAddressValue, unsigned>;

SymbolicValueMemoryObject *memoryObject;

/// This is the number of indices in the derived address.
const unsigned numElements;

static DerivedAddressValue *create(SymbolicValueMemoryObject *memoryObject,
ArrayRef<unsigned> elements,
ASTContext &astContext) {
auto byteSize =
DerivedAddressValue::totalSizeToAlloc<unsigned>(elements.size());
auto *rawMem = astContext.Allocate(byteSize, alignof(DerivedAddressValue));

// Placement initialize the object.
auto dav =
::new (rawMem) DerivedAddressValue(memoryObject, elements.size());
std::uninitialized_copy(elements.begin(), elements.end(),
dav->getTrailingObjects<unsigned>());
return dav;
}

/// Return the access path for this derived address, which is an array of
/// indices drilling into the memory object.
ArrayRef<unsigned> getElements() const {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this comment be reworded and made a bit more detailed? I suppose 'getElements' returns the access path of this derived address, which is expected to be a sequence of constant unsigned ints. Perhaps, mentioning that would be better.

return {getTrailingObjects<unsigned>(), numElements};
}

// This is used by the llvm::TrailingObjects base class.
size_t numTrailingObjects(OverloadToken<unsigned>) const {
return numElements;
}

private:
DerivedAddressValue() = delete;
DerivedAddressValue(const DerivedAddressValue &) = delete;
DerivedAddressValue(SymbolicValueMemoryObject *memoryObject,
unsigned numElements)
: memoryObject(memoryObject), numElements(numElements) {}
};
} // end namespace swift

/// Return a symbolic value that represents the address of a memory object
/// indexed by a path.
SymbolicValue SymbolicValue::getAddress(SymbolicValueMemoryObject *memoryObject,
ArrayRef<unsigned> indices,
ASTContext &astContext) {
if (indices.empty())
return getAddress(memoryObject);

auto dav = DerivedAddressValue::create(memoryObject, indices, astContext);
SymbolicValue result;
result.representationKind = RK_DerivedAddress;
result.value.derivedAddress = dav;
return result;
}

/// Return the memory object of this reference along with any access path
/// indices involved.
SymbolicValueMemoryObject *
SymbolicValue::getAddressValue(SmallVectorImpl<unsigned> &accessPath) const {
assert(getKind() == Address);

accessPath.clear();
if (representationKind == RK_DirectAddress)
return value.directAddress;
assert(representationKind == RK_DerivedAddress);

auto *dav = value.derivedAddress;

// The first entry is the object ID, the rest are indices in the accessPath.
accessPath.assign(dav->getElements().begin(), dav->getElements().end());
return dav->memoryObject;
}

//===----------------------------------------------------------------------===//
// Higher level code
//===----------------------------------------------------------------------===//
Expand Down
Loading