This is an archive of the discontinued LLVM Phabricator instance.

[clang] Add WriteOnly attribute
Needs ReviewPublic

Authored by homerdin on May 1 2018, 7:04 AM.

Details

Summary

This patch adds a WriteOnly attribute.

I am working to mark the math functions that write errno as WriteOnly in the backend to enable some optimizations. Currently I plan to use this attribute in SemaDecl to mark the math functions that may set errno when we care about errno. Then to add the llvm WriteOnly attribute in CGCall when a function adds this attribute.

Any feedback would be appreciated!

Diff Detail

Event Timeline

homerdin created this revision.May 1 2018, 7:04 AM

I am working to mark the math functions that write errno as WriteOnly in the backend to enable some optimizations. Currently I plan to use this attribute in SemaDecl to mark the math functions that may set errno when we care about errno. Then to add the llvm WriteOnly attribute in CGCall when a function adds this attribute.

Are you planning to mark the math functions explicitly by adding the attribute to their declarations, or are you planning to mark them implicitly from SemaDecl so that the libc headers don't have to add the annotation?

The patch is also missing plumbing such as adding the parsed attribute to the AST (in SemaDeclAttr.cpp), lowering it to LLVM, tests, etc, so it's a bit hard to understand the overall design you're going for.

include/clang/Basic/Attr.td
1784

What subjects should this attribute appertain to?

1785

Typically, this attribute would be named write_only_func instead.

1786

No new, undocumented attributes, please.

homerdin updated this revision to Diff 145694.May 8 2018, 8:42 AM

Thanks for the feedback! I plan to mark the math function implicitly from SemaDecl. I am going to open this in a separate diff, but I still need to update some tests.

if (getLangOpts().MathErrno && !FD->hasAttr<ConstAttr>() &&
    Context.BuiltinInfo.isConstWithoutErrno(BuiltinID))
  FD->addAttr(WriteOnlyFuncAttr::CreateImplicit(Context, FD->getLocation()));

Let me know if there is any additional plumbing that should be added or if you have thoughts on the overall design.

Thanks for the feedback! I plan to mark the math function implicitly from SemaDecl. I am going to open this in a separate diff, but I still need to update some tests.

if (getLangOpts().MathErrno && !FD->hasAttr<ConstAttr>() &&
    Context.BuiltinInfo.isConstWithoutErrno(BuiltinID))
  FD->addAttr(WriteOnlyFuncAttr::CreateImplicit(Context, FD->getLocation()));

Let me know if there is any additional plumbing that should be added or if you have thoughts on the overall design.

If I understand properly, which I might not, it sounds like users shouldn't be writing this attribute explicitly, but instead the attribute should only be applied implicitly under specific circumstances. Is that correct? If so, then I would consider one of two things: 0) forgo the AST-level attribute entirely and just lower to the LLVM IR attribute directly, or 1) do not use a Sema handler or spelling and instead use CreateImplicit to create the AST attribute as-needed (see MaxFieldAlignment for an example of this). If you do expect users to write this attribute manually, then I think the documentation needs more exposition because it's not really clear under what circumstances users should use this attribute, what will go wrong if they misapply it, what the effects of the attribute are, etc.

homerdin updated this revision to Diff 152124.Jun 20 2018, 12:06 PM

Sorry about the slow turn around. I've updated the documentation to be clearer about this attribute's use.

While I'm mainly interested in marking math functions, exposing this attribute to users should be beneficial as I'm working to enable some optimizations in the backend making use of the WriteOnly LLVM attribute.

I haven't added any additional documentation concerning consequences and effects because at this point the WriteOnly LLVM attribute is not being used extensively. Would it be best to wait until additional uses (CSE, Hoisting) are in effect then update the documentation?

I've uploaded a diff that applies this attribute to libm functions when we care about errno: https://reviews.llvm.org/D48386

Sorry about the slow turn around. I've updated the documentation to be clearer about this attribute's use.

While I'm mainly interested in marking math functions, exposing this attribute to users should be beneficial as I'm working to enable some optimizations in the backend making use of the WriteOnly LLVM attribute.

I haven't added any additional documentation concerning consequences and effects because at this point the WriteOnly LLVM attribute is not being used extensively. Would it be best to wait until additional uses (CSE, Hoisting) are in effect then update the documentation?

It's better to document things up front -- this makes it more likely that the attribute is used in practice because people know what it is, what it does, and when it's beneficial to use.

include/clang/Basic/Attr.td
1786

What happens if I apply this attribute to a virtual function such as in the following example:

int memory;

struct Base {
  [[clang::write_only_func]] virtual int f(int i) const { return memory = i; }
};

struct Derived1 : Base {
  int f(int i) const override { return memory =  i + 12; }
};

struct Derived2 : Base {
  int f(int i) const override { return memory = i + memory; }
};

Further -- what happens if you mark a function as write_only_func and it doesn't adhere to the statements in the documentation? e.g., should Derived2::f() generate a warning and drop the attribute?

include/clang/Basic/AttrDocs.td
3466

may write to but does not read from memory -> may write to, but does not read from, memory

A write only -> a write-only

3467

writes memory the same

Does this mean it writes the same value to the same memory location when called with the same set of arguments and global state? If so, it's probably best to spell that out.

homerdin updated this revision to Diff 157363.Jul 25 2018, 2:27 PM

Updated documentation

This is missing a bunch of semantic tests that ensure the attribute appertains only to the expected subject, accepts no arguments, etc.

include/clang/Basic/AttrDocs.td
3475–3476

I'm not too keen on adding a new attribute that when used improperly will cause a miscompile without any diagnostics involved. How difficult would it be to add semantic checking to check functions with this attribute applied to it?

homerdin updated this revision to Diff 158629.Aug 1 2018, 2:06 PM

I've added some additional test including semantic tests.

homerdin added inline comments.Aug 1 2018, 2:13 PM
include/clang/Basic/AttrDocs.td
3475–3476

I think the main purpose for having this attribute is to expose LLVM's WriteOnly attribute when we don't have access to the module. We can infer that a function is write-only in LLVM (https://reviews.llvm.org/D48387) when we have access to the function body. This attribute would allow us access to this information when we don't have access to the function body.

homerdin marked 5 inline comments as done.Aug 1 2018, 2:15 PM
hfinkel added inline comments.Aug 1 2018, 2:21 PM
include/clang/Basic/AttrDocs.td
3475–3476

Do we have wording list this around the pure/const attributes (that we borrowed from GCC)? If we wish to say anything, and we probably should, the wording should be something like:

If a function with this attribute reads from memory, the behavior of the program is undefined.

Also, I'll add that this attribute, at the LLVM level, is an important part of optimizations we're working on for libm functions (when they set errno). We can do this "automatically" for actual libm functions, but we should also provide the user the ability to add these to functions of his or her choice (because we, to the extent possible, should allow a user to create functions that get optimized as well as the system-provided ones).

aaron.ballman added inline comments.Aug 2 2018, 1:20 PM
include/clang/Basic/AttrDocs.td
3475–3476

Do we have wording list this around the pure/const attributes (that we borrowed from GCC)?

Unfortunately, those are both undocumented attributes for us currently.

If we wish to say anything, and we probably should, the wording should be something like:

If a function with this attribute reads from memory, the behavior of the program is undefined.

I think this would clarity the documentation.

3475–3476

This attribute would allow us access to this information when we don't have access to the function body.

Understood, but I'm talking about the case where we see the function body because we're compiling it (rather than just using the declaration). Basically, I'm thinking of this case:

int i;
__attribute__((write_only_func)) int t0(void) {
  return i;
}

It seems like we'd want to tell the user "Hey, just so you know, this is bad." and ideally, drop the attribute so the user doesn't get a miscompile.

homerdin updated this revision to Diff 163096.Aug 29 2018, 8:30 AM
homerdin retitled this revision from [clang] Add WriteOnlyFunc attribute to [clang] Add WriteOnly attribute.
homerdin edited the summary of this revision. (Show Details)

Rebased. Updated wording in documentation

I added the functionality to apply this attribute to parameters in order to fully expose LLVM's WriteOnly attribute. Renaming the attribute to WriteOnly accordingly.

I would like to implement the semantic checking (for when we can see that the attribute is being misused) in a separate patch and include the logic to check for other attributes (pure). I'm looking into where they best place to implement this would be (any suggestions would be appreciated).

I'd like to see some C++ tests that check reference behavior (and perhaps more esoteric pointerish non-pointer types like member function pointers).

include/clang/Basic/AttrDocs.td
3465

an argument -> a parameter of pointer type

(question: what about parameters of reference type?)

3480

argument -> parameter

lib/Sema/SemaDeclAttr.cpp
1492

I think you want warn_attribute_pointer_or_reference_only if this accepts references as well as pointers.

1493

You can pass in AL directly these days.

6396–6400

No need to split these with a predicate, handleWriteOnlyAttrParameter() already does the right thing in both cases.

homerdin updated this revision to Diff 165144.Sep 12 2018, 2:27 PM

Added additional tests and addressed the inline comments

homerdin marked 5 inline comments as done.Sep 12 2018, 2:28 PM

Re-ping.

Thank you for re-pinging this, it fell off my radar entirely. I suspect the patch will need to be rebased as well.

include/clang/Basic/AttrDocs.td
3482

pointer -> pointer or reference

3485

pointer -> pointer or reference

lib/Sema/SemaDeclAttr.cpp
1485–1486

We don't usually early-return for invalid declarations when attaching attributes. I'd remove this.

1489–1490
if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
  QualType T = PVD->getType();
  ...
}
test/CodeGen/attributes.c
120

I'd also like to see a test that this works fine on redeclarations. e.g.,

void tNN() __attribute__((write_only));
void tNN()  {} // The definition should still have the attribute

void tMM(int *b __attribute__((write_only)));
void tMM(int *b) {} // The definition should still have the attribute