kit

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

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