From 1d943da0cf9154e7ce78ce867cdbb91531c5d78e Mon Sep 17 00:00:00 2001 From: Matthew Sotoudeh Date: Tue, 25 Jul 2023 14:58:33 -0700 Subject: initial dietc commit --- scripts/dietcc | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100755 scripts/dietcc (limited to 'scripts/dietcc') diff --git a/scripts/dietcc b/scripts/dietcc new file mode 100755 index 0000000..8898df2 --- /dev/null +++ b/scripts/dietcc @@ -0,0 +1,171 @@ +#!/bin/python3 +import subprocess +import sys +import re +import tempfile +import os + +if sys.argv[1:] == ["--version"]: + print("DIETC") + sys.exit(0) + +# Have to do this because ./configure seems to do some nasty stuff +# https://lists.gnu.org/archive/html/bug-make/2022-11/msg00041.html +if "-E" in sys.argv[1:]: + os.system(f"gcc {' '.join(sys.argv[1:])}") + sys.exit(0) + +# https://stackoverflow.com/questions/595305 +dietc_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + +def check_result(spr): + if not spr.returncode: return spr.stdout + print("Failed to run gcc wrapper:", ' '.join(sys.argv), file=sys.stderr) + if spr.stderr: + print(spr.stderr.decode("utf-8"), file=sys.stderr) + assert False + +def preprocess_pass(c, meta): + with tempfile.TemporaryDirectory() as tmpdir: + t_file = open(f"{tmpdir}/file.c", "wb") + t_file.write(f"#include \"{dietc_dir}/scripts/stdincludes/dietc_defines.h\"\n".encode("utf-8")) + t_file.write(c) + t_file.flush() + dirname = os.path.dirname(os.path.realpath(meta["c_path"])) + return check_result( + subprocess.run(f"gcc -I{dirname} -E {meta['argstr']} {t_file.name}", + shell=True, capture_output=True)) + +def strip_after_preprocess_pass(c, meta): + c = re.sub(rb"\[\[[^\]]*\]\]", b"", c) + c = re.sub(rb"__asm__", b"", c) + return c + +def dietc_pass(c, meta): + with tempfile.TemporaryDirectory() as tmpdir: + t_file = open(f"{tmpdir}/file.c", "wb") + t_file.write(c) + t_file.flush() + dirname = os.path.dirname(os.path.realpath(meta["c_path"])) + return check_result( + subprocess.run(f"timeout 5s {dietc_dir}/dietc {t_file.name}", + shell=True, capture_output=True)) + +def final_cleanup_pass(dietc, meta): + # (1) treat builtins as builtins + patterns = { + rb"extern Type_[0-9]* __builtin_[^ ]* ;\n": b"", + rb"extern Type_[0-9]* __(div|mul)[ds]c3 ;\n": b"", + rb"extern Type_[0-9]* __btowc_alias ;\n": b"", + } + for pattern, replace in patterns.items(): + dietc = re.sub(pattern, b"", dietc) + + # (2) builtins must be called directly + # NOTE: if passes mess this up, e.g., by doing optimizations, we could be in + # trouble here + pattern = rb"\t(t[0-9]*) = (__builtin_[^ ]*|alloca) ;\n" + match_ = re.search(pattern, dietc) + while match_ is not None: + temp = match_.groups()[0] + builtin = match_.groups()[1] + # (2a) delete the match + dietc = dietc.replace(b"\t" + temp + b" = " + builtin + b" ;\n", b"") + # (2b) delete the declaration of the builtin + dietc = re.sub(b"\tType_[0-9]* " + temp + b" ;\n", b"", dietc) + # (2c) replace uses of the temp with the builtin + dietc = dietc.replace(b" " + temp + b" ", b" " + builtin + b" ") + dietc = dietc.replace(b"\t" + temp + b" ", b"\t" + builtin + b" ") + + match_ = re.search(pattern, dietc) + + # (3) __builtin_va_arg_pack* must be called *super* directly, and must be in + # an always-inline method + # NOTE: if passes mess this up, e.g., by doing optimizations, we could be in + # trouble here + pattern = rb"\t(t[0-9]*) = (__builtin_va_arg_pack[^ ]*) \( \) ;\n" + match_ = re.search(pattern, dietc) + any_arg_pack = (match_ is not None) + while match_ is not None: + temp = match_.groups()[0] + builtin = match_.groups()[1] + # (2a) delete the match + dietc = dietc.replace(b"\t" + temp + b" = " + builtin + b" ( ) ;\n", b"") + # (2b) delete the declaration of the result + dietc = re.sub(b"\tType_[0-9]* " + temp + b" ;\n", b"", dietc) + # (2c) replace uses of the temp with the builtin + dietc = dietc.replace(b" " + temp + b" ", b" " + builtin + b" ( ) ") + + match_ = re.search(pattern, dietc) + + # (3b) any functions using __builtin_va_arg_pack* must be inlined + if any_arg_pack or b"\t__builtin_va_start " in dietc: + lines = dietc.split(b"\n") + inline_is = set() + last_decl_i = 0 + for i, line in enumerate(lines): + if not line.startswith(b"\t"): + last_decl_i = i + elif b"__builtin_va_arg_pack" in line: + inline_is.add(last_decl_i) + elif b"\t__builtin_va_start " in line: + # second argument of __builtin_va_start must be last named argument to + # function + fn, _po, arg1, _c, arg2, _pc, _sc = line.split() + # {, ), ..., <,>, [arg] + real_arg2 = lines[last_decl_i].split()[-5] + lines[i] = b"\t" + b" ".join([fn, _po, arg1, _c, real_arg2, _pc, _sc]) + for i in sorted(inline_is): + if "static" in lines[i]: + lines[i] = b"inline __attribute__ ((__gnu_inline__)) " + lines[i] + else: + lines[i] = b"extern inline __attribute__ ((__gnu_inline__)) " + lines[i] + dietc = b"\n".join(lines) + + return dietc + +PASSES = [preprocess_pass, + strip_after_preprocess_pass, + dietc_pass, + final_cleanup_pass] + +def main(): + args = sys.argv[1:] + args.insert(0, f"-I{dietc_dir}/scripts/stdincludes") + # first, parse out any DietC passes + dietc_passes = [] + while "--dietc-pass" in args: + i = args.index("--dietc-pass") + args.pop(i) + dietc_passes.append(args.pop(i)) + + # then process all of the C files + out_dir = tempfile.TemporaryDirectory() + c_files = [arg for arg in args if arg.endswith(".c")] + diet_files = [] + for i, c_file in enumerate(c_files): + subargs = [arg for arg in args if not arg.endswith(".c")] + if "-o" in subargs: + i = subargs.index("-o") + subargs.pop(i) + subargs.pop(i) + argstr = " ".join(subargs) + + last = open(c_file, "rb").read() + for dietpass in PASSES: + last = dietpass(last, {"c_path": c_file, "argstr": argstr}) + + diet_files.append(f"{out_dir.name}/file{i}.c") + with open(diet_files[-1], "wb") as f: + f.write(last) + + # then reconstruct the arguments, using the processed C files + renaming = dict({c: diet for c, diet in zip(c_files, diet_files)}) + args = [renaming.get(arg, arg) for arg in args] + if c_files and "-o" not in args: + if "-c" in args: + args += ["-o", c_files[0].replace(".c", ".o")] + gcc = subprocess.run(f"gcc {' '.join(args)} -Wno-int-to-pointer-cast -Wno-builtin-declaration-mismatch", shell=True) + check_result(gcc) + +main() -- cgit v1.2.3