| 1 | // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,LINUX |
| 2 | // RUN: %clang_cc1 -triple x86_64-windows-pc -fms-compatibility -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WINDOWS |
| 3 | |
| 4 | #ifdef _WIN64 |
| 5 | #define ATTR(X) __declspec(X) |
| 6 | #else |
| 7 | #define ATTR(X) __attribute__((X)) |
| 8 | #endif // _MSC_VER |
| 9 | |
| 10 | // Each called version should have an IFunc. |
| 11 | // LINUX: @SingleVersion.ifunc = ifunc void (), void ()* ()* @SingleVersion.resolver |
| 12 | // LINUX: @TwoVersions.ifunc = ifunc void (), void ()* ()* @TwoVersions.resolver |
| 13 | // LINUX: @TwoVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @TwoVersionsSameAttr.resolver |
| 14 | // LINUX: @ThreeVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @ThreeVersionsSameAttr.resolver |
| 15 | |
| 16 | ATTR(cpu_specific(ivybridge)) |
| 17 | void SingleVersion(void){} |
| 18 | // LINUX: define void @SingleVersion.S() #[[S:[0-9]+]] |
| 19 | // WINDOWS: define dso_local void @SingleVersion.S() #[[S:[0-9]+]] |
| 20 | |
| 21 | ATTR(cpu_specific(ivybridge)) |
| 22 | void NotCalled(void){} |
| 23 | // LINUX: define void @NotCalled.S() #[[S]] |
| 24 | // WINDOWS: define dso_local void @NotCalled.S() #[[S:[0-9]+]] |
| 25 | |
| 26 | // Done before any of the implementations. Also has an undecorated forward |
| 27 | // declaration. |
| 28 | void TwoVersions(void); |
| 29 | |
| 30 | ATTR(cpu_dispatch(ivybridge, knl)) |
| 31 | void TwoVersions(void); |
| 32 | // LINUX: define void ()* @TwoVersions.resolver() |
| 33 | // LINUX: call void @__cpu_indicator_init |
| 34 | // LINUX: ret void ()* @TwoVersions.Z |
| 35 | // LINUX: ret void ()* @TwoVersions.S |
| 36 | // LINUX: call void @llvm.trap |
| 37 | // LINUX: unreachable |
| 38 | |
| 39 | // WINDOWS: define dso_local void @TwoVersions() |
| 40 | // WINDOWS: call void @__cpu_indicator_init() |
| 41 | // WINDOWS: call void @TwoVersions.Z() |
| 42 | // WINDOWS-NEXT: ret void |
| 43 | // WINDOWS: call void @TwoVersions.S() |
| 44 | // WINDOWS-NEXT: ret void |
| 45 | // WINDOWS: call void @llvm.trap |
| 46 | // WINDOWS: unreachable |
| 47 | |
| 48 | ATTR(cpu_specific(ivybridge)) |
| 49 | void TwoVersions(void){} |
| 50 | // CHECK: define {{.*}}void @TwoVersions.S() #[[S]] |
| 51 | |
| 52 | ATTR(cpu_specific(knl)) |
| 53 | void TwoVersions(void){} |
| 54 | // CHECK: define {{.*}}void @TwoVersions.Z() #[[K:[0-9]+]] |
| 55 | |
| 56 | ATTR(cpu_specific(ivybridge, knl)) |
| 57 | void TwoVersionsSameAttr(void){} |
| 58 | // CHECK: define {{.*}}void @TwoVersionsSameAttr.S() #[[S]] |
| 59 | // CHECK: define {{.*}}void @TwoVersionsSameAttr.Z() #[[K]] |
| 60 | |
| 61 | ATTR(cpu_specific(atom, ivybridge, knl)) |
| 62 | void ThreeVersionsSameAttr(void){} |
| 63 | // CHECK: define {{.*}}void @ThreeVersionsSameAttr.O() #[[O:[0-9]+]] |
| 64 | // CHECK: define {{.*}}void @ThreeVersionsSameAttr.S() #[[S]] |
| 65 | // CHECK: define {{.*}}void @ThreeVersionsSameAttr.Z() #[[K]] |
| 66 | |
| 67 | void usages() { |
| 68 | SingleVersion(); |
| 69 | // LINUX: @SingleVersion.ifunc() |
| 70 | // WINDOWS: @SingleVersion() |
| 71 | TwoVersions(); |
| 72 | // LINUX: @TwoVersions.ifunc() |
| 73 | // WINDOWS: @TwoVersions() |
| 74 | TwoVersionsSameAttr(); |
| 75 | // LINUX: @TwoVersionsSameAttr.ifunc() |
| 76 | // WINDOWS: @TwoVersionsSameAttr() |
| 77 | ThreeVersionsSameAttr(); |
| 78 | // LINUX: @ThreeVersionsSameAttr.ifunc() |
| 79 | // WINDOWS: @ThreeVersionsSameAttr() |
| 80 | } |
| 81 | |
| 82 | // has an extra config to emit! |
| 83 | ATTR(cpu_dispatch(ivybridge, knl, atom)) |
| 84 | void TwoVersionsSameAttr(void); |
| 85 | // LINUX: define void ()* @TwoVersionsSameAttr.resolver() |
| 86 | // LINUX: ret void ()* @TwoVersionsSameAttr.Z |
| 87 | // LINUX: ret void ()* @TwoVersionsSameAttr.S |
| 88 | // LINUX: ret void ()* @TwoVersionsSameAttr.O |
| 89 | // LINUX: call void @llvm.trap |
| 90 | // LINUX: unreachable |
| 91 | |
| 92 | // WINDOWS: define dso_local void @TwoVersionsSameAttr() |
| 93 | // WINDOWS: call void @TwoVersionsSameAttr.Z |
| 94 | // WINDOWS-NEXT: ret void |
| 95 | // WINDOWS: call void @TwoVersionsSameAttr.S |
| 96 | // WINDOWS-NEXT: ret void |
| 97 | // WINDOWS: call void @TwoVersionsSameAttr.O |
| 98 | // WINDOWS-NEXT: ret void |
| 99 | // WINDOWS: call void @llvm.trap |
| 100 | // WINDOWS: unreachable |
| 101 | |
| 102 | ATTR(cpu_dispatch(atom, ivybridge, knl)) |
| 103 | void ThreeVersionsSameAttr(void){} |
| 104 | // LINUX: define void ()* @ThreeVersionsSameAttr.resolver() |
| 105 | // LINUX: call void @__cpu_indicator_init |
| 106 | // LINUX: ret void ()* @ThreeVersionsSameAttr.Z |
| 107 | // LINUX: ret void ()* @ThreeVersionsSameAttr.S |
| 108 | // LINUX: ret void ()* @ThreeVersionsSameAttr.O |
| 109 | // LINUX: call void @llvm.trap |
| 110 | // LINUX: unreachable |
| 111 | |
| 112 | // WINDOWS: define dso_local void @ThreeVersionsSameAttr() |
| 113 | // WINDOWS: call void @__cpu_indicator_init |
| 114 | // WINDOWS: call void @ThreeVersionsSameAttr.Z |
| 115 | // WINDOWS-NEXT: ret void |
| 116 | // WINDOWS: call void @ThreeVersionsSameAttr.S |
| 117 | // WINDOWS-NEXT: ret void |
| 118 | // WINDOWS: call void @ThreeVersionsSameAttr.O |
| 119 | // WINDOWS-NEXT: ret void |
| 120 | // WINDOWS: call void @llvm.trap |
| 121 | // WINDOWS: unreachable |
| 122 | |
| 123 | // No Cpu Specific options. |
| 124 | ATTR(cpu_dispatch(atom, ivybridge, knl)) |
| 125 | void NoSpecifics(void); |
| 126 | // LINUX: define void ()* @NoSpecifics.resolver() |
| 127 | // LINUX: call void @__cpu_indicator_init |
| 128 | // LINUX: ret void ()* @NoSpecifics.Z |
| 129 | // LINUX: ret void ()* @NoSpecifics.S |
| 130 | // LINUX: ret void ()* @NoSpecifics.O |
| 131 | // LINUX: call void @llvm.trap |
| 132 | // LINUX: unreachable |
| 133 | |
| 134 | // WINDOWS: define dso_local void @NoSpecifics() |
| 135 | // WINDOWS: call void @__cpu_indicator_init |
| 136 | // WINDOWS: call void @NoSpecifics.Z |
| 137 | // WINDOWS-NEXT: ret void |
| 138 | // WINDOWS: call void @NoSpecifics.S |
| 139 | // WINDOWS-NEXT: ret void |
| 140 | // WINDOWS: call void @NoSpecifics.O |
| 141 | // WINDOWS-NEXT: ret void |
| 142 | // WINDOWS: call void @llvm.trap |
| 143 | // WINDOWS: unreachable |
| 144 | |
| 145 | ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) |
| 146 | void HasGeneric(void); |
| 147 | // LINUX: define void ()* @HasGeneric.resolver() |
| 148 | // LINUX: call void @__cpu_indicator_init |
| 149 | // LINUX: ret void ()* @HasGeneric.Z |
| 150 | // LINUX: ret void ()* @HasGeneric.S |
| 151 | // LINUX: ret void ()* @HasGeneric.O |
| 152 | // LINUX: ret void ()* @HasGeneric.A |
| 153 | // LINUX-NOT: call void @llvm.trap |
| 154 | |
| 155 | // WINDOWS: define dso_local void @HasGeneric() |
| 156 | // WINDOWS: call void @__cpu_indicator_init |
| 157 | // WINDOWS: call void @HasGeneric.Z |
| 158 | // WINDOWS-NEXT: ret void |
| 159 | // WINDOWS: call void @HasGeneric.S |
| 160 | // WINDOWS-NEXT: ret void |
| 161 | // WINDOWS: call void @HasGeneric.O |
| 162 | // WINDOWS-NEXT: ret void |
| 163 | // WINDOWS: call void @HasGeneric.A |
| 164 | // WINDOWS-NEXT: ret void |
| 165 | // WINDOWS-NOT: call void @llvm.trap |
| 166 | |
| 167 | ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) |
| 168 | void HasParams(int i, double d); |
| 169 | // LINUX: define void (i32, double)* @HasParams.resolver() |
| 170 | // LINUX: call void @__cpu_indicator_init |
| 171 | // LINUX: ret void (i32, double)* @HasParams.Z |
| 172 | // LINUX: ret void (i32, double)* @HasParams.S |
| 173 | // LINUX: ret void (i32, double)* @HasParams.O |
| 174 | // LINUX: ret void (i32, double)* @HasParams.A |
| 175 | // LINUX-NOT: call void @llvm.trap |
| 176 | |
| 177 | // WINDOWS: define dso_local void @HasParams(i32, double) |
| 178 | // WINDOWS: call void @__cpu_indicator_init |
| 179 | // WINDOWS: call void @HasParams.Z(i32 %0, double %1) |
| 180 | // WINDOWS-NEXT: ret void |
| 181 | // WINDOWS: call void @HasParams.S(i32 %0, double %1) |
| 182 | // WINDOWS-NEXT: ret void |
| 183 | // WINDOWS: call void @HasParams.O(i32 %0, double %1) |
| 184 | // WINDOWS-NEXT: ret void |
| 185 | // WINDOWS: call void @HasParams.A(i32 %0, double %1) |
| 186 | // WINDOWS-NEXT: ret void |
| 187 | // WINDOWS-NOT: call void @llvm.trap |
| 188 | |
| 189 | ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) |
| 190 | int HasParamsAndReturn(int i, double d); |
| 191 | // LINUX: define i32 (i32, double)* @HasParamsAndReturn.resolver() |
| 192 | // LINUX: call void @__cpu_indicator_init |
| 193 | // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.Z |
| 194 | // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.S |
| 195 | // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.O |
| 196 | // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.A |
| 197 | // LINUX-NOT: call void @llvm.trap |
| 198 | |
| 199 | // WINDOWS: define dso_local i32 @HasParamsAndReturn(i32, double) |
| 200 | // WINDOWS: call void @__cpu_indicator_init |
| 201 | // WINDOWS: %[[RET:.+]] = musttail call i32 @HasParamsAndReturn.Z(i32 %0, double %1) |
| 202 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 203 | // WINDOWS: %[[RET:.+]] = musttail call i32 @HasParamsAndReturn.S(i32 %0, double %1) |
| 204 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 205 | // WINDOWS: %[[RET:.+]] = musttail call i32 @HasParamsAndReturn.O(i32 %0, double %1) |
| 206 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 207 | // WINDOWS: %[[RET:.+]] = musttail call i32 @HasParamsAndReturn.A(i32 %0, double %1) |
| 208 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 209 | // WINDOWS-NOT: call void @llvm.trap |
| 210 | |
| 211 | ATTR(cpu_dispatch(atom, generic, pentium)) |
| 212 | int GenericAndPentium(int i, double d); |
| 213 | // LINUX: define i32 (i32, double)* @GenericAndPentium.resolver() |
| 214 | // LINUX: call void @__cpu_indicator_init |
| 215 | // LINUX: ret i32 (i32, double)* @GenericAndPentium.O |
| 216 | // LINUX: ret i32 (i32, double)* @GenericAndPentium.B |
| 217 | // LINUX-NOT: ret i32 (i32, double)* @GenericAndPentium.A |
| 218 | // LINUX-NOT: call void @llvm.trap |
| 219 | |
| 220 | // WINDOWS: define dso_local i32 @GenericAndPentium(i32, double) |
| 221 | // WINDOWS: call void @__cpu_indicator_init |
| 222 | // WINDOWS: %[[RET:.+]] = musttail call i32 @GenericAndPentium.O(i32 %0, double %1) |
| 223 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 224 | // WINDOWS: %[[RET:.+]] = musttail call i32 @GenericAndPentium.B(i32 %0, double %1) |
| 225 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 226 | // WINDOWS-NOT: call i32 @GenericAndPentium.A |
| 227 | // WINDOWS-NOT: call void @llvm.trap |
| 228 | |
| 229 | ATTR(cpu_dispatch(atom, pentium)) |
| 230 | int DispatchFirst(void); |
| 231 | // LINUX: define i32 ()* @DispatchFirst.resolver |
| 232 | // LINUX: ret i32 ()* @DispatchFirst.O |
| 233 | // LINUX: ret i32 ()* @DispatchFirst.B |
| 234 | |
| 235 | // WINDOWS: define dso_local i32 @DispatchFirst() |
| 236 | // WINDOWS: %[[RET:.+]] = musttail call i32 @DispatchFirst.O() |
| 237 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 238 | // WINDOWS: %[[RET:.+]] = musttail call i32 @DispatchFirst.B() |
| 239 | // WINDOWS-NEXT: ret i32 %[[RET]] |
| 240 | |
| 241 | ATTR(cpu_specific(atom)) |
| 242 | int DispatchFirst(void) {return 0;} |
| 243 | // LINUX: define i32 @DispatchFirst.O |
| 244 | // LINUX: ret i32 0 |
| 245 | |
| 246 | // WINDOWS: define dso_local i32 @DispatchFirst.O() |
| 247 | // WINDOWS: ret i32 0 |
| 248 | |
| 249 | ATTR(cpu_specific(pentium)) |
| 250 | int DispatchFirst(void) {return 1;} |
| 251 | // LINUX: define i32 @DispatchFirst.B |
| 252 | // LINUX: ret i32 1 |
| 253 | |
| 254 | // WINDOWS: define dso_local i32 @DispatchFirst.B |
| 255 | // WINDOWS: ret i32 1 |
| 256 | |
| 257 | // CHECK: attributes #[[S]] = {{.*}}"target-features"="+avx,+cmov,+cx8,+f16c,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" |
| 258 | // CHECK: attributes #[[K]] = {{.*}}"target-features"="+adx,+avx,+avx2,+avx512cd,+avx512er,+avx512f,+avx512pf,+bmi,+cmov,+cx8,+f16c,+fma,+lzcnt,+mmx,+movbe,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87,+xsave" |
| 259 | // CHECK: attributes #[[O]] = {{.*}}"target-features"="+cmov,+cx8,+mmx,+movbe,+sse,+sse2,+sse3,+ssse3,+x87" |
| 260 | |