Files
llvm-project/clang/test/CIR/CodeGen/new-array-size-conv.cpp
Andy Kaylor 729480c4ae [CIR] Generalize cxx alloc new size handling (#187790)
The non-constant size handling in `emitCXXNewAllocSize` was making the
incorrect assumption that the default behavior of the size value being
explicitly cast to size_t would be the only behavior we'd see. This is
actually only true with C++14 and later. To properly handle earlier
standards, we need the more robust checking that classic codegen does in
the equivalent function. This change adds that handling.

Assisted-by: Cursor / claude-4.6-opus-high
2026-03-23 11:47:16 -07:00

71 lines
3.3 KiB
C++

// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
// Tests for array new expressions where the array size is not implicitly
// converted to size_t by Sema (pre-C++14 behavior). The CIR codegen must
// handle the signed/width conversion itself.
typedef __typeof__(sizeof(int)) size_t;
// Sized non-allocating (placement) new.
void *operator new[](size_t, void *p) noexcept { return p; }
struct S {
int x;
S();
};
// Signed int array size with multi-byte element (typeSizeMultiplier != 1).
// The sign extension is done and the multiply overflow catches negative values.
void t_new_signed_size(int n) {
auto p = new double[n];
}
// CIR-LABEL: cir.func {{.*}} @_Z17t_new_signed_sizei
// CIR: %[[N:.*]] = cir.load{{.*}} %[[ARG_ALLOCA:.*]]
// CIR: %[[N_SEXT:.*]] = cir.cast integral %[[N]] : !s32i -> !s64i
// CIR: %[[N_SIZE_T:.*]] = cir.cast integral %[[N_SEXT]] : !s64i -> !u64i
// CIR: %[[ELEMENT_SIZE:.*]] = cir.const #cir.int<8> : !u64i
// CIR: %[[RESULT:.*]], %[[OVERFLOW:.*]] = cir.mul.overflow %[[N_SIZE_T]], %[[ELEMENT_SIZE]] : !u64i -> !u64i
// CIR: %[[ALL_ONES:.*]] = cir.const #cir.int<18446744073709551615> : !u64i
// CIR: %[[ALLOC_SIZE:.*]] = cir.select if %[[OVERFLOW]] then %[[ALL_ONES]] else %[[RESULT]]
// LLVM-LABEL: define{{.*}} void @_Z17t_new_signed_sizei
// LLVM: %[[N:.*]] = load i32, ptr %{{.+}}
// LLVM: %[[N_SIZE_T:.*]] = sext i32 %[[N]] to i64
// LLVM: %[[MUL_OVERFLOW:.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %[[N_SIZE_T]], i64 8)
// OGCG-LABEL: define{{.*}} void @_Z17t_new_signed_sizei
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
// OGCG: %[[N_SIZE_T:.*]] = sext i32 %[[N]] to i64
// OGCG: %[[MUL_OVERFLOW:.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %[[N_SIZE_T]], i64 8)
// Signed int array size with single-byte element (typeSizeMultiplier == 1).
// A signed comparison catches negative values directly.
void t_new_signed_size_char(int n) {
auto p = new char[n];
}
// CIR-LABEL: cir.func {{.*}} @_Z22t_new_signed_size_chari
// CIR: %[[N:.*]] = cir.load{{.*}} %[[ARG_ALLOCA:.*]]
// CIR: %[[N_SEXT:.*]] = cir.cast integral %[[N]] : !s32i -> !s64i
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s64i
// CIR: %[[IS_NEG:.*]] = cir.cmp lt %[[N_SEXT]], %[[ZERO]] : !s64i
// CIR: %[[N_SIZE_T:.*]] = cir.cast integral %[[N_SEXT]] : !s64i -> !u64i
// CIR: %[[ALL_ONES:.*]] = cir.const #cir.int<18446744073709551615> : !u64i
// CIR: %[[ALLOC_SIZE:.*]] = cir.select if %[[IS_NEG]] then %[[ALL_ONES]] else %[[N_SIZE_T]]
// LLVM-LABEL: define{{.*}} void @_Z22t_new_signed_size_chari
// LLVM: %[[N:.*]] = load i32, ptr %{{.+}}
// LLVM: %[[N_SIZE_T:.*]] = sext i32 %[[N]] to i64
// LLVM: %[[IS_NEG:.*]] = icmp slt i64 %[[N_SIZE_T]], 0
// OGCG-LABEL: define{{.*}} void @_Z22t_new_signed_size_chari
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
// OGCG: %[[N_SIZE_T:.*]] = sext i32 %[[N]] to i64
// OGCG: %[[IS_NEG:.*]] = icmp slt i64 %[[N_SIZE_T]], 0