zhaomo added a comment.

Addressed @rsmith's comments.



================
Comment at: clang/include/clang/Driver/CC1Options.td:356-357
+    HelpText<"Enable VTable interleaving">;
+def disable_vtable_interleaving : Flag<["-"], "disable-vtable-interleaving">,
+    HelpText<"Disable VTable interleaving">;
 
//===----------------------------------------------------------------------===//
----------------
rsmith wrote:
> We usually only expose the non-default flag in `-cc1`, so that there are no 
> ordering concerns and we can determine whether a feature is enabled with just 
> `hasArg`. Also, `-fvtable-interleaving` would seem like a more natural flag 
> name to me.
Changed it in the latest patch. 


================
Comment at: clang/include/clang/Frontend/CodeGenOptions.h:274
+  /// the interleaving layout is decided.
+  bool EnableVTableInterleaving;
+
----------------
rsmith wrote:
> `Enable` here seems redundant. Is thee a reason to declare this explicitly 
> rather than adding it to CodeGenOptions.def?
Changed it in the latest patch.


================
Comment at: clang/lib/CodeGen/ItaniumCXXABI.cpp:973-976
+    // The name of a offset global variable has the format "__[type
+    // id]$[offset]".
+    std::string Name =
+        "__" + RD->getNameAsString() + "$" + std::to_string(Offset);
----------------
rsmith wrote:
> The `__`s here are atypical. If you want a name that can't collide with a 
> name generated by the frontend, putting a period in it is the usual strategy. 
> Also, a name prefix that indicates what this global is for would make the IR 
> more readable and make this less likely to collide with other things. Maybe 
> `clang.vtable.index.<CLASS>.<OFFSET>` or similar?
> 
> Also, there's no guarantee that `RD->getNameAsString()` produces something 
> unique within the translation unit, so you'll need to make sure that name 
> collisions are handled somehow (I *think* `GlobalVariable` already deals with 
> this for you, but I'm not sure... however, I think its strategy is to add a 
> number to the end of the mangling, which will lead to some quite peculiar 
> results given that you already have a number there). It might be easier to 
> base the name of this symbol on the mangled name of the class; then you could 
> give the global variable the same linkage as the class, which might save your 
> interleaving pass a little bit of work later on as the duplicates will 
> already have been combined -- but that's up to you, and I don't have enough 
> of the big picture here to give you advice.
The encoding of the name of such a global variable is just for debugging 
purposes. The back-end pass does not care about the name. Instead, it relies on 
the metadata associated with the global variable, and the metadata does not 
rely on `RD->getNameAsString() `. Also, the back-end pass does not care how 
many global variables associated with the same metadata.

I did not know that it is using periods is a convention in LLVM. I changed the 
name to `clang.vtable.offset.<CLASS>.<OFFSET>`.


================
Comment at: clang/lib/CodeGen/ItaniumCXXABI.cpp:987
+  }
+  return llvm::ConstantExpr::getPtrToInt(OffsetGV, OffsetType);
+}
----------------
rsmith wrote:
> Representing this offset as a `ptrtoint` on a global variable seems really 
> strange. Is there really no better way to model this in IR? (I mean, if not, 
> then carry on as you are, but this seems like a hack.)
The global variable we create for each referenced vtable offset is actually a 
zero-length array, and we use the address of the array as the placeholder for a 
referenced vtable offset. I was told that this is a common trick used in LLVM. 

@pcc: Peter, would you provide more information here? 


================
Comment at: clang/lib/CodeGen/ItaniumCXXABI.cpp:1767
 
+  bool InBounds = shouldInterleaveVTables(VTableClass) ? false : true;
   return llvm::ConstantExpr::getGetElementPtr(VTable->getValueType(), VTable,
----------------
rsmith wrote:
> zhaomo wrote:
> > pcc wrote:
> > > Remind me why this needs to be here? (And the explanation needs to be in 
> > > a comment.)
> > The calculation of address point is essentially base_vtable_addr + offset, 
> > where offset is from the indices of gep.
> > In the interleaving pass, we replace base_vtable_addr with 
> > (addr_point_in_interleaved_layout - offset).
> > 
> > The LLVM language reference says that the base address of a inbounds gep 
> > must be an in bound address of the object. The new base address 
> > addr_point_in_interleaved_layout - offset, however, may not be an in bound 
> > address.
> I still find the need for this confusing. Suppose we have:
> 
> ```
> struct A { virtual void f(); };
> struct B { virtual void g(); virtual void h(); };
> struct C : A, B {};
> ```
> 
> such that `C` has a naive vtable containing 7 elements {{offset, typeid, 
> &A::f}, {offset, typeid, &B::f, &B::h}}, with address point indexes (0, 2) 
> and (1, 2). Here we emit a `gep inbounds @vtable, 0, 1, 2` to get the B-in-C 
> vtable. Now, if this were instead emitted as `gep inbounds (cast @vtable to 
> i8*), 24`, and the interleaving process were replacing @vtable with a 
> non-`inbounds` GEP on its interleaved ubervtable, I could understand the need 
> to drop `inbounds` here -- because the replacement for @vtable might be 
> before the start of the ubervtable. But based on my reading of the algorithm 
> description, that's not what happens: first we split `@vtable` into multiple 
> individual vtables, and that splitting process presumably must identify these 
> GEPs to figure out which slice of the vtable they need. That presumably 
> either removes this GEP entirely, or leaves this being a correct and trivial 
> `inbounds` GEP on a split-out piece of the vtable. (I'm imagining we 
> effectively rewrite `gep inbounds @vtable, 0, 1, 2` as `@vtable-slice-2` as 
> part of the splitting.) And then after interleaving, I'm imagining we replace 
> all uses of `@vtable-slice-2` with the appropriate address point in the 
> ubervtable, which is again an `inbounds` GEP (but that would be a new GEP, 
> unrelated to this one).
> 
> That is, if you need this change, it seems like that indicates a bug in the 
> vtable splitter. But maybe I'm misunderstanding how this all fits together.
GlobalSplit, the pass that splits vtables, relies on `inrange` instead of 
`inbounds`. 

From the language reference:

> If the inrange keyword is present before any index, loading from or storing 
> to any pointer derived from the getelementptr has undefined behavior if the 
> load or store would access memory outside of the bounds of the element 
> selected by the index marked as inrange. 

`inbounds` means something different. Again from the language reference:

> If the inbounds keyword is present,** the result value of the getelementptr 
> is a poison value if the base pointer is not an in bounds address of an 
> allocated object**, or if any of the addresses that would be formed by 
> successive addition of the offsets implied by the indices to the base address 
> with infinitely precise signed arithmetic are not an in bounds address of 
> that allocated object.

We need to drop `inbounds` because that the **base pointer** may not be in 
bounds address of an interleaved vtable.

Here is how an address point is calculated in an original vtable. 
`original_address_pt` = `start_original_vtable` + `original_offset`, where 
`start_original_vtable` is the address of the original vtable and 
`original_offset` is the offset of the address point in the original vtable.

Our interleaving pass calculates the address of this address point in the 
interleaved object. Let's call it `new_address_pt` and `new_address_pt` = 
`start_interleaved_vtable` + `new_offset`.
Then we replace `start_original_vtable` with `(new_address_pt - 
original_offset)`. Because `start_original_vtable` is the **base pointer** of 
the `gep`, and `new_offset` may be smaller than `original_offset`, we have to 
drop inbounds. 

> I'm imagining we effectively rewrite gep inbounds @vtable, 0, 1, 2 as 
> @vtable-slice-2 
gep inbounds @vtable, 0, 1, 2 is rewritten to gep inbounds @vtable-slice-2, 0, 
2 in your example.











================
Comment at: clang/lib/CodeGen/ItaniumCXXABI.cpp:1832
+  auto *RecordDecl = MethodDecl->getParent();
+  llvm::Value *VTable = CGF.GetVTablePtr(This, CGM.Int8PtrTy, RecordDecl);
 
----------------
rsmith wrote:
> Do you really need to change this to load the vptr at the "wrong" type and 
> then cast it? (Could the cast, if necessary at all, live in 
> `EmitVTableTypeCheckedLoad` instead?)
I have to because both branches of the if statement rely on `OffsetConstant`, 
which is a byte offset.


================
Comment at: clang/lib/CodeGen/ItaniumCXXABI.cpp:3618-3622
+  if (!CXXABI.shouldInterleaveVTables(RD)) {
+    uint64_t UpperBits = (uint64_t)Offset << 8;
+    uint64_t OffsetFlags = UpperBits | LowerBits;
+    return llvm::ConstantInt::get(OffsetFlagsTy, OffsetFlags);
+  }
----------------
rsmith wrote:
> Shouldn't you also do this if the base is non-virtual? In that case,`Offset` 
> is an offset within the class layout, not an index into the vtable.
You are right. This bug is fixed in the latest patch.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D51905/new/

https://reviews.llvm.org/D51905



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to