-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathuvg.jou
175 lines (142 loc) · 5.47 KB
/
uvg.jou
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import "stdlib/io.jou"
import "stdlib/str.jou"
import "stdlib/mem.jou"
import "./errors_and_warnings.jou"
import "./types.jou"
@public
enum UvgInstructionKind:
Use # something = *x
Set # *x = something
DontAnalyze # do_something_complicated(&x)
Statement # basically a line of code, used to detect code that never runs
@public
class UvgInstruction:
location: Location
kind: UvgInstructionKind
var: int # not used for UvgInstructionKind.Statement
@public
class UvgBranch:
then: UvgBlock*
otherwise: UvgBlock*
@public
enum UvgTerminatorKind:
NotSet # must be first so it's zero memory
Jump
Branch
Return
Unreachable
@public
class UvgTerminator:
kind: UvgTerminatorKind
union:
jump_block: UvgBlock* # UvgTerminatorKind.Jump
branch: UvgBranch # UvgTerminatorKind.Branch
@public
class UvgBlock:
instructions: UvgInstruction*
ninstructions: int
terminator: UvgTerminator
def free(self) -> None:
free(self->instructions)
def jumps_to(self, other: UvgBlock*) -> bool:
match self->terminator.kind:
case UvgTerminatorKind.Jump:
return other == self->terminator.jump_block
case UvgTerminatorKind.Branch:
return other == self->terminator.branch.then or other == self->terminator.branch.otherwise
case _:
return False
# We build one UVG for each function.
@public
class Uvg:
signature: Signature*
# Each block is allocated separately so that we can pass them around as
# pointers, and they don't become invalid when adding more blocks.
blocks: UvgBlock**
nblocks: int
varnames: byte[100]*
nvars: int
def free(self) -> None:
for i = 0; i < self->nblocks; i++:
self->blocks[i]->free()
free(self->blocks[i])
free(self->blocks)
free(self->varnames)
def index_of_block(self, b: UvgBlock*) -> int:
for i = 0; i < self->nblocks; i++:
if self->blocks[i] == b:
return i
assert False
def print(self) -> None:
sigstr = self->signature->to_string(True, True)
printf("===== UVG for %s =====\n", sigstr)
free(sigstr)
assert self->nblocks > 0
for i = 0; i < self->nblocks; i++:
if i == 0:
printf("block 0 (start):\n")
else:
printf("block %d:\n", i)
b = self->blocks[i]
for ins = b->instructions; ins < &b->instructions[b->ninstructions]; ins++:
s: byte[50]
sprintf(s, " [line %-5d]", ins->location.lineno)
# Move the ']' right next to the number because I like it that way :D
while strstr(s, " ]") != NULL:
memswap(strstr(s, " ]"), strstr(s, "]"), 1)
printf("%s", s)
match ins->kind:
case UvgInstructionKind.Use:
printf("use %s\n", self->varnames[ins->var])
case UvgInstructionKind.Set:
printf("set %s\n", self->varnames[ins->var])
case UvgInstructionKind.DontAnalyze:
printf("don't analyze %s\n", self->varnames[ins->var])
case UvgInstructionKind.Statement:
printf("statement\n")
printf(" ")
match b->terminator.kind:
case UvgTerminatorKind.Jump:
printf("Jump to block %d.\n", self->index_of_block(b->terminator.jump_block))
case UvgTerminatorKind.Branch:
printf(
"Jump to either block %d or %d depending on some condition.\n",
self->index_of_block(b->terminator.branch.then),
self->index_of_block(b->terminator.branch.otherwise),
)
case UvgTerminatorKind.Return:
printf("Return from function.\n")
case UvgTerminatorKind.Unreachable:
printf("The end of this block is unreachable. It will never run.\n")
case UvgTerminatorKind.NotSet:
printf("(terminator not set)\n")
printf("\n")
def add_block(self) -> UvgBlock*:
b: UvgBlock* = malloc(sizeof(*b))
assert b != NULL
memset(b, 0, sizeof(*b))
self->blocks = realloc(self->blocks, sizeof(self->blocks[0]) * (self->nblocks + 1))
assert self->blocks != NULL
self->blocks[self->nblocks++] = b
return b
def has_local_var(self, varname: byte*) -> bool:
assert varname != NULL
for i = 0; i < self->nvars; i++:
if strcmp(self->varnames[i], varname) == 0:
return True
return False
def get_local_var_ptr(self, varname: byte*) -> int:
if varname != NULL:
for i = 0; i < self->nvars; i++:
if strcmp(self->varnames[i], varname) == 0:
# Reuse existing
return i
self->varnames = realloc(self->varnames, sizeof(self->varnames[0]) * (self->nvars + 1))
assert self->varnames != NULL
var_id = self->nvars++
if varname == NULL:
sprintf(self->varnames[var_id], "$%d", var_id)
else:
assert strlen(varname) < sizeof(self->varnames[var_id])
strcpy(self->varnames[var_id], varname)
return var_id