windows-o1-abi-smoke.sh (12650B)
1 #!/usr/bin/env bash 2 # test/coff/windows-o1-abi-smoke.sh - focused -O1 Windows ABI coverage for PE. 3 # 4 # Builds one optimized C program for x86_64-windows and aarch64-windows. The 5 # program deliberately exercises ABI shapes that are easy to regress in the 6 # optimizer: nonvolatile GPR/FP preservation across calls, mixed int/FP arg 7 # assignment, stack arguments, varargs, aggregate return/by-value passing, 8 # indirect callbacks, and a UCRT qsort callback. 9 set -u 10 11 ROOT=${KIT_TEST_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)} 12 KIT=${KIT:-"$ROOT/build/kit"} 13 SDK=${KIT_SYSROOT:-} 14 15 KIT_KIT_DIR="$ROOT/test/lib" 16 . "$ROOT/test/lib/kit_sh_kit.sh" 17 kit_report_init 18 19 LABEL_SUITE=windows-o1-abi-smoke 20 21 find_sdk() { 22 local arch=$1 23 local d 24 for d in \ 25 "$ROOT"/build/llvm-mingw/*/ucrt/"$arch"-w64-mingw32 \ 26 /tmp/llvm-mingw*/llvm-mingw-*-ucrt-*/"$arch"-w64-mingw32 \ 27 /tmp/llvm-mingw*/"$arch"-w64-mingw32 \ 28 /private/tmp/llvm-mingw*/llvm-mingw-*-ucrt-*/"$arch"-w64-mingw32 \ 29 /private/tmp/llvm-mingw*/"$arch"-w64-mingw32; do 30 if [ -d "$d/lib" ] && [ -r "$d/include/windows.h" ]; then 31 printf '%s\n' "$d" 32 return 0 33 fi 34 done 35 return 1 36 } 37 38 sdk_for_arch() { 39 local arch=$1 40 local base 41 if [ -n "$SDK" ]; then 42 if [ "$(basename "$SDK")" = "$arch-w64-mingw32" ]; then 43 printf '%s\n' "$SDK" 44 return 0 45 fi 46 base=$(dirname "$SDK") 47 if [ -d "$base/$arch-w64-mingw32/lib" ] && 48 [ -r "$base/$arch-w64-mingw32/include/windows.h" ]; then 49 printf '%s\n' "$base/$arch-w64-mingw32" 50 return 0 51 fi 52 fi 53 find_sdk "$arch" 54 } 55 56 if [ ! -x "$KIT" ]; then 57 kit_fail "$LABEL_SUITE/kit-present" "kit binary not found: $KIT" 58 kit_summary "$LABEL_SUITE" 59 kit_exit 60 fi 61 62 TMP=${TMPDIR:-/tmp} 63 work=$(mktemp -d "$TMP/kit-windows-o1-abi-smoke.XXXXXX") 64 trap 'rm -rf "$work"' EXIT 65 66 ABI_C=$work/o1-abi.c 67 68 cat >"$ABI_C" <<'SRC' 69 #include <stdarg.h> 70 #include <stdint.h> 71 #include <stdlib.h> 72 73 typedef struct Pair { 74 uint64_t a; 75 uint64_t b; 76 } Pair; 77 78 typedef struct Big { 79 uint64_t a; 80 uint64_t b; 81 uint64_t c; 82 uint64_t d; 83 uint64_t e; 84 double f; 85 } Big; 86 87 static volatile double g_fp[16] = { 88 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 89 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 90 }; 91 92 static volatile uint64_t g_int[16] = { 93 11u, 23u, 37u, 41u, 53u, 67u, 71u, 83u, 94 97u, 101u, 109u, 113u, 127u, 131u, 137u, 149u, 95 }; 96 97 int run_fp_probe(void); 98 int run_int_probe(void); 99 100 __attribute__((noinline)) uint64_t opaque_u64(uint64_t x) { 101 return (x * 33u) ^ 0x123456789abcdef0ull; 102 } 103 104 __attribute__((noinline)) void touch_fp_pressure(double seed) { 105 double a0 = g_fp[0] + seed; 106 double a1 = g_fp[1] + seed; 107 double a2 = g_fp[2] + seed; 108 double a3 = g_fp[3] + seed; 109 double a4 = g_fp[4] + seed; 110 double a5 = g_fp[5] + seed; 111 double a6 = g_fp[6] + seed; 112 double a7 = g_fp[7] + seed; 113 double a8 = g_fp[8] + seed; 114 double a9 = g_fp[9] + seed; 115 double a10 = g_fp[10] + seed; 116 double a11 = g_fp[11] + seed; 117 uint64_t k = opaque_u64((uint64_t)seed); 118 double sum = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11; 119 sum += (double)(k & 255u); 120 g_fp[15] = sum; 121 } 122 123 __attribute__((noinline)) void touch_int_pressure(uint64_t seed) { 124 uint64_t a0 = g_int[0] + seed; 125 uint64_t a1 = g_int[1] + seed; 126 uint64_t a2 = g_int[2] + seed; 127 uint64_t a3 = g_int[3] + seed; 128 uint64_t a4 = g_int[4] + seed; 129 uint64_t a5 = g_int[5] + seed; 130 uint64_t a6 = g_int[6] + seed; 131 uint64_t a7 = g_int[7] + seed; 132 uint64_t a8 = g_int[8] + seed; 133 uint64_t a9 = g_int[9] + seed; 134 uint64_t k = opaque_u64(seed + 7u); 135 g_int[15] = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + k; 136 } 137 138 #if defined(__x86_64__) 139 __asm__( 140 ".text\n" 141 ".globl run_fp_probe\n" 142 "run_fp_probe:\n" 143 "pushq %rbp\n" 144 "movq %rsp, %rbp\n" 145 "subq $0x30, %rsp\n" 146 "movaps %xmm6, -0x10(%rbp)\n" 147 "movq $0x405edd2f1a9fbe77, %rax\n" 148 "movq %rax, %xmm6\n" 149 "movq $0x401c000000000000, %rax\n" 150 "movq %rax, %xmm0\n" 151 "callq touch_fp_pressure\n" 152 "movq %xmm6, %rax\n" 153 "movq $0x405edd2f1a9fbe77, %rcx\n" 154 "cmpq %rcx, %rax\n" 155 "sete %al\n" 156 "movzbl %al, %eax\n" 157 "movaps -0x10(%rbp), %xmm6\n" 158 "leave\n" 159 "retq\n" 160 ".globl run_int_probe\n" 161 "run_int_probe:\n" 162 "pushq %rbp\n" 163 "movq %rsp, %rbp\n" 164 "subq $0x30, %rsp\n" 165 "movq %r13, -0x8(%rbp)\n" 166 "movq $0xfedcba9876543210, %r13\n" 167 "movq $5, %rcx\n" 168 "callq touch_int_pressure\n" 169 "movq %r13, %rax\n" 170 "movq $0xfedcba9876543210, %rcx\n" 171 "cmpq %rcx, %rax\n" 172 "sete %al\n" 173 "movzbl %al, %eax\n" 174 "movq -0x8(%rbp), %r13\n" 175 "leave\n" 176 "retq\n"); 177 #elif defined(__aarch64__) 178 __asm__( 179 ".text\n" 180 ".globl run_fp_probe\n" 181 "run_fp_probe:\n" 182 "stp x29, x30, [sp, #-32]!\n" 183 "mov x29, sp\n" 184 "str d8, [sp, #16]\n" 185 "mov x9, #0xbe77\n" 186 "movk x9, #0x1a9f, lsl #16\n" 187 "movk x9, #0xdd2f, lsl #32\n" 188 "movk x9, #0x405e, lsl #48\n" 189 "fmov d8, x9\n" 190 "mov x9, #0\n" 191 "movk x9, #0x401c, lsl #48\n" 192 "fmov d0, x9\n" 193 "bl touch_fp_pressure\n" 194 "fmov x10, d8\n" 195 "mov x9, #0xbe77\n" 196 "movk x9, #0x1a9f, lsl #16\n" 197 "movk x9, #0xdd2f, lsl #32\n" 198 "movk x9, #0x405e, lsl #48\n" 199 "cmp x10, x9\n" 200 "cset w0, eq\n" 201 "ldr d8, [sp, #16]\n" 202 "ldp x29, x30, [sp], #32\n" 203 "ret\n" 204 ".globl run_int_probe\n" 205 "run_int_probe:\n" 206 "stp x29, x30, [sp, #-32]!\n" 207 "mov x29, sp\n" 208 "str x19, [sp, #16]\n" 209 "mov x19, #0x3210\n" 210 "movk x19, #0x7654, lsl #16\n" 211 "movk x19, #0xba98, lsl #32\n" 212 "movk x19, #0xfedc, lsl #48\n" 213 "mov x0, #5\n" 214 "bl touch_int_pressure\n" 215 "mov x10, x19\n" 216 "mov x9, #0x3210\n" 217 "movk x9, #0x7654, lsl #16\n" 218 "movk x9, #0xba98, lsl #32\n" 219 "movk x9, #0xfedc, lsl #48\n" 220 "cmp x10, x9\n" 221 "cset w0, eq\n" 222 "ldr x19, [sp, #16]\n" 223 "ldp x29, x30, [sp], #32\n" 224 "ret\n"); 225 #else 226 #error unsupported arch 227 #endif 228 229 __attribute__((noinline)) uint64_t mixed_args(int a, uint64_t b, double c, 230 float d, int e, double f, 231 uint64_t g, int h, double i, 232 uint64_t j) { 233 return (uint64_t)a + b + (uint64_t)(c * 10.0) + (uint64_t)(d * 10.0f) + 234 (uint64_t)e + (uint64_t)(f * 10.0) + g + (uint64_t)h + 235 (uint64_t)(i * 10.0) + j; 236 } 237 238 __attribute__((noinline)) uint64_t stack_args(uint64_t a1, uint64_t a2, 239 uint64_t a3, uint64_t a4, 240 uint64_t a5, uint64_t a6, 241 uint64_t a7, uint64_t a8, 242 uint64_t a9, uint64_t a10, 243 uint64_t a11, uint64_t a12) { 244 return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12; 245 } 246 247 __attribute__((noinline)) Pair make_pair(uint64_t base) { 248 Pair p; 249 p.a = base + 1u; 250 p.b = base + 2u; 251 return p; 252 } 253 254 __attribute__((noinline)) Big make_big(uint64_t base) { 255 Big b; 256 b.a = base + 1u; 257 b.b = base + 2u; 258 b.c = base + 3u; 259 b.d = base + 4u; 260 b.e = base + 5u; 261 b.f = (double)(base + 6u) + 0.5; 262 return b; 263 } 264 265 __attribute__((noinline)) uint64_t take_big(Big b, Pair p, uint64_t tail) { 266 return b.a + b.b * 2u + b.c * 3u + b.d * 4u + b.e * 5u + 267 (uint64_t)(b.f * 10.0) + p.a + p.b + tail; 268 } 269 270 __attribute__((noinline)) uint64_t sum_varargs(int tag, ...) { 271 va_list ap; 272 uint64_t a; 273 double b; 274 uint64_t c; 275 int d; 276 double e; 277 va_start(ap, tag); 278 a = va_arg(ap, uint64_t); 279 b = va_arg(ap, double); 280 c = va_arg(ap, uint64_t); 281 d = va_arg(ap, int); 282 e = va_arg(ap, double); 283 va_end(ap); 284 return (uint64_t)tag + a + (uint64_t)(b * 10.0) + c + (uint64_t)d + 285 (uint64_t)(e * 10.0); 286 } 287 288 /* Loop over a long, boundary-crossing variadic list mixing integer and 289 * floating-point arguments. Exercises three easy-to-regress shapes at once: 290 * the loop accumulator stays live across va_start (so no scratch register may 291 * clobber it), the >8 arguments overflow the register slots onto the stack (so 292 * the ARM64 GP home area must be contiguous with the incoming stack args), and 293 * the floating-point arguments route through integer slots on ARM64. */ 294 __attribute__((noinline)) uint64_t loop_varargs(int n, ...) { 295 va_list ap; 296 uint64_t sum = (uint64_t)n; 297 va_start(ap, n); 298 for (int i = 0; i < n; ++i) { 299 if (i & 1) 300 sum += (uint64_t)(va_arg(ap, double) * 2.0); 301 else 302 sum += va_arg(ap, uint64_t); 303 } 304 va_end(ap); 305 return sum; 306 } 307 308 typedef uint64_t (*MixCallback)(int, double, uint64_t, double, int, int, 309 uint64_t); 310 311 __attribute__((noinline)) uint64_t callback_impl(int a, double b, uint64_t c, 312 double d, int e, int f, 313 uint64_t g) { 314 return (uint64_t)a + (uint64_t)(b * 10.0) + c * 2u + 315 (uint64_t)(d * 10.0) + (uint64_t)e * 3u + (uint64_t)f * 5u + g; 316 } 317 318 __attribute__((noinline)) uint64_t call_callback(MixCallback cb) { 319 return cb(3, 2.5, 7u, 1.5, 4, 5, 9u); 320 } 321 322 static int cmp_u32(const void* a, const void* b) { 323 uint32_t aa = *(const uint32_t*)a; 324 uint32_t bb = *(const uint32_t*)b; 325 return (aa > bb) - (aa < bb); 326 } 327 328 int main(void) { 329 uint32_t vals[6] = {9u, 1u, 7u, 3u, 5u, 2u}; 330 Pair p; 331 Big b; 332 if (!run_fp_probe()) return 10; 333 if (!(g_fp[15] > 0.0)) return 11; 334 if (!run_int_probe()) return 20; 335 if (g_int[15] == 0u) return 21; 336 if (mixed_args(1, 2u, 1.5, 2.5f, 3, 3.5, 4u, 5, 4.5, 6u) != 141u) 337 return 30; 338 if (stack_args(1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u) != 78u) 339 return 31; 340 p = make_pair(100u); 341 if (p.a != 101u || p.b != 102u) return 40; 342 b = make_big(10u); 343 if (take_big(b, p, 5u) != 578u) return 41; 344 if (sum_varargs(3, (uint64_t)10u, 2.5, (uint64_t)20u, 7, 1.5) != 80u) 345 return 50; 346 if (loop_varargs(10, (uint64_t)1, 2.0, (uint64_t)3, 4.0, (uint64_t)5, 6.0, 347 (uint64_t)7, 8.0, (uint64_t)9, 10.0) != 95u) 348 return 51; 349 if (call_callback(callback_impl) != 103u) return 60; 350 qsort(vals, 6u, sizeof vals[0], cmp_u32); 351 if (vals[0] != 1u || vals[1] != 2u || vals[5] != 9u) return 70; 352 return 0; 353 } 354 SRC 355 356 no_legacy_crt_imports() { 357 local name=$1 dump=$2 358 if grep -Eiq 'DLL Name: (msvcrt|ucrt)\.dll' "$dump"; then 359 grep -Ei 'DLL Name: (msvcrt|ucrt)\.dll' "$dump" > "$work/$name.diag" 360 not_ok "$name" "$work/$name.diag" 361 else 362 ok "$name" 363 fi 364 } 365 366 run_vm_if_available() { 367 local label=$1 arch=$2 exe=$3 368 case "$arch" in 369 x64) 370 if [ -n "${KIT_WINDOWS_VM_X64:-${KIT_WINDOWS_VM_AMD64:-}}" ]; then 371 if "$ROOT/scripts/windows_vm.sh" run x64 "$exe" \ 372 > "$work/$label-vm.out" 2> "$work/$label-vm.err"; then 373 ok "$label-vm" 374 else 375 not_ok "$label-vm" "$work/$label-vm.err" 376 fi 377 else 378 skip_test "$label-vm" "KIT_WINDOWS_VM_X64 not set" 379 fi 380 ;; 381 aarch64) 382 if [ -n "${KIT_WINDOWS_VM_AARCH64:-${KIT_WINDOWS_VM_ARM64:-}}" ]; then 383 if "$ROOT/scripts/windows_vm.sh" run aarch64 "$exe" \ 384 > "$work/$label-vm.out" 2> "$work/$label-vm.err"; then 385 ok "$label-vm" 386 else 387 not_ok "$label-vm" "$work/$label-vm.err" 388 fi 389 else 390 skip_test "$label-vm" "KIT_WINDOWS_VM_AARCH64 not set" 391 fi 392 ;; 393 esac 394 } 395 396 ran=0 397 for arch in x86_64 aarch64; do 398 case "$arch" in 399 x86_64) target=x86_64-windows; label=x64 ;; 400 aarch64) target=aarch64-windows; label=aarch64 ;; 401 esac 402 403 if ! ARCH_SDK=$(sdk_for_arch "$arch"); then 404 skip_test "$LABEL_SUITE/$label-sysroot" "no $arch llvm-mingw UCRT sysroot" 405 continue 406 fi 407 if [ ! -r "$ARCH_SDK/include/windows.h" ] || 408 [ ! -r "$ARCH_SDK/lib/libucrt.a" ]; then 409 skip_test "$LABEL_SUITE/$label-sysroot" "invalid UCRT llvm-mingw sysroot: $ARCH_SDK" 410 continue 411 fi 412 413 ran=1 414 exe=$work/o1-abi-$arch.exe 415 dump=$work/o1-abi-$arch.dump 416 run_ok "$label-o1-abi-build" "$KIT" cc -target "$target" --sysroot "$ARCH_SDK" \ 417 -O1 "$ABI_C" -o "$exe" 418 if [ -f "$exe" ]; then 419 run_ok "$label-o1-abi-objdump" "$KIT" objdump -p "$exe" 420 if [ -s "$work/$label-o1-abi-objdump.out" ]; then 421 cp "$work/$label-o1-abi-objdump.out" "$dump" 422 no_legacy_crt_imports "$label-o1-abi-no-legacy-crt" "$dump" 423 contains "$label-o1-abi-qsort-import" "$dump" "Name: qsort" 424 fi 425 run_vm_if_available "$label-o1-abi" "$label" "$exe" 426 fi 427 done 428 429 if [ "$ran" -eq 0 ]; then 430 skip_test "$LABEL_SUITE" "set KIT_SYSROOT or install llvm-mingw UCRT under /tmp/llvm-mingw*" 431 fi 432 433 kit_summary "$LABEL_SUITE" 434 kit_exit