summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Sotoudeh <matthew@masot.net>2023-07-26 11:02:15 -0700
committerMatthew Sotoudeh <matthew@masot.net>2023-07-26 11:02:15 -0700
commite29b2b00691b12ae70e97a072664bad073d81c4f (patch)
tree4cf2c79380baa41369ff8223f70e17d3da2bd812
initial libdietc
-rw-r--r--.gitignore3
-rw-r--r--README.txt1
-rw-r--r--python/README.txt19
-rw-r--r--python/dietc.py141
-rw-r--r--python/examples/test_files/zero_init.c20
-rwxr-xr-xpython/examples/zero_init13
6 files changed, 197 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fb0cc7b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+__pycache__
+.*.sw*
+a.out
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..fc43b6e
--- /dev/null
+++ b/README.txt
@@ -0,0 +1 @@
+libdietc: helper libraries for writing dietc passes
diff --git a/python/README.txt b/python/README.txt
new file mode 100644
index 0000000..8129c48
--- /dev/null
+++ b/python/README.txt
@@ -0,0 +1,19 @@
+Python library for building dietcc passes
+
+Example of using a pass:
+
+ The examples/zero_init pass zero-initializes all local variables. It can be
+ run by passing a --dietc-pass flag to dietcc:
+
+ $ /path/to/dietcc examples/test_files/zero_init.c -Wuninitialized
+ ... warning: _xyz_3 is used ininitialized ...
+ $ ./a.out
+ Foo return value: 0
+ Foo return value: 32765
+ Foo return value: 32765
+ $ /path/to/dietcc examples/test_files/zero_init.c --dietc-pass $PWD/examples/zero_init -Wuninitialized
+ [no warnings]
+ $ ./a.out
+ Foo return value: 0
+ Foo return value: 0
+ Foo return value: 0
diff --git a/python/dietc.py b/python/dietc.py
new file mode 100644
index 0000000..b3a0326
--- /dev/null
+++ b/python/dietc.py
@@ -0,0 +1,141 @@
+import sys
+from dataclasses import *
+import re
+
+@dataclass
+class PointerType:
+ base: any
+
+@dataclass
+class ArrayType:
+ base: any
+ length: int
+
+@dataclass
+class FunctionType:
+ return_type: any
+ params: [any]
+ variadic: bool
+
+@dataclass
+class AggregateType:
+ fields: [any]
+
+@dataclass
+class BasicType:
+ name: str
+
+@dataclass
+class Function:
+ decl_line: str
+ body: [str]
+
+ def locals(self, and_params=False):
+ assert not and_params
+ return [line[1] for line in self.body[:self.code_start()]]
+
+ def code_start(self):
+ return next(i for i, l in enumerate(self.body)
+ if not l.line.startswith("\tType_"))
+
+ def insert(self, i, string):
+ self.body.insert(i, Instruction(string))
+
+ def cfg(self):
+ assert self.body is not None
+ labels = {node[0]: i for i, node in enumerate(self.body)
+ if node[1] == ":"}
+ cfg = dict()
+ for i, node in enumerate(self.body):
+ if node[0] not in ("goto", "return"):
+ cfg[node] = [i + 1]
+ if node[0] in ("goto", "if"):
+ cfg[node] = [labels[node[-2]]]
+ return cfg
+
+@dataclass
+class Instruction:
+ line: str
+ tokens: [str] = field(init=False)
+ def __post_init__(self): self.tokens = self.line.split()
+ def __getitem__(self, i): return self.tokens[i]
+ def lhs_type(self, program):
+ assert "=" in self.tokens
+ if self[0] == "*":
+ return self.program.object_types[self[1]].base
+ return self.program.object_types[self[0]]
+
+class Program:
+ def __init__(self, string):
+ lines = string.decode("utf-8").split("\n")
+ self.preamble = []
+ self.types = dict()
+ self.object_types = dict()
+ while lines and not (lines[1:] and lines[1].startswith("\t")):
+ self.preamble.append(lines.pop(0))
+ for line in self.preamble:
+ if not line.startswith("typedef"): continue
+ types = [t for t in line.split()
+ if any(t.startswith(k) for k in ("Type_", "Struct_", "Union_"))]
+ if " * " in line:
+ self.types[types[1]] = PointerType(self.types[types[0]])
+ elif " [ " in line:
+ arrlen = line.split("[")[1].split("]")[0].strip()
+ self.types[types[1]] = ArrayType(self.types[types[0]], arrlen)
+ elif " ( " in line:
+ self.types[types[1]] = FunctionType(
+ self.types[types[0]], [self.types[t] for t in types[2:]],
+ "..." in line)
+ elif " struct " in line or " union " in line:
+ if " { " in line:
+ parts = line.split()
+ # typedef struct ___
+ # { Type_ [ident] ; Type_ [ident] ; ... } Type_ ;
+ field_names = [parts[i]
+ for i in range(5, len(parts) - 1, 3)]
+ field_types = [self.types[t] for t in types[:-1]]
+ assert len(field_names) == len(field_types)
+ self.types[types[-1]].fields \
+ = list(zip(field_names, field_types))
+ else:
+ assert types[-1] not in self.types
+ self.types[types[-1]] = AggregateType([])
+ else:
+ self.types[types[0]] = BasicType(line.split()[1:-2])
+ # assign object types for globals
+ for line in self.preamble:
+ if not line.startswith("extern"): continue
+ parts = line.split()
+ assert parts[-2] not in self.object_types
+ self.object_types[parts[-2]] = self.types[parts[-3]]
+ # recover functions & local object types
+ self.functions = []
+ while lines:
+ start_line = lines.pop(0)
+ if not start_line.strip(): continue
+ params = start_line.split("(")[1].split(")")[0].split(",")
+ for param in params:
+ if not param.strip(): continue
+ if "..." in param: continue
+ type_, name = param.split()
+ self.object_types[name] = self.types[type_]
+ body = []
+ while lines[0] != "}":
+ body_line = lines.pop(0)
+ if body_line.startswith("\tType_"):
+ parts = body_line.split()
+ assert parts[-2] not in self.object_types
+ self.object_types[parts[1]] = self.types[parts[0]]
+ body.append(Instruction(body_line))
+ lines.pop(0)
+ self.functions.append(Function(start_line, body))
+
+ def to_c(self):
+ c = "\n".join(self.preamble) + "\n"
+ for function in self.functions:
+ c += function.decl_line + "\n"
+ c += "\n".join([l.line for l in function.body]) + "\n}\n"
+ return c
+
+ def print(self):
+ print(self.to_c().strip())
diff --git a/python/examples/test_files/zero_init.c b/python/examples/test_files/zero_init.c
new file mode 100644
index 0000000..5db109a
--- /dev/null
+++ b/python/examples/test_files/zero_init.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+int set_xyz() {
+ int xyz = 1;
+ return xyz;
+}
+
+int foo() {
+ int xyz;
+ return xyz;
+}
+
+int main() {
+ printf("Foo return value: %d\n", foo());
+ set_xyz();
+ printf("Foo return value: %d\n", foo());
+ set_xyz();
+ printf("Foo return value: %d\n", foo());
+ return 0;
+}
diff --git a/python/examples/zero_init b/python/examples/zero_init
new file mode 100755
index 0000000..e2ab92e
--- /dev/null
+++ b/python/examples/zero_init
@@ -0,0 +1,13 @@
+#!/bin/python3
+import os
+import sys
+sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+import dietc
+
+prog = dietc.Program(open(sys.argv[1], "rb").read())
+for function in prog.functions:
+ fn_locals = function.locals()
+ start_i = function.code_start()
+ for local in fn_locals:
+ function.insert(start_i, f"\tMEMZERO ( {local} ) ;")
+prog.print()
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback