================
@@ -1511,3 +1532,258 @@ void 
CIRABIRewriteContext::rewriteFunctionAddress(cir::GetGlobalOp addrOp,
                                      cir::CastKind::bitcast, addrOp.getAddr());
   addrOp.getAddr().replaceAllUsesExcept(bitcast.getResult(), bitcast);
 }
+
+mlir::LogicalResult
+CIRABIRewriteContext::rewriteVAArg(mlir::Operation *vaArgOp,
+                                   const ArgClassification &ac,
+                                   mlir::OpBuilder &opBuilder) {
+  auto op = mlir::cast<cir::VAArgOp>(vaArgOp);
+  CIRBaseBuilderTy builder(opBuilder);
+  mlir::Location loc = op.getLoc();
+  mlir::Type resultTy = op.getType();
+  mlir::Value valist = op.getArgList();
+
+  auto reportNYI = [&](llvm::StringRef what) {
+    op->emitOpError() << "va_arg of " << what
+                      << " not yet implemented in CallConvLowering";
+    return mlir::failure();
+  };
+
+  // An ignored type is passed in no register and no stack slot, so the fetch
+  // has nothing to read and must leave the va_list cursor where it found it,
+  // for the fetches that come after this one.  The type holds no bytes, so
+  // the value the fetch produces carries no information either.
+  if (ac.kind == ArgKind::Ignore) {
+    builder.setInsertionPoint(op);
+    op.getResult().replaceAllUsesWith(builder.createDummyValue(
+        loc, resultTy,
+        clang::CharUnits::fromQuantity(dl.getTypeABIAlignment(resultTy))));
+    op->erase();
+    return mlir::success();
+  }
+
+  if (ac.kind == ArgKind::Indirect && !ac.byVal)
+    return reportNYI("a non-trivially-copyable type");
+
+  // neededInt counts 8-byte integer slots and neededSse counts 16-byte vector
+  // slots.  Zero of both means the type travels in memory and is read
+  // straight from the overflow area.
+  unsigned neededInt = ac.neededIntRegs;
+  unsigned neededSse = ac.neededSseRegs;
+
+  // Which coerced-pair element (0 = low eightbyte, 1 = high) is SSE rather
+  // than INTEGER class.  Only meaningful when isRegPair is set.
+  std::array<bool, 2> pairIsSse = {false, false};
+  bool isRegPair = false;
+
+  // A value needing two registers is either a coerced pair, one register per
+  // member, or a single wide scalar read from one address.
+  if (ac.kind == ArgKind::Direct && neededInt + neededSse == 2 &&
+      ac.coercedType) {
+    if (auto pairTy = mlir::dyn_cast<cir::RecordType>(ac.coercedType)) {
+      if (pairTy.getNumElements() != 2)
+        return reportNYI("a register coercion that is not two eightbytes");
+      assert(!ac.directOffset &&
+             "a pair already spans both eightbytes, so it cannot also start "
+             "partway into the value");
+      for (auto [i, memberTy] : llvm::enumerate(pairTy.getMembers()))
+        pairIsSse[i] = isSSERegisterClass(memberTy);
+      isRegPair = true;
+    }
+  }
+
+  auto vaListRecTy = mlir::dyn_cast<cir::RecordType>(
+      mlir::cast<cir::PointerType>(valist.getType()).getPointee());
+  if (!vaListRecTy || vaListRecTy.getNumElements() != 4) {
+    return reportNYI("a va_list that is not the four-field gp_offset / "
+                     "fp_offset / overflow_arg_area / reg_save_area cursor");
+  }
+  llvm::ArrayRef<mlir::Type> vaFields = vaListRecTy.getMembers();
+  cir::IntType byteTy = builder.getUIntNTy(8);
+  cir::PointerType bytePtrTy = builder.getPointerTo(byteTy);
+
+  builder.setInsertionPoint(op);
+
+  // Reading the overflow area also advances the cursor past the argument.
+  auto buildMemAddr = [&](CIRBaseBuilderTy &b) -> mlir::Value {
+    mlir::Value overflowP = b.createGetMember(loc, b.getPointerTo(vaFields[2]),
+                                              valist, "overflow_arg_area", 2);
+    mlir::Value overflow = b.createLoad(loc, overflowP);
+    mlir::Value bytePtr = b.createPtrBitcast(overflow, byteTy);
+    uint64_t tyAlign = argumentAreaAlign(resultTy, module, dl);
+    if (tyAlign > 8) {
+      mlir::Type wordTy = b.getUIntNTy(64);
+      mlir::Value asInt = cir::CastOp::create(
+          b, loc, wordTy, cir::CastKind::ptr_to_int, bytePtr);
+      mlir::Value bumped = b.createNUWAdd(
+          loc, asInt, b.getConstantInt(loc, wordTy, tyAlign - 1));
+      mlir::Value rounded = b.createAnd(
+          loc, bumped, b.getConstantInt(loc, wordTy, ~(tyAlign - 1)));
+      bytePtr = cir::CastOp::create(b, loc, bytePtrTy,
+                                    cir::CastKind::int_to_ptr, rounded);
+    }
+    uint64_t tySize = dl.getTypeSize(resultTy).getFixedValue();
+    uint64_t stride = (tySize + 7) & ~UINT64_C(7);
+    mlir::Value strideVal = b.getSignedInt(loc, stride, 32);
+    mlir::Value next = b.createPtrStride(loc, bytePtr, strideVal);
+    b.createStore(loc, next, overflowP);
+    return bytePtr;
+  };
+
+  mlir::Value addr;
+  if (neededInt == 0 && neededSse == 0) {
+    addr = buildMemAddr(builder);
+  } else {
+    mlir::Value gpOffsetP, fpOffsetP, gpOffset, fpOffset, inRegs;
+    if (neededInt) {
+      gpOffsetP = builder.createGetMember(
+          loc, builder.getPointerTo(vaFields[0]), valist, "gp_offset", 0);
+      gpOffset = builder.createLoad(loc, gpOffsetP);
+      mlir::Value limit =
+          builder.getConstantInt(loc, gpOffset.getType(), 48 - neededInt * 8);
+      inRegs = builder.createCompare(loc, cir::CmpOpKind::le, gpOffset, limit);
+    }
+    if (neededSse) {
+      fpOffsetP = builder.createGetMember(
+          loc, builder.getPointerTo(vaFields[1]), valist, "fp_offset", 1);
+      fpOffset = builder.createLoad(loc, fpOffsetP);
+      mlir::Value limit =
+          builder.getConstantInt(loc, fpOffset.getType(), 176 - neededSse * 
16);
+      mlir::Value fitsInFp =
+          builder.createCompare(loc, cir::CmpOpKind::le, fpOffset, limit);
+      inRegs =
+          inRegs ? builder.createLogicalAnd(loc, inRegs, fitsInFp) : fitsInFp;
+    }
+
+    // A two-eightbyte pair that is purely INTEGER class is contiguous in the
+    // register-save area, since GP slots are 8-byte packed, so the address of
+    // its low eightbyte is already the address of the whole value.  A pure
+    // SSE pair or a mixed pair is not contiguous, since SSE slots are 16-byte
+    // spaced and a mixed pair's halves live in disjoint areas, so each half is
+    // copied into a temp laid out as the coerced pair.
+    bool pairNeedsReassembly = isRegPair && neededSse != 0;
+    mlir::Value regPairTemp;
+    if (pairNeedsReassembly) {
+      // The temp is written one member at a time through the coerced pair, so
+      // it has to meet that type's alignment, and read back as the result, so
+      // it has to meet the result's declared one too.
+      regPairTemp = builder.createAlloca(
+          loc, builder.getPointerTo(ac.coercedType), "vaarg.reg",
+          clang::CharUnits::fromQuantity(
+              std::max(dl.getTypeABIAlignment(ac.coercedType),
+                       argumentAreaAlign(resultTy, module, dl))));
+    }
+
+    addr =
+        cir::TernaryOp::create(
+            builder, loc, inRegs,
+            /*trueBuilder=*/
+            [&](mlir::OpBuilder &ob, mlir::Location l) {
+              CIRBaseBuilderTy b(ob);
+              mlir::Value regSaveArea = b.createLoad(
+                  l, b.createGetMember(l, b.getPointerTo(vaFields[3]), valist,
+                                       "reg_save_area", 3));
+              regSaveArea = b.createPtrBitcast(regSaveArea, byteTy);
+
+              mlir::Value regAddr;
+              if (pairNeedsReassembly) {
+                auto pairTy = mlir::cast<cir::RecordType>(ac.coercedType);
+                // Same-class eightbytes sit regSize bytes apart within
+                // their own area (8 for GP, 16 for SSE), so track how
+                // many of each class came before.
+                unsigned seenOfClass[2] = {0, 0};
+                for (unsigned i = 0; i < 2; ++i) {
+                  bool isSse = pairIsSse[i];
+                  mlir::Value base = isSse ? fpOffset : gpOffset;
+                  unsigned regSize = isSse ? 16 : 8;
+                  unsigned prior = seenOfClass[isSse]++;
+                  mlir::Value off = base;
+                  if (prior) {
+                    off = b.createAdd(
+                        l, base,
+                        b.getConstantInt(l, base.getType(), prior * regSize));
+                  }
+                  mlir::Value src = b.createPtrStride(l, regSaveArea, off);
+                  mlir::Type elemTy = pairTy.getElementType(i);
+                  mlir::Value val =
+                      b.createLoad(l, b.createPtrBitcast(src, elemTy));
+                  b.createStore(l, val,
+                                b.createGetMember(l, b.getPointerTo(elemTy),
+                                                  regPairTemp, "", i));
+                }
+                regAddr = b.createPtrBitcast(regPairTemp, byteTy);
+              } else {
+                mlir::Value off = neededSse ? fpOffset : gpOffset;
+                regAddr = b.createPtrStride(l, regSaveArea, off);
+                uint64_t resultAlign = argumentAreaAlign(resultTy, module, dl);
+                uint64_t regSize = neededInt ? neededInt * 8 : 16;
----------------
adams381 wrote:

They did disagree.  The size follows the class the offset came from now, with 
an assert that only one is in play, since a fetch needing both is a pair and 
goes through reassembly.

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

Reply via email to