Skip to content
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
47 changes: 39 additions & 8 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2325,14 +2325,45 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
const QualType typeToSize = e->getTypeOfArgument();
const mlir::Location loc = cgf.getLoc(e->getSourceRange());
if (auto kind = e->getKind();
kind == UETT_SizeOf || kind == UETT_DataSizeOf) {
if (cgf.getContext().getAsVariableArrayType(typeToSize)) {
cgf.getCIRGenModule().errorNYI(e->getSourceRange(),
"sizeof operator for VariableArrayType",
e->getStmtClassName());
return builder.getConstant(
loc, cir::IntAttr::get(cgf.cgm.uInt64Ty,
llvm::APSInt(llvm::APInt(64, 1), true)));
kind == UETT_SizeOf || kind == UETT_DataSizeOf || kind == UETT_CountOf) {
if (const VariableArrayType *vat =
cgf.getContext().getAsVariableArrayType(typeToSize)) {
// For _Countof, we only want to evaluate if the extent is actually
// variable as opposed to a multi-dimensional array whose extent is
// constant but whose element type is variable.
bool evaluateExtent = true;
if (kind == UETT_CountOf && vat->getElementType()->isArrayType()) {
evaluateExtent =
!vat->getSizeExpr()->isIntegerConstantExpr(cgf.getContext());
}

if (evaluateExtent) {
if (e->isArgumentType()) {
// sizeof(type) - make sure to emit the VLA size.
cgf.emitVariablyModifiedType(typeToSize);
} else {
// C99 6.5.3.4p2: If the argument is an expression of type
// VLA, it is evaluated.
cgf.getCIRGenModule().errorNYI(
e->getSourceRange(),
"sizeof operator for VariableArrayType & evaluateExtent "
"ignoredExpr",
e->getStmtClassName());
return {};
}

// For _Countof, we just want to return the size of a single dimension.
if (kind == UETT_CountOf)
return cgf.getVLAElements1D(vat).numElts;

cgf.getCIRGenModule().errorNYI(
e->getSourceRange(),
"sizeof operator for VariableArrayType & evaluateExtent",
e->getStmtClassName());
return builder.getConstant(
loc, cir::IntAttr::get(cgf.cgm.uInt64Ty,
-llvm::APSInt(llvm::APInt(64, 1), true)));
}
}
} else if (e->getKind() == UETT_OpenMPRequiredSimdAlign) {
cgf.getCIRGenModule().errorNYI(
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,14 @@ CIRGenFunction::getVLASize(const VariableArrayType *type) {
return {numElements, elementType};
}

CIRGenFunction::VlaSizePair
CIRGenFunction::getVLAElements1D(const VariableArrayType *vla) {
mlir::Value vlaSize = vlaSizeMap[vla->getSizeExpr()];
assert(vlaSize && "no size for VLA!");
assert(vlaSize->getType() == sizeTy);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vlaSize isn't a pointer, so this fails to build:

runner/_work/llvm-project/llvm-project/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
2025-11-26T19:23:08.6857824Z /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/CIR/CodeGen/CIRGenFunction.cpp:1145:17: error: member reference type 'mlir::Value' is not a pointer; did you mean to use '.'?
2025-11-26T19:23:08.6859083Z  1145 |   assert(vlaSize->getType() == sizeTy);
2025-11-26T19:23:08.6859503Z       |          ~~~~~~~^~
2025-11-26T19:23:08.6859814Z       |                 .
2025-11-26T19:23:08.6860251Z /usr/include/assert.h:103:27: note: expanded from macro 'assert'
2025-11-26T19:23:08.6860727Z   103 |      (static_cast <bool> (expr)                                         \
2025-11-26T19:23:08.6861052Z       |                           ^~~~

Note this seems to have been caught by pre-commit CI as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm preparing a fix for it now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching it

return {vlaSize, vla->getElementType()};
}

// TODO(cir): Most of this function can be shared between CIRGen
// and traditional LLVM codegen
void CIRGenFunction::emitVariablyModifiedType(QualType type) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,10 @@ class CIRGenFunction : public CIRGenTypeCache {
VlaSizePair(mlir::Value num, QualType ty) : numElts(num), type(ty) {}
};

/// Return the number of elements for a single dimension
/// for the given array type.
VlaSizePair getVLAElements1D(const VariableArrayType *vla);

/// Returns an MLIR::Value+QualType pair that corresponds to the size,
/// in non-variably-sized elements, of a variable length array type,
/// plus that largest non-variably-sized element type. Assumes that
Expand Down
52 changes: 52 additions & 0 deletions clang/test/CIR/CodeGen/count-of.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -std=c2y -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG

unsigned long vla_with_array_element_type_with_const_size() {
long size;
return _Countof(int[5][size]);
}

// CIR: %[[RET_ADDR:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["__retval"]
// CIR: %[[SIZE_ADDR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["size"]
// CIR: %[[CONST_5:.*]] = cir.const #cir.int<5> : !u64i
// CIR: cir.store %[[CONST_5]], %[[RET_ADDR]] : !u64i, !cir.ptr<!u64i>
// CIR: %[[RET_VAL:.*]] = cir.load %[[RET_ADDR]] : !cir.ptr<!u64i>, !u64i
// CIR: cir.return %[[RET_VAL]] : !u64i

// LLVM: %[[RET_ADDR:.*]] = alloca i64, i64 1, align 8
// LLVM: %[[SIZE_ADDR:.*]] = alloca i64, i64 1, align 8
// LLVM: store i64 5, ptr %[[RET_ADDR]], align 8
// LLVM: %[[RET_VAL:.*]] = load i64, ptr %[[RET_ADDR]], align 8
// LLVM: ret i64 %[[RET_VAL]]

// OGCG: %[[SIZE_ADDR:.*]] = alloca i64, align 8
// OGCG: ret i64 5

unsigned long vla_with_array_element_type_non_const_size() {
long size;
return _Countof(int[size][size]);
}

// CIR: %[[REET_ADDR:.*]] = cir.alloca !u64i, !cir.ptr<!u64i>, ["__retval"]
// CIR: %[[SIZE_ADDR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["size"]
// CIR: %[[TMP_SIZE:.*]] = cir.load {{.*}} %[[SIZE_ADDR]] : !cir.ptr<!s64i>, !s64i
// CIR: %[[TMP_SIZE_U64:.*]] = cir.cast integral %[[TMP_SIZE]] : !s64i -> !u64i
// CIR: cir.store %[[TMP_SIZE_U64]], %[[RET_ADDR]] : !u64i, !cir.ptr<!u64i>
// CIR: %[[TMP_RET:.*]] = cir.load %[[RET_ADDR]] : !cir.ptr<!u64i>, !u64i
// CIR: cir.return %[[TMP_RET]] : !u64i

// LLVM: %[[RET_ADDR:.*]] = alloca i64, i64 1, align 8
// LLVM: %[[SIZE_ADDR:.*]] = alloca i64, i64 1, align 8
// LLVM: %[[TMP_SIZE:.*]] = load i64, ptr %[[SIZE_ADDR]], align 8
// LLVM: store i64 %[[TMP_SIZE]], ptr %[[RET_ADDR]], align 8
// LLVM: %[[TMP_RET:.*]] = load i64, ptr %[[RET_ADDR]], align 8
// LLVM: ret i64 %[[TMP_RET]]

// OGCG: %[[SIZE_ADDR:.*]] = alloca i64, align 8
// OGCG: %[[TMP_SIZE:.*]] = load i64, ptr %[[SIZE_ADDR]], align 8
// OGCG: %[[TMP_SIZE_2:.*]] = load i64, ptr %[[SIZE_ADDR]], align 8
// OGCG: ret i64 %[[TMP_SIZE]]
Loading