Skip to content
← Back

src/ir/context.ghul

1
namespace IR is
2
use System.NotImplementedException;
3
use IO.File;
4
5
use Values.Call;
6
7
class AssemblyOutput(want_comments: bool) abstract is
8
write(value: object) is
9
throw NotImplementedException("assembly output must implement write_line");
10
si
11
12
write_line(value: object) is
13
write(value);
14
write("\n");
15
si
16
17
write_line(indent: int, value: object) is
18
if want_comments then
19
write_indent(indent);
20
write_line(value);
21
else
22
write_line(value);
23
fi
24
si
25
26
write_line(indent: int, value: object, comment: object) is
27
if want_comments then
28
write_indent(indent);
29
write_line("{value} // {comment}");
30
else
31
write_line(value);
32
fi
33
si
34
35
write_comment_line(indent: int, comment: object) is
36
if want_comments then
37
write_indent(indent);
38
write_line("// {comment}");
39
fi
40
si
41
42
write_indent(indent: int) is
43
for i in 0..indent do
44
write(" ");
45
od
46
si
47
48
close() is
49
50
si
51
si
52
53
class INVALID_ASSEMBLER_OUTPUT: AssemblyOutput is
54
init() is
55
super.init(false);
56
si
57
58
write(value: object) is
59
assert false else "code generated outside of block";
60
si
61
si
62
63
class FILE_ASSEMBLER_OUTPUT(
64
path: string,
65
writer: IO.TextWriter,
66
want_comments: bool
67
): AssemblyOutput is
68
super(want_comments);
69
70
write(value: object) is
71
writer.write(value);
72
si
73
74
close() is
75
writer.close();
76
si
77
si
78
79
class NULL_ASSEMBLER_OUTPUT: AssemblyOutput is
80
init() is
81
super.init(false);
82
si
83
84
write(value: object) is
85
// do nothing
86
si
87
si
88
89
class BUFFER_ASSEMBLER_OUTPUT: AssemblyOutput is
90
buffer: System.Text.StringBuilder;
91
92
init(want_comments: bool) is
93
super.init(want_comments);
94
95
buffer = System.Text.StringBuilder();
96
si
97
98
write(value: object) is
99
buffer.append(value);
100
si
101
si
102
103
class CONTEXT is
104
_logger: Logging.Logger;
105
_indent: int;
106
107
_seen: Collections.SET[string];
108
_outputs: Collections.STACK[AssemblyOutput];
109
110
entry_point_name: string public;
111
seen_entrypoint: bool public;
112
113
throw_on_fixme: bool public;
114
115
// Reads `--debug` off the shared build-flags singleton so
116
// non-debug builds pay nothing for the `mark_location` check.
117
want_debug_info: bool =>
118
IoC.CONTAINER.instance.build_flags.want_debug;
119
120
// Last emitted `.line` source position (file + start_line +
121
// start_column). mark_location only writes a fresh directive
122
// when the position changes, keeping the IL stream sparse
123
// while still letting two distinct constructs that share a
124
// source line (e.g. both arms of `if c then a else b fi`
125
// written on one line) each get their own sequence point.
126
_last_line_file: string?;
127
_last_line_line: int;
128
_last_line_column: int;
129
130
init(
131
logger: Logging.Logger,
132
entry_point_name: string
133
) is
134
_logger = logger;
135
self.entry_point_name = entry_point_name;
136
137
_seen = Collections.SET[string]();
138
_outputs = Collections.STACK[AssemblyOutput]();
139
140
_outputs.push(NULL_ASSEMBLER_OUTPUT());
141
si
142
143
// Note a source-language position at the current emission
144
// point. If debug info is enabled and the (file, line,
145
// column) triple differs from the most recently emitted
146
// one, write a `.line` directive ahead of the next
147
// instruction. Internal / sentinel locations are ignored.
148
mark_location(location: Source.LOCATION?) is
149
if !want_debug_info \/ !location? \/ location.is_internal then
150
return;
151
fi
152
153
// Emit an absolute path so the resulting Portable PDB's
154
// Document records match what an IDE will send in a
155
// breakpoint request (which is the file's absolute path).
156
// A bare basename here would still produce a syntactically
157
// valid PDB but every breakpoint would silently fail to
158
// bind.
159
let file = IO.Path.get_full_path(location.file_name);
160
let line = location.start_line;
161
let column = location.start_column;
162
163
if
164
_last_line_file? /\ _last_line_file =~ file /\
165
_last_line_line == line /\ _last_line_column == column
166
then
167
return;
168
fi
169
170
_last_line_file = file;
171
_last_line_line = line;
172
_last_line_column = column;
173
174
let end_line = location.end_line;
175
let end_col_exclusive = location.end_column + 1;
176
177
write_line(".line {line},{end_line} : {column},{end_col_exclusive} '{file}'");
178
si
179
180
// Per-method reset called when entering a new method body
181
// so the first statement always gets a `.line` directive
182
// even if it sits on the same source position the previous
183
// method ended on.
184
reset_line_tracking() is
185
_last_line_file = null;
186
_last_line_line = 0;
187
_last_line_column = 0;
188
si
189
190
current_output: AssemblyOutput => _outputs.peek();
191
192
fixme(value: object) is
193
if throw_on_fixme then
194
throw System.NotImplementedException("FIXME: {value}");
195
else
196
write_line("// FIXME: {value}");
197
fi
198
si
199
200
write(value: string) is
201
current_output.write(value);
202
si
203
204
write_line(value: object) is
205
current_output.write_line(_indent, value);
206
si
207
208
write_line(value: object, comment: object) is
209
current_output.write_line(_indent, value, comment);
210
si
211
212
write_comment_line(comment: object) is
213
current_output.write_comment_line(_indent, comment);
214
si
215
216
indent() is
217
_indent = _indent + 1;
218
si
219
220
outdent() is
221
_indent = _indent - 1;
222
si
223
224
enter_invalid() is
225
_outputs.push(INVALID_ASSEMBLER_OUTPUT());
226
si
227
228
leave_invalid() is
229
assert
230
_outputs.count > 0 /\
231
isa INVALID_ASSEMBLER_OUTPUT(current_output)
232
else
233
"IL output context stack corrupt";
234
235
_outputs.pop();
236
si
237
238
enter_file(path: string, want_comments: bool) is
239
let writer: IO.TextWriter mut;
240
241
if _seen.contains(path) then
242
writer = File.append_text(path);
243
else
244
writer = File.create_text(path);
245
_seen.add(path);
246
fi
247
248
_outputs.push(FILE_ASSEMBLER_OUTPUT(path, writer, want_comments));
249
si
250
251
leave_file(path: string) is
252
assert
253
_outputs.count > 0 /\
254
isa FILE_ASSEMBLER_OUTPUT(current_output) /\
255
cast FILE_ASSEMBLER_OUTPUT(current_output).path =~ path
256
else
257
"IL output context stack corrupt";
258
259
current_output.close();
260
_outputs.pop();
261
si
262
263
enter_buffer(want_comments: bool) is
264
_outputs.push(BUFFER_ASSEMBLER_OUTPUT(want_comments));
265
si
266
267
leave_buffer() -> string is
268
assert
269
_outputs.count > 0 /\
270
isa BUFFER_ASSEMBLER_OUTPUT(current_output)
271
else
272
"IL output context stack corrupt";
273
274
let result = cast BUFFER_ASSEMBLER_OUTPUT(current_output).buffer;
275
276
_outputs.pop();
277
278
return result.to_string();
279
si
280
si
281
si