. "$BST_LIB" require whetstone whetstone/present # whetstone 1.2 (Painter's 1998 C translation of the 1972 Curnow-Wichmann # FORTRAN benchmark) — the floating-point counterpart to 84-dhrystone. # # WHAT THIS SUITE IS ACTUALLY FOR, AND WHY IT NEEDS TWO BINARIES. # # Upstream's whetstone prints exactly one useful line: a KIPS/MIPS rate. A rate # is not an invariant — a build with broken f64 codegen, a wrong libm or a # mis-linked indirect call prints a plausible number just as readily as a # correct one — so a suite that only reads the score is coverage in name only. # The per-module results the program computes on the way to that score ARE # checkable, but upstream compiles their printer out unless PRINTOUT is defined. # # So the image ships both: /usr/bin/whetstone exactly as meta-oe builds it (the # benchmark, whose published measurement rules govern it), and # /usr/bin/whetstone-printout, the same source and the same flags plus # -DPRINTOUT (the oracle). See recipes-benchmark/whetstone/whetstone_%.bbappend. # # THE VALUES BELOW ARE AN x86_64 CONTROL OF THE SAME SOURCE, not a transcription # of this arm's output — the point is to catch this arm disagreeing with a # reference machine. Comparing them exactly at the %12.4e POUT prints is safe and # was verified rather than assumed: -O0, -O2 and -Ofast (with -ffast-math and its # reassociation licence) all produce a byte-identical block natively, because # whetstone's convergent modules are contractive — each step multiplies by # T=0.499975 or advances a fixed-point iteration — so ulp-level differences # shrink instead of accumulating. # # LOOP IS PINNED AT 1000 and that is load-bearing: the module values are a # function of the loop count, decaying from ~1e-2 at LOOP=1000 to ~1e-48 at # LOOP=50000. 1000 is also the program's own default and its documented # "one million Whetstone instructions per major loop" calibration. work=${TMPDIR:-/tmp}/bst-whet.$$ rm -rf "$work"; mkdir -p "$work" # --- the stock binary's own documented behaviours ---------------------------- # whetstone times itself with time(0) — WALL CLOCK, at one-second resolution — # and refuses to divide by a zero duration. A loop count that finishes inside a # single second therefore takes the "Insufficient duration" branch and exits 1. # LOOP=1 can never cross a second on any machine, so this asserts the branch # without assuming anything about this arm's speed. It is upstream behaviour, not # a fault: `whetstone` with NO argument does the same thing on any machine built # after ~2005, because the 1998 default of 1000 loops is now sub-millisecond. whetstone 1 > "$work/tiny" 2>&1; rc=$? check_eq whetstone/short-run-rc 1 "$rc" check_out whetstone/short-run-message "Insufficient duration" cat "$work/tiny" # An argument that is neither -c nor a positive integer is a usage error. NB a # bare argument beginning with 'c' means CONTINUOUS (an infinite re-run), which # is why the bad argument here deliberately does not start with one. whetstone zzz > "$work/bad" 2>&1; rc=$? check_eq whetstone/bad-arg-rc 1 "$rc" check_out whetstone/bad-arg-usage "usage: whetdc" cat "$work/bad" # --- the score -------------------------------------------------------------- # THE CASE ITS SIBLING BENCHMARK CANNOT HAVE. dhrystone times itself with # times(2) from inside its own process and so can never report a rate on this # arm, because a live task's CPU time is settled only when it exits # (brintos/linux#38). whetstone reads time(0) — wall clock, from outside the # task's own accounting — so the same defect does not reach it. This case is # what proves that, and it must therefore assert the RATE LINE and a clean exit, # not merely that the program ran. # # N is chosen so the run clears the one-second floor with margin on this arm # while staying inside the nightly's per-suite budget. No throughput floor is # asserted: the score is quantised by time(0)'s one-second resolution and the # build host is shared, so a rate assertion would be flaky rather than strict. N=400000 whetstone $N > "$work/big" 2>&1; rc=$? check_eq whetstone/score-rc 0 "$rc" check_out whetstone/score-loops "Loops: $N, Iterations: 1" cat "$work/big" if grep -qE "C Converted Double Precision Whetstones: [0-9.]+ (MIPS|KIPS)" "$work/big"; then bst_ok whetstone/score else bst_notok whetstone/score "no rate at LOOP=$N: $(_bst_1line "$(cat "$work/big")")" fi # --- the correctness oracle ------------------------------------------------- require whetstone-printout whetstone/printout-present LOOP=1000 whetstone-printout $LOOP > "$work/po" 2>&1 # rc is deliberately not checked here: at LOOP=1000 the run is far under the # one-second floor, so it exits 1 down the "Insufficient duration" branch — # AFTER printing every module line, which is all this half of the suite wants. # POUT prints "%7ld %7ld %7ld %12.4e %12.4e %12.4e %12.4e"; collapse the column # padding so a single expected string per module can be compared. mod() { sed -n "${1}p" "$work/po" | sed 's/^ *//; s/ */ /g; s/ *$//'; } nlines=$(grep -c 'e[+-][0-9][0-9]' "$work/po") check_eq whetstone/block-lines 10 "$nlines" # One case per module, named for the language feature it is the residue of, so a # single wrong value says WHICH part of this arm's codegen or libm broke. # Modules 5 and 12 are absent by design — upstream eliminated them, as the # FORTRAN original did. check_eq whetstone/mod1-identifiers \ "0 0 0 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00" "$(mod 1)" check_eq whetstone/mod2-array-elements \ "12000 14000 12000 -1.3190e-01 -1.8218e-01 -4.3145e-01 -4.8173e-01" "$(mod 2)" check_eq whetstone/mod3-array-as-parameter \ "14000 12000 12000 2.2103e-02 -2.7271e-02 -3.7914e-02 -8.7290e-02" "$(mod 3)" check_eq whetstone/mod4-conditional-jumps \ "345000 1 1 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00" "$(mod 4)" check_eq whetstone/mod6-integer-arithmetic \ "210000 1 2 6.0000e+00 6.0000e+00 -3.7914e-02 -8.7290e-02" "$(mod 5)" # Module 7 is the trigonometric one: X = T*atan(T2*sin(X)*cos(X)/(cos(X+Y)+cos(X-Y)-1)). # It is the only case here that exercises libm's sin, cos and atan at all. check_eq whetstone/mod7-trig-sin-cos-atan \ "32000 1 2 9.1176e-02 9.1176e-02 9.1174e-02 9.1174e-02" "$(mod 6)" # Module 8 is P3(X,Y,&Z) — call-by-reference through a double*, 899000 times. check_eq whetstone/mod8-procedure-calls \ "899000 1 2 1.0000e+00 1.0000e+00 9.9994e-01 9.9994e-01" "$(mod 7)" # Module 9 is P0(), a three-way permutation of E1[] through the globals J,K,L — # array indexing by variable subscript, and the only module whose result depends # on the iteration count modulo 3. check_eq whetstone/mod9-array-references \ "616000 1 2 3.0000e+00 2.0000e+00 3.0000e+00 -8.7290e-02" "$(mod 8)" check_eq whetstone/mod10-integer-arithmetic \ "0 2 3 1.0000e+00 -1.0000e+00 -1.0000e+00 -1.0000e+00" "$(mod 9)" # Module 11 is X = sqrt(exp(log(X)/T1)) — libm's sqrt, exp and log on a # contractive iteration that converges to 1.0. check_eq whetstone/mod11-sqrt-exp-log \ "93000 2 3 1.0000e+00 1.0000e+00 1.0000e+00 1.0000e+00" "$(mod 10)" # --- the loop count really is read ------------------------------------------ # Every case above pins LOOP=1000. If the argument were ignored and the built-in # default used instead, all ten would still pass — they would simply be right for # the wrong reason. A second, different count whose module-2 line differs is what # rules that out. whetstone-printout 100 > "$work/po100" 2>&1 check_eq whetstone/loop-honoured \ "1200 1400 1200 -8.2250e-02 -4.2758e-01 -6.9768e-01 -1.0430e+00" \ "$(sed -n '2p' "$work/po100" | sed 's/^ *//; s/ */ /g; s/ *$//')" rm -rf "$work"