-
Notifications
You must be signed in to change notification settings - Fork 10.5k
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
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 |
---|---|---|
|
@@ -30,7 +30,9 @@ class SerializedSILLoader; | |
|
||
struct APIntSymbolicValue; | ||
struct ArraySymbolicValue; | ||
struct DerivedAddressValue; | ||
struct EnumWithPayloadSymbolicValue; | ||
struct SymbolicValueMemoryObject; | ||
struct UnknownSymbolicValue; | ||
|
||
extern llvm::cl::opt<unsigned> ConstExprLimit; | ||
|
@@ -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. | ||
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). | ||
|
@@ -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, | ||
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. 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 { | ||
|
@@ -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; | ||
|
@@ -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. | ||
|
@@ -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, | ||
|
@@ -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; | ||
|
@@ -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 | ||
|
||
|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
@@ -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; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -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: | ||
|
@@ -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; | ||
} | ||
} | ||
|
||
|
@@ -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: | ||
|
@@ -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 | ||
//===----------------------------------------------------------------------===// | ||
|
@@ -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( | ||
|
@@ -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> { | ||
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 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 { | ||
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. 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 | ||
//===----------------------------------------------------------------------===// | ||
|
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 "that is has" -> that has