Skip to content

Commit a07dbdf

Browse files
authored
[CIR] Implement array new handling for variable array size (#1313)
Implement CIR code generation for array new in the case where the array size is represented by a variable.
1 parent 6d1b387 commit a07dbdf

File tree

3 files changed

+365
-53
lines changed

3 files changed

+365
-53
lines changed

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

+107-2
Original file line numberDiff line numberDiff line change
@@ -672,8 +672,113 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &CGF, const CXXNewExpr *e,
672672
size = CGF.getBuilder().getConstInt(Loc, allocationSize);
673673
}
674674
} else {
675-
// TODO: Handle the variable size case
676-
llvm_unreachable("NYI");
675+
// Create a value for the variable number of elements
676+
numElements = CGF.emitScalarExpr(*e->getArraySize());
677+
auto numElementsType = mlir::cast<cir::IntType>(numElements.getType());
678+
unsigned numElementsWidth = numElementsType.getWidth();
679+
680+
// We might need check for overflow.
681+
682+
mlir::Value hasOverflow = nullptr;
683+
// The clang LLVM IR codegen checks for the size variable being signed,
684+
// having a smaller width than size_t, and having a larger width than
685+
// size_t. However, the AST implicitly casts the size variable to size_t
686+
// so none of these conditions will ever be met.
687+
bool isSigned =
688+
(*e->getArraySize())->getType()->isSignedIntegerOrEnumerationType();
689+
assert(!isSigned && (numElementsWidth == sizeWidth) &&
690+
(numElements.getType() == CGF.SizeTy) &&
691+
"Expected array size to be implicitly cast to size_t!");
692+
693+
// There are up to three conditions we need to test for:
694+
// 1) if minElements > 0, we need to check whether numElements is smaller
695+
// than that.
696+
// 2) we need to compute
697+
// sizeWithoutCookie := numElements * typeSizeMultiplier
698+
// and check whether it overflows; and
699+
// 3) if we need a cookie, we need to compute
700+
// size := sizeWithoutCookie + cookieSize
701+
// and check whether it overflows.
702+
703+
if (minElements) {
704+
// Don't allow allocation of fewer elements than we have initializers.
705+
if (!hasOverflow) {
706+
// FIXME: Avoid creating this twice. It may happen above.
707+
mlir::Value minElementsV = CGF.getBuilder().getConstInt(
708+
Loc, llvm::APInt(sizeWidth, minElements));
709+
hasOverflow = CGF.getBuilder().createCompare(Loc, cir::CmpOpKind::lt,
710+
numElements, minElementsV);
711+
}
712+
}
713+
714+
size = numElements;
715+
716+
// Multiply by the type size if necessary. This multiplier
717+
// includes all the factors for nested arrays.
718+
//
719+
// This step also causes numElements to be scaled up by the
720+
// nested-array factor if necessary. Overflow on this computation
721+
// can be ignored because the result shouldn't be used if
722+
// allocation fails.
723+
if (typeSizeMultiplier != 1) {
724+
mlir::Value tsmV = CGF.getBuilder().getConstInt(Loc, typeSizeMultiplier);
725+
auto mulResult = CGF.getBuilder().createBinOpOverflowOp(
726+
Loc, mlir::cast<cir::IntType>(CGF.SizeTy),
727+
cir::BinOpOverflowKind::Mul, size, tsmV);
728+
729+
if (hasOverflow)
730+
hasOverflow =
731+
CGF.getBuilder().createOr(hasOverflow, mulResult.overflow);
732+
else
733+
hasOverflow = mulResult.overflow;
734+
735+
size = mulResult.result;
736+
737+
// Also scale up numElements by the array size multiplier.
738+
if (arraySizeMultiplier != 1) {
739+
// If the base element type size is 1, then we can re-use the
740+
// multiply we just did.
741+
if (typeSize.isOne()) {
742+
assert(arraySizeMultiplier == typeSizeMultiplier);
743+
numElements = size;
744+
745+
// Otherwise we need a separate multiply.
746+
} else {
747+
mlir::Value asmV =
748+
CGF.getBuilder().getConstInt(Loc, arraySizeMultiplier);
749+
numElements = CGF.getBuilder().createMul(numElements, asmV);
750+
}
751+
}
752+
} else {
753+
// numElements doesn't need to be scaled.
754+
assert(arraySizeMultiplier == 1);
755+
}
756+
757+
// Add in the cookie size if necessary.
758+
if (cookieSize != 0) {
759+
sizeWithoutCookie = size;
760+
mlir::Value cookieSizeV = CGF.getBuilder().getConstInt(Loc, cookieSize);
761+
auto addResult = CGF.getBuilder().createBinOpOverflowOp(
762+
Loc, mlir::cast<cir::IntType>(CGF.SizeTy),
763+
cir::BinOpOverflowKind::Add, size, cookieSizeV);
764+
765+
if (hasOverflow)
766+
hasOverflow =
767+
CGF.getBuilder().createOr(hasOverflow, addResult.overflow);
768+
else
769+
hasOverflow = addResult.overflow;
770+
771+
size = addResult.result;
772+
}
773+
774+
// If we had any possibility of dynamic overflow, make a select to
775+
// overwrite 'size' with an all-ones value, which should cause
776+
// operator new to throw.
777+
if (hasOverflow) {
778+
mlir::Value allOnes =
779+
CGF.getBuilder().getConstInt(Loc, llvm::APInt::getAllOnes(sizeWidth));
780+
size = CGF.getBuilder().createSelect(Loc, hasOverflow, allOnes, size);
781+
}
677782
}
678783

679784
if (cookieSize == 0)

0 commit comments

Comments
 (0)