Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR][CIRGen] Handle initilization of arrays #431

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
void buildCopy(QualType type, const AggValueSlot &dest,
const AggValueSlot &src);

void buildArrayInit(Address DestPtr, mlir::cir::ArrayType AType,
QualType ArrayQTy, Expr *ExprToVisit,
ArrayRef<Expr *> Args, Expr *ArrayFiller);

AggValueSlot::NeedsGCBarriers_t needsGC(QualType T) {
if (CGF.getLangOpts().getGC() && TypeRequiresGCollection(T))
llvm_unreachable("garbage collection is NYI");
Expand Down Expand Up @@ -394,6 +398,113 @@ void AggExprEmitter::buildCopy(QualType type, const AggValueSlot &dest,
CGF.buildAggregateCopy(DestLV, SrcLV, type, dest.mayOverlap(), false);
}

// FIXME(cir): This function could be shared with traditional LLVM codegen
/// Determine if E is a trivial array filler, that is, one that is
/// equivalent to zero-initialization.
static bool isTrivialFiller(Expr *E) {
if (!E)
return true;

if (isa<ImplicitValueInitExpr>(E))
return true;

if (auto *ILE = dyn_cast<InitListExpr>(E)) {
if (ILE->getNumInits())
return false;
return isTrivialFiller(ILE->getArrayFiller());
}

if (auto *Cons = dyn_cast_or_null<CXXConstructExpr>(E))
return Cons->getConstructor()->isDefaultConstructor() &&
Cons->getConstructor()->isTrivial();

// FIXME: Are there other cases where we can avoid emitting an initializer?
return false;
}

void AggExprEmitter::buildArrayInit(Address DestPtr, mlir::cir::ArrayType AType,
QualType ArrayQTy, Expr *ExprToVisit,
ArrayRef<Expr *> Args, Expr *ArrayFiller) {
uint64_t NumInitElements = Args.size();

uint64_t NumArrayElements = AType.getSize();
assert(NumInitElements != 0 && "expected at least one initializaed value");
assert(NumInitElements <= NumArrayElements);

QualType elementType =
CGF.getContext().getAsArrayType(ArrayQTy)->getElementType();

auto cirElementType = CGF.convertType(elementType);
auto cirElementPtrType = mlir::cir::PointerType::get(
CGF.getBuilder().getContext(), cirElementType);
auto loc = CGF.getLoc(ExprToVisit->getSourceRange());

// Cast from cir.ptr<cir.array<elementType> to cir.ptr<elementType>
auto begin = CGF.getBuilder().create<mlir::cir::CastOp>(
loc, cirElementPtrType, mlir::cir::CastKind::array_to_ptrdecay,
DestPtr.getPointer());

CharUnits elementSize = CGF.getContext().getTypeSizeInChars(elementType);
CharUnits elementAlign =
DestPtr.getAlignment().alignmentOfArrayElement(elementSize);

// Exception safety requires us to destroy all the
// already-constructed members if an initializer throws.
// For that, we'll need an EH cleanup.
[[maybe_unused]] QualType::DestructionKind dtorKind =
elementType.isDestructedType();
[[maybe_unused]] Address endOfInit = Address::invalid();
assert(!CGF.needsEHCleanup(dtorKind) && "destructed types NIY");

// The 'current element to initialize'. The invariants on this
// variable are complicated. Essentially, after each iteration of
// the loop, it points to the last initialized element, except
// that it points to the beginning of the array before any
// elements have been initialized.
mlir::Value element = begin;

// Don't build the 'one' before the cycle to avoid
// emmiting the redundant cir.const(1) instrs.
mlir::Value one;

// Emit the explicit initializers.
for (uint64_t i = 0; i != NumInitElements; ++i) {
if (i == 1)
one = CGF.getBuilder().getConstInt(
loc, CGF.PtrDiffTy.cast<mlir::cir::IntType>(), 1);

// Advance to the next element.
if (i > 0) {
element = CGF.getBuilder().create<mlir::cir::PtrStrideOp>(
loc, cirElementPtrType, element, one);

// Tell the cleanup that it needs to destroy up to this
// element. TODO: some of these stores can be trivially
// observed to be unnecessary.
assert(!endOfInit.isValid() && "destructed types NIY");
}

LValue elementLV = CGF.makeAddrLValue(
Address(element, cirElementType, elementAlign), elementType);
buildInitializationToLValue(Args[i], elementLV);
}

// Check whether there's a non-trivial array-fill expression.
bool hasTrivialFiller = isTrivialFiller(ArrayFiller);

// Any remaining elements need to be zero-initialized, possibly
// using the filler expression. We can skip this if the we're
// emitting to zeroed memory.
if (NumInitElements != NumArrayElements &&
!(Dest.isZeroed() && hasTrivialFiller &&
CGF.getTypes().isZeroInitializable(elementType))) {
llvm_unreachable("zero-initialization of arrays NIY");
}

// Leave the partial-array cleanup if we entered one.
assert(!dtorKind && "destructed types NIY");
}

/// True if the given aggregate type requires special GC API calls.
bool AggExprEmitter::TypeRequiresGCollection(QualType T) {
// Only record types have members that might require garbage collection.
Expand Down Expand Up @@ -885,6 +996,16 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
LValue DestLV = CGF.makeAddrLValue(Dest.getAddress(), ExprToVisit->getType());

// Handle initialization of an array.
if (ExprToVisit->getType()->isConstantArrayType()) {
auto AType = cast<mlir::cir::ArrayType>(Dest.getAddress().getElementType());
buildArrayInit(Dest.getAddress(), AType, ExprToVisit->getType(),
ExprToVisit, InitExprs, ArrayFiller);
return;
} else if (ExprToVisit->getType()->isVariableArrayType()) {
llvm_unreachable("variable arrays NYI");
return;
}

if (ExprToVisit->getType()->isArrayType()) {
llvm_unreachable("NYI");
}
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CIR/CodeGen/array-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,22 @@ void foo() {
// CHECK-NEXT: %1 = cir.const(#cir.const_array<[9.000000e+00, 8.000000e+00, 7.000000e+00]> : !cir.array<f64 x 3>) : !cir.array<f64 x 3>
// CHECK-NEXT: cir.store %1, %0 : !cir.array<f64 x 3>, cir.ptr <!cir.array<f64 x 3>>

void bar(int a, int b, int c) {
int arr[] = {a,b,c};
}

// CHECK: cir.func @bar
// CHECK: [[ARR:%.*]] = cir.alloca !cir.array<!s32i x 3>, cir.ptr <!cir.array<!s32i x 3>>, ["arr", init] {alignment = 4 : i64}
// CHECK-NEXT: cir.store %arg0, [[A:%.*]] : !s32i, cir.ptr <!s32i>
// CHECK-NEXT: cir.store %arg1, [[B:%.*]] : !s32i, cir.ptr <!s32i>
// CHECK-NEXT: cir.store %arg2, [[C:%.*]] : !s32i, cir.ptr <!s32i>
// CHECK-NEXT: [[FI_EL:%.*]] = cir.cast(array_to_ptrdecay, [[ARR]] : !cir.ptr<!cir.array<!s32i x 3>>), !cir.ptr<!s32i>
// CHECK-NEXT: [[LOAD_A:%.*]] = cir.load [[A]] : cir.ptr <!s32i>, !s32i
// CHECK-NEXT: cir.store [[LOAD_A]], [[FI_EL]] : !s32i, cir.ptr <!s32i>
// CHECK-NEXT: [[ONE:%.*]] = cir.const(#cir.int<1> : !s64i) : !s64i
// CHECK-NEXT: [[SE_EL:%.*]] = cir.ptr_stride(%4 : !cir.ptr<!s32i>, [[ONE]] : !s64i), !cir.ptr<!s32i>
// CHECK-NEXT: [[LOAD_B:%.*]] = cir.load [[B]] : cir.ptr <!s32i>, !s32i
// CHECK-NEXT: cir.store [[LOAD_B]], [[SE_EL]] : !s32i, cir.ptr <!s32i>
// CHECK-NEXT: [[TH_EL:%.*]] = cir.ptr_stride(%7 : !cir.ptr<!s32i>, [[ONE]] : !s64i), !cir.ptr<!s32i>
// CHECK-NEXT: [[LOAD_C:%.*]] = cir.load [[C]] : cir.ptr <!s32i>, !s32i
// CHECK-NEXT: cir.store [[LOAD_C]], [[TH_EL]] : !s32i, cir.ptr <!s32i>
Loading