================
@@ -1473,3 +1425,255 @@ 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");
+
+ // How many eightbytes of each register class the fetched type occupies.
+ // Zero of both means the type travels in memory and is read straight from
+ // the overflow area.
+ unsigned neededInt = 0, neededSse = 0;
+ // 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;
+
+ if (ac.kind == ArgKind::Extend) {
+ neededInt = 1;
+ } else if (ac.kind == ArgKind::Direct) {
+ if (mlir::Type coerced = ac.coercedType) {
+ if (auto pairTy = mlir::dyn_cast<cir::RecordType>(coerced)) {
+ assert(pairTy.getNumElements() == 2 &&
+ "a register-class coercion spans at most two eightbytes");
+ for (auto [i, memberTy] : llvm::enumerate(pairTy.getMembers())) {
+ if (isSSERegisterClass(memberTy)) {
+ pairIsSse[i] = true;
+ ++neededSse;
+ } else {
+ ++neededInt;
+ }
+ }
+ isRegPair = true;
+ } else if (isSSERegisterClass(coerced)) {
+ if (fitsOneVectorSlot(coerced, dl))
+ neededSse = 1;
+ } else {
+ neededInt = isWide128BitInt(coerced) ? 2 : 1;
+ }
+ } else if (auto fp = mlir::dyn_cast<cir::FPTypeInterface>(resultTy)) {
+ // An uncoerced scalar float is SSE, unless it is x87 long double,
+ // which the SysV ABI always classifies MEMORY. That is recognized
+ // from the semantics here, because the classification spells a
+ // memory-class scalar the same way it spells a register one.
+ if (&fp.getFloatSemantics() != &llvm::APFloat::x87DoubleExtended())
+ neededSse = 1;
+ } else if (mlir::isa<cir::VectorType>(resultTy)) {
+ if (fitsOneVectorSlot(resultTy, dl))
+ neededSse = 1;
+ } else if (mlir::isa<cir::IntType, cir::PointerType, cir::BoolType>(
+ resultTy)) {
+ neededInt = isWide128BitInt(resultTy) ? 2 : 1;
+ } else {
+ return reportNYI("this type");
+ }
+ } else if (ac.kind != ArgKind::Indirect) {
+ return reportNYI("a type with an argument classification this fetch does "
+ "not model");
+ }
+ // Indirect is the one remaining kind, and it takes no register.
+
+ auto vaListRecTy = mlir::cast<cir::RecordType>(
+ mlir::cast<cir::PointerType>(valist.getType()).getPointee());
+ llvm::ArrayRef<mlir::Type> vaFields = vaListRecTy.getMembers();
+ assert(vaFields.size() == 4 &&
+ "expected the four-field gp_offset / fp_offset / overflow_arg_area / "
+ "reg_save_area argument cursor");
+ 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);
----------------
adams381 wrote:
`rewriteVAArg` is down from 254 lines to about 80. The round-up, overflow arm,
register gate, pair reassembly, temp copy and cursor write-back are each their
own function now.
`ptrmask` needs a `cir.ptr_mask` op, which CIR does not have yet. I'll put
that up as its own PR and adopt it here once it lands.
https://github.com/llvm/llvm-project/pull/222420
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits