kit

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

hostas_toy.sh (9107B)


      1 #!/usr/bin/env bash
      2 # test/asm/hostas_toy.sh — prove kit's `cc -S` is STANDARD assembly by feeding
      3 # the SAME `cc -S` output to two assemblers, linking + running both, and asserting
      4 # the toy exit-code oracle for each.  (Type C — shared corpus harness.)
      5 #
      6 # Per toy case (native target; both O0 and O1):
      7 #     kit cc -S                       -> s.s   (one native .s, shared by both)
      8 #   A /kit-as:  kit as s.s | kit ld | ./a.out      exit == oracle
      9 #   B /clang-as:  clang   -c s.s | kit ld | ./b.out    exit == oracle
     10 #
     11 # The only difference between the two lanes is the ASSEMBLER, so this is a
     12 # controlled test of whether kit's emitted asm is something a third-party
     13 # toolchain accepts and that means the same thing. Lane A is the baseline (kit
     14 # both writes and reads the text, so a private-dialect quirk can hide). Lane B is
     15 # the real test: a standard assembler (clang) can't paper over such a quirk —
     16 # same idea as test/asm/diff_llvm.sh for the C corpus, but checked by EXECUTION
     17 # (exit code), not by matching bytes (kit and clang produce different code, so
     18 # a byte/text match would be meaningless).
     19 #
     20 # GATED: `cc -S` is now object-format-aware (src/api/asm_emit.c's AsmSyntax
     21 # vtable + the aarch64 ArchAsmOps reloc-operand hook), so on the native Mach-O
     22 # target it emits clean Mach-O assembly clang/llvm-mc accept — no ELF `.type`/
     23 # `.size`, `.section __SEG,__SECT`, `.p2align`, and `sym@PAGE`/`@PAGEOFF`
     24 # relocation operands. kit's own `as` parses the same Mach-O dialect (it
     25 # dispatches on its target format), so BOTH lanes pass. The clang-as lane (B)
     26 # gates by default; set KIT_HOSTAS_ENFORCE_CLANG=0 to demote it to XFAIL (e.g.
     27 # while bringing up a new arch/format whose printer side isn't done yet).
     28 #
     29 # Lanes (no KIT_TEST_PATHS knob — both lanes always run; lane B's verdict is
     30 # gated by KIT_HOSTAS_ENFORCE_CLANG instead):
     31 #   A  kit-as -> kit ld -> run         exit == oracle (the baseline)
     32 #   B  clang-as -> kit ld -> run         exit == oracle (the real test)
     33 # Every lane hook writes only under $KIT_WORK and records via kit_*, so the runner
     34 # is parallel-safe; KIT_HOSTAS_PARALLEL flips dispatch.
     35 
     36 set -u
     37 
     38 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     39 export KIT_LIB_DIR="$ROOT/test/lib"
     40 # shellcheck source=../lib/kit_corpus.sh
     41 . "$ROOT/test/lib/kit_corpus.sh"
     42 
     43 KIT="${KIT:-$ROOT/build/kit}"
     44 CASES="$ROOT/test/toy/cases"
     45 BUILD_DIR="$ROOT/build/test/asm/hostas_toy"
     46 ENFORCE_CLANG="${KIT_HOSTAS_ENFORCE_CLANG:-1}"
     47 PAR="${KIT_HOSTAS_PARALLEL:-1}"
     48 
     49 # The original harness exits on a_fail (+ b_efail when enforcing) only — skips
     50 # (just the known 141 case) never gated. Keep that: SKIP must not fail the run.
     51 KIT_SKIP_IS_FAILURE=0
     52 
     53 # Opt axis. KIT_TEST_OPTS carries "O"-prefixed levels (e.g. "O0 O1"); the
     54 # corpus engine expects bare levels (KIT_OPT_LEVELS="0 1"). Strip the prefix.
     55 OPTS="${KIT_TEST_OPTS:-O0 O1}"
     56 OPT_LEVELS=""
     57 for _o in $OPTS; do OPT_LEVELS="$OPT_LEVELS ${_o#O}"; done
     58 
     59 # Filter ($1) preserved -> the engine filters discovery by KIT_TEST_FILTER.
     60 FILTER="${1:-${KIT_TEST_FILTER:-}}"
     61 export KIT_TEST_FILTER="$FILTER"
     62 
     63 CLANG="${CLANG:-$(command -v clang 2>/dev/null || true)}"
     64 
     65 color_red() { printf '\033[31m%s\033[0m' "$1"; }
     66 color_yel() { printf '\033[33m%s\033[0m' "$1"; }
     67 
     68 if [ ! -x "$KIT" ]; then
     69     printf 'hostas-toy: %s kit missing — run "make bin"\n' "$(color_red FATAL)" >&2
     70     exit 1
     71 fi
     72 if [ -z "$CLANG" ] || [ ! -x "$CLANG" ]; then
     73     printf 'hostas-toy: %s no clang (host assembler); skipping\n' "$(color_yel SKIP)"
     74     exit 0
     75 fi
     76 mkdir -p "$BUILD_DIR"
     77 
     78 # Native target tuple/triple for the corpus engine + `cc -S`/`as`.
     79 TEST_ARCH="${KIT_TEST_ARCH:-aa64}"
     80 TEST_OBJ="${KIT_TEST_OBJ:-macho}"
     81 case "$TEST_ARCH" in
     82   aa64|aarch64|arm64) TEST_ARCH=aa64 ;;
     83   x64|x86_64|amd64)   TEST_ARCH=x64 ;;
     84   rv64|riscv64)       TEST_ARCH=rv64 ;;
     85 esac
     86 CUR_TUPLE="$TEST_ARCH-$TEST_OBJ"
     87 
     88 # First meaningful diagnostic from an assembler's stderr. clang prints a harmless
     89 # `-Wmissing-sysroot` warning first (we never link with clang, so the SDK is
     90 # irrelevant), so prefer the first real `error:` line over a blind `head -1`.
     91 err_reason() {
     92     local f="$1" line=""
     93     line=$(grep -m1 -E 'error:' "$f" 2>/dev/null | sed 's|.*error: *||')
     94     [ -z "$line" ] && line=$(grep -m1 -E 'unknown directive|unknown section type' "$f" 2>/dev/null | sed 's|^[[:space:]]*||')
     95     [ -z "$line" ] && line=$(head -1 "$f" 2>/dev/null | sed 's|.*: ||')
     96     printf '%s' "$line"
     97 }
     98 # Terse first-line reason (kit's own diagnostics: "<tool>: <msg>").
     99 first_reason() { head -1 "$1" 2>/dev/null | sed 's|.*: ||'; }
    100 
    101 # Cases blocked on a separate, known `cc -S` symbolizer gap (the round-trip lane
    102 # quarantines the same set): 141 emits an unsymbolized `adrp x,0x0` for a
    103 # thread-local access (TLS symbolization, tracked separately). Sidecar-driven
    104 # via test/toy/cases/141_threadlocal_mutate.link.skip, read by KIT_READ_CASE
    105 # under the synthetic "link" lane name so the same .link.skip the toy/roundtrip
    106 # harnesses honor skips the WHOLE case here too.
    107 hostas_read_case() {
    108     local reason
    109     if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" "link"); then
    110         KIT_SKIP_CASE="$reason (cc -S symbolizer gap)"
    111     fi
    112 }
    113 
    114 # Shared `cc -S` build for both lanes (one native .s per item). Cached per item
    115 # in $KIT_WORK so lanes A and B reuse it. Sets CCS_S (path) + CCS_RC (0 ok).
    116 # A cc -S failure is a lane-A failure only (lane B records nothing for the item,
    117 # matching the original `continue`).
    118 _ccs_build() {
    119     CCS_S="$KIT_WORK/s.s"
    120     [ -f "$KIT_WORK/.ccs.done" ] && { CCS_RC=$(cat "$KIT_WORK/.ccs.rc"); return "$CCS_RC"; }
    121     if "$KIT" cc -S "-O$KIT_OPT" "$KIT_SRC" -o "$CCS_S" 2>"$KIT_WORK/ccs.err"; then
    122         CCS_RC=0
    123     else
    124         CCS_RC=1
    125     fi
    126     echo "$CCS_RC" >"$KIT_WORK/.ccs.rc"; : >"$KIT_WORK/.ccs.done"
    127     return "$CCS_RC"
    128 }
    129 
    130 # ---- lane A: kit-as -> kit ld -> run (the baseline) ---------------------
    131 kit_lane_A() {
    132     if ! _ccs_build; then
    133         kit_fail "$KIT_NAME/kit-as" "cc -S: $(first_reason "$KIT_WORK/ccs.err")"
    134         return
    135     fi
    136     if ! "$KIT" as "$CCS_S" -o "$KIT_WORK/a.o" 2>"$KIT_WORK/a.as.err"; then
    137         kit_fail "$KIT_NAME/kit-as" "as: $(first_reason "$KIT_WORK/a.as.err")"; return
    138     fi
    139     if ! "$KIT" ld "$KIT_WORK/a.o" -o "$KIT_WORK/a.out" 2>"$KIT_WORK/a.ld.err" || [ -s "$KIT_WORK/a.ld.err" ]; then
    140         kit_fail "$KIT_NAME/kit-as" "ld: $(first_reason "$KIT_WORK/a.ld.err")"; return
    141     fi
    142     chmod +x "$KIT_WORK/a.out" 2>/dev/null || true
    143     "$KIT_WORK/a.out" >"$KIT_WORK/a.out.txt" 2>"$KIT_WORK/a.run.err"; local arc=$?
    144     local exp=$((KIT_EXPECTED & 255))   # exit codes wrap at 256 (oracle 312 -> 56)
    145     if [ -s "$KIT_WORK/a.run.err" ]; then
    146         kit_fail "$KIT_NAME/kit-as" "run stderr: $(head -1 "$KIT_WORK/a.run.err")"
    147     elif [ "$arc" -eq "$exp" ]; then
    148         kit_pass "$KIT_NAME/kit-as"
    149     else
    150         kit_fail "$KIT_NAME/kit-as" "exit $arc != $exp"
    151     fi
    152 }
    153 
    154 # ---- lane B: clang-as -> kit ld -> run (the real test) -------------------
    155 # Gated by ENFORCE_CLANG: pass+enforce -> PASS, pass+!enforce -> XPASS;
    156 # fail+enforce -> FAIL, fail+!enforce -> XFAIL. kit ld on both lanes isolates
    157 # the assembler as the only variable. Records NOTHING on a shared cc -S failure
    158 # (matching the original `continue`).
    159 kit_lane_B() {
    160     if ! _ccs_build; then return; fi
    161     local ok=0 reason="" exp=$((KIT_EXPECTED & 255))
    162     if ! "$CLANG" -c "$CCS_S" -o "$KIT_WORK/b.o" 2>"$KIT_WORK/b.as.err"; then
    163         reason="clang as: $(err_reason "$KIT_WORK/b.as.err")"
    164     elif ! "$KIT" ld "$KIT_WORK/b.o" -o "$KIT_WORK/b.out" 2>"$KIT_WORK/b.ld.err" || [ -s "$KIT_WORK/b.ld.err" ]; then
    165         reason="kit ld: $(first_reason "$KIT_WORK/b.ld.err")"
    166     else
    167         chmod +x "$KIT_WORK/b.out" 2>/dev/null || true
    168         "$KIT_WORK/b.out" >"$KIT_WORK/b.out.txt" 2>"$KIT_WORK/b.run.err"; local brc=$?
    169         if [ -s "$KIT_WORK/b.run.err" ]; then reason="run stderr: $(head -1 "$KIT_WORK/b.run.err")"
    170         elif [ "$brc" -eq "$exp" ]; then ok=1
    171         else reason="exit $brc != $exp"; fi
    172     fi
    173     if [ "$ok" -eq 1 ]; then
    174         if [ "$ENFORCE_CLANG" = "1" ]; then kit_pass "$KIT_NAME/clang-as"
    175         else kit_xpass "$KIT_NAME/clang-as"; fi
    176     else
    177         if [ "$ENFORCE_CLANG" = "1" ]; then kit_fail "$KIT_NAME/clang-as" "$reason"
    178         else kit_xfail "$KIT_NAME/clang-as" "$reason"; fi
    179     fi
    180 }
    181 
    182 # ---- drive the corpus ------------------------------------------------------
    183 printf 'hostas-toy: kit=%s\n' "$KIT"
    184 printf 'hostas-toy: clang=%s opts="%s" enforce_clang=%s\n' "$CLANG" "$OPT_LEVELS" "$ENFORCE_CLANG"
    185 
    186 # Lane B's XFAIL/XPASS already carry the enforce semantics; gate xpass under
    187 # KIT_HOSTAS_ENFORCE_CLANG=1 is N/A here (enforce=1 means lane B counts as
    188 # PASS/FAIL, not XPASS/XFAIL), so leave KIT_STRICT_XFAIL at its default.
    189 KIT_LABEL=test-hostas-toy KIT_BUILD_DIR="$BUILD_DIR" \
    190   KIT_CORPUS_GLOBS="$CASES/*.toy" KIT_CORPUS_EXT=toy KIT_SIDECAR_DIR="$CASES" \
    191   KIT_LANES="A B" KIT_OPT_LEVELS="$OPT_LEVELS" KIT_TUPLES="$CUR_TUPLE" \
    192   KIT_TARGETS_EXT="" KIT_READ_CASE=hostas_read_case KIT_PARALLELIZABLE="$PAR" \
    193   kit_corpus_run
    194 
    195 kit_summary test-hostas-toy
    196 kit_exit