| // 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} |
| //. |