This is an archive of the discontinued LLVM Phabricator instance.

[os_log] Improve the way we extend the lifetime of objects passed to __builtin_os_log_format
Needs ReviewPublic

Authored by ahatanak on Jul 16 2020, 7:08 PM.

Details

Summary

This patch fixes several shortcomings of the way we currently extend the lifetime of objects passed to calls to __builtin_os_log_format:

  • Sema doesn't diagnose code that jumps into or out of the scope of a lifetime-extended object.
  • Lifetime of objects in comma operators or expressions for ObjC property access isn't extended.
  • We extend the lifetime of an object by emitting an extra pair of retain and release calls instead of delaying the release call.
  • Calls to clang.arc.use are emitted on exception paths even though they are needed only on normal paths.

rdar://problem/62610583

Diff Detail

Event Timeline

ahatanak created this revision.Jul 16 2020, 7:08 PM

Why is the lifetime extended to the enclosing block scope anyway? I understand why we need a clang.arc.use — the optimizer can't reasonably understand that the object has to live within the buffer — but isn't the buffer only used for the duration of the call? Why is extension necessary?

clang/include/clang/Serialization/ASTBitCodes.h
1987

This seems like an excessively general name for what's happening here.

The use case for this is a macro in which the call to __builtin_os_log_format that writes to the buffer and the call that uses the buffer appear in two different statements. For example:

__builtin_os_log_format(buf, "%@", getObj());
...
use_buffer(buf);

The object returned by the call to getObj has to be kept alive until use_buffer is called, but currently it gets destructed at the end of the full expression. I think an alternate solution would be to provide users a means to tell ARC optimizer not to move the release call for a local variable past any calls, i.e., something that is stricter than NS_VALID_UNTIL_END_OF_SCOPE, but that places more burden on the users.

In the os_log macro, the result of the call to __builtin_os_log_format is passed directly to the call that uses the buffer, so it doesn't require any lifetime extension as you pointed out.

martong added inline comments.Jul 27 2020, 9:15 AM
clang/lib/AST/ASTImporter.cpp
8000

I am not sure how this hunk is related to os_log. Maybe you mistakenly mixed another unrelated change into this patch?

The use case for this is a macro in which the call to __builtin_os_log_format that writes to the buffer and the call that uses the buffer appear in two different statements. For example:

__builtin_os_log_format(buf, "%@", getObj());
...
use_buffer(buf);

The object returned by the call to getObj has to be kept alive until use_buffer is called, but currently it gets destructed at the end of the full expression. I think an alternate solution would be to provide users a means to tell ARC optimizer not to move the release call for a local variable past any calls, i.e., something that is stricter than NS_VALID_UNTIL_END_OF_SCOPE, but that places more burden on the users.

In the os_log macro, the result of the call to __builtin_os_log_format is passed directly to the call that uses the buffer, so it doesn't require any lifetime extension as you pointed out.

So are there actually any uses that take advantage of these semantics? Because as I understand it, this builtin exists entirely to support the os_log macro. If that macro doesn't need the extension semantics, let's just not do them.