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