In addition to __builtin_assume_aligned, GCC also supports an assume_aligned attribute which specifies the alignment (and optional offset) of the function's return value. Building on the llvm.assume patches, and also functionality added in http://reviews.llvm.org/D149, this patch implements support for the assume_aligned attribute in Clang.
Details
Diff Detail
Event Timeline
Somewhat unrelated to this patch in particular, but we should add a UBSan check to catch failed assumptions.
include/clang/Basic/Attr.td | ||
---|---|---|
867 | These should probably both be ExprArguments, in order to support dependent alignment/offset expressions in templates. | |
868 | Please add documentation. | |
lib/CodeGen/CGExpr.cpp | ||
3352 | Please add parens around this if. | |
lib/Sema/SemaDeclAttr.cpp | ||
1219–1227 | I think there's a helper function for this sequence of checks/diagnostics. If not, there should be. | |
1232–1235 | Do you really need to check that the Offset fits into an uint32_t? Truncating the offset to the width of Alignment seems correct in all cases. |
lib/CodeGen/CGExpr.cpp | ||
---|---|---|
3352 | s/parens/braces/ =) |
lib/CodeGen/CGExpr.cpp | ||
---|---|---|
3350–3364 | Is this really the right place for this code, rather than in the called EmitCall function? Various places call the lower-level overload directly. | |
lib/Sema/SemaDeclAttr.cpp | ||
1104–1105 | Update this comment, please. | |
1104–1105 | Is this really appropriate for your attribute? (It makes almost no sense for __attribute__((nonnull)) either, but I couldn't find anyone at Apple to tell me what the relevant radar says, so I have no idea why we have this "feature".) | |
1234 | You don't need the type-dependency check here. | |
1256 | Likewise here. | |
lib/Sema/SemaTemplateInstantiateDecl.cpp | ||
203–205 ↗ | (On Diff #13228) | You shouldn't have these 'is dependent' checks here; the other code above and below is wrong to include them. Testcase: template<typename T> __attribute__((assume_aligned(sizeof(int(T()))))) T *f(); void *p = f<void>(); This should result in a hard error, because int(void()) is invalid, but I think you'll accept it, because sizeof(int(T())) is not value-dependent, so you never actually perform the substitution. |
lib/CodeGen/CGExpr.cpp | ||
---|---|---|
3350–3364 | When I first looked, I thought that there were no other relevant callers, but maybe EmitCallAndReturnForThunk and EmitForwardingCallToLambda would need this handling too? Because of the way that the lower-level EmitCall is structured, I'd need to wrap it (it has too many early returns). What would I call it? | |
lib/Sema/SemaDeclAttr.cpp | ||
1104–1105 | Nice :-) -- Honestly, I don't have a strong opinion regarding whether or not we strip references, but I like the idea of consistency between the two attributes. The set of returned pointers about which I can assert something about the alignment should be the same as that about which I can assert a nonnull property. That having been said, it looks like the CodeGen for returns_nonnull that adds the nonnull IR attribute (and the associated UBSan code) does not handle references-to-pointers correctly. I'll fix that as a follow-up too. | |
lib/Sema/SemaTemplateInstantiateDecl.cpp | ||
203–205 ↗ | (On Diff #13228) | Interestingly, the current code does generate an error: invalid application of 'sizeof' to a function type Removing the type-dependent checks does not seem to change any of the current behavior. |
lib/CodeGen/CGExpr.cpp | ||
---|---|---|
3350–3364 | EmitCXXMemberOrOperatorCall, EmitCXXMemberPointerCallExpr, and EmitNewDeleteCall should also get this handling. Maybe factor out the switch that produces the returned RValue from the call? | |
lib/Sema/SemaDeclAttr.cpp | ||
1104–1105 | It seems really weird for either of these to look through references to the referenced pointer. And it's ambiguous: does the attribute apply to the reference or to the pointer that the reference refers to? (For the nonnull attribute, GCC and Clang make different choices here, at least in the cases where you can persuade GCC not to reject the attribute!) | |
lib/Sema/SemaTemplateInstantiateDecl.cpp | ||
203–205 ↗ | (On Diff #13228) | Ha, sorry, bitten by a parse ambiguity. Six closing parens wasn't enough; let's go up to seven: template<typename T> __attribute__((assume_aligned(sizeof((int(T())))))) T *f(); void *p = f<void>(); |
lib/Sema/SemaDeclAttr.cpp | ||
---|---|---|
1104–1105 | Indeed, unlike nonnull, this does become ambiguous. Perhaps it is best to prohibit it? How do Clang and GCC make different choices for nonnull; is that a bug? Because a reference cannot be null, it would seem that it should apply to the pointer being bound. |
lib/Sema/SemaDeclAttr.cpp | ||
---|---|---|
1104–1105 | Here's what I can piece together:
It seems our options are to either not support assume_aligned on references (which would be a shame, because it's natural and useful there just as it is for pointers), or make nonnull and assume_aligned inconsistent on pointers-to-references, or remove our (dubious, IMO) extension of the GNU nonnull semantics. I'm not happy with the third option since I don't know what the motivation for the extension is nor how many people might be relying on it, but either of the first two seem fine to me. I don't think the connection between nonnull and assume_aligned is strong enough that the difference would be jarring for people. |
These should probably both be ExprArguments, in order to support dependent alignment/offset expressions in templates.