blob: e514ba18152290c6a7ec9e23ca94a085ac46b17f [file] [edit]
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
//
// FRONTEND prefix: post-mem2reg/sroa output of clang -O0 lit-side. Pins
// down the IR emitCountedBySize emits directly (so a regression that
// re-folds __bdos at AST time would lose 'counted_by.load' / 'mul' /
// 'flexible_array_member_size' here). The same prefix is used for both
// the default constant-evaluator and -fexperimental-new-constant-interpreter
// runs: those must produce identical IR for every test below, which is the
// regression guard for "the bytecode interpreter also defers to runtime
// for counted_by FAM __bdos."
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -Wno-missing-declarations \
// RUN: -disable-O0-optnone -emit-llvm -o - %s \
// RUN: | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefix=FRONTEND
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -Wno-missing-declarations \
// RUN: -fexperimental-new-constant-interpreter -disable-O0-optnone \
// RUN: -emit-llvm -o - %s \
// RUN: | opt -S -passes=mem2reg,sroa | FileCheck %s --check-prefix=FRONTEND
// O2 prefix: post-optimization shape; pins down the concrete folded
// constants for the '&fam' layout tests.
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wno-missing-declarations \
// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefix=O2
// See https://github.com/llvm/llvm-project/issues/200014
//
// __builtin_dynamic_object_size on a flexible array member must consult the
// 'counted_by' attribute on the same expression shapes the IR runtime path
// in CGBuiltin handles -- and *only* those.
//
// Pre-fix, the direct-struct-access tests (local/global x {fam, &fam[idx]})
// were folded by the AST constant evaluator to layout-derived constants
// (trailing struct padding for locals; trailing initializer data for
// globals), silently bypassing emitCountedBySize.
//
// The '&fam' (array-as-a-whole) tests are *intentionally* layout-derived in
// both GCC and Clang: '&af.fam' designates the array object itself, not
// the pointer-to-element that 'af.fam' decays to, and findStructFieldAccess
// bails out on that shape.
//
// Behavior matrix (verified against GCC 16 for __bdos(_, 1)):
//
// form \ base | local af (count=%n) | global gaf (count=10,init=15) | pointer p
// --------------+---------------------+-------------------------------+-----------
// fam (decay) | runtime: count*1 | runtime: count*1 | runtime
// &fam[idx] | runtime: count-idx | runtime: count-idx | runtime
// &fam (whole) | layout: 7 (pad) | layout: 15 (init) | unknown:-1
//
// See the RUN-line block above for the prefix conventions
// (FRONTEND covers both the default and bytecode -O0 const-evaluators;
// O2 covers post-optimization).
typedef __SIZE_TYPE__ size_t;
struct annotated_flex {
size_t count;
char induce_padding;
char fam[] __attribute__((counted_by(count)));
};
// Initializer length 15 bytes ("i am very long" + NUL); count lies as 10.
struct annotated_flex gaf = {
.fam = "i am very long",
.count = 10,
};
extern size_t sink;
// Test: local x fam (decayed pointer-to-element).
// Pre-fix bug: AST const-eval folded this to trailing struct padding (7).
// Expected: runtime counted_by path -- look for 'counted_by.load' in
// FRONTEND IR and 'smax(%n, 0)' at -O2.
// FRONTEND-LABEL: define dso_local i64 @local_fam(
// FRONTEND-SAME: i64 noundef [[N:%.*]]) #[[ATTR0:[0-9]+]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// FRONTEND-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
// FRONTEND-NEXT: store i64 [[N]], ptr [[COUNT]], align 8
// FRONTEND-NEXT: [[INDUCE_PADDING:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 1
// FRONTEND-NEXT: store i8 0, ptr [[INDUCE_PADDING]], align 8
// FRONTEND-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AF]], i64 9
// FRONTEND-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP0]], i8 0, i64 7, i1 false)
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0
// FRONTEND-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64
// FRONTEND-NEXT: store i64 [[TMP1]], ptr @sink, align 8
// FRONTEND-NEXT: [[FAM1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM1]], i64 0, i64 0
// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], -1
// FRONTEND-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP3]]
//
// O2-LABEL: define dso_local noundef range(i64 0, -9223372036854775808) i64 @local_fam(
// O2-SAME: i64 noundef [[N:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6:[0-9]+]]
// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 9
// O2-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA5:![0-9]+]]
// O2-NEXT: [[TMP2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: ret i64 [[TMP2]]
//
size_t local_fam(size_t n) {
struct annotated_flex af = { .count = n };
sink = (size_t)(__UINTPTR_TYPE__)af.fam;
return __builtin_dynamic_object_size(af.fam, 1);
}
// Test: local x &fam[idx].
// Pre-fix bug: AST const-eval folded this to padding-minus-idx (5).
// Expected: runtime counted_by path with index subtracted -- look for
// 'counted_by.load' + 'sub' in FRONTEND IR and 'add %n, -2' at -O2.
// FRONTEND-LABEL: define dso_local i64 @local_subscript(
// FRONTEND-SAME: i64 noundef [[N:%.*]]) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// FRONTEND-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
// FRONTEND-NEXT: store i64 [[N]], ptr [[COUNT]], align 8
// FRONTEND-NEXT: [[INDUCE_PADDING:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 1
// FRONTEND-NEXT: store i8 0, ptr [[INDUCE_PADDING]], align 8
// FRONTEND-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AF]], i64 9
// FRONTEND-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP0]], i8 0, i64 7, i1 false)
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 2
// FRONTEND-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[ARRAYIDX]] to i64
// FRONTEND-NEXT: store i64 [[TMP1]], ptr @sink, align 8
// FRONTEND-NEXT: [[FAM1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM1]], i64 0, i64 2
// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[RESULT:%.*]] = sub nuw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 2
// FRONTEND-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1
// FRONTEND-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// FRONTEND-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i64 [[RESULT]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP4]]
//
// O2-LABEL: define dso_local noundef range(i64 0, -9223372036854775808) i64 @local_subscript(
// O2-SAME: i64 noundef [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 11
// O2-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARRAYIDX]] to i64
// O2-NEXT: store i64 [[TMP0]], ptr @sink, align 8, !tbaa [[LONG_TBAA5]]
// O2-NEXT: [[RESULT:%.*]] = add i64 [[N]], -2
// O2-NEXT: [[TMP1:%.*]] = call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: ret i64 [[TMP1]]
//
size_t local_subscript(size_t n) {
struct annotated_flex af = { .count = n };
sink = (size_t)(__UINTPTR_TYPE__)&af.fam[2];
return __builtin_dynamic_object_size(&af.fam[2], 1);
}
// Test: local x &fam (array-as-a-whole).
// Intentionally layout-derived, NOT counted_by (matches GCC). The frontend
// must fall through to @llvm.objectsize, which folds to the trailing struct
// padding (7) at -O2.
// FRONTEND-LABEL: define dso_local i64 @local_addrof(
// FRONTEND-SAME: i64 noundef [[N:%.*]]) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// FRONTEND-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
// FRONTEND-NEXT: store i64 [[N]], ptr [[COUNT]], align 8
// FRONTEND-NEXT: [[INDUCE_PADDING:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 1
// FRONTEND-NEXT: store i8 0, ptr [[INDUCE_PADDING]], align 8
// FRONTEND-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AF]], i64 9
// FRONTEND-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[TMP0]], i8 0, i64 7, i1 false)
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[FAM]] to i64
// FRONTEND-NEXT: store i64 [[TMP1]], ptr @sink, align 8
// FRONTEND-NEXT: [[FAM1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[TMP2:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr [[FAM1]], i1 false, i1 true, i1 true)
// FRONTEND-NEXT: ret i64 [[TMP2]]
//
// O2-LABEL: define dso_local noundef i64 @local_addrof(
// O2-SAME: i64 noundef [[N:%.*]]) local_unnamed_addr #[[ATTR0]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[AF:%.*]] = alloca [[STRUCT_ANNOTATED_FLEX:%.*]], align 8
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 9
// O2-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA5]]
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: ret i64 7
//
size_t local_addrof(size_t n) {
struct annotated_flex af = { .count = n };
sink = (size_t)(__UINTPTR_TYPE__)&af.fam;
return __builtin_dynamic_object_size(&af.fam, 1);
}
// Test: global x fam (decayed pointer-to-element).
// Pre-fix bug: AST const-eval folded this to the static initializer length
// (15 = strlen("i am very long")+1), bypassing the counted_by lie of 10.
// Expected: runtime path -- 'load i64, ptr @gaf' (the count field) in both
// FRONTEND and O2 IR.
// FRONTEND-LABEL: define dso_local i64 @global_fam(
// FRONTEND-SAME: ) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr @gaf, align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], -1
// FRONTEND-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP1]]
//
// O2-LABEL: define dso_local range(i64 0, -9223372036854775808) i64 @global_fam(
// O2-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr @gaf, align 8
// O2-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[COUNTED_BY_LOAD]], i64 0)
// O2-NEXT: ret i64 [[TMP0]]
//
size_t global_fam(void) {
return __builtin_dynamic_object_size(gaf.fam, 1);
}
// Test: global x &fam[idx].
// Pre-fix bug: folded to (initializer_length - idx) = 12.
// Expected: runtime path with index subtracted -- 'load i64, ptr @gaf' then
// 'sub' in FRONTEND IR / 'add %count, -3' at -O2.
// FRONTEND-LABEL: define dso_local i64 @global_subscript(
// FRONTEND-SAME: ) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr @gaf, align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[RESULT:%.*]] = sub nuw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 3
// FRONTEND-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
// FRONTEND-NEXT: [[TMP1:%.*]] = and i1 true, [[TMP0]]
// FRONTEND-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i64 [[RESULT]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP2]]
//
// O2-LABEL: define dso_local range(i64 0, -9223372036854775808) i64 @global_subscript(
// O2-SAME: ) local_unnamed_addr #[[ATTR2]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr @gaf, align 8
// O2-NEXT: [[RESULT:%.*]] = add i64 [[COUNTED_BY_LOAD]], -3
// O2-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
// O2-NEXT: ret i64 [[TMP0]]
//
size_t global_subscript(void) {
return __builtin_dynamic_object_size(&gaf.fam[3], 1);
}
// Test: global x &fam (array-as-a-whole).
// Intentionally layout-derived. Frontend emits @llvm.objectsize, which
// folds to the initializer length (15) at -O2. Matches GCC.
// FRONTEND-LABEL: define dso_local i64 @global_addrof(
// FRONTEND-SAME: ) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[TMP0:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr getelementptr inbounds nuw (i8, ptr @gaf, i64 9), i1 false, i1 true, i1 true)
// FRONTEND-NEXT: ret i64 [[TMP0]]
//
// O2-LABEL: define dso_local noundef i64 @global_addrof(
// O2-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: ret i64 15
//
size_t global_addrof(void) {
return __builtin_dynamic_object_size(&gaf.fam, 1);
}
// Test: pointer x fam (decayed pointer-to-element).
// This form already worked pre-fix: const-eval can't resolve %p, so it
// fell through to CGBuiltin's counted_by path. Kept here as a regression
// boundary -- the runtime IR must keep loading the count from %p.
// FRONTEND-LABEL: define dso_local i64 @ptr_fam(
// FRONTEND-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX:%.*]], ptr [[P]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0
// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[P]], i32 0, i32 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], -1
// FRONTEND-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP1]]
//
// O2-LABEL: define dso_local range(i64 0, -9223372036854775808) i64 @ptr_fam(
// O2-SAME: ptr nofree noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[P]], align 4
// O2-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[COUNTED_BY_LOAD]], i64 0)
// O2-NEXT: ret i64 [[TMP0]]
//
size_t ptr_fam(struct annotated_flex *p) {
return __builtin_dynamic_object_size(p->fam, 1);
}
// Test: pointer x &fam[idx].
// Same regression-boundary purpose as ptr_fam: runtime path with index
// subtracted from the count loaded via %p.
// FRONTEND-LABEL: define dso_local i64 @ptr_subscript(
// FRONTEND-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX:%.*]], ptr [[P]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 3
// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[P]], i32 0, i32 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[RESULT:%.*]] = sub nuw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 3
// FRONTEND-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
// FRONTEND-NEXT: [[TMP1:%.*]] = and i1 true, [[TMP0]]
// FRONTEND-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i64 [[RESULT]], i64 0
// FRONTEND-NEXT: ret i64 [[TMP2]]
//
// O2-LABEL: define dso_local range(i64 0, -9223372036854775808) i64 @ptr_subscript(
// O2-SAME: ptr nofree noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR4]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[P]], align 4
// O2-NEXT: [[RESULT:%.*]] = add i64 [[COUNTED_BY_LOAD]], -3
// O2-NEXT: [[TMP0:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
// O2-NEXT: ret i64 [[TMP0]]
//
size_t ptr_subscript(struct annotated_flex *p) {
return __builtin_dynamic_object_size(&p->fam[3], 1);
}
// Test: pointer x &fam (array-as-a-whole over an opaque base).
// Layout-derived path, but @llvm.objectsize can't determine the size of
// the underlying object, so it returns -1 (size_t max). Matches GCC.
// FRONTEND-LABEL: define dso_local i64 @ptr_addrof(
// FRONTEND-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX:%.*]], ptr [[P]], i32 0, i32 2
// FRONTEND-NEXT: [[TMP0:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr [[FAM]], i1 false, i1 true, i1 true)
// FRONTEND-NEXT: ret i64 [[TMP0]]
//
// O2-LABEL: define dso_local i64 @ptr_addrof(
// O2-SAME: ptr nofree noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR3]] {
// O2-NEXT: [[ENTRY:.*:]]
// O2-NEXT: ret i64 -1
//
size_t ptr_addrof(struct annotated_flex *p) {
return __builtin_dynamic_object_size(&p->fam, 1);
}
//.
// O2: [[META3:![0-9]+]] = !{!"omnipotent char", [[META4:![0-9]+]], i64 0}
// O2: [[META4]] = !{!"Simple C/C++ TBAA"}
// O2: [[LONG_TBAA5]] = !{[[META6:![0-9]+]], [[META6]], i64 0}
// O2: [[META6]] = !{!"long", [[META3]], i64 0}
//.