This is an archive of the discontinued LLVM Phabricator instance.

Addition of __attribute__((pass_object_size)) to Clang
ClosedPublic

Authored by george.burgess.iv on Sep 29 2015, 11:46 AM.

Details

Summary

This was a fun patch :)

This patch adds the parameter attribute pass_object_size to Clang. The design doc for this attribute is available here: https://docs.google.com/document/d/14TwNLxFL_jhiYn7_kppNUDiaTopRAIY5bc-B8NcnKMM/edit?usp=sharing

General notes:

  • Never mangled things before, so I'm open to any suggestions on how to make the mangling for pass_object_size better :)
  • There's a tiny diagnostics bugfix in here -- I'm happy to make that its own patch. (See: tryGetFunctionProtoType in SemaOverload.cpp)
  • A lot of the additions to Sema (anything that's a boolean named like TakingCandidateAddress) are to improve diagnostics on templated functions with pass_object_size on their parameters. I'm happy to simplify the diff a bit if we think that the improved diag ("functions with pass_object_size params cannot have their address taken" vs "assigning to 'int (*)(void *)' from incompatible type 'int (void *const pass_object_size)'") isn't worth the extra cruft.

@rsmith: Other than the name change, the design has changed (since the last time we talked) such that having pass_object_size(N) on some parameter P makes the function act like there's an enable_if that disables the function if we can't determine the object size of P at the callsite. More concretely:

  • One can no longer take the address of a function with pass_object_size
  • If an overload candidate parameter with pass_object_size on it cannot have its object size determined, then the candidate is not viable.
    • If there's no overloading going on at all (e.g. in C), it's still an error to call a function with pass_object_size on one or more of its params if the object size of that param can't be determined.

Diff Detail

Repository
rL LLVM

Event Timeline

george.burgess.iv retitled this revision from to Addition of __attribute__((pass_object_size)) to Clang.
george.burgess.iv updated this object.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added subscribers: cfe-commits, rsmith.
rsmith edited edge metadata.Nov 5 2015, 6:31 PM

As discussed off-line:

  1. Can we avoid tracking the pass_object_size attributes in the canonical function type? As the only permitted use of a function with this attribute is as a direct callee in a function call, it seems like we'll always have the parameters to hand when we need to know how they affect the calling convention.
  1. In order to pass non-frontend-constant object sizes (those computed in the middle-end by @llvm.objectsize) into pass_object_size parameters, we need to keep functions viable even if the object size is not known to be a constant.
include/clang/AST/Expr.h
631–634 ↗(On Diff #37935)

... and what if the current expression isn't a pointer? Is that a precondition (function might assert / crash) or does it result in 'return false'?

Also, don't use "/// FunctionName - [...]" in new code, use "/// \brief [...]" instead.

include/clang/AST/Type.h
3110–3112 ↗(On Diff #37935)

Seems nicer to put this right after HasAnyConsumedParams. (I'm also surprised to find that we have a spare bit here...)

3131–3134 ↗(On Diff #37935)

Do we really want just flags here rather than the type value? Should int (void * __attribute__((pass_object_size(1)))) and int (void *__attribute((pass_object_size(2)))) be the same type?

include/clang/Basic/AttrDocs.td
269–270 ↗(On Diff #37935)

Rather than "not yet standardized", perhaps "subject to change" is more useful for user-facing documentation. Might be worth noting that asm("blah") can be used to avoid ABI changes if we do change the mangling.

351 ↗(On Diff #37935)

This should only apply to the function definition; the top-level const has no effect on a non-defining declaration.

352 ↗(On Diff #37935)

Missing period.

354–365 ↗(On Diff #37935)

I would expect this to result in an error: neither A nor B is viable because their conditions are non-constant.

include/clang/Basic/DiagnosticSemaKinds.td
5101 ↗(On Diff #37935)

params -> parameters

or better, something like:

cannot take address of function %0 because parameter %1 uses pass_object_size attribute
include/clang/Sema/TemplateDeduction.h
261 ↗(On Diff #37935)

Spaces around =.

lib/AST/ExprConstant.cpp
6390–6391 ↗(On Diff #37935)

Comment doesn't really add anything.

6398 ↗(On Diff #37935)

You explicitly capture WasError but don't use it. Just use [&]?

6505–6507 ↗(On Diff #37935)

Where is the handling for Type == 3 now?

9523 ↗(On Diff #37935)

I don't see where you handle types that decay to pointers (in particular, for the case where the expression has array type).

lib/AST/ItaniumMangle.cpp
501 ↗(On Diff #37935)

Use U16pass_object_size instead, per http://mentorembedded.github.io/cxx-abi/abi.html#mangling-type

I would suggest mangling this as if it were a qualifier on the type of the parameter rather than a modifier on the function type itself.

lib/AST/MicrosoftMangle.cpp
1731–1760 ↗(On Diff #37935)

@majnemer, what would be a good mangling scheme for this extension in the MS ABI?

lib/AST/Type.cpp
2813–2817 ↗(On Diff #37935)

Update this comment.

2848–2858 ↗(On Diff #37935)

This doesn't quite work: a function type with consumed parameters and a function type with pass_object_size parameters could profile the same.

lib/CodeGen/CGCall.cpp
97–99 ↗(On Diff #37935)

I don't think the "prefix" nature is relevant to this function (that's an implementation detail of the callers). "appendParameterTypes" might be a better name.

lib/Sema/SemaDeclAttr.cpp
828 ↗(On Diff #37935)

The second check here is redundant if APType is more than 2 bits wide (and looks wrong when Int is of type bool).

aaron.ballman added inline comments.Nov 11 2015, 1:52 PM
include/clang/Basic/DiagnosticSemaKinds.td
2068 ↗(On Diff #37935)

Instead of making a new diagnostic, I think it better to modify warn_attribute_pointers_only to use a %select statement (then err_attribute_pointers_only also gets updated). I would separate that into a prequel patch since it's likely to introduce a fair amount of unrelated changes to this patch.

lib/Sema/SemaDeclAttr.cpp
820 ↗(On Diff #37935)

We don't usually ignore parens and casts for attributes like this for other attributes. Is that important for you uses for some reason?

828 ↗(On Diff #37935)

I'm wondering why the magic value 3 at all? It seems like this (and the above code) should be replaced by a call to checkFunctionOrMethodParameterIndex().

828 ↗(On Diff #37935)

There should be comments explaining the magic number 3.

829 ↗(On Diff #37935)

That isn't the correct diagnostic to emit. That one is used when a function attribute that refers to a parameter (like format_arg) uses an index that does not refer to a parameter. For this one, I would probably modify err_attribute_argument_outof_range to not be specific to 'init_priority'.

george.burgess.iv edited edge metadata.
george.burgess.iv marked 22 inline comments as done.
  • Removed pass_object_size-related information from FunctionType
  • Unified enable_if/pass_object_size error handling when the address of a function with enable_if/pass_object_size is taken (this fixes bugs in enable_if and simplifies code in a number of places; will add enable_if-related tests in a followup patch, because this one is large enough as-is).
  • Added more tests
  • Otherwise addressed all comments

Thanks for all of the feedback! :)

include/clang/AST/Expr.h
631–634 ↗(On Diff #37935)

... and what if the current expression isn't a pointer? Is that a precondition (function might assert / crash) or does it result in 'return false'?

Fixed-ish. Do you think the new comment is sufficient, or would you like to see each parameter documented for consistency?

Also, don't use "/ FunctionName - [...]" in new code, use "/ \brief [...]" instead

Thanks!

include/clang/AST/Type.h
3110–3112 ↗(On Diff #37935)

No longer an issue. (I was too.)

3131–3134 ↗(On Diff #37935)

No longer an issue

include/clang/Basic/AttrDocs.td
354–365 ↗(On Diff #37935)

I agree; I have no clue why I thought this was sane. ¯\_(ツ)_/¯

include/clang/Basic/DiagnosticSemaKinds.td
2068 ↗(On Diff #37935)

There were only four small distinct changes, so I just rolled it all into this patch; I'm happy to pull it out into its own if we feel like that's a bit too much clutter.

lib/AST/ExprConstant.cpp
6505–6507 ↗(On Diff #37935)

It was moved to CodeGenFunction::emitBuiltinSizeOf. I feel it fits better there because it's more of an implementation detail of @llvm.objectsize; I'm happy to move the check back here if you disagree.

9523 ↗(On Diff #37935)

Do you have recommendations for how to test that? Code like

int fn(void *__attribute__((pass_object_size(0))));
int a[5];
int r = fn(a);

Generates an implicit Array->Pointer decay on a in fn(a), which is handled fine; so, AFAICT, the support for being handed Exprs decayable types is unnecessary. I'll just remove it.

lib/AST/ItaniumMangle.cpp
501 ↗(On Diff #37935)

WFM; changed to U17pass_object_size${Type}.

lib/AST/Type.cpp
2813–2817 ↗(On Diff #37935)

No longer necessary now that we're leaving FPTs alone.

2848–2858 ↗(On Diff #37935)

Oooh, nice catch. Thankfully, this change isn't necessary now :)

lib/Sema/SemaDeclAttr.cpp
820 ↗(On Diff #37935)

Not important -- it's gone now. :)

828 ↗(On Diff #37935)

I'm wondering why the magic value 3 at all? It seems like this (and the above code) should be replaced by a call to checkFunctionOrMethodParameterIndex().

Marking this as done because checkFunctionOrMethodParameterIndex doesn't seem to accomplish our goal.

There should be comments explaining the magic number 3.

The docs for pass_object_size(N) state that N is passed as the second parameter to __builtin_object_size, which means that N must be in the range [0, 3]. I'll add a comment, but given your other comment, it looks like there may have been a miscommunication about the purpose of pass_object_size's argument. :)

829 ↗(On Diff #37935)

Ahh, that makes sense. Thanks :)

aaron.ballman added inline comments.Nov 17 2015, 10:06 AM
lib/Sema/SemaDeclAttr.cpp
829 ↗(On Diff #40185)

I'm wondering why the magic value 3 at all? It seems like this (and the above code) should be replaced by a call to checkFunctionOrMethodParameterIndex().

Marking this as done because checkFunctionOrMethodParameterIndex doesn't seem to accomplish our goal.

There should be comments explaining the magic number 3.

The docs for pass_object_size(N) state that N is passed as the second parameter to __builtin_object_size, which means that N must be in the range [0, 3]. I'll add a comment, but given your other comment, it looks like there may have been a miscommunication about the purpose of pass_object_size's argument. :)

Ah, yes, I was confused. This isn't checking that the number matches a parameter index. :-)

rsmith added inline comments.Nov 18 2015, 6:18 PM
include/clang/AST/Expr.h
631–634 ↗(On Diff #40185)

Looks OK, but you have an undesirable "either" in the comment now.

include/clang/Basic/AttrDocs.td
272 ↗(On Diff #40185)

Missing `` around pass_object_size here.

317 ↗(On Diff #40185)

And here.

351–355 ↗(On Diff #40185)

And here.

359 ↗(On Diff #40185)

And around "const" here, maybe.

include/clang/Sema/Initialization.h
832 ↗(On Diff #40185)

Add a period at the end of this comment.

lib/AST/ExprConstant.cpp
6507–6509 ↗(On Diff #40185)

I don't disagree, but it seems logically similar to the HasSideEffects case, which is still here. Maybe that should be moved to CodeGen too.

9534 ↗(On Diff #40185)

I would expect this to produce a size_t, rather than an arbitrary-seeming signed 64-bit integer. It also seems suspicious to be converting a uint64_t to a signed APInt here.

lib/AST/ItaniumMangle.cpp
2205 ↗(On Diff #40185)

if (FD) is more common in these parts.

lib/CodeGen/CGCall.cpp
110 ↗(On Diff #40185)

Given that this appends, reserve(prefix.size() + FPT->getNumParams()) seems better.

2847–2849 ↗(On Diff #40185)

These seem to be in the wrong order, and the call to Args.back() will misbehave if a parameter is marked with __attribute__((nonnull, pass_object_size(N))). Swap these calls over.

2862–2864 ↗(On Diff #40185)

Likewise.

lib/Sema/SemaOverload.cpp
8832–8834 ↗(On Diff #40185)

Any reason why you factor out HasPassObjSize here and not in the previous (essentially identical) function? And can you factor this out into a getPassObjectSizeParamIndex function or something, to avoid some of the duplication between this and the previous function?

george.burgess.iv marked 13 inline comments as done.
  • Addressed all feedback
  • Fixed a few formatting errors
  • Deduplicated the checkAddressOfFunctionIsAvailable suite of functions
  • Added tests to ensure that we don't lower __builtin_object_size(E, N) to @llvm.objectsize if E has side-effects
include/clang/AST/Expr.h
631–634 ↗(On Diff #40185)

Danke

lib/AST/ExprConstant.cpp
6507–6509 ↗(On Diff #40185)

They seem sufficiently different to me. GCC's docs for __builtin_object_size say "__builtin_object_size never evaluates its arguments for side-effects. If there are any side-effects in them, it returns (size_t) -1 for type 0 or 1 and (size_t) 0 for type 2 or 3," so this is more a feature of the builtin itself than an artifact of how we generate code. That said, I can't tell if our partial relaxation of this restriction is intentional or not. :)

FWIW, the static analyzer also apparently depends on us returning Success here if there are side-effects; tests fail if I move this check to CodeGen.

lib/CodeGen/CGCall.cpp
110 ↗(On Diff #40185)

Nice catch -- thanks.

lib/Sema/SemaOverload.cpp
8832–8834 ↗(On Diff #40185)

Any reason why you factor out HasPassObjSize here and not in the previous (essentially identical) function?

Artifact of not cleaning up completely after playing with different ways to do this -- sorry.

And can you factor this out into a getPassObjectSizeParamIndex function or something, to avoid some of the duplication between this and the previous function?

I tried merging the functions entirely; if we don't like the result, I'm happy to go back to the way they were and just factor out the find_if bit. :)

Rebased + made Windows pass_object_size mangling scheme more like Itanium’s.

I can’t find an explicit extension point here, so the current approach is to mangle void foo(void *__attribute__((pass_object_size(0)))); as if it was void foo(void *, ::__ext::__pass_object_size0);, and hope that there’s never a collision. Suggestions for better ways to do this are welcome. :)

majnemer added inline comments.Dec 1 2015, 1:38 PM
lib/AST/MicrosoftMangle.cpp
1844–1857 ↗(On Diff #40998)

This seems fine to me, I'd change the namespace to something like __clang though.

rsmith accepted this revision.Dec 1 2015, 3:45 PM
rsmith edited edge metadata.

OK, a couple of trivial changes then I think this is fine to commit.

include/clang/Basic/AttrDocs.td
351–355 ↗(On Diff #40998)

Not done? =)

lib/AST/ExprConstant.cpp
6507–6509 ↗(On Diff #40998)

I'd view the GCC documentation as more what you'd call "guidelines" than actual rules. The key part here is that side-effects in the operand of __builtin_object_size do not occur; I think the fact that GCC always returns -1 or 0 if there are side-effects is an implementation detail of GCC's approach rather than a formal part of the design, and we should not be required to treat __builtin_object_size as a constant if it's applied to an expression with side-effects. If CodeGen can compute the object size, it should be permitted to do so even if side-effects are present, so long as those side-effects do not actually occur.

However, I think this change is doing enough already, so let's go ahead with things as they are; we can consider changing this at a later date if we are so inclined.

lib/AST/MicrosoftMangle.cpp
1844–1857 ↗(On Diff #40998)

Yes, __clang would make more sense to me too.

This revision is now accepted and ready to land.Dec 1 2015, 3:45 PM
george.burgess.iv edited edge metadata.
george.burgess.iv marked an inline comment as done.
  • Changed Microsoft mangling from __ext::__pass_object_size to __clang::__pass_object_size
  • Moved MS mangling tests to a more appropriate file

Oops -- I didn't see Richard's comments before I pressed "save". Will address that feedback too. Sorry! :)

This revision was automatically updated to reflect the committed changes.
george.burgess.iv marked 2 inline comments as done.Dec 2 2015, 2:01 PM
george.burgess.iv added inline comments.
lib/AST/ExprConstant.cpp
6507–6509 ↗(On Diff #41651)

WFM!