This is an archive of the discontinued LLVM Phabricator instance.

[ObjC++] Never pass structs with `__weak` fields in registers
ClosedPublic

Authored by ahatanak on Apr 6 2018, 12:04 PM.

Details

Summary

This patch fixes a bug in r328731 that caused structs with __weak fields to be passed in registers. This happens when a struct doesn't have a __weak field but one of its subobjects does. To fix this, I added flag CXXRecordDecl::CannotPassInRegisters that propagates outwards and tracks whether a __weak field was seen. I added a new flag instead of reusing the CanPassInRegisters flag since in C++ we cannot always determine the value of a class' CanPassInRegisters flag by looking at its subobject's CanPassInRegisters flag (CanPassInRegisters=true in the subclass doesn't mean CanPassInRegisters=true in the derived class in C++).

rdar://problem/39194693

Diff Detail

Repository
rC Clang

Event Timeline

ahatanak created this revision.Apr 6 2018, 12:04 PM

Well, but I think CanPassInRegisters==false in the base class does always mean CanPassInRegisters==false in the subclass.

Well, but I think CanPassInRegisters==false in the base class does always mean CanPassInRegisters==false in the subclass.

I think there are cases that is not true. If I compile the following code, S0 (base class) is passed indirectly and S1 (derived class) is passed directly.

struct S0 {
  S0();
  S0(const S0 &) = default;
  S0(S0 &&);
  ~S0() = default;
  int *p;
};

struct S1 : S0 {
  S1();
  S1(const S1 &) = default;
  S1(S1 &&) = delete;
  ~S1() = default;
  int a;
};

void foo1(S0);
void foo1(S1);

void test0(S0 *a) {
  foo1(*a);
}

void test1(S1 *a) {
  foo1(*a);
}

By the way, I meant to say "CanPassInRegisters=true in the *base class*" not "CanPassInRegisters=true in the subclass".

Well, but I think CanPassInRegisters==false in the base class does always mean CanPassInRegisters==false in the subclass.

I think there are cases that is not true. If I compile the following code, S0 (base class) is passed indirectly and S1 (derived class) is passed directly.

Yes, I think you're right. That's exciting.

So "cannot pass in registers" is tracking whether there's a primitive reason that the class cannot be passed in registers, something that's above and beyond the C++ rules? Okay.

include/clang/AST/DeclCXX.h
486 ↗(On Diff #141396)

You should definitely explain the semantic difference with CanPassInRegisters in this comment, and I think the field/method names should underline that. Maybe this is a place to pull out "Primitive" again?

Yes. I intended it as a property that propagates to classes that contain or derive from the type.

Would it make it less confusing if I merged CXXRecordDecl::CanPassInRegisters and RecordDecl::CannotPassInRegisters into a single enum? For example, the enum could have three enumerators, "CanPass", "CannotPass", "CanNeverPass", or something. Both "CannotPass" and "CanNeverPass" would force the type to be passed indirectly, and the only difference is that "CanNeverPass" propagates its property outwards and "CannotPass" doesn't. C structs are either "CanPass" or "CanNeverPass" while C++ structs can take any of the three values.

Yes. I intended it as a property that propagates to classes that contain or derive from the type.

Would it make it less confusing if I merged CXXRecordDecl::CanPassInRegisters and RecordDecl::CannotPassInRegisters into a single enum? For example, the enum could have three enumerators, "CanPass", "CannotPass", "CanNeverPass", or something. Both "CannotPass" and "CanNeverPass" would force the type to be passed indirectly, and the only difference is that "CanNeverPass" propagates its property outwards and "CannotPass" doesn't. C structs are either "CanPass" or "CanNeverPass" while C++ structs can take any of the three values.

Yes, I think that would help a lot.

ahatanak updated this revision to Diff 141471.Apr 6 2018, 6:39 PM

Replace flags CanPassInRegisters and CannotPassInRegisters with a 2-bit enum in RecordDecl.

Just a couple minor requests; if you accept them, feel free to commit.

include/clang/AST/Decl.h
3556 ↗(On Diff #141471)

I think it's probably worth spelling out why this can happen in C++, something like:

This value in required by C++ because, in uncommon situations, it is possible for a class to have only trivial copy/move constructors even when one of its subobjects has a non-trivial copy/move constructor (if e.g. the corresponding copy/move constructor in the derived class is deleted).
3601 ↗(On Diff #141471)

I think maybe "ArgPassingRestrictions" would be better, since we don't necessarily honor this in the ABI.

ahatanak updated this revision to Diff 141730.Apr 9 2018, 1:41 PM
ahatanak marked 2 inline comments as done.

Address review comments.

ahatanak accepted this revision.Apr 9 2018, 1:44 PM
This revision is now accepted and ready to land.Apr 9 2018, 1:44 PM
ahatanak closed this revision.Apr 9 2018, 1:45 PM

Committed in r329617.