kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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