Skip to content
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

Fix a bug in the LLVM memory model (with SMT array backed allocations) #603

Merged
merged 6 commits into from
Feb 26, 2021

Conversation

travitch
Copy link
Contributor

Most of this commit is additional comments that try to clarify some of the
things that came up while diagnosing the bug. The actual fix is in
writeMemWithAllocationCheck.

The observed behavior was that a test case (in another repository) generated
formulas where reads and writes to SMT array-backed allocations did not refer to
the same byte offsets, when they should have. Specifically, the scenario was
that the test case has a single large allocation backed by an SMT array. The
function exhibiting the problem started with a pointer to the middle of that
allocation. Some of the reads and writes were properly in the middle of the
allocation (with a large offset appearing in SMT array select operations),
while others were using inappropriate low offsets (around zero).

The problem was a bug in the optimization in the memory model that coalesces
writes to SMT array backed allocations (which reduces the number of muxes
generated in many cases). The goal of this optimization (which is now
documented) is to turn sequential updates to an SMT array backed allocation into
a single entry in the write log by pretending that the small write actually
overwrites the entire SMT array with new values (which are the original contents
of the SMT array plus the new values just written, expressed as an SMT array
update).

To do this correctly, the updated array must overwrite the entire
allocation (i.e., starting at offset 0 within the block). The bug was that the
array was being written at the offset in the pointer to the start of the new
write.
In the case outlined above, this meant that the new array was spliced
over the old one halfway through the allocation. This had a few consequences:

  • The offsets of reads no longer matched the offsets of their corresponding
    writes (due to the intricacies of how read offsets are calculated in SMT array
    allocations)
  • The expected optimized path for array reads was no longer taken, as writes
    looked like they spanned two different array allocations
  • The ultimate SMT queries actually had no update operations in them at all
    because the reads looked independent of all of the writes (due to the
    misalignment)

Most of this commit is additional comments that try to clarify some of the
things that came up while diagnosing the bug.  The actual fix is in
`writeMemWithAllocationCheck`.

The observed behavior was that a test case (in another repository) generated
formulas where reads and writes to SMT array-backed allocations did not refer to
the same byte offsets, when they should have.  Specifically, the scenario was
that the test case has a single large allocation backed by an SMT array.  The
function exhibiting the problem started with a pointer to the middle of that
allocation.  Some of the reads and writes were properly in the middle of the
allocation (with a large offset appearing in SMT array `select` operations),
while others were using inappropriate low offsets (around zero).

The problem was a bug in the optimization in the memory model that coalesces
writes to SMT array backed allocations (which reduces the number of muxes
generated in many cases).  The goal of this optimization (which is now
documented) is to turn sequential updates to an SMT array backed allocation into
a single entry in the write log by pretending that the small write actually
overwrites the entire SMT array with new values (which are the original contents
of the SMT array plus the new values just written, expressed as an SMT array
`update`).

To do this correctly, the updated array must overwrite the entire
allocation (i.e., starting at offset 0 within the block).  *The bug was that the
array was being written at the offset in the pointer to the start of the new
write.*  In the case outlined above, this meant that the new array was spliced
over the old one halfway through the allocation.  This had a few consequences:
- The offsets of reads no longer matched the offsets of their corresponding
  writes (due to the intricacies of how read offsets are calculated in SMT array
  allocations)
- The expected optimized path for array reads was no longer taken, as writes
  looked like they spanned two different array allocations
- The ultimate SMT queries actually had no `update` operations in them at all
  because the reads looked independent of all of the writes (due to the
  misalignment)
Copy link
Member

@kquick kquick left a comment

Choose a reason for hiding this comment

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

The additional documentation really helps, thanks!

@robdockins
Copy link
Contributor

Nice catch, that's a subtle bug! How hard would it be to add a test case for this situation?

@travitch
Copy link
Contributor Author

I tried - the previous test I added attempted to trigger this (a simplified version of the real program), but some of the optimizations in the memory model ended up eliminating all of the actual memory options. Andrei said he thinks he can cook up a test case that will tickle it.

@andreistefanescu
Copy link
Contributor

@travitch the problem was that asMemAllocationArrayStore did not check that the pointer offset is 0, thus incorrectly identifying an array store at an incorrect pointer as covering the entire allocation (yes, in this case, two wrongs make a right).

@andreistefanescu andreistefanescu merged commit dc5a5ae into master Feb 26, 2021
@andreistefanescu andreistefanescu deleted the tr/array-mem-model-fix branch February 26, 2021 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants