[clang] [Clang] Add __builtin_counted_by_ref builtin (PR #114495)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 15:47:38 PST 2024
================
@@ -0,0 +1,182 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=X86_64
+// RUN: %clang_cc1 -triple i386-unknown-unknown -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=I386
+
+struct a {
+ char x;
+ short count;
+ int array[] __attribute__((counted_by(count)));
+};
+
+// X86_64-LABEL: define dso_local noalias noundef ptr @test1(
+// X86_64-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// X86_64-NEXT: [[ENTRY:.*:]]
+// X86_64-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64
+// X86_64-NEXT: [[MUL:%.*]] = shl nsw i64 [[CONV]], 2
+// X86_64-NEXT: [[ADD:%.*]] = add nsw i64 [[MUL]], 4
+// X86_64-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[ADD]]) #[[ATTR3:[0-9]+]]
+// X86_64-NEXT: [[CONV1:%.*]] = trunc i32 [[SIZE]] to i16
+// X86_64-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 2
+// X86_64-NEXT: store i16 [[CONV1]], ptr [[DOT_COUNTED_BY_GEP]], align 2, !tbaa [[TBAA2:![0-9]+]]
+// X86_64-NEXT: ret ptr [[CALL]]
+//
+// I386-LABEL: define dso_local noalias noundef ptr @test1(
+// I386-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// I386-NEXT: [[ENTRY:.*:]]
+// I386-NEXT: [[MUL:%.*]] = shl i32 [[SIZE]], 2
+// I386-NEXT: [[ADD:%.*]] = add i32 [[MUL]], 4
+// I386-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i32 noundef [[ADD]]) #[[ATTR3:[0-9]+]]
+// I386-NEXT: [[CONV:%.*]] = trunc i32 [[SIZE]] to i16
+// I386-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i32 2
+// I386-NEXT: store i16 [[CONV]], ptr [[DOT_COUNTED_BY_GEP]], align 2, !tbaa [[TBAA3:![0-9]+]]
+// I386-NEXT: ret ptr [[CALL]]
+//
+struct a *test1(int size) {
+ struct a *p = __builtin_malloc(sizeof(struct a) + sizeof(int) * size);
+
+ *__builtin_counted_by_ref(p->array) = size;
+ *__builtin_counted_by_ref(&p->array[0]) = size;
+ return p;
+}
+
+struct b {
+ int _filler;
+ struct {
+ int __filler;
+ struct {
+ int ___filler;
+ struct {
+ char count;
+ };
+ };
+ };
+ struct {
+ int filler_;
+ struct {
+ int filler__;
+ struct {
+ long array[] __attribute__((counted_by(count)));
+ };
+ };
+ };
+};
+
+// X86_64-LABEL: define dso_local noalias noundef ptr @test2(
+// X86_64-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// X86_64-NEXT: [[ENTRY:.*:]]
+// X86_64-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64
+// X86_64-NEXT: [[MUL:%.*]] = shl nsw i64 [[CONV]], 2
+// X86_64-NEXT: [[ADD:%.*]] = add nsw i64 [[MUL]], 4
+// X86_64-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[ADD]]) #[[ATTR3]]
+// X86_64-NEXT: [[CONV1:%.*]] = trunc i32 [[SIZE]] to i8
+// X86_64-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 12
+// X86_64-NEXT: store i8 [[CONV1]], ptr [[DOT_COUNTED_BY_GEP]], align 1, !tbaa [[TBAA6:![0-9]+]]
+// X86_64-NEXT: ret ptr [[CALL]]
+//
+// I386-LABEL: define dso_local noalias noundef ptr @test2(
+// I386-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// I386-NEXT: [[ENTRY:.*:]]
+// I386-NEXT: [[MUL:%.*]] = shl i32 [[SIZE]], 2
+// I386-NEXT: [[ADD:%.*]] = add i32 [[MUL]], 4
+// I386-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i32 noundef [[ADD]]) #[[ATTR3]]
+// I386-NEXT: [[CONV:%.*]] = trunc i32 [[SIZE]] to i8
+// I386-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i32 12
+// I386-NEXT: store i8 [[CONV]], ptr [[DOT_COUNTED_BY_GEP]], align 1, !tbaa [[TBAA7:![0-9]+]]
+// I386-NEXT: ret ptr [[CALL]]
+//
+struct b *test2(int size) {
+ struct b *p = __builtin_malloc(sizeof(struct a) + sizeof(int) * size);
+
+ *__builtin_counted_by_ref(p->array) = size;
+ *__builtin_counted_by_ref(&p->array[0]) = size;
+ return p;
+}
+
+struct c {
+ char x;
+ short count;
+ int array[];
+};
+
+// X86_64-LABEL: define dso_local noalias noundef ptr @test3(
+// X86_64-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// X86_64-NEXT: [[ENTRY:.*:]]
+// X86_64-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64
+// X86_64-NEXT: [[MUL:%.*]] = shl nsw i64 [[CONV]], 2
+// X86_64-NEXT: [[ADD:%.*]] = add nsw i64 [[MUL]], 4
+// X86_64-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[ADD]]) #[[ATTR3]]
+// X86_64-NEXT: ret ptr [[CALL]]
+//
+// I386-LABEL: define dso_local noalias noundef ptr @test3(
+// I386-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// I386-NEXT: [[ENTRY:.*:]]
+// I386-NEXT: [[MUL:%.*]] = shl i32 [[SIZE]], 2
+// I386-NEXT: [[ADD:%.*]] = add i32 [[MUL]], 4
+// I386-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i32 noundef [[ADD]]) #[[ATTR3]]
+// I386-NEXT: ret ptr [[CALL]]
+//
+struct c *test3(int size) {
+ struct c *p = __builtin_malloc(sizeof(struct c) + sizeof(int) * size);
+ unsigned long int __ignored;
+
+ *_Generic(
+ __builtin_counted_by_ref(&p->array[0]),
+ void *: &__ignored,
+ default: __builtin_counted_by_ref(&p->array[0])) = size;
+
+ return p;
+}
+
+struct d {
+ char x;
+ short count;
+ int array[] __attribute__((counted_by(count)));
+};
+
+// X86_64-LABEL: define dso_local noalias noundef ptr @test4(
+// X86_64-SAME: i32 noundef [[SIZE:%.*]], i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// X86_64-NEXT: [[ENTRY:.*:]]
+// X86_64-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64
+// X86_64-NEXT: [[MUL:%.*]] = shl nsw i64 [[CONV]], 2
+// X86_64-NEXT: [[ADD:%.*]] = add nsw i64 [[MUL]], 4
+// X86_64-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[ADD]]) #[[ATTR3]]
+// X86_64-NEXT: [[CONV1:%.*]] = trunc i32 [[SIZE]] to i16
+// X86_64-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 2
+// X86_64-NEXT: store i16 [[CONV1]], ptr [[DOT_COUNTED_BY_GEP]], align 2, !tbaa [[TBAA2]]
+// X86_64-NEXT: ret ptr [[CALL]]
+//
+// I386-LABEL: define dso_local noalias noundef ptr @test4(
+// I386-SAME: i32 noundef [[SIZE:%.*]], i32 noundef [[IDX:%.*]]) local_unnamed_addr #[[ATTR0]] {
+// I386-NEXT: [[ENTRY:.*:]]
+// I386-NEXT: [[MUL:%.*]] = shl i32 [[SIZE]], 2
+// I386-NEXT: [[ADD:%.*]] = add i32 [[MUL]], 4
+// I386-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i32 noundef [[ADD]]) #[[ATTR3]]
+// I386-NEXT: [[CONV:%.*]] = trunc i32 [[SIZE]] to i16
+// I386-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i32 2
+// I386-NEXT: store i16 [[CONV]], ptr [[DOT_COUNTED_BY_GEP]], align 2, !tbaa [[TBAA3]]
+// I386-NEXT: ret ptr [[CALL]]
+//
+struct d *test4(int size, int idx) {
+ struct d *p = __builtin_malloc(sizeof(struct d) + sizeof(int) * size);
+ unsigned long int __ignored;
+
+ *_Generic(
+ __builtin_counted_by_ref(&p->array[0]),
+ void *: &__ignored,
+ default: __builtin_counted_by_ref(&p->array[idx++])) = size;
----------------
bwendling wrote:
If I do something like this:
```c
*_Generic(
__flex_counter((p++)->array),
void *: &__ignored_assignment,
default: __flex_counter(p->array)) = 42;
```
The code generation seems to take it in stride (with a warning of course):
```
define dso_local noalias noundef ptr @alloc_s(i32 noundef %size) local_unnamed_addr #0 {
entry:
%0 = load ptr, ptr @p, align 8, !tbaa !5
%..counted_by.gep = getelementptr inbounds i8, ptr %0, i64 4
store i8 42, ptr %..counted_by.gep, align 1, !tbaa !9
ret ptr null
}
```
I'm not sure if that's good or bad. In truth, the warning should probably be upgraded to an error, as it will lead to unexpected behavior if the warning is ignored. Thoughts?
https://github.com/llvm/llvm-project/pull/114495
More information about the cfe-commits
mailing list