This is an archive of the discontinued LLVM Phabricator instance.

[DebugInfo] Check size of variable in ConvertDebugDeclareToDebugValue
ClosedPublic

Authored by bjope on Jun 11 2018, 6:55 AM.

Details

Summary

Do not convert a DbgDeclare to DbgValue if the store
instruction only refer to a fragment of the variable
described by the DbgDeclare.

Problem was seen when for example having an alloca for an
array or struct, and there were stores to individual elements.
In the past we inserted a DbgValue intrinsics for each store,
just as if the store wrote the whole variable.

When handling store instructions we insert a DbgValue that
indicates that the variable is "undefined", as we do not know
which part of the variable that is updated by the store.

When ConvertDebugDeclareToDebugValue is used with a load/phi
instruction we assert that the referenced value is large enough
to cover the whole variable. Afaict this should be true for all
scenarios where those methods are used on trunk. If the assert
blows in the future I guess we could simply skip to insert a
dbg.value instruction.

In the future I think we should examine which part of the variable
that is accessed, and add a DbgValue instrinsic with an appropriate
DW_OP_LLVM_fragment expression.

Diff Detail

Repository
rL LLVM

Event Timeline

bjope created this revision.Jun 11 2018, 6:55 AM
aprantl added inline comments.Jun 11 2018, 8:47 AM
lib/Transforms/Utils/Local.cpp
1231 ↗(On Diff #150744)

... by a llvm.dbg intrinsic.

1232 ↗(On Diff #150744)

I think that getSizeOfFragment() would be more a more accurate name?

1237 ↗(On Diff #150744)

Unknown

Is this really unreachable, i.e.: would the Verifier reject a variable with a size of zero? In C (but not C++) I believe the size of struct Empty {}; is zero. In Swift you can also have zero-sized types.

1242 ↗(On Diff #150744)

How about: fragmentCoversEntireVariable()

1272 ↗(On Diff #150744)

This part seems reasonable (though sort of unsatisfying :-).

aprantl requested changes to this revision.Jun 11 2018, 8:47 AM
This revision now requires changes to proceed.Jun 11 2018, 8:47 AM
bjope added inline comments.Jun 11 2018, 9:03 AM
lib/Transforms/Utils/Local.cpp
1237 ↗(On Diff #150744)

We only end up in the unreachable if getSizeInBits failed to determine the size (it returns an Optional<uint64_t>). So *VarSize could still be zero.

But maybe we need to handle the situation more gracefully. I should probably let this method return an Optional<uint64_t> as well, and then deal with it somehow in IsVariableCoveredByType.

1242 ↗(On Diff #150744)

In my world the variable and the fragment is both related to the same thing.
But I can change it to: valueCoversEntireFragment(Type *Ty, DbgInfoIntrinsic *DII)

aprantl added inline comments.Jun 11 2018, 9:26 AM
lib/Transforms/Utils/Local.cpp
1237 ↗(On Diff #150744)

Oh, I didn't realize that this was an Optional! That said, the new code must not throw an unreachable for any input that is accepted by the Verifier. Both making the new code more permissive or tightening the Verifier are acceptable solutions.

1242 ↗(On Diff #150744)

That name makes sense to me, perhaps the doxygen comment can be improved, too, by removing the negation.

bjope updated this revision to Diff 150782.Jun 11 2018, 9:59 AM

Updated after comments from aprantl.

Also added the new getFragmentSize() method to class DbgInfoIntrinsic.

bjope updated this revision to Diff 150784.Jun 11 2018, 10:02 AM

Additional cleanups.

aprantl added inline comments.Jun 11 2018, 10:12 AM
include/llvm/IR/IntrinsicInst.h
98 ↗(On Diff #150784)

getFragmentSizeInBits() ?

I'm still curious whether it makes sense to support the case where this returns None, or if we could add a Verifier check that is effectively assert_di(getFragmentSize()). Doesn't need to be in this commit though.

lib/Transforms/Utils/Local.cpp
1233 ↗(On Diff #150784)

Are all functions in this file starting with an upper case letter? Otherwise it would be better to follow the LLVM naming conventions. Perhaps also just doing this in a later NFC commit.

1310 ↗(On Diff #150784)

I think we usually spell out the error, not the assertion: load is not loading the full variable fragment

bjope updated this revision to Diff 150931.Jun 12 2018, 5:13 AM

More updates after review.

aprantl accepted this revision.Jun 12 2018, 9:12 AM

Few more small nitpicks inline but otherwise LGTM.

include/llvm/IR/IntrinsicInst.h
96 ↗(On Diff #150931)

I think the last comma is not necessary in this sentence.

lib/IR/IntrinsicInst.cpp
54 ↗(On Diff #150931)

Why not just
return getVariable()->getSizeInBits() ?

lib/Transforms/Utils/Local.cpp
1258 ↗(On Diff #150931)

variable's

This revision is now accepted and ready to land.Jun 12 2018, 9:12 AM
bjope updated this revision to Diff 151134.Jun 13 2018, 5:43 AM

After some more testing I discovered a case when the added assert in

void llvm::ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
                                           PHINode *APN, DIBuilder &Builder)

failed.

I think it is more correct to use getTypeStoreSizeInBits() instead of getTypeSizeInBits() in valueCoversEntireFragment(), as the store size tells the number of bits that would be overwritten if writing the value to memory (and the dbg.declare that we want to replace describes memory).

So this update is adding a test case that would trigger the assert, and an update in valueCoversEntireFragment() to use the store size.

@adrian: Is this still good?

bjope added inline comments.Jun 13 2018, 5:48 AM
include/llvm/IR/IntrinsicInst.h
98 ↗(On Diff #150784)

I don't know if we can do that check in the verifier. I guess it is bad if we can't determine the size of the variable (as we would lose debug info).

All I know is that there is a comment about "broken types" in the implementation of DIVariable::getSizeInBits():

Optional<uint64_t> DIVariable::getSizeInBits() const {
  // This is used by the Verifier so be mindful of broken types.
  ...

I have no idea if those "broken types" only exist in handwritten / poorly stripped IR, or if they are common in real use cases.

aprantl accepted this revision.Jun 13 2018, 10:10 AM

Still works for me, but please add a comment explaining why getStoreSize is used here and what that means.

include/llvm/IR/IntrinsicInst.h
98 ↗(On Diff #150784)

That "broken types" comment refers to the need for the Verifier to be robust against malformed debug metadata. The Verifier should not crash in the presence of malformed input, and it also shouldn't accept malformed input. Given the many embedded uses of LLVM both of these situations could be conceivably security vulnerabilities.

This revision was automatically updated to reflect the committed changes.

An assertion introduced by this patch is failing on a build bot: http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-autoconf/builds/25013

Please take a look.

bjope added a comment.Jun 14 2018, 9:07 AM

An assertion introduced by this patch is failing on a build bot: http://lab.llvm.org:8011/builders/sanitizer-x86_64-linux-autoconf/builds/25013

Please take a look.

Ok, I'll revert and then try to reproduce the fault. Thanks for the notification!