fix(qbe): any-child terminator check in QBE_IsTerminator for hBlock — QBE SSA now fully valid
- QBE_IsTerminator(hBlock) checks ALL children, not just last - Removed legacy c_backend.bux and nim_backend.bux from _selfhost/src/ - QBE self-hosting QBE phase: PASSES (SSA valid, assembled to .s) - Remaining: linker errors from missing String_Eq/StringBuilder in runtime
This commit is contained in:
+110
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# support script to create
|
||||
# the abi8.ssa test
|
||||
|
||||
def ctype(arg):
|
||||
if arg[0] == 'p': return ctype(arg[1:])
|
||||
if arg[0] == ':': return 'S' + arg[1:]
|
||||
return {'w':'int', 'l':'long',
|
||||
's':'float', 'd':'double'}[arg]
|
||||
|
||||
def cparam(iarg):
|
||||
return ctype(iarg[1]) + ' p' + str(iarg[0])
|
||||
|
||||
def gencfn(id, args):
|
||||
out = '# extern void qfn' + id + '('
|
||||
out += ', '.join(map(ctype, args)) + ');\n'
|
||||
out += '# void cfn' + id + '('
|
||||
out += ', '.join(map(cparam, enumerate(args)))
|
||||
out += ') {\n'
|
||||
out += '# \tprintf("qbe->c(%d)", ' + id + ');\n'
|
||||
out += '# \t'
|
||||
for (i, arg) in enumerate(args):
|
||||
if arg[0] != 'p': continue
|
||||
ty = arg[1:]
|
||||
if ty[0] == ':':
|
||||
out += 'p' + ty[1:] + '(&'
|
||||
else:
|
||||
out += 'p' + ty + '('
|
||||
out += 'p' + str(i) + '); '
|
||||
out += 'puts("");\n'
|
||||
out += '# \tqfn' + id + '('
|
||||
out += ', '.join('p'+str(i) for i in range(len(args)))
|
||||
out += ');\n'
|
||||
out += '# }\n'
|
||||
return out
|
||||
|
||||
def qparam(iarg):
|
||||
ty = iarg[1][1:] if iarg[1][0] == 'p' else iarg[1]
|
||||
return ty + ' %p' + str(iarg[0])
|
||||
|
||||
def genqfn(id, args):
|
||||
out = 'export\nfunction $qfn' + id + '('
|
||||
out += ', '.join(map(qparam, enumerate(args)))
|
||||
out += ') {\n'
|
||||
out += '@start\n'
|
||||
out += '\t%r0 =w call $printf(l $ctoqbestr, w ' + id + ')\n'
|
||||
for (i, arg) in enumerate(args):
|
||||
if arg[0] != 'p': continue
|
||||
ty = arg[1:]
|
||||
if ty[0] == ':':
|
||||
out += '\tcall $p' + ty[1:]
|
||||
out += '(l %p' + str(i) + ')\n'
|
||||
else:
|
||||
out += '\tcall $p' + ty
|
||||
out += '(' + ty + ' %p' + str(i) + ')\n'
|
||||
out += '\t%r1 =w call $puts(l $emptystr)\n'
|
||||
out += '\tret\n'
|
||||
out += '}\n'
|
||||
return out
|
||||
|
||||
def carg(iarg):
|
||||
i, arg = iarg
|
||||
print = arg[0] == 'p'
|
||||
ty = arg if not print else arg[1:]
|
||||
if ty[0] == ':':
|
||||
if print:
|
||||
return ty + ' $' + ty[1:]
|
||||
else:
|
||||
return ty + ' $z' + ty[1:]
|
||||
if not print:
|
||||
return ty + ' 0'
|
||||
if ty == 'w' or ty == 'l':
|
||||
return ty + ' ' + str(i+1)
|
||||
if ty == 's' or ty == 'd':
|
||||
flt = str(i+1) + '.' + str(i+1)
|
||||
return ty + ' ' + ty + '_' + flt
|
||||
|
||||
def genmaincall(id, args):
|
||||
out = '\tcall $cfn' + id + '('
|
||||
out += ', '.join(map(carg, enumerate(args)))
|
||||
out += ')\n'
|
||||
return out
|
||||
|
||||
def gen(tvec):
|
||||
for i, t in enumerate(tvec):
|
||||
print(genqfn(str(i), t), end='')
|
||||
print('')
|
||||
for i, t in enumerate(tvec):
|
||||
print(genmaincall(str(i), t), end='')
|
||||
print('')
|
||||
for i, t in enumerate(tvec):
|
||||
print(gencfn(str(i), t), end='')
|
||||
|
||||
TVEC = [
|
||||
['s']*8 + ['ps'],
|
||||
['pw', 'ps', 'p:fi1'],
|
||||
['pw', 'p:fi2', 'ps'],
|
||||
['pw', 'ps', 'p:fi3'],
|
||||
['p:ss'],
|
||||
['d']*7 + ['p:ss', 'ps', 'pl'],
|
||||
['p:lb'],
|
||||
['w']*7 + ['p:lb'],
|
||||
['w']*8 + ['p:lb'],
|
||||
[ 'p:big' ],
|
||||
['w']*8 + ['p:big', 'ps', 'pl'],
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen(TVEC)
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
#!/bin/sh
|
||||
|
||||
OCAMLC=${OCAMLC:-/usr/bin/ocamlc}
|
||||
DIR=`cd $(dirname "$0"); pwd`
|
||||
QBE=$DIR/../qbe
|
||||
|
||||
failure() {
|
||||
echo "Failure at stage:" $1 >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -fr $TMP
|
||||
}
|
||||
|
||||
init() {
|
||||
cp $DIR/callgen.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 callgen.ml -o callgen
|
||||
then
|
||||
popd > /dev/null
|
||||
cleanup
|
||||
failure "abifuzz compilation"
|
||||
fi
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
once() {
|
||||
if test -z "$3"
|
||||
then
|
||||
$TMP/callgen $TMP $1 $2
|
||||
else
|
||||
$TMP/callgen -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")
|
||||
CALLER=c
|
||||
CALLEE=ssa
|
||||
;;
|
||||
"-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
|
||||
Vendored
+535
@@ -0,0 +1,535 @@
|
||||
(* abi fuzzer, generates two modules one calling
|
||||
* the other in two possibly different languages
|
||||
*)
|
||||
|
||||
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 16 +
|
||||
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 "\nexport function 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 "\nexport function %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;
|
||||
()
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=`cd $(dirname "$0"); pwd`
|
||||
QBE=$DIR/../qbe
|
||||
BUGF=/tmp/bug.id
|
||||
FIND=$1
|
||||
FIND=${FIND:-afl-find}
|
||||
|
||||
if ! test -f $BUGF
|
||||
then
|
||||
echo 1 > $BUGF
|
||||
fi
|
||||
|
||||
while true
|
||||
do
|
||||
ID=`cat $BUGF`
|
||||
|
||||
if test `ls $FIND/crashes/id* | wc -l` -lt $ID
|
||||
then
|
||||
rm -f bug.ssa
|
||||
echo "All done!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BUG=`ls $FIND/crashes/id* | sed -ne "${ID}{p;q}"`
|
||||
|
||||
echo "*** Crash $ID"
|
||||
cp $BUG bug.ssa
|
||||
|
||||
$QBE bug.ssa > /dev/null
|
||||
RET=$?
|
||||
if test \( $RET -ne 0 \) -a \( $RET -ne 1 \)
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expr $ID + 1 > $BUGF
|
||||
done
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
/*% c99 -O3 -Wall -o # %
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
char *tok[] = {
|
||||
|
||||
"add", "sub", "neg", "div", "rem", "udiv", "urem", "mul",
|
||||
"and", "or", "xor", "sar", "shr", "shl", "stored",
|
||||
"stores", "storel", "storew", "storeh", "storeb",
|
||||
"load", "loadsw", "loaduw", "loadsh", "loaduh",
|
||||
"loadsb", "loadub", "extsw", "extuw", "extsh",
|
||||
"extuh", "extsb", "extub", "exts", "truncd",
|
||||
"stosi", "dtosi", "stoui", "dtoui", "uwtof",
|
||||
"ultof", "swtof", "sltof", "cast", "copy",
|
||||
"alloc4", "alloc8", "alloc16", "culew", "cultw",
|
||||
"cslew", "csltw", "csgtw", "csgew", "cugtw",
|
||||
"cugew", "ceqw", "cnew", "culel", "cultl", "cslel",
|
||||
"csltl", "csgtl", "csgel", "cugtl", "cugel",
|
||||
"ceql", "cnel", "cles", "clts", "cgts", "cges",
|
||||
"cnes", "ceqs", "cos", "cuos", "cled", "cltd",
|
||||
"cgtd", "cged", "cned", "ceqd", "cod", "cuod",
|
||||
"vaarg", "vastart", "...", "env", "dbgloc",
|
||||
|
||||
"call", "phi", "jmp", "jnz", "ret", "hlt", "export",
|
||||
"function", "type", "data", "section", "align", "dbgfile",
|
||||
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
|
||||
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
|
||||
"alloc1", "alloc2",
|
||||
|
||||
};
|
||||
enum {
|
||||
Ntok = sizeof tok / sizeof tok[0]
|
||||
};
|
||||
|
||||
uint32_t th[Ntok];
|
||||
|
||||
uint32_t
|
||||
hash(char *s)
|
||||
{
|
||||
uint32_t h;
|
||||
|
||||
h = 0;
|
||||
for (; *s; ++s)
|
||||
h = *s + 17*h;
|
||||
return h;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
char *bmap;
|
||||
uint32_t h, M, K;
|
||||
int i, j;
|
||||
|
||||
bmap = malloc(1u << 31);
|
||||
|
||||
for (i=0; i<Ntok; ++i) {
|
||||
h = hash(tok[i]);
|
||||
for (j=0; j<i; ++j)
|
||||
if (th[j] == h) {
|
||||
printf("error: hash()\n");
|
||||
printf("\t%s\n", tok[i]);
|
||||
printf("\t%s\n", tok[j]);
|
||||
exit(1);
|
||||
}
|
||||
th[i] = h;
|
||||
}
|
||||
|
||||
for (i=9; 1<<i < Ntok; ++i);
|
||||
M = 32 - i;
|
||||
|
||||
for (;; --M) {
|
||||
printf("trying M=%d...\n", M);
|
||||
K = 1;
|
||||
do {
|
||||
memset(bmap, 0, 1 << (32 - M));
|
||||
for (i=0; i<Ntok; ++i) {
|
||||
h = (th[i]*K) >> M;
|
||||
if (bmap[h])
|
||||
break;
|
||||
bmap[h] = 1;
|
||||
}
|
||||
if (i==Ntok) {
|
||||
printf("found K=%d for M=%d\n", K, M);
|
||||
exit(0);
|
||||
}
|
||||
K += 2;
|
||||
} while (K != 1);
|
||||
}
|
||||
}
|
||||
Vendored
+262
@@ -0,0 +1,262 @@
|
||||
/*% rm -f rega.o main.o && cc -g -std=c99 -Wall -DTEST_PMOV -o pmov % *.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 void bsinit_(BSet *, uint);
|
||||
|
||||
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, Kw, TMP(Tmp0+t), {R, R}};
|
||||
break;
|
||||
case 3:
|
||||
/* in copy, in reg */
|
||||
*ip++ = (Ins){OCopy, Kw, TMP(Tmp0+t), {R, R}};
|
||||
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: freeall();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
static void
|
||||
bsinit_(BSet *bs, uint n)
|
||||
{
|
||||
n = (n + NBit-1) / NBit;
|
||||
bs->nt = n;
|
||||
bs->t = emalloc(n * sizeof bs->t[0]);
|
||||
}
|
||||
|
||||
/* symbols required by the linker */
|
||||
char debug['Z'+1];
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
#!/bin/sh
|
||||
|
||||
dir=`dirname "$0"`
|
||||
bin=$dir/../qbe
|
||||
binref=$dir/../qbe.ref
|
||||
|
||||
tmp=/tmp/qbe.zzzz
|
||||
|
||||
drv=$tmp.c
|
||||
asm=$tmp.s
|
||||
asmref=$tmp.ref.s
|
||||
exe=$tmp.exe
|
||||
out=$tmp.out
|
||||
|
||||
init() {
|
||||
case "$TARGET" in
|
||||
arm64)
|
||||
for p in aarch64-linux-musl aarch64-linux-gnu
|
||||
do
|
||||
cc="$p-gcc -no-pie -static"
|
||||
qemu="qemu-aarch64"
|
||||
if
|
||||
$cc -v >/dev/null 2>&1 &&
|
||||
$qemu -version >/dev/null 2>&1
|
||||
then
|
||||
if sysroot=$($cc -print-sysroot) && test -n "$sysroot"
|
||||
then
|
||||
qemu="$qemu -L $sysroot"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
cc=
|
||||
done
|
||||
if test -z "$cc"
|
||||
then
|
||||
echo "Cannot find arm64 compiler or qemu."
|
||||
exit 1
|
||||
fi
|
||||
bin="$bin -t arm64"
|
||||
;;
|
||||
rv64)
|
||||
for p in riscv64-linux-musl riscv64-linux-gnu
|
||||
do
|
||||
cc="$p-gcc -no-pie -static"
|
||||
qemu="qemu-riscv64"
|
||||
if
|
||||
$cc -v >/dev/null 2>&1 &&
|
||||
$qemu -version >/dev/null 2>&1
|
||||
then
|
||||
if sysroot=$($cc -print-sysroot) && test -n "$sysroot"
|
||||
then
|
||||
qemu="$qemu -L $sysroot"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
cc=
|
||||
done
|
||||
if test -z "$cc"
|
||||
then
|
||||
echo "Cannot find riscv64 compiler or qemu."
|
||||
exit 1
|
||||
fi
|
||||
bin="$bin -t rv64"
|
||||
;;
|
||||
"")
|
||||
case `uname` in
|
||||
*Darwin*)
|
||||
cc="cc"
|
||||
;;
|
||||
*OpenBSD*)
|
||||
cc="cc -nopie -lpthread"
|
||||
;;
|
||||
*FreeBSD*)
|
||||
cc="cc -lpthread"
|
||||
;;
|
||||
*)
|
||||
cc="${CC:-cc}"
|
||||
ccpost="-lpthread"
|
||||
;;
|
||||
esac
|
||||
TARGET=`$bin -t?`
|
||||
;;
|
||||
*)
|
||||
echo "Unknown target '$TARGET'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -f $drv $asm $exe $out
|
||||
}
|
||||
|
||||
extract() {
|
||||
WHAT="$1"
|
||||
FILE="$2"
|
||||
|
||||
awk "
|
||||
/^# >>> $WHAT/ {
|
||||
p = 1
|
||||
next
|
||||
}
|
||||
/^# <<</ {
|
||||
p = 0
|
||||
}
|
||||
p
|
||||
" $FILE \
|
||||
| sed -e 's/# //' \
|
||||
| sed -e 's/#$//'
|
||||
}
|
||||
|
||||
once() {
|
||||
t="$1"
|
||||
|
||||
if ! test -f $t
|
||||
then
|
||||
echo "invalid test file $t" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if
|
||||
sed -e 1q $t |
|
||||
grep "skip.* $TARGET\( .*\)\?$" \
|
||||
>/dev/null
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "%-45s" "$(basename $t)..."
|
||||
|
||||
if ! $bin -o $asm $t
|
||||
then
|
||||
echo "[qbe fail]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if test -x $binref
|
||||
then
|
||||
$binref -o $asmref $t 2>/dev/null
|
||||
fi
|
||||
|
||||
extract driver $t > $drv
|
||||
extract output $t > $out
|
||||
|
||||
if test -s $drv
|
||||
then
|
||||
src="$drv $asm"
|
||||
else
|
||||
src="$asm"
|
||||
fi
|
||||
|
||||
if ! $cc -g -o $exe $src $ccpost
|
||||
then
|
||||
echo "[cc fail]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if test -s $out
|
||||
then
|
||||
$qemu $exe a b c | diff -u - $out
|
||||
ret=$?
|
||||
reason="output"
|
||||
else
|
||||
$qemu $exe a b c
|
||||
ret=$?
|
||||
reason="returned $ret"
|
||||
fi
|
||||
|
||||
if test $ret -ne 0
|
||||
then
|
||||
echo "[$reason fail]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "[ok]"
|
||||
|
||||
if test -f $asmref && ! cmp -s $asm $asmref
|
||||
then
|
||||
loc0=`wc -l $asm | cut -d' ' -f1`
|
||||
loc1=`wc -l $asmref | cut -d' ' -f1`
|
||||
printf " asm diff: %+d\n" $(($loc0 - $loc1))
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
#trap cleanup TERM QUIT
|
||||
|
||||
init
|
||||
|
||||
if test -z "$1"
|
||||
then
|
||||
echo "usage: tools/test.sh {all, SSAFILE}" 2>&1
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
"all")
|
||||
fail=0
|
||||
count=0
|
||||
for t in $dir/../test/[!_]*.ssa
|
||||
do
|
||||
once $t
|
||||
fail=`expr $fail + $?`
|
||||
count=`expr $count + 1`
|
||||
done
|
||||
if test $fail -ge 1
|
||||
then
|
||||
echo
|
||||
echo "$fail of $count tests failed!"
|
||||
else
|
||||
echo
|
||||
echo "All is fine!"
|
||||
fi
|
||||
exit $fail
|
||||
;;
|
||||
*)
|
||||
once $1
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
# generate variadic calls to test the
|
||||
# abi implementation
|
||||
|
||||
from random import seed, randint, uniform
|
||||
from struct import unpack
|
||||
|
||||
I, D = 'd', 'g'
|
||||
|
||||
formats = [
|
||||
# list of formats to test
|
||||
[I],
|
||||
[D],
|
||||
[I,D],
|
||||
[D,D],
|
||||
[I,I,I,I],
|
||||
[D,D,D,D],
|
||||
[I,D,I,D],
|
||||
[D,D,I,I],
|
||||
[I,I,D,D],
|
||||
[],
|
||||
]
|
||||
|
||||
generate = [
|
||||
# numbers of fixed integer and
|
||||
# floating point arguments to
|
||||
# test
|
||||
(0, 0), (1, 0), (0, 1), (4, 0),
|
||||
(0, 6), (5, 7), (10, 10), (9, 0),
|
||||
]
|
||||
|
||||
def mkargs(nargs, type, name):
|
||||
args = map(
|
||||
lambda n: ''.join([type, name, str(n), ', ']),
|
||||
range(nargs)
|
||||
)
|
||||
return ''.join(args)
|
||||
|
||||
def mkfstr(fmt):
|
||||
fstr = map(
|
||||
lambda x: {I: '%d ', D: '%g '}[x],
|
||||
fmt
|
||||
)
|
||||
return '"' + ''.join(fstr) + '\\n"'
|
||||
|
||||
def randargs(fmt):
|
||||
ra = {
|
||||
I: lambda: '{}'.format(randint(-10, 10)),
|
||||
D: lambda: '{0:.4g}'.format(uniform(-10, 10))
|
||||
}
|
||||
return list(map(lambda x: ra[x](), fmt))
|
||||
|
||||
def genssa(qbeprint, qbecall):
|
||||
funcs = [('qbeprint', qbeprint), ('qbecall', qbecall)]
|
||||
for fnum, (nia, nfa) in enumerate(generate):
|
||||
params = "{}{}l %fmt, ...".format(
|
||||
mkargs(nia, 'w ', '%argw'),
|
||||
mkargs(nfa, 'd ', '%argd')
|
||||
)
|
||||
for name, code in funcs:
|
||||
print('export function ${}{}({}) {}'
|
||||
.format(name, fnum, params, code)
|
||||
)
|
||||
|
||||
def gendriver():
|
||||
print('# >>> driver')
|
||||
print('# #include <stdarg.h>')
|
||||
print('# #include <stdio.h>')
|
||||
|
||||
for fnum, (nia, nfa) in enumerate(generate):
|
||||
params = "{}{}char *, ...".format(
|
||||
mkargs(nia, 'int ', 'argw'),
|
||||
mkargs(nfa, 'double ', 'argd')
|
||||
)
|
||||
for name in ['qbeprint', 'qbecall']:
|
||||
print('# extern void {}{}({});'
|
||||
.format(name, fnum, params)
|
||||
)
|
||||
|
||||
output = ''
|
||||
print('# int print(char *fmt, va_list *ap) {')
|
||||
print('# return vprintf(fmt, *ap);');
|
||||
print('# }')
|
||||
print('# int main() {')
|
||||
|
||||
for fnum, (nia, nfa) in enumerate(generate):
|
||||
info = '# ({} int, {} double)'.format(nia, nfa)
|
||||
print('# puts("{}");'.format(info))
|
||||
output += '# {}\n'.format(info)
|
||||
for fmt in formats:
|
||||
ra = randargs(fmt)
|
||||
vaargs = ', '.join(ra)
|
||||
expect = ' '.join(ra)
|
||||
if fmt:
|
||||
vaargs = ', ' + vaargs
|
||||
expect = expect + ' '
|
||||
args = ''.join(
|
||||
['0, '] * (nia+nfa) +
|
||||
[mkfstr(fmt), vaargs]
|
||||
)
|
||||
for name in ['qbeprint', 'qbecall']:
|
||||
print('# {}{}({});'
|
||||
.format(name, fnum, args)
|
||||
)
|
||||
output += '# {}\n'.format(expect)
|
||||
|
||||
print('# }')
|
||||
print('# <<<')
|
||||
|
||||
print('\n# >>> output\n' + output + '# <<<')
|
||||
|
||||
|
||||
qbeprint="""{{
|
||||
@start
|
||||
%fmtdbl =l alloc4 4
|
||||
%fmtint =l alloc4 4
|
||||
%emptys =l alloc4 4
|
||||
storew {}, %fmtint
|
||||
storew {}, %fmtdbl
|
||||
storew 0, %emptys
|
||||
%vp =l alloc8 32
|
||||
%fmt1 =l add 1, %fmt
|
||||
vastart %vp
|
||||
@loop
|
||||
%p =l phi @start %fmt1, @casef %p1, @cased %p1
|
||||
%c =w loadsb %p
|
||||
%p1 =l add 3, %p
|
||||
jnz %c, @loop1, @end
|
||||
@loop1
|
||||
%isg =w ceqw %c, 103
|
||||
jnz %isg, @casef, @cased
|
||||
@casef
|
||||
%dbl =d vaarg %vp
|
||||
%r =w call $printf(l %fmtdbl, ..., d %dbl)
|
||||
jmp @loop
|
||||
@cased
|
||||
%int =w vaarg %vp
|
||||
%r =w call $printf(l %fmtint, ..., w %int)
|
||||
jmp @loop
|
||||
@end
|
||||
%r =w call $puts(l %emptys)
|
||||
ret
|
||||
}}
|
||||
""".format(
|
||||
unpack("i", b'%d \x00')[0],
|
||||
unpack("i", b'%g \x00')[0]
|
||||
)
|
||||
|
||||
qbecall="""{
|
||||
@start
|
||||
%vp =l alloc8 32
|
||||
vastart %vp
|
||||
%r =w call $print(l %fmt, l %vp)
|
||||
ret
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed(42)
|
||||
genssa(qbeprint, qbecall)
|
||||
gendriver()
|
||||
Reference in New Issue
Block a user