Page MenuHomePhabricator

[DebugInfo] Add a new DI flag to record if a C++ record is a trivial type
ClosedPublic

Authored by asmith on Mar 31 2018, 8:17 AM.

Details

Summary

This flag is used when emitting debug info and is needed to initialize subprogram and member function attributes (function options) for Codeview. These function options are used to create an accurate compiler type for UDT symbols (class/struct/union) from PDBs.

It is not easy to determine if a C++ record is trivial or not based on the current DICompositeType flags and other accessible debug information from Codeview. For example, without this flag the metadata for a non-trivial C++ record with user-defined ctor and a trivial one with a defaulted ctor are the same.

struct S { S(); }
struct S { S() = default; }

This change introduces a new DI flag and corresponding clang::CXXRecordDecl::isTrivial method to set the flag in the frontend.

Diff Detail

Repository
rL LLVM

Event Timeline

asmith created this revision.Mar 31 2018, 8:17 AM

Is "trivial" the right flag? In DWARF v5 there is a "defaulted" attribute, which also distinguishes between in-class and out-of-class defaulting. I'd rather not have a bunch of different flags meaning only slightly different things if we can come up with a more coherent way to track the information we need.

llvm/IR/DebugInfoFlags.def
49 ↗(On Diff #140540)

Is there some reason FixedEnum wants to be last?

Have you looked at how the new trivial_abi attribute is handled in DWARF? We set the calling convention of C++ types accordingly. Could you just reuse the same information in CodeView?

See test/CodeGenCXX/debug-info-composite-cc.cpp for reference.

dblaikie added a subscriber: asmith.Apr 2 2018, 8:50 AM

Yeah, +1 for verifying whether or not DIFlagTypePassByValue/Reference is
sufficient for this.

asmith added a comment.EditedApr 2 2018, 12:40 PM

I don't think those DWARF flags help in this case but let's check. Here are the results of changing the flag as suggested and rerunning the test from D45123. We can see that several places that are not marked as Trivial are marked as PassByValue meaning that we cannot rely on PassByValue to mean Trivial.

$ grep ByValue function-options.ll

// These are not Trivial but they are PassByValue

!19 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "BClass", file: !9, line: 10, size: 8, flags: DIFlagTypePassByValue, elements: !20, identifier: ".?AVBClass@@")
!43 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "C2Class", file: !9, line: 25, size: 8, flags: DIFlagTypePassByValue, elements: !44, identifier: ".?AVC2Class@@")
!55 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "DClass", file: !9, line: 32, size: 8, flags: DIFlagTypePassByValue, elements: !56, identifier: ".?AVDClass@@")
!107 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "BStruct", file: !9, line: 63, size: 8, flags: DIFlagTypePassByValue, elements: !108, identifier: ".?AUBStruct@@")

If you look at the corresponding change to clang in D45124 you will see that Trivial and PassByValue/Reference are a bit different.

if (auto CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
  if (CGM.getCXXABI().getRecordArgABI(CXXRD) == CGCXXABI::RAA_Indirect)
    Flags |= llvm::DINode::FlagTypePassByReference;
  else
    Flags |= llvm::DINode::FlagTypePassByValue;

  // When emitting codeview, record if a C++ record is trivial type.
  if (CGM.getCodeGenOpts().EmitCodeView) {
    if (CXXRD->isTrivial())
      Flags |= llvm::DINode::FlagTrivial;
  }
}

Does the proposed clang change (using 'isTrivial') match MSVC's behavior in
these cases?

Any idea what this information is used for by the debugger? (because it
seems /likely/ it uses it for similar things to the PassByValue/Reference
stuff - that's something important to a debugger)

The meaning of trivial is the same. There are some differences with MSVC in the generated CodeView that are documented in the FIXMEs in D45123.

LLDB is going to use this to construct a CXXRecordDecl when parsing record symbols (PDBSymbolTypeUdt and PDBSymbolBaseclass). FunctionOptions, ClassOptions and MethodOptions rely on this information which is currently missing in LLVMs CV.

See D44406 for what kicked all this off originally.

I'm still not sure this description/name matches the examples given in the
other review from Reid.

Sounds like this is more about whether it has a trivial default
constructor, specifically? (which, yes, is different from the triviality of
copy construction which is relevant to the DWARF flag we already have) -
some test cases/demonstrations of the MSVC behavior might be helpful?

D44406 is not completely the same as this one yes. Did you look at the
MSVC examples in D45123?

This still doesn't /sound/ right to me - the triviality of a class doesn't
seem like it would correctly allow you to deduce the triviality of specific
special members? Because the the class would only be considered trivial if
all the special members are trivial, probably?

I suspect the CxxReturnUdt might be related to the PassByValue/Reference
feature we already have for DWARF.

For example does MSVC put CxxReturnUdt on return types where they have a
non-trivial default ctor, but a trivial copy ctor? or other variations like
that.

Reid/Zach - you two looked at this sort of thing & have a better sense of
what each of these flags represent? (maybe ideas about what we should do
for types marked with Clang's [[trivial_abi]] attribute (obviously wouldn't
be interoperable with code compiled with MSVC since it doesn't support the
attribute - but still be good to know how to describe the type to the
debugger)?

rnk added a comment.Apr 10 2018, 1:32 PM

In D44406, I was imagining instead that we'd mark DISubprograms as trivial or non-trivial, not the type itself, and then CodeViewDebug.cpp in LLVM could skip trivial constructors when looking for classes.

I think for CxxReturnUdt we can go ahead and use the DIFlagPassByReference marker. The flag name isn't really accurate since it also covers cases that would use inalloca. My ideas aren't that much better, though: DIFlagPassingIsNonTrivial, DIFlagCopyIsNonTrivial, DIFlagNonTriviallyCopyable, blech.

Only considering DIFlagPassByReference for CxxReturnUdt will not cover all
cases. There are times when DIFlagPassByValue is also CxxReturnUdt.

rnk added a comment.Apr 11 2018, 9:30 AM

Only considering DIFlagPassByReference for CxxReturnUdt will not cover all
cases. There are times when DIFlagPassByValue is also CxxReturnUdt.

Can you describe them? Sorry, I can't investigate myself right now because my Windows workstation died over the weekend.

I'm guessing the issue is in this code:

if (auto CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
  if (CGM.getCXXABI().getRecordArgABI(CXXRD) == CGCXXABI::RAA_Indirect)
    Flags |= llvm::DINode::FlagTypePassByReference;
  else
    Flags |= llvm::DINode::FlagTypePassByValue;
}

getRecordArgABI returns three possible results, and this code only effectively handles two. I think it would be correct to:

  1. Rename FlagTypePassByReference to FlagPassNonTrivial (or something like that but less terrible)
  2. Set the flag when the RAA is either indirect or RAA_DirectInMemory.

Truly, the minimal change is to invert the logic of the if to check for RAA_Default and set FlagTypePassByReference in all other cases. That's how LLVM sees inalloca anyway. =p

Please read the previous discussion in this code review for the examples of why those DWARF flags don't work.

"I don't think those DWARF flags help in this case but let's check. Here are the results of changing the flag as suggested..."

I'll ask Reid to take another look at this next week. Sorry for the delay, he's been on a long vacation.

rnk accepted this revision.Jul 20 2018, 2:06 PM

lgtm

We should also be able to use this to mark explicitly defaulted but trivial special members, i.e.:

struct Foo {
  int x;
  ~Foo() = default;
};
Foo gv;

Right now we emit a destructor, which is silly.

This revision is now accepted and ready to land.Jul 20 2018, 2:06 PM
asmith updated this revision to Diff 156662.Jul 20 2018, 10:37 PM
asmith edited the summary of this revision. (Show Details)
This revision was automatically updated to reflect the committed changes.