run_all.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #!/bin/bash
  2. # Run every check. Non-zero exit if anything fails.
  3. #
  4. # Six suites, in increasing order of "how much could be lying to me":
  5. #
  6. # 1. compile matrix every fixture in tests/fixtures/expected.tsv, verdict +
  7. # code size + data size ASSERTED from that file. Fast,
  8. # no pty. The count is deliberately not repeated here:
  9. # it changes whenever a fixture is added, and a number
  10. # that is only right until the next edit is how a
  11. # comment starts claiming things it cannot support.
  12. # 2. .COM linker links every fixture, then re-verifies the bytes with an
  13. # independent checker that restates the layout constants
  14. # instead of asking the compiler.
  15. # 3. UI error path pty: W, C on a bad file, editor opens on the error,
  16. # Ctrl-K D, Q. Covers the shell behaviour that only
  17. # exists interactively.
  18. # 4. UI success path pty: Options -> Destination=COM, C, then the .COM is
  19. # checked ON DISK. This is the end-to-end path from
  20. # keystroke to artifact.
  21. #
  22. # 5. frame displ links the two fixtures that touch a stack frame and
  23. # asserts the [BP+off] displacement encoding, which no
  24. # other check can see: disp8 and disp16 are both
  25. # well-formed and both decode cleanly, and only one of
  26. # them reads the variable the symbol table named.
  27. #
  28. # 6. EXECUTE (Exec86) the same fixtures a second time, through
  29. # shell/Exec86.mod - this project's own in-process 8086
  30. # interpreter - and require its output to agree with
  31. # both the hand-derived .out and qemu, byte for byte.
  32. # This is the second, INDEPENDENT execution oracle: it
  33. # was written against the 8086's own reference, where
  34. # qemu-system-i386's lowest CPU model is a 486 (`0F 84'
  35. # is an ordinary JZ there) and FCML's -m16 is a 386. Two
  36. # checks that merely agreed with each other would be one
  37. # check wearing two hats.
  38. #
  39. # 7. UI run (R) pty: W, then R - the path that produces NO artifact.
  40. # The image is poked into the interpreter at 0100h and
  41. # run in-process, so the only evidence is the guest's
  42. # own output, asserted against the hand-derived .out,
  43. # plus the reported AH=4Ch status and a non-zero step
  44. # count. Nothing else in this file can observe R.
  45. #
  46. # RtProbe dumps the runtime size and its 14 entry offsets, so a
  47. # runtime change that moves an entry is visible here.
  48. #
  49. # Three runtime suites run before all of those, because everything else
  50. # trusts the runtime's bytes:
  51. #
  52. # audit_helpers disassembles every one-line emitter in BOTH Runtime.mod
  53. # and Compiler.mod and compares the DECODE against the
  54. # procedure's NAME. This is the only check that can see
  55. # a wrong ModRM that still decodes cleanly, which is the
  56. # mistake this project actually makes: MovSiBx and
  57. # CmpSiBx were both `DC` (= MOV SP,BX / CMP SP,BX) for a
  58. # long time, and EmXchgAxCx was `93` (XCHG BX,AX) under a
  59. # name that says XCHG AX,CX.
  60. #
  61. # It sweeps Compiler.mod because that is where the
  62. # emitter was. Auditing one module said "every helper
  63. # agrees with its name" about a module it had not
  64. # examined, which is why the XCHG fault survived a green
  65. # run. It also cross-checks its own coverage: a
  66. # procedure that emits bytes but that the audit never
  67. # matched is itself a failure, because a helper that
  68. # drops out of the audit when you add a comment is worse
  69. # than a helper that is checked and found wanting.
  70. # check_comimage asserts that a linked .COM's layout is described in
  71. # exactly one file and that every reader imports it.
  72. # Each reader used to keep its own copy: two had their
  73. # own find_header, one a single constant of it, and one
  74. # nothing but a drifted literal for where the runtime
  75. # ends - which let its sweep begin inside the runtime
  76. # and report PASS over bytes the program never executes.
  77. # Silent, because each copy is self-consistent on its
  78. # own terms.
  79. # check_runtime sweeps the built runtime's code region with FCML: no
  80. # desync, every entry and every branch target on an
  81. # instruction boundary, and the whole disassembly equal
  82. # to tests/runtime.golden.
  83. # nonvacuity breaks the runtime and the compiler in the ways each
  84. # named check is meant to catch, and asserts it goes red
  85. # for the STATED reason, not merely red. Slower, so it
  86. # is not in the default run: run it when a check's
  87. # sensitivity is in question, or after editing a check.
  88. #
  89. # nonvacuity.sh is NOT run by default: it rebuilds the runtime and the compiler
  90. # a dozen times over and it is a meta-check, so it belongs to "is the harness
  91. # honest" reviews rather than to every change. Run tests/nonvacuity.sh
  92. # directly after touching any of the checks above. (The count of rebuilds is
  93. # not stated here on purpose: it drifts every time a case is added, and a
  94. # number in a comment that is only right until the next edit is the kind of
  95. # restated constant that hides a fault here.)
  96. #
  97. # Two more checks guard the ModR/M table itself, which is the input every hand
  98. # written emitter in Runtime.mod depends on:
  99. #
  100. # probe/modrm11.py asks GNU `as` (.code16) to encode the register moves
  101. # and FCML to decode them, and requires the result to
  102. # agree with the hard-coded bytes, the four
  103. # hand-checking anchors, and the table in Runtime.mod.
  104. # This is the only check that can catch the TABLE being
  105. # wrong rather than one emitter: the table shipped once
  106. # with AX dropped off the front of the mod=11 column
  107. # and a duplicate BX invented at the end, which shifts
  108. # every code down by one and makes `89 DC` (MOV SP,BX)
  109. # look like the SI move the name asks for.
  110. # probe/modrm19.s the execution probe for mod=00/01/10, kept as the
  111. # record of how the memory forms were measured. It is
  112. # not run by default: it needs qemu and a hand-built
  113. # floppy image, and its result is already written into
  114. # the table in Runtime.mod, which modrm11.py checks.
  115. # See probe/README.md to re-run it.
  116. #
  117. # rt_exec.py DOES run. It used to run on Unicorn and fail 33 of 33, because
  118. # Unicorn 2.1.4 mis-decodes 16-bit ModRM memory operands - so every failure was
  119. # the emulator's, and a wrong oracle is worse than none: it cannot tell "my
  120. # codegen is broken" from "the machine is broken". It now boots the same
  121. # bootcom.s the .COM fixtures use, one fresh machine per case, so every case
  122. # gets a machine that has provably never run anything. See its own docstring
  123. # and the EXPECTATION FIXES section in tests/rt_exec.py.
  124. set -u
  125. D=/home/eric/Projets/Projets-Modula2/MyWork/TP3-comp/shell
  126. GM2=/home/eric/bin/Modula2/Gm2/bin/gm2
  127. cd "$D" || exit 9
  128. fail=0
  129. run () {
  130. name=$1
  131. shift
  132. echo
  133. echo "=============================================================="
  134. echo "== $name"
  135. echo "=============================================================="
  136. if "$@" ; then
  137. echo "-- $name: PASS"
  138. else
  139. echo "-- $name: FAIL (rc=$?)"
  140. fail=1
  141. fi
  142. }
  143. # Scratch and logs go in the local tmp/ folder, beside the tree that produced
  144. # them, so a failing run's evidence can be read next to the code it describes.
  145. mkdir -p ../tmp
  146. echo "== build =="
  147. make clean >/dev/null 2>&1
  148. if ! make >../tmp/all.make.log 2>&1 ; then
  149. echo "BUILD FAIL"
  150. grep -m10 "error:" ../tmp/all.make.log
  151. exit 1
  152. fi
  153. echo "make rc=0, tpshell $(stat -c%s tpshell) bytes"
  154. # gm2 reports a pass-3 rollup on the compiler during phase 1 of the two-phase
  155. # link; that is expected and is not a build failure (make rc=0 is the check).
  156. # The probe step asks rt_exec.py for its dump rather than building RtProbe a
  157. # second time by hand. Two recipes for one artifact is how a stale binary gets
  158. # believed: ensure_rtprobe() is the only one that knows Runtime.mod's mtime, and
  159. # a probe older than the runtime it reports on is worse than no probe, because
  160. # its failure -- a blob with the wrong addresses baked in -- is invisible at the
  161. # byte level and catastrophic at the behaviour level.
  162. run "runtime probe" python3 tests/rt_exec.py --probe
  163. run "helper audit" python3 tests/audit_helpers.py
  164. run "layout helper" python3 tests/check_comimage.py
  165. run "mod=11 table" python3 tests/probe/modrm11.py
  166. run "runtime image" python3 tests/check_runtime.py
  167. run "compile matrix" tests/run_compile_tests.sh
  168. run "COM linker" tests/run_com_tests.sh
  169. # The byte checks above can only ask "are these bytes well formed". This one
  170. # asks "does the program do the right thing": every fixture with a .out is
  171. # compiled, booted in qemu, and its output compared byte for byte, together
  172. # with the DOS exit code. It is the step that found eleven runtime bugs, the
  173. # FOR off-by-one, the missing procedure-skip jump and a parser bug - none of
  174. # which produced a malformed byte.
  175. run "EXECUTE under qemu" python3 tests/run_com_exec.py
  176. # The same images a second time, inside this project's own interpreter, which
  177. # must agree with qemu byte for byte. This is the only check that can fail on
  178. # something qemu cannot do at all: an 8086-only fault, or the interpreter
  179. # itself misreading one. It has no --rebless by design - the .out files are
  180. # hand-derived from Pascal's semantics, and an oracle that could bless its own
  181. # output would pass by construction.
  182. run "EXECUTE (Exec86)" python3 tests/run_exec86.py
  183. # And this one asks whether the runtime ENTRIES do what they claim, one qemu
  184. # boot per case plus a pre-flight. It is the only check that can catch
  185. # an entry that is well-formed, decodes cleanly, preserves every register the
  186. # driver happens to need - and computes the wrong answer: wrchar and wrbool
  187. # borrowed BP to reach their argument and never gave it back, which a byte check
  188. # and a register-convention check both call correct. One case (four calls back
  189. # to back) noticed; the BP-contract pre-flight is what stops the next entry from
  190. # depending on there being such a case.
  191. run "runtime entries" python3 tests/rt_exec.py
  192. run "frame displ" python3 tests/check_framedisp.py
  193. # The machine this compiler targets is an 8086, and the byte checks above all
  194. # ask "is this well formed" -- which `0F 84 lo hi' is. qemu-system-i386's
  195. # lowest CPU model is 486, so the execution suite could not object either.
  196. # This one asks "does the target have this opcode at all", and asserts the
  197. # lowering that replaced the two illegal ones is the original compiler's shape.
  198. run "8086 opcodes" python3 tests/check_8086.py
  199. run "UI error path" python3 tests/uitest.py
  200. run "UI success path" python3 tests/comtest.py
  201. # And the path that produces NO artifact: R compiles, pokes the image into the
  202. # in-process interpreter at 0100h and runs it here. Its evidence is the
  203. # guest's own output against the hand-derived .out, plus the reported INT 21h
  204. # status and step count - there is no file on disk for any other check to
  205. # read, which is why this one drives the shell itself.
  206. run "UI run (R)" python3 tests/runtest.py
  207. echo
  208. echo "=============================================================="
  209. if [ "$fail" -eq 0 ]; then
  210. echo "OVERALL: ALL PASS"
  211. else
  212. echo "OVERALL: FAILURES PRESENT"
  213. fi
  214. echo "=============================================================="
  215. exit $fail