307 lines
14 KiB
Python
307 lines
14 KiB
Python
import argparse
|
|
import re
|
|
import os
|
|
import subprocess
|
|
from pretty_print import Print_C
|
|
|
|
# "([1-4]\d|51).*\.sy"
|
|
|
|
BUILD_DIR = "build/"
|
|
LIB_PATH = "tools/sylib/libsysy_x86.a"
|
|
LIB_PATH_RV64 = "tools/sylib/libsysy_rv64.a"
|
|
HDR_PATH = "tools/sylib/sylib.h"
|
|
|
|
def case_collector(re_selector, dir):
|
|
selector = re.compile(re_selector)
|
|
testcases = []
|
|
for filename in os.listdir(dir):
|
|
if selector.match(filename):
|
|
testcases.append(os.path.splitext(filename)[0])
|
|
return sorted(testcases)
|
|
|
|
|
|
|
|
class Compiler:
|
|
def __init__(self, scheme, target_dir, testcases):
|
|
self.scheme = scheme
|
|
self.testcases = testcases
|
|
self.target_dir = target_dir
|
|
self.sy_template = f"{target_dir}/{{testcase}}/{{testcase}}.sy"
|
|
self.prefix = f"{target_dir}/{{testcase}}/{scheme['name']}"
|
|
self.llvm_ir_template = f"{self.prefix}/{{testcase}}.ll"
|
|
self.asm_template = f"{self.prefix}/{{testcase}}.s"
|
|
self.obj_template = f"{self.prefix}/{{testcase}}.o"
|
|
self.bin_template = f"{self.prefix}/{{testcase}}"
|
|
self.compile_log = f"{self.prefix}/{{testcase}}_compile.log"
|
|
self.runner_log = f"{self.prefix}/{{testcase}}_run_{{kase}}.out"
|
|
self.myout_template = f"{self.prefix}/{{testcase}}.out"
|
|
self.in_template = f"{target_dir}/{{testcase}}/{{testcase}}.in"
|
|
self.count_error = 0
|
|
|
|
def clean_case(self, testcase):
|
|
os.system(f"rm -rf {self.target_dir}/{testcase}/{self.scheme['name']}")
|
|
os.makedirs(f"{self.target_dir}/{testcase}/{self.scheme['name']}")
|
|
|
|
def prepare_dir(self):
|
|
for testcase in self.testcases:
|
|
self.clean_case(testcase)
|
|
|
|
def sy_to_ir(self, frontend_instr, testcase):
|
|
sy = self.sy_template.format(testcase=testcase)
|
|
ir = self.llvm_ir_template.format(testcase=testcase)
|
|
log = self.compile_log.format(testcase=testcase)
|
|
log_file = open(log, "a+")
|
|
|
|
Print_C.print_procedure(f"Generating {ir} from {sy}")
|
|
completed = subprocess.run(frontend_instr.format(sy=sy, ir=ir).split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
log_file.close()
|
|
if completed.returncode != 0:
|
|
Print_C.print_error(f"Generate {ir} failed! See {log}")
|
|
self.count_error += 1
|
|
return False
|
|
return True
|
|
|
|
|
|
def ir_to_asm(self, ir_asm_instr, testcase):
|
|
ir = self.llvm_ir_template.format(testcase=testcase)
|
|
asm = self.asm_template.format(testcase=testcase)
|
|
log = self.compile_log.format(testcase=testcase)
|
|
|
|
log_file = open(log, "a+")
|
|
Print_C.print_procedure(f"Generating {asm} from {ir}")
|
|
completed = subprocess.run(ir_asm_instr.format(ir=ir, asm=asm).split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
log_file.close()
|
|
if completed.returncode != 0:
|
|
Print_C.print_error(f"Generate {asm} failed! See {log}")
|
|
self.count_error += 1
|
|
return False
|
|
return True
|
|
|
|
|
|
def asm_to_obj(self, asm_obj_instr, testcase):
|
|
asm = self.asm_template.format(testcase=testcase)
|
|
obj = self.obj_template.format(testcase=testcase)
|
|
log = self.compile_log.format(testcase=testcase)
|
|
|
|
log_file = open(log, "a+")
|
|
Print_C.print_procedure(f"Generating {obj} from {asm}")
|
|
completed = subprocess.run(asm_obj_instr.format(asm=asm,obj=obj).split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
log_file.close()
|
|
if completed.returncode != 0:
|
|
Print_C.print_error(f"Generate {obj} failed! See {log}")
|
|
self.count_error += 1
|
|
return False
|
|
return True
|
|
|
|
|
|
def obj_to_bin(self, obj_bin_instr, testcase):
|
|
obj = self.obj_template.format(testcase=testcase)
|
|
bin = self.bin_template.format(testcase=testcase)
|
|
log = self.compile_log.format(testcase=testcase)
|
|
|
|
log_file = open(log, "a+")
|
|
Print_C.print_procedure(f"Generating {bin}")
|
|
completed = subprocess.run(obj_bin_instr.format(obj=obj,bin=bin).split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
log_file.close()
|
|
if completed.returncode != 0:
|
|
Print_C.print_error(f"Generate {bin} failed! See {log}")
|
|
self.count_error += 1
|
|
return False
|
|
return True
|
|
|
|
|
|
def sy_to_asm(self, sy_asm_instr, testcase):
|
|
asm = self.asm_template.format(testcase=testcase)
|
|
sy = self.sy_template.format(testcase=testcase)
|
|
log = self.compile_log.format(testcase=testcase)
|
|
|
|
log_file = open(log, "a+")
|
|
Print_C.print_procedure(f"Generating {asm}")
|
|
completed = subprocess.run(sy_asm_instr.format(asm=asm, sy=sy).split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
log_file.close()
|
|
if completed.returncode != 0:
|
|
Print_C.print_error(f"Generate {bin} failed! See {log}")
|
|
self.count_error += 1
|
|
return False
|
|
return True
|
|
|
|
|
|
def compile_case(self, testcase, emit_llvm_ir=True):
|
|
if "sy_ir" not in self.scheme.keys():
|
|
emit_llvm_ir = False
|
|
Print_C.print_subheader(f"[Compiling {self.scheme['name']} | {testcase}]")
|
|
if emit_llvm_ir:
|
|
if not self.sy_to_ir(self.scheme["sy_ir"], testcase=testcase): return
|
|
if not self.ir_to_asm(self.scheme["ir_asm"], testcase=testcase): return
|
|
else:
|
|
if not self.sy_to_asm(self.scheme["sy_asm"], testcase=testcase): return
|
|
if not self.asm_to_obj(self.scheme["asm_obj"], testcase=testcase): return
|
|
if not self.obj_to_bin(self.scheme["obj_bin"], testcase=testcase): return
|
|
|
|
def compile_all_tests(self, error_tolerance=1, emit_llvm_ir=True):
|
|
for testcase in self.testcases:
|
|
self.compile_case(testcase, emit_llvm_ir)
|
|
if self.count_error >= error_tolerance:
|
|
Print_C.print_error(f"Test script stopped due to {self.count_error} errors")
|
|
return
|
|
|
|
def run_case(self, testcase, kase=0):
|
|
bin = self.bin_template.format(testcase=testcase)
|
|
if not os.path.exists(bin):
|
|
Print_C.print_pass(f"testcase {testcase} not compiled, skipped")
|
|
return False
|
|
if "run" in self.scheme.keys():
|
|
bin = self.scheme["run"].format(bin=bin)
|
|
stdin = self.in_template.format(testcase=testcase)
|
|
myout = self.myout_template.format(testcase=testcase)
|
|
log = self.runner_log.format(testcase=testcase, kase=kase)
|
|
myout_file = open(myout, "w")
|
|
log_file = open(log, "w")
|
|
null_file = open(os.devnull, "w")
|
|
|
|
Print_C.print_procedure(f"Running {self.scheme['name']}_{testcase} [case: {kase}]")
|
|
|
|
if os.path.exists(stdin):
|
|
stdin_file = open(stdin, "r")
|
|
if kase == 0:
|
|
p = subprocess.run(f"{bin}".split(), stdin=stdin_file, stdout=myout_file, stderr=log_file, bufsize=1)
|
|
subprocess.run(f"echo".split(), stdout=myout_file, bufsize=1)
|
|
subprocess.run(f"echo {p.returncode}".split(), stdout=myout_file, bufsize=1)
|
|
else:
|
|
p = subprocess.run(f"{bin}".split(), stdin=stdin_file, stdout=null_file, stderr=null_file, bufsize=1)
|
|
stdin_file.close()
|
|
else:
|
|
if kase == 0:
|
|
p = subprocess.run(f"{bin}".split(), stdout=myout_file, stderr=log_file, bufsize=1)
|
|
subprocess.run(f"echo".split(), stdout=myout_file, bufsize=1)
|
|
subprocess.run(f"echo {p.returncode}".split(), stdout=myout_file, bufsize=1)
|
|
else:
|
|
p = subprocess.run(f"{bin}".split(), stdout=null_file, stderr=null_file, bufsize=1)
|
|
Print_C.print_pass(f" {self.scheme['name']}_{testcase} [case: {kase}] exit with {p.returncode}")
|
|
myout_file.close()
|
|
log_file.close()
|
|
return True
|
|
|
|
def run_all_tests(self, run_kases=1):
|
|
for kase in range(run_kases):
|
|
Print_C.print_subheader(f"[Running CASE {kase}]")
|
|
for testcase in self.testcases:
|
|
self.run_case(testcase=testcase, kase=kase)
|
|
|
|
class BatTest:
|
|
def __init__(self, compiler, compiler_rf, testcases):
|
|
self.compiler = compiler
|
|
self.compiler_rf = compiler_rf
|
|
self.testcases = testcases
|
|
self.count_error = 0
|
|
self.label = f"{compiler.scheme['name']}_{compiler_rf.scheme['name']}"
|
|
self.diffout_template = f"{compiler.target_dir}/{{testcase}}/{self.label}.diff"
|
|
|
|
def bat_case(self, testcase, compile=False):
|
|
Print_C.print_subheader(f"Diff test {self.label} on {testcase}")
|
|
if compile:
|
|
self.compiler.clean_case(testcase)
|
|
self.compiler_rf.clean_case(testcase)
|
|
self.compiler.compile_case(testcase)
|
|
self.compiler_rf.compile_case(testcase)
|
|
if not compiler.run_case(testcase, 0): return False
|
|
if not compiler_rf.run_case(testcase, 0): return False
|
|
myout = compiler.myout_template.format(testcase=testcase)
|
|
refout = compiler_rf.myout_template.format(testcase=testcase)
|
|
diffout = self.diffout_template.format(testcase=testcase)
|
|
diffout_file = open(diffout, "w")
|
|
p = subprocess.run(f"diff {myout} {refout}".split(), stdout=diffout_file, stderr=diffout_file, bufsize=1)
|
|
diffout_file.close()
|
|
if p.returncode != 0:
|
|
Print_C.print_error(f"Different output for {testcase}. See {diffout}")
|
|
return False
|
|
return True
|
|
|
|
def bat_all_tests(self, compile=False):
|
|
|
|
for testcase in self.testcases:
|
|
if not self.bat_case(testcase, compile): break
|
|
|
|
scheme_ref = {
|
|
"name": "ref",
|
|
"sy_ir": f"clang -x c -c -fPIE -m32 -S -emit-llvm -include {HDR_PATH} {{sy}} -o {{ir}}",
|
|
"ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}",
|
|
"asm_obj": "as --32 {asm} -o {obj}",
|
|
"obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}",
|
|
"sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}"
|
|
}
|
|
|
|
scheme_my = {
|
|
"name": "my",
|
|
"sy_ir": f"build/sysy -S {{sy}} -o {{ir}} -emit-llvm -O1",
|
|
"ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}",
|
|
"asm_obj": "as --32 {asm} -o {obj}",
|
|
"obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}",
|
|
"sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}"
|
|
}
|
|
|
|
# it is really annoying to debug compile commands
|
|
# useful help source:
|
|
# llc --version
|
|
# llc --help
|
|
# llc -march=riscv64 -mcpu=help
|
|
# https://discourse.llvm.org/t/cant-link-soft-float-modules-with-double-float-modules/67521
|
|
scheme_ref_rv64 = {
|
|
"name" : "ref_rv64",
|
|
"sy_ir": f"clang -x c -c -fPIE -S -emit-llvm --target=riscv64-linux-gnu -include {HDR_PATH} {{sy}} -o {{ir}}",
|
|
"ir_asm": "llc --march=riscv64 --relocation-model=pic --float-abi=hard -mcpu=sifive-7-rv64 -mattr=+f,+m,+d {ir} -o {asm}",
|
|
"asm_obj": "riscv64-linux-gnu-as {asm} -o {obj}",
|
|
"obj_bin": f"riscv64-linux-gnu-gcc -static {{obj}} {LIB_PATH_RV64} -o {{bin}}",
|
|
"sy_asm": f"clang -x c -S -no-integrated-as --target=riscv64-linux-gnu -march=rv64gc -fPIE -mfloat-abi=hard -mcpu=sifive-7-rv64 -include {HDR_PATH} {{sy}} -o {{asm}}",
|
|
"run": f"spike /usr/local/riscv64-linux-gnu/bin/pk {{bin}}",
|
|
}
|
|
|
|
schemes = {
|
|
"my": scheme_my,
|
|
"ref": scheme_ref,
|
|
"ref_rv64": scheme_ref_rv64,
|
|
}
|
|
# subprocess.run(f"llc -O3 -march=arm -mcpu=cortex-a72 -float-abi=hard -filetype=asm {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
# subprocess.run(f"as -march=armv7-a -mfloat-abi=hard {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
# subprocess.run(f"clang -Ofast -marm -march=armv7-a -mfpu=neon -mfloat-abi=hard {obj} {lib} -o {bin}".split(), stdout=log_file, stderr=log_file, bufsize=1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(prog="SysY test script")
|
|
parser.add_argument('--scheme', default='ref')
|
|
parser.add_argument('--case_prefix', default='testcases/functional')
|
|
parser.add_argument('--case_selector', default='.*\.sy')
|
|
parser.add_argument("--clean", action='store_true', default=False)
|
|
parser.add_argument('--compile', action='store_true', default=False)
|
|
parser.add_argument('--run', action='store_true', default=False)
|
|
parser.add_argument('--bat', action='store_true', default=False)
|
|
args = parser.parse_args()
|
|
print(args)
|
|
testcases = case_collector(args.case_selector, args.case_prefix)
|
|
target_dir = os.path.join(BUILD_DIR, args.case_prefix.replace("../", "").replace("./", ""))
|
|
if args.clean:
|
|
os.system(f"rm -rfI {target_dir}")
|
|
if not os.path.exists(target_dir):
|
|
os.makedirs(target_dir)
|
|
for testcase in testcases:
|
|
testcase_dir = os.path.join(target_dir, testcase)
|
|
testcase_src = os.path.join(args.case_prefix, testcase)
|
|
if not os.path.exists(testcase_dir):
|
|
os.makedirs(testcase_dir)
|
|
os.system(f"cp {testcase_src}.* {testcase_dir}/")
|
|
current_schem = schemes[args.scheme]
|
|
if args.bat:
|
|
compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases)
|
|
compiler_rf = Compiler(scheme=schemes["ref"],target_dir=target_dir, testcases=testcases)
|
|
battester = BatTest(compiler, compiler_rf, testcases)
|
|
battester.bat_all_tests(args.compile)
|
|
elif args.compile:
|
|
compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases)
|
|
compiler.prepare_dir()
|
|
compiler.compile_all_tests(error_tolerance=1, emit_llvm_ir=True)
|
|
if args.run:
|
|
compiler.run_all_tests()
|
|
elif args.run:
|
|
compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases)
|
|
compiler.run_all_tests()
|