summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/abi.ml532
-rwxr-xr-xsrc/tools/abitest.sh104
-rw-r--r--src/tools/fptox.c18
-rw-r--r--src/tools/pmov.c252
-rwxr-xr-xsrc/tools/regress.sh17
5 files changed, 923 insertions, 0 deletions
diff --git a/src/tools/abi.ml b/src/tools/abi.ml
new file mode 100644
index 0000000..d845c74
--- /dev/null
+++ b/src/tools/abi.ml
@@ -0,0 +1,532 @@
+(* fuzzer *)
+
+type _ bty =
+  | Char: int bty
+  | Short: int bty
+  | Int: int bty
+  | Long: int bty
+  | Float: float bty
+  | Double: float bty
+
+type _ sty =
+  | Field: 'a bty * 'b sty -> ('a * 'b) sty
+  | Empty: unit sty
+
+type _ aty =
+  | Base: 'a bty -> 'a aty
+  | Struct: 'a sty -> 'a aty
+
+type anyb = AB: _ bty -> anyb (* kinda boring... *)
+type anys = AS: _ sty -> anys
+type anya = AA: _ aty -> anya
+type testb = TB: 'a bty * 'a -> testb
+type testa = TA: 'a aty * 'a -> testa
+
+
+let align a x =
+  let m = x mod a in
+  if m <> 0 then x + (a-m) else x
+
+let btysize: type a. a bty -> int = function
+  | Char -> 1
+  | Short -> 2
+  | Int -> 4
+  | Long -> 8
+  | Float -> 4
+  | Double -> 8
+
+let btyalign = btysize
+
+let styempty: type a. a sty -> bool = function
+  | Field _ -> false
+  | Empty -> true
+
+let stysize s =
+  let rec f: type a. int -> a sty -> int =
+    fun sz -> function
+    | Field (b, s) ->
+      let a = btyalign b in
+      f (align a sz + btysize b) s
+    | Empty -> sz in
+  f 0 s
+
+let rec styalign: type a. a sty -> int = function
+  | Field (b, s) -> max (btyalign b) (styalign s)
+  | Empty -> 1
+
+
+(* Generate types and test vectors. *)
+module Gen = struct
+  module R = Random
+
+  let init = function
+    | None ->
+      let f = open_in "/dev/urandom" in
+      let seed =
+        Char.code (input_char f) lsl 8 +
+        Char.code (input_char f) in
+      close_in f;
+      R.init seed;
+      seed
+    | Some seed ->
+      R.init seed;
+      seed
+
+  let int sz =
+    let bound = 1 lsl (8 * min sz 3 - 1) in
+    let i = R.int bound in
+    if R.bool () then - i else i
+
+  let float () =
+    let f = R.float 1000. in
+    if R.bool () then -. f else f
+
+  let testv: type a. a aty -> a =
+    let tb: type a. a bty -> a = function (* eh, dry... *)
+      | Float  -> float ()
+      | Double -> float ()
+      | Char   -> int (btysize Char)
+      | Short  -> int (btysize Short)
+      | Int    -> int (btysize Int)
+      | Long   -> int (btysize Long) in
+    let rec ts: type a. a sty -> a = function
+      | Field (b, s) -> (tb b, ts s)
+      | Empty -> () in
+    function
+    | Base b -> tb b
+    | Struct s -> ts s
+
+  let b () = (* uniform *)
+    match R.int 6 with
+    | 0 -> AB Char
+    | 1 -> AB Short
+    | 2 -> AB Int
+    | 3 -> AB Long
+    | 4 -> AB Float
+    | _ -> AB Double
+
+  let smax = 5      (* max elements in structs *)
+  let structp = 0.3 (* odds of having a struct type *)
+  let amax = 8      (* max function arguments *)
+
+  let s () =
+    let rec f n =
+      if n = 0 then AS Empty else
+      let AB bt = b () in
+      let AS st = f (n-1) in
+      AS (Field (bt, st)) in
+    f (1 + R.int (smax-1))
+
+  let a () =
+    if R.float 1.0 > structp then
+      let AB bt = b () in
+      AA (Base bt)
+    else
+      let AB bt = b () in
+      let AS st = s () in
+      AA (Struct (Field (bt, st)))
+
+  let test () =
+    let AA ty = a () in
+    let t = testv ty in
+    TA (ty, t)
+
+  let tests () =
+    let rec f n =
+      if n = 0 then [] else
+      test () :: f (n-1) in
+    f (R.int amax)
+
+end
+
+
+(* Code generation for C *)
+module OutC = struct
+  open Printf
+
+  let ctypelong oc name =
+    let cb: type a. a bty -> unit = function
+      | Char   -> fprintf oc "char"
+      | Short  -> fprintf oc "short"
+      | Int    -> fprintf oc "int"
+      | Long   -> fprintf oc "long"
+      | Float  -> fprintf oc "float"
+      | Double -> fprintf oc "double" in
+    let rec cs: type a. int -> a sty -> unit =
+      fun i -> function
+      | Field (b, s) ->
+        cb b;
+        fprintf oc " f%d; " i;
+        cs (i+1) s;
+      | Empty -> () in
+    function
+    | Base b ->
+      cb b;
+    | Struct s ->
+      fprintf oc "struct %s { " name;
+      cs 1 s;
+      fprintf oc "}";
+      ()
+
+  let ctype: type a. out_channel -> string -> a aty -> unit =
+    fun oc name -> function
+    | Struct _ -> fprintf oc "struct %s" name
+    | t -> ctypelong oc "" t
+
+  let base: type a. out_channel -> a bty * a -> unit =
+    fun oc -> function
+    | Char, i   -> fprintf oc "%d" i
+    | Short, i  -> fprintf oc "%d" i
+    | Int, i    -> fprintf oc "%d" i
+    | Long, i   -> fprintf oc "%d" i
+    | Float, f  -> fprintf oc "%ff" f
+    | Double, f -> fprintf oc "%f" f
+
+  let init oc name (TA (ty, t)) =
+    let inits s =
+      let rec f: type a. a sty * a -> unit = function
+        | Field (b, s), (tb, ts) ->
+          base oc (b, tb);
+          fprintf oc ", ";
+          f (s, ts)
+        | Empty, () -> () in
+      fprintf oc "{ ";
+      f s;
+      fprintf oc "}"; in
+    ctype oc name ty;
+    fprintf oc " %s = " name;
+    begin match (ty, t) with
+    | Base b, tb -> base oc (b, tb)
+    | Struct s, ts -> inits (s, ts)
+    end;
+    fprintf oc ";\n";
+    ()
+
+  let extension = ".c"
+
+  let comment oc s =
+    fprintf oc "/* %s */\n" s
+
+  let prelude oc = List.iter (fprintf oc "%s\n")
+    [ "#include <stdio.h>"
+    ; "#include <stdlib.h>"
+    ; ""
+    ; "static void fail(char *chk)"
+    ; "{"
+    ; "\tfprintf(stderr, \"fail: checking %s\\n\", chk);"
+    ; "\tabort();"
+    ; "}"
+    ; ""
+    ]
+
+  let typedef oc name = function
+    | TA (Struct ts, _) ->
+      ctypelong oc name (Struct ts);
+      fprintf oc ";\n";
+    | _ -> ()
+
+  let check oc name =
+    let chkbase: type a. string -> a bty * a -> unit =
+      fun name t ->
+        fprintf oc "\tif (%s != " name;
+        base oc t;
+        fprintf oc ")\n\t\tfail(%S);\n" name; in
+    function
+    | TA (Base b, tb) -> chkbase name (b, tb)
+    | TA (Struct s, ts) ->
+      let rec f: type a. int -> a sty * a -> unit =
+        fun i -> function
+        | Field (b, s), (tb, ts) ->
+          chkbase (Printf.sprintf "%s.f%d" name i) (b, tb);
+          f (i+1) (s, ts);
+        | Empty, () -> () in
+      f 1 (s, ts)
+
+  let argname i = "arg" ^ string_of_int (i+1)
+
+  let proto oc (TA (tret, _)) args =
+    ctype oc "ret" tret;
+    fprintf oc " f(";
+    let narg = List.length args in
+    List.iteri (fun i (TA (targ, _)) ->
+      ctype oc (argname i) targ;
+      fprintf oc " %s" (argname i);
+      if i <> narg-1 then
+        fprintf oc ", ";
+    ) args;
+    fprintf oc ")";
+    ()
+
+  let caller oc ret args =
+    let narg = List.length args in
+    prelude oc;
+    typedef oc "ret" ret;
+    List.iteri (fun i arg ->
+      typedef oc (argname i) arg;
+    ) args;
+    proto oc ret args;
+    fprintf oc ";\n\nint main()\n{\n";
+    List.iteri (fun i arg ->
+      fprintf oc "\t";
+      init oc (argname i) arg;
+    ) args;
+    fprintf oc "\t";
+    let TA (tret, _) = ret in
+    ctype oc "ret" tret;
+    fprintf oc " ret;\n\n";
+    fprintf oc "\tret = f(";
+    List.iteri (fun i _ ->
+      fprintf oc "%s" (argname i);
+      if i <> narg-1 then
+        fprintf oc ", ";
+    ) args;
+    fprintf oc ");\n";
+    check oc "ret" ret;
+    fprintf oc "\n\treturn 0;\n}\n";
+    ()
+
+  let callee oc ret args =
+    prelude oc;
+    typedef oc "ret" ret;
+    List.iteri (fun i arg ->
+      typedef oc (argname i) arg;
+    ) args;
+    fprintf oc "\n";
+    proto oc ret args;
+    fprintf oc "\n{\n\t";
+    init oc "ret" ret;
+    fprintf oc "\n";
+    List.iteri (fun i arg ->
+      check oc (argname i) arg;
+    ) args;
+    fprintf oc "\n\treturn ret;\n}\n";
+    ()
+
+end
+
+(* Code generation for QBE *)
+module OutIL = struct
+  open Printf
+
+  let comment oc s =
+    fprintf oc "# %s\n" s
+
+  let tmp, lbl =
+    let next = ref 0 in
+    (fun () -> incr next; "%t" ^ (string_of_int !next)),
+    (fun () -> incr next; "@l" ^ (string_of_int !next))
+
+  let bvalue: type a. a bty * a -> string = function
+    | Char, i   -> sprintf "%d" i
+    | Short, i  -> sprintf "%d" i
+    | Int, i    -> sprintf "%d" i
+    | Long, i   -> sprintf "%d" i
+    | Float, f  -> sprintf "s_%f" f
+    | Double, f -> sprintf "d_%f" f
+
+  let btype: type a. a bty -> string = function
+    | Char   -> "w"
+    | Short  -> "w"
+    | Int    -> "w"
+    | Long   -> "l"
+    | Float  -> "s"
+    | Double -> "d"
+
+  let extension = ".ssa"
+
+  let argname i = "arg" ^ string_of_int (i+1)
+
+  let siter oc base s g =
+    let rec f: type a. int -> int -> a sty * a -> unit =
+      fun id off -> function
+      | Field (b, s), (tb, ts) ->
+        let off = align (btyalign b) off in
+        let addr = tmp () in
+        fprintf oc "\t%s =l add %d, %s\n" addr off base;
+        g id addr (TB (b, tb));
+        f (id + 1) (off + btysize b) (s, ts);
+     | Empty, () -> () in
+   f 0 0 s
+
+  let bmemtype b =
+    if AB b = AB Char  then "b" else
+    if AB b = AB Short then "h" else
+    btype b
+
+  let init oc = function
+    | TA (Base b, tb) -> bvalue (b, tb)
+    | TA (Struct s, ts) ->
+      let base = tmp () in
+      fprintf oc "\t%s =l alloc%d %d\n"
+        base (styalign s) (stysize s);
+      siter oc base (s, ts)
+      begin fun _ addr (TB (b, tb)) ->
+        fprintf oc "\tstore%s %s, %s\n"
+          (bmemtype b) (bvalue (b, tb)) addr;
+      end;
+      base
+
+  let check oc id name =
+    let bcheck = fun id name (b, tb) ->
+      let tcmp = tmp () in
+      let nxtl = lbl () in
+      fprintf oc "\t%s =w ceq%s %s, %s\n"
+        tcmp (btype b) name (bvalue (b, tb));
+      fprintf oc "\tstorew %d, %%failcode\n" id;
+      fprintf oc "\tjnz %s, %s, @fail\n" tcmp nxtl;
+      fprintf oc "%s\n" nxtl; in
+    function
+    | TA (Base Char, i) ->
+      let tval = tmp () in
+      fprintf oc "\t%s =w extsb %s\n" tval name;
+      bcheck id tval (Int, i)
+    | TA (Base Short, i) ->
+      let tval = tmp () in
+      fprintf oc "\t%s =w extsh %s\n" tval name;
+      bcheck id tval (Int, i)
+    | TA (Base b, tb) ->
+      bcheck id name (b, tb)
+    | TA (Struct s, ts) ->
+      siter oc name (s, ts)
+      begin fun id' addr (TB (b, tb)) ->
+        let tval = tmp () in
+        let lsuffix =
+          if AB b = AB Char  then "sb" else
+          if AB b = AB Short then "sh" else
+          "" in
+        fprintf oc "\t%s =%s load%s %s\n"
+          tval (btype b) lsuffix addr;
+        bcheck (100*id + id'+1) tval (b, tb);
+      end;
+      ()
+
+  let ttype name = function
+    | TA (Base b, _)   -> btype b
+    | TA (Struct _, _) -> ":" ^ name
+
+  let typedef oc name =
+    let rec f: type a. a sty -> unit = function
+      | Field (b, s) ->
+        fprintf oc "%s" (bmemtype b);
+        if not (styempty s) then
+          fprintf oc ", ";
+        f s;
+      | Empty -> () in
+    function
+    | TA (Struct ts, _) ->
+      fprintf oc "type :%s = { " name;
+      f ts;
+      fprintf oc " }\n";
+    | _ -> ()
+
+  let postlude oc = List.iter (fprintf oc "%s\n")
+    [ "@fail"
+    ;  "# failure code"
+    ; "\t%fcode =w loadw %failcode"
+    ; "\t%f0 =w call $printf(l $failstr, w %fcode)"
+    ; "\t%f1 =w call $abort()"
+    ; "\tret 0"
+    ; "}"
+    ; ""
+    ; "data $failstr = { b \"fail on check %d\\n\", b 0 }"
+    ]
+
+  let caller oc ret args =
+    let narg = List.length args in
+    List.iteri (fun i arg ->
+      typedef oc (argname i) arg;
+    ) args;
+    typedef oc "ret" ret;
+    fprintf oc "\nfunction w $main() {\n";
+    fprintf oc "@start\n";
+    fprintf oc "\t%%failcode =l alloc4 4\n";
+    let targs = List.mapi (fun i arg ->
+      comment oc ("define argument " ^ (string_of_int (i+1)));
+      (ttype (argname i) arg, init oc arg)
+    ) args in
+    comment oc "call test function";
+    fprintf oc "\t%%ret =%s call $f(" (ttype "ret" ret);
+    List.iteri (fun i (ty, tmp) ->
+      fprintf oc "%s %s" ty tmp;
+      if i <> narg-1 then
+        fprintf oc ", ";
+    ) targs;
+    fprintf oc ")\n";
+    comment oc "check the return value";
+    check oc 0 "%ret" ret;
+    fprintf oc "\tret 0\n";
+    postlude oc;
+    ()
+
+  let callee oc ret args =
+    let narg = List.length args in
+    List.iteri (fun i arg ->
+      typedef oc (argname i) arg;
+    ) args;
+    typedef oc "ret" ret;
+    fprintf oc "\nfunction %s $f(" (ttype "ret" ret);
+    List.iteri (fun i arg ->
+      let a = argname i in
+      fprintf oc "%s %%%s" (ttype a arg) a;
+      if i <> narg-1 then
+        fprintf oc ", ";
+    ) args;
+    fprintf oc ") {\n";
+    fprintf oc "@start\n";
+    fprintf oc "\t%%failcode =l alloc4 4\n";
+    List.iteri (fun i arg ->
+      comment oc ("checking argument " ^ (string_of_int (i+1)));
+      check oc (i+1) ("%" ^ argname i) arg;
+    ) args;
+    comment oc "define the return value";
+    let rettmp = init oc ret in
+    fprintf oc "\tret %s\n" rettmp;
+    postlude oc;
+    ()
+
+end
+
+
+module type OUT = sig
+  val extension: string
+  val comment: out_channel -> string -> unit
+  val caller: out_channel -> testa -> testa list -> unit
+  val callee: out_channel -> testa -> testa list -> unit
+end
+
+let _ =
+  let usage code =
+    Printf.eprintf "usage: abi.ml [-s SEED] DIR {c,ssa} {c,ssa}\n";
+    exit code in
+
+  let outmod = function
+    | "c"   -> (module OutC : OUT)
+    | "ssa" -> (module OutIL: OUT)
+    | _ -> usage 1 in
+
+  let seed, dir, mcaller, mcallee =
+    match Sys.argv with
+    | [| _; "-s"; seed; dir; caller; callee |] ->
+      let seed =
+        try Some (int_of_string seed) with
+        Failure _ -> usage 1 in
+      seed, dir, outmod caller, outmod callee
+    | [| _; dir; caller; callee |] ->
+      None, dir, outmod caller, outmod callee
+    | [| _; "-h" |] ->
+      usage 0
+    | _ ->
+      usage 1 in
+
+  let seed = Gen.init seed in
+  let tret = Gen.test () in
+  let targs = Gen.tests () in
+  let module OCaller = (val mcaller : OUT) in
+  let module OCallee = (val mcallee : OUT) in
+  let ocaller = open_out (dir ^ "/caller" ^ OCaller.extension) in
+  let ocallee = open_out (dir ^ "/callee" ^ OCallee.extension) in
+  OCaller.comment ocaller (Printf.sprintf "seed %d" seed);
+  OCallee.comment ocallee (Printf.sprintf "seed %d" seed);
+  OCaller.caller ocaller tret targs;
+  OCallee.callee ocallee tret targs;
+  ()
diff --git a/src/tools/abitest.sh b/src/tools/abitest.sh
new file mode 100755
index 0000000..d5b16e5
--- /dev/null
+++ b/src/tools/abitest.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+OCAMLC=/usr/bin/ocamlc
+QBE=`pwd`/qbe
+
+failure() {
+	echo "Failure at stage:" $1 >&2
+	exit 1
+}
+
+cleanup() {
+	rm -fr $TMP
+}
+
+init() {
+	cp tools/abi.ml $TMP
+	pushd $TMP > /dev/null
+
+	cat > Makefile << EOM
+
+.PHONY: test
+test: caller.o callee.o
+	c99 -o \$@ caller.o callee.o
+%.o: %.c
+	c99 -c -o \$@ \$<
+%.o: %.ssa
+	$QBE -o \$*.s \$<
+	c99 -c -o \$@ \$*.s
+
+EOM
+
+	if ! $OCAMLC abi.ml -o gentest
+	then
+		popd > /dev/null
+		cleanup
+		failure "abifuzz compilation"
+	fi
+	popd > /dev/null
+}
+
+once() {
+	if test -z "$3"
+	then
+		$TMP/gentest $TMP $1 $2
+	else
+		$TMP/gentest -s $3 $TMP $1 $2
+	fi
+	make -C $TMP test > /dev/null || failure "building"
+	$TMP/test || failure "runtime"
+}
+
+usage() {
+	echo "usage: abitest.sh [-callssa] [-callc] [-s SEED] [-n ITERATIONS]" >&2
+	exit 1
+}
+
+N=1
+CALLER=c
+CALLEE=ssa
+
+while test -n "$1"
+do
+	case "$1" in
+	"-callssa")
+		;;
+	"-callc")
+		CALLER=ssa
+		CALLEE=c
+		;;
+	"-s")
+		test -n "$2" || usage
+		shift
+		SEED="$1"
+		;;
+	"-n")
+		test -n "$2" || usage
+		shift
+		N="$1"
+		;;
+	*)
+		usage
+		;;
+	esac
+	shift
+done
+
+TMP=`mktemp -d abifuzz.XXXXXX`
+
+init
+
+if test -n "$S"
+then
+	once $CALLER $CALLEE $SEED
+else
+	for n in `seq $N`
+	do
+		once $CALLER $CALLEE
+		echo "$n" | grep "00$"
+	done
+fi
+
+echo "All done."
+
+cleanup
diff --git a/src/tools/fptox.c b/src/tools/fptox.c
new file mode 100644
index 0000000..a2bc155
--- /dev/null
+++ b/src/tools/fptox.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int ac, char *av[])
+{
+	double d;
+	float f;
+
+	if (ac < 2) {
+	usage:
+		fputs("usage: fptox NUMBER\n", stderr);
+		return 1;
+	}
+	f = d = strtod(av[1], 0);
+	printf("0x%08x 0x%016llx\n", *(unsigned *)&f, *(unsigned long long*)&d);
+	return 0;
+}
diff --git a/src/tools/pmov.c b/src/tools/pmov.c
new file mode 100644
index 0000000..efbecd7
--- /dev/null
+++ b/src/tools/pmov.c
@@ -0,0 +1,252 @@
+/*% rm -f rega.o main.o && cc -g -std=c99 -Wall -DTEST_PMOV -o # % *.o
+ *
+ * This is a test framwork for the dopm() function
+ * in rega.c, use it when you want to modify it or
+ * all the parallel move functions.
+ *
+ * You might need to decrease NIReg to see it
+ * terminate, I used NIReg == 7 at most.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void assert_test(char *, int), fail(void), iexec(int *);
+
+#include "../rega.c"
+
+static RMap mbeg;
+static Ins ins[NIReg], *ip;
+static Blk dummyb = { .ins = ins };
+
+int
+main()
+{
+	Ins *i1;
+	unsigned long long tm, rm, cnt;
+	RMap mend;
+	int reg[NIReg], val[NIReg+1];
+	int t, i, r, nr;
+
+	tmp = (Tmp[Tmp0+NIReg]){{{0}}};
+	for (t=0; t<Tmp0+NIReg; t++)
+		if (t >= Tmp0) {
+			tmp[t].cls = Kw;
+			tmp[t].hint.r = -1;
+			tmp[t].hint.m = 0;
+			tmp[t].slot = -1;
+			sprintf(tmp[t].name, "tmp%d", t-Tmp0+1);
+		}
+
+	bsinit(mbeg.b, Tmp0+NIReg);
+	bsinit(mend.b, Tmp0+NIReg);
+	cnt = 0;
+	for (tm = 0; tm < 1ull << (2*NIReg); tm++) {
+		mbeg.n = 0;
+		bszero(mbeg.b);
+		ip = ins;
+
+		/* find what temporaries are in copy and
+		 * wether or not they are in register
+		 */
+		for (t=0; t<NIReg; t++)
+			switch ((tm >> (2*t)) & 3) {
+			case 0:
+				/* not in copy, not in reg */
+				break;
+			case 1:
+				/* not in copy, in reg */
+				radd(&mbeg, Tmp0+t, t+1);
+				break;
+			case 2:
+				/* in copy, not in reg */
+				*ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw};
+				break;
+			case 3:
+				/* in copy, in reg */
+				*ip++ = (Ins){OCopy, TMP(Tmp0+t), {R, R}, Kw};
+				radd(&mbeg, Tmp0+t, t+1);
+				break;
+			}
+
+		if (ip == ins)
+			/* cancel if the parallel move
+			 * is empty
+			 */
+			goto Nxt;
+
+		/* find registers for temporaries
+		 * in mbeg
+		 */
+		nr = ip - ins;
+		rm = (1ull << (nr+1)) - 1;
+		for (i=0; i<nr; i++)
+			reg[i] = i+1;
+
+		for (;;) {
+			/* set registers on copies
+			 */
+			for (i=0, i1=ins; i1<ip; i1++, i++)
+				i1->arg[0] = TMP(reg[i]);
+
+			/* compile the parallel move
+			 */
+			rcopy(&mend, &mbeg);
+			dopm(&dummyb, ip-1, &mend);
+			cnt++;
+
+			/* check that mend contain mappings for
+			 * source registers and does not map any
+			 * assigned temporary, then check that
+			 * all temporaries in mend are mapped in
+			 * mbeg and not used in the copy
+			 */
+			for (i1=ins; i1<ip; i1++) {
+				r = i1->arg[0].val;
+				assert(rfree(&mend, r) == r);
+				t = i1->to.val;
+				assert(!bshas(mend.b, t));
+			}
+			for (i=0; i<mend.n; i++) {
+				t = mend.t[i];
+				assert(bshas(mbeg.b, t));
+				t -= Tmp0;
+				assert(((tm >> (2*t)) & 3) == 1);
+			}
+
+			/* execute the code generated and check
+			 * that all assigned temporaries got their
+			 * value, and that all live variables's
+			 * content got preserved
+			 */
+			 for (i=1; i<=NIReg; i++)
+			 	val[i] = i;
+			 iexec(val);
+			 for (i1=ins; i1<ip; i1++) {
+			 	t = i1->to.val;
+			 	r = rfind(&mbeg, t);
+			 	if (r != -1)
+			 		assert(val[r] == i1->arg[0].val);
+			 }
+			 for (i=0; i<mend.n; i++) {
+			 	t = mend.t[i];
+			 	r = mend.r[i];
+			 	assert(val[t-Tmp0+1] == r);
+			 }
+
+			/* find the next register assignment */
+			i = nr - 1;
+			for (;;) {
+				r = reg[i];
+				rm &= ~(1ull<<r);
+				do
+					r++;
+				while (r <= NIReg && (rm & (1ull<<r)));
+				if (r == NIReg+1) {
+					if (i == 0)
+						goto Nxt;
+					i--;
+				} else {
+					rm |= (1ull<<r);
+					reg[i++] = r;
+					break;
+				}
+			}
+			for (; i<nr; i++)
+				for (r=1; r<=NIReg; r++)
+					if (!(rm & (1ull<<r))) {
+						rm |= (1ull<<r);
+						reg[i] = r;
+						break;
+					}
+		}
+	Nxt:	;
+	}
+	printf("%llu tests successful!\n", cnt);
+	exit(0);
+}
+
+
+/* execute what pmgen() wrote (swap, copy) */
+
+#define validr(r)           \
+	rtype(r) == RTmp && \
+	r.val > 0 &&        \
+	r.val <= NIReg
+
+static void
+iexec(int val[])
+{
+	Ins *i;
+	int t;
+
+	for (i=insb; i<curi; i++)
+		switch (i->op) {
+		default:
+			assert(!"iexec: missing case\n");
+			exit(1);
+		case OSwap:
+			assert(validr(i->arg[0]));
+			assert(validr(i->arg[1]));
+			t = val[i->arg[0].val];
+			val[i->arg[0].val] = val[i->arg[1].val];
+			val[i->arg[1].val] = t;
+			break;
+		case OCopy:
+			assert(validr(i->to));
+			assert(validr(i->arg[0]));
+			val[i->to.val] = val[i->arg[0].val];
+			break;
+		}
+}
+
+
+/* failure diagnostics */
+
+static int re;
+
+static void
+replay()
+{
+	RMap mend;
+
+	re = 1;
+	bsinit(mend.b, Tmp0+NIReg);
+	rcopy(&mend, &mbeg);
+	dopm(&dummyb, ip-1, &mend);
+}
+
+static void
+fail()
+{
+	Ins *i1;
+	int i;
+
+	printf("\nIn registers: ");
+	for (i=0; i<mbeg.n; i++)
+		printf("%s(r%d) ",
+			tmp[mbeg.t[i]].name,
+			mbeg.r[i]);
+	printf("\n");
+	printf("Parallel move:\n");
+	for (i1=ins; i1<ip; i1++)
+		printf("\t %s <- r%d\n",
+			tmp[i1->to.val].name,
+			i1->arg[0].val);
+	replay();
+	abort();
+}
+
+static void
+assert_test(char *s, int x)
+{
+	if (x)
+		return;
+	if (re)
+		abort();
+	printf("!assertion failure: %s\n", s);
+	fail();
+}
+
+/* symbols required by the linker */
+char debug['Z'+1];
diff --git a/src/tools/regress.sh b/src/tools/regress.sh
new file mode 100755
index 0000000..4106b00
--- /dev/null
+++ b/src/tools/regress.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for t in test/*
+do
+	printf "Test $t ... "
+
+	./qbe   $t >/tmp/out.0 2>&1
+	./qbe.1 $t >/tmp/out.1 2>&1
+
+	if diff /tmp/out.0 /tmp/out.1 > /dev/null
+	then
+		echo "OK"
+	else
+		echo "KO"
+		break
+	fi
+done