================
@@ -599,29 +622,209 @@ static mlir::Value emitCopiedCoercedLoad(mlir::OpBuilder 
&builder,
          "a direct offset must leave the record as the slot's type");
   mlir::OpBuilder::InsertionGuard guard(builder);
   builder.setInsertionPoint(load);
-  mlir::Value recView = emitViewAtOffset(builder, loc, recTy, slot, ignored, 
0);
-  cir::CopyOp::create(builder, loc, recView, load.getAddr(),
-                      builder.getI64IntegerAttr(slot.getAlignment()),
-                      builder.getI64IntegerAttr(srcAlign));
+  mlir::Value recView =
+      emitViewAtOffset(builder, loc, recTy, slot, ignored, /*offset=*/0);
+  auto copy =
+      cir::CopyOp::create(builder, loc, recView, load.getAddr(),
+                          builder.getI64IntegerAttr(slot.getAlignment()),
+                          builder.getI64IntegerAttr(srcAlign));
+  copy.setIsVolatile(load.getIsVolatile());
+  return slot;
+}
+
+/// Copy the bytes of the record \p load reads into a coercion slot, as
+/// emitCopiedCoercionSlot does, then read \p coercedTy out of the slot at
+/// \p offset.  The read takes the slot's alignment at \p offset.
+static mlir::Value emitCopiedCoercedLoad(mlir::OpBuilder &builder,
+                                         mlir::Location loc, cir::LoadOp load,
+                                         mlir::Type coercedTy,
+                                         mlir::Block *slotBlock,
+                                         const mlir::DataLayout &dl,
+                                         unsigned offset) {
+  cir::AllocaOp slot = emitCopiedCoercionSlot(builder, loc, load, coercedTy,
+                                              slotBlock, dl, offset);
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPoint(load);
+  SmallPtrSet<mlir::Operation *, 4> ignored;
   mlir::Value coercedView =
       emitViewAtOffset(builder, loc, coercedTy, slot, ignored, offset);
   auto coerced = cir::LoadOp::create(builder, loc, coercedView);
   coerced.setAlignment(llvm::MinAlign(slot.getAlignment(), offset));
   return coerced;
 }
 
+/// Read \p coercedTy at \p offset out of the bytes the record load \p load
+/// reads: straight out of its storage if \p load is not volatile and
+/// canReadInPlace accepts it, and otherwise out of a coercion slot at the 
start
+/// of \p slotBlock that those bytes are copied into.  emitInPlaceCoercedLoad
+/// does not mark its read volatile, so a volatile load is read through the
+/// volatile copy instead.  Either way no new load of the record type is
+/// emitted.
+static mlir::Value emitCoercedRecordLoad(
+    mlir::OpBuilder &builder, mlir::Location loc, cir::LoadOp load,
+    mlir::Type coercedTy, mlir::Block *slotBlock, const mlir::DataLayout &dl,
+    const cir::CIRDataLayout &cirDataLayout, unsigned offset) {
+  if (!load.getIsVolatile() &&
+      canReadInPlace(load, coercedTy, cirDataLayout, offset))
+    return emitInPlaceCoercedLoad(builder, loc, load, coercedTy, offset);
+  return emitCopiedCoercedLoad(builder, loc, load, coercedTy, slotBlock, dl,
+                               offset);
+}
+
+/// Whether \p ty is a struct that ends in pad members, as CIRGen emits for 
tail
+/// padding another object may reuse.  It is also true of other structs CIRGen
+/// ends in pad members, such as an over-aligned one.
+static bool hasTailPadding(mlir::Type ty) {
+  auto structTy = mlir::dyn_cast<cir::StructType>(ty);
+  if (!structTy)
+    return false;
+  llvm::ArrayRef<mlir::Type> members = structTy.getMembers();
+  llvm::ArrayRef<cir::RecordMemberKind> kinds = structTy.getMemberKinds();
+  for (auto [member, kind] : llvm::reverse(llvm::zip_equal(members, kinds)))
+    if (cir::memberOwnsBytes(member))
+      return kind == cir::RecordMemberKind::Pad;
+  return false;
+}
+
+/// Whether a cir.copy of \p ty into \p dst that stands in for a store of it
+/// skips tail padding.  Unless \p dst is an alloca, which holds a complete
+/// object, it can be a potentially-overlapping subobject, such as a
+/// [[no_unique_address]] member or a base, whose tail padding holds another
+/// object.  Classic CodeGen writes only the data size into a destination it is
+/// handed (EmitCall) and into one that may overlap (EmitAggregateCopy).
+/// Copying the whole object into an alloca is equivalent, since nothing else
+/// lives in its tail padding.
+static bool shouldSkipTailPadding(mlir::Value dst, mlir::Type ty) {
+  return !cir::getUnderlyingAlloca(dst) && hasTailPadding(ty);
+}
+
+/// Rewire the uses of the record value \p val, whose bytes sit at \p addr,
+/// which is aligned to \p addrAlign, so that each one finds the record in
+/// memory.  \p val may already have been retyped to the type it is passed as, 
a
+/// coerced type or a pointer, but its uses still expect the record.
+///
+/// A store of \p val becomes a cir.copy from \p addr at the store, volatile if
+/// the store is and skipping tail padding as shouldSkipTailPadding says.  A
+/// store of the record value writes only what its lowered type stores, so
+/// another union member's data past those bytes would be lost.  A consumer may
+/// be rewritten before \p val's defining op, and has then already stored \p 
val
+/// into a slot of its own.  That store becomes a copy here too.
+///
+/// Every other use is pointed at one cir.load of \p addr created at the
+/// builder's insertion point.  Only a call argument, a return and a read of a
+/// member that is not itself a record may use it, since those either read
+/// \p addr when they are rewritten or read a field.  Uses by an operation in
+/// \p exceptions are left alone.  Fails, after reporting it, on an atomic 
store
+/// of \p val or a use by anything else, before anything is rewired.
+static mlir::LogicalResult
+rewireRecordUses(mlir::OpBuilder &builder, mlir::Location loc, mlir::Value val,
+                 mlir::Value addr, uint64_t addrAlign,
+                 const mlir::DataLayout &dl,
+                 const SmallPtrSetImpl<mlir::Operation *> &exceptions) {
+  mlir::Type recTy = mlir::cast<cir::PointerType>(addr.getType()).getPointee();
+  assert(mlir::isa<cir::RecordType>(recTy) && "only a record is rewired");
+  SmallVector<cir::StoreOp> stores;
+  SmallVector<mlir::OpOperand *> otherUses;
+  for (mlir::OpOperand &use : val.getUses()) {
+    mlir::Operation *user = use.getOwner();
+    if (exceptions.contains(user))
+      continue;
+    auto store = mlir::dyn_cast<cir::StoreOp>(user);
+    if (!store) {
----------------
adams381 wrote:

Restructured it that way, so the store case comes first and the `dyn_cast` sits 
in the `if`.  I left off the `else`, since the store branch ends in `continue` 
and the coding standard asks us not to follow that with an `else`.

https://github.com/llvm/llvm-project/pull/231663
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to