This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Make __builtin_debugtrap() a sink
AbandonedPublic

Authored by xazax.hun on Nov 2 2017, 7:52 AM.

Details

Summary

For some reason, __builtin_debugtrap is not a sink for the analyzer. I also added some test cases to demonstrate that __builtin_trap and __builtin_unreachable are handled properly. The former is not marked as noreturn while the last two are. See Builtins.def for details. It was made non-noreturn deliberately, see https://reviews.llvm.org/rL166345
I have, however, no idea why.

Diff Detail

Event Timeline

xazax.hun created this revision.Nov 2 2017, 7:52 AM
xazax.hun edited the summary of this revision. (Show Details)Nov 2 2017, 7:52 AM

In the meantime, I found this discussion: http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20121015/153735.html
It looks like the reasoning behind this intrinsic not being noreturn is that the user might continue the execution in the debugger.
I think the main reason behind not marking this noreturn is to not allow the optimizer to remove code based on the usage of this builtin and thus prohibit debugging certain part of the code.
But it still might make sense to treat this builtin as a sink in the analyzer. What do you think?

dcoughlin edited edge metadata.Nov 2 2017, 1:12 PM

I believe that the intent of __builtin_debugtrap() is to provide an in-source mechanism so that the developer can trap into the debugger and then continue execution. For example, in the Swift codebase this is used in combination with a debug flag to break into the debugger at the beginning a specified pass. https://github.com/apple/swift/blob/master/lib/SILOptimizer/PassManager/PassManager.cpp#L339

Since the intent is to continue after the trap, I don't think the analyzer should treat it as a sink.

Did you get requests from users to treat it like a sink? Or false positives that would be resolved if were a sink? Or is this something you noticed by inspection?

xazax.hun abandoned this revision.Nov 2 2017, 1:32 PM

I believe that the intent of __builtin_debugtrap() is to provide an in-source mechanism so that the developer can trap into the debugger and then continue execution. For example, in the Swift codebase this is used in combination with a debug flag to break into the debugger at the beginning a specified pass. https://github.com/apple/swift/blob/master/lib/SILOptimizer/PassManager/PassManager.cpp#L339

Oh, ok. I think this use is very convincing.

Since the intent is to continue after the trap, I don't think the analyzer should treat it as a sink.

I see. I misinterpreted slightly the intent. From the discussion I linked:

This patch is to fix radar://8426430. It is about llvm support of
__builtin_debugtrap()
which is supposed to consistently raise SIGTRAP across all systems. In
contrast,
__builtin_trap() behave differently on different systems. e.g. it raises
SIGTRAP on ARM, and
SIGILL on X86. The purpose of __builtin_debugtrap() is to consistently
provide "trap"
functionality, in the mean time preserve the compatibility with on gcc on
__builtin_trap().

So according to my interpretation __builtin_debugtrap() is not only a way to express that the user would like to continue with a debugger but also to provide consistent behavior across platforms. Also, I assumed this is mainly used for cases where something already went wrong, so it might make sense to suppress results on that path which might be the side effect of error that triggered to take that path in the first place. But the PassManager example you provided proves this wrong.

Did you get requests from users to treat it like a sink? Or false positives that would be resolved if were a sink? Or is this something you noticed by inspection?

I was just surprised by this behavior when I saw this happening and wanted to make sure whether this is intended. I did not get any user request.