|
| 1 | +// RUN: %riscv64_cheri_purecap_cc1 %s -emit-llvm -o - -O0 | FileCheck %s --check-prefixes=CHECK --implicit-check-not=llvm.memcpy |
| 2 | +/// Check that we can add the no_preserve_cheri_tags attribute for copies |
| 3 | +/// to/from globals (as those have a fixed effective type). |
| 4 | + |
| 5 | +static __uint128_t u128 = 0; |
| 6 | +extern __uint128_t *u128_ptr; |
| 7 | +extern __uint128_t u128_array[4]; |
| 8 | +extern __uint128_t u128_array_unsized[]; |
| 9 | + |
| 10 | +void test_u128(void *buf) { |
| 11 | + // CHECK-LABEL: @test_u128( |
| 12 | + // No need to preserve tags since we can see the definition: |
| 13 | + __builtin_memcpy(buf, &u128, sizeof(u128)); |
| 14 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 15 | + // CHECK-SAME: , i64 16, i1 false) [[NO_PRESERVE_TAGS:#[0-9]+]]{{$}} |
| 16 | + |
| 17 | + // We can see the variable declaration but it's a pointer so we can't add the attribute |
| 18 | + __builtin_memcpy(buf, u128_ptr, sizeof(*u128_ptr)); |
| 19 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 20 | + // CHECK-SAME: , i64 16, i1 false){{$}} |
| 21 | + |
| 22 | + // Here we can see the array definition, so no need to preserve tags: |
| 23 | + __builtin_memcpy(buf, u128_array, sizeof(u128_array)); |
| 24 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 25 | + // CHECK-SAME: , i64 64, i1 false) [[NO_PRESERVE_TAGS]]{{$}} |
| 26 | + // Same for unsized arrays: |
| 27 | + __builtin_memcpy(buf, u128_array_unsized, 80); |
| 28 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 29 | + // CHECK-SAME: , i64 80, i1 false) [[NO_PRESERVE_TAGS]]{{$}} |
| 30 | +} |
| 31 | + |
| 32 | +// CHECK: declare void @llvm.memcpy.p200i8.p200i8.i64( |
| 33 | + |
| 34 | +static unsigned __intcap ucap = 0; |
| 35 | +extern unsigned __intcap *ucap_ptr; |
| 36 | +extern unsigned __intcap ucap_array[4]; |
| 37 | +extern unsigned __intcap ucap_array_unsized[]; |
| 38 | + |
| 39 | +void test_ucap(void *buf) { |
| 40 | + // CHECK-LABEL: @test_ucap( |
| 41 | + // In all of these cases we should add the must_preserve_tags attribute since |
| 42 | + // we are copying a capability type. |
| 43 | + __builtin_memcpy(buf, &ucap, sizeof(ucap)); |
| 44 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 45 | + // CHECK-SAME: , i64 16, i1 false) [[MUST_PRESERVE_UINTCAP:#[0-9]+]]{{$}} |
| 46 | + __builtin_memcpy(buf, ucap_ptr, sizeof(*ucap_ptr)); |
| 47 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 48 | + // CHECK-SAME: , i64 16, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}} |
| 49 | + __builtin_memcpy(buf, ucap_array, sizeof(ucap_array)); |
| 50 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 51 | + // CHECK-SAME: , i64 64, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}} |
| 52 | + __builtin_memcpy(buf, ucap_array_unsized, 80); |
| 53 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 54 | + // CHECK-SAME: , i64 80, i1 false) [[MUST_PRESERVE_UINTCAP]]{{$}} |
| 55 | +} |
| 56 | + |
| 57 | +struct NoCaps { |
| 58 | + int i; |
| 59 | + long l[15]; |
| 60 | +}; |
| 61 | +extern struct NoCaps nocap_struct; |
| 62 | +extern struct NoCaps *nocap_struct_ptr; |
| 63 | +extern struct NoCaps nocap_struct_array[4]; |
| 64 | +extern struct NoCaps nocap_struct_array_unsized[]; |
| 65 | + |
| 66 | +void test_struct_nocaps(void *buf) { |
| 67 | + // CHECK-LABEL: @test_struct_nocaps( |
| 68 | + // No need to preserve tags since we can see the definition: |
| 69 | + __builtin_memcpy(buf, &nocap_struct, sizeof(nocap_struct)); |
| 70 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 71 | + // CHECK-SAME: , i64 128, i1 false) [[NO_PRESERVE_TAGS]]{{$}} |
| 72 | + |
| 73 | + // We can see the variable declaration but it's a pointer so we can't add the attribute |
| 74 | + __builtin_memcpy(buf, nocap_struct_ptr, sizeof(*nocap_struct_ptr)); |
| 75 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 76 | + // CHECK-SAME: , i64 128, i1 false){{$}} |
| 77 | + |
| 78 | + /// Here we can see the array definition, so no need to preserve tags: |
| 79 | + __builtin_memcpy(buf, nocap_struct_array, sizeof(nocap_struct_array)); |
| 80 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 81 | + // CHECK-SAME: , i64 512, i1 false){{$}} |
| 82 | + // TODO: We don't check the size against the array size so we assume this could hold tags |
| 83 | + // TODO-CHECK-SAME: , i64 512, i1 false) [[NO_PRESERVE_TAGS]]{{$}} |
| 84 | + /// TODO: For unsized global arrays we could assume that the copy stays in-bounds |
| 85 | + /// for the tag-preservation analysis, but for now we just don't set the attribute. |
| 86 | + __builtin_memcpy(buf, nocap_struct_array_unsized, sizeof(struct NoCaps) * 3); |
| 87 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 88 | + // CHECK-SAME: , i64 384, i1 false){{$}} |
| 89 | + // TODO-CHECK-SAME: , i64 384, i1 false) [[NO_PRESERVE_TAGS]]{{$}} |
| 90 | +} |
| 91 | + |
| 92 | +struct WithCaps { |
| 93 | + int i; |
| 94 | + union { |
| 95 | + long l; |
| 96 | + void *p; |
| 97 | + } u; |
| 98 | +}; |
| 99 | +extern struct WithCaps withcap_struct; |
| 100 | +extern struct WithCaps *withcap_struct_ptr; |
| 101 | +extern struct WithCaps withcap_struct_array[4]; |
| 102 | +extern struct WithCaps withcap_struct_array_unsized[]; |
| 103 | + |
| 104 | +void test_struct_with_caps(void *buf) { |
| 105 | + // CHECK-LABEL: @test_struct_with_caps( |
| 106 | + // In all of these cases we should add the must_preserve_tags attribute since |
| 107 | + // we are copying a capability type. |
| 108 | + __builtin_memcpy(buf, &withcap_struct, sizeof(withcap_struct)); |
| 109 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 110 | + // CHECK-SAME: , i64 32, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP:#[0-9]+]]{{$}} |
| 111 | + __builtin_memcpy(buf, withcap_struct_ptr, sizeof(*withcap_struct_ptr)); |
| 112 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 113 | + // CHECK-SAME: , i64 32, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}} |
| 114 | + __builtin_memcpy(buf, withcap_struct_array, sizeof(withcap_struct_array)); |
| 115 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 116 | + // CHECK-SAME: , i64 128, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}} |
| 117 | + __builtin_memcpy(buf, withcap_struct_array_unsized, 80); |
| 118 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 119 | + // CHECK-SAME: , i64 80, i1 false) [[MUST_PRESERVE_STRUCT_WITHCAP]]{{$}} |
| 120 | +} |
| 121 | + |
| 122 | +extern char global_char_array[32]; |
| 123 | +extern unsigned char global_uchar_array[32]; |
| 124 | +extern signed char global_schar_array[32]; |
| 125 | + |
| 126 | +void test_char_array(void *buf) { |
| 127 | + // CHECK-LABEL: @test_char_array( |
| 128 | + // Arrays of type "(unsigned) char" are special and can be assumed to hold any type. |
| 129 | + // We extend this to signed char as well to avoid potentially surprising behaviour. |
| 130 | + // None of these cases should add the no_preserve_tags attribute: |
| 131 | + __builtin_memcpy(buf, &global_char_array, sizeof(global_char_array)); |
| 132 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 133 | + // CHECK-SAME: , i64 32, i1 false){{$}} |
| 134 | + __builtin_memcpy(buf, &global_uchar_array, sizeof(global_uchar_array)); |
| 135 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 136 | + // CHECK-SAME: , i64 32, i1 false){{$}} |
| 137 | + __builtin_memcpy(buf, &global_schar_array, sizeof(global_schar_array)); |
| 138 | + // CHECK: call void @llvm.memcpy.p200i8.p200i8.i64( |
| 139 | + // CHECK-SAME: , i64 32, i1 false){{$}} |
| 140 | +} |
| 141 | + |
| 142 | +// CHECK: [[NO_PRESERVE_TAGS]] = { no_preserve_cheri_tags } |
| 143 | +// CHECK: [[MUST_PRESERVE_UINTCAP]] = { must_preserve_cheri_tags "frontend-memtransfer-type"="'unsigned __intcap'" } |
| 144 | +// CHECK: [[MUST_PRESERVE_STRUCT_WITHCAP]] = { must_preserve_cheri_tags "frontend-memtransfer-type"="'struct WithCaps'" } |
0 commit comments