-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassembly_segment.h
More file actions
303 lines (217 loc) · 7.99 KB
/
assembly_segment.h
File metadata and controls
303 lines (217 loc) · 7.99 KB
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#include <cstdint>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
namespace gnossen {
namespace assembly {
class OffsetInterface {
public:
// Returns the maximum offset distance between two code segments. It is valid
// to call this method regardless of the finalization state of either index.
virtual size_t maximum_distance(unsigned int a, unsigned int b) const = 0;
// Returns the absolute offset of the segment with index `segment_index`.
virtual size_t absolute_offset(unsigned int segment_index) const = 0;
};
/* There is a lifecycle associated with an assembly segment. It starts off
* with both indeterminate size and contents. After the, determine_size method
* has been called, the segment's size is known, but its contents may still be
* indeterminate.
*
* After all segments have had their sizes determined, their absolute offset is
* calculable, and, therefore, so are their relative offsets. At this point,
* `write_code` may be called.
*/
class AssemblySegment {
public:
virtual void write_code(uint8_t** code) const = 0;
virtual std::string debug_string() const = 0;
virtual void determine_size(const OffsetInterface* offset_if) = 0;
virtual void determine_offset(const OffsetInterface* offset_if) = 0;
// Only valid to call this after write_code has been called.
virtual size_t size() const = 0;
// The maximum size that this segment will take on. It is valid to call this
// method before write_code.
virtual size_t max_size() const = 0;
virtual unsigned int id() const = 0;
};
class AssemblySubroutine : public OffsetInterface {
public:
AssemblySubroutine() = default;
AssemblySubroutine(const AssemblySubroutine&) = default;
AssemblySubroutine(AssemblySubroutine&&) = default;
AssemblySubroutine& operator=(const AssemblySubroutine&) = default;
AssemblySubroutine& operator=(AssemblySubroutine&&) = default;
size_t maximum_distance(unsigned int a, unsigned int b) const override;
size_t absolute_offset(unsigned int segment_index) const override;
void add_segment(std::unique_ptr<AssemblySegment> segment);
// After this method has been called, no further segments may be added.
void finalize();
// The buffer passed in must be at least as big as size().
void write_code(uint8_t* buffer) const;
// Must not be called until after finalize().
size_t size() const;
// GOAL: Should be able to be assembled without any modifications.
std::string debug_string() const;
private:
std::vector<std::unique_ptr<AssemblySegment>> segments_;
// Mapping from segment indices as supplied by the caller to offsets into the
// segments_ container.
std::unordered_map<unsigned int, unsigned int> index_mapping_;
};
// You could argue that this is a hack and I should eliminate it in a
// prior step.
class NoOp : public AssemblySegment {
public:
NoOp(unsigned int id) : id_(id) {}
void write_code(uint8_t** code) const override {
// Do nothing.
}
void determine_size(const OffsetInterface* offset_if) override {
// Do nothing.
}
void determine_offset(const OffsetInterface* offset_if) override {
// Do nothing.
}
std::string debug_string() const override {
return " // No-op section\n";
}
size_t size() const override { return 0; }
size_t max_size() const override {
return 0;
}
unsigned int id() const override {
return id_;
}
private:
const unsigned int id_;
static const uint8_t kCode[];
};
// This segment is not stored in an AssemblySubroutine and therefore
// it does not have its own index. It is only used within the context of a
// *parent* segment.
class JumpEqualSegment : public AssemblySegment {
public:
JumpEqualSegment(unsigned int parent_index,
unsigned int parent_offset,
unsigned int jmp_index) noexcept :
parent_index_(parent_index),
parent_offset_(parent_offset),
jmp_index_(jmp_index) {}
void determine_size(const OffsetInterface* offset_if) noexcept override;
void determine_offset(const OffsetInterface* offset_if) noexcept override;
void write_code(uint8_t** code) const noexcept override;
std::string debug_string() const override;
size_t size() const noexcept override;
size_t max_size() const noexcept override;
unsigned int id() const override {
// This should never be called.
return 0;
}
private:
static const uint8_t kCodeRel8[];
static const uint8_t kCodeRel16Or32[];
// The index of the parent segment.
unsigned int parent_index_;
// The offset of this instruction within the parent.
unsigned int parent_offset_;
// The index of the segment to which to jump.
unsigned int jmp_index_;
size_t offset_size_;
int32_t relative_offset_;
};
// Consumes a single character if there's a match and jumps to the specified
// section. Otherwise, does not consume a character and continues to the next
// section.
class ConsumingMatchNonConsumingNonMatch : public AssemblySegment {
public:
ConsumingMatchNonConsumingNonMatch(unsigned int index,
char letter, unsigned int jmp_index) noexcept :
index_(index),
letter_(letter),
jmp_index_(jmp_index),
jmp_segment_(index, 3, jmp_index) {}
void write_code(uint8_t** code) const noexcept override;
void determine_size(const OffsetInterface* offset_if) noexcept override;
void determine_offset(const OffsetInterface* offset_if) noexcept override;
std::string debug_string() const override;
size_t size() const noexcept override;
size_t max_size() const noexcept override;
unsigned int id() const override {
return index_;
}
private:
static const uint8_t kCodePreamble[];
static const uint8_t kCodeConclusion[];
// TODO: Rename to id_.
unsigned int index_;
char letter_;
unsigned int jmp_index_;
JumpEqualSegment jmp_segment_;
};
// Consumes a single character. If there's a match, continue to the next
// section, otherwise jump to the given section.
class ConsumingMatchElse : public AssemblySegment {
public:
ConsumingMatchElse(unsigned int index,
char letter, unsigned int jmp_index) noexcept :
index_(index),
letter_(letter),
jmp_index_(jmp_index),
jmp_segment_(index, 3, jmp_index) {}
void write_code(uint8_t** code) const override;
void determine_size(const OffsetInterface* offset_if) noexcept override;
void determine_offset(const OffsetInterface* offset_if) noexcept override;
std::string debug_string() const override;
size_t size() const override;
size_t max_size() const noexcept override;
unsigned int id() const override;
private:
static const uint8_t kCodePreamble[];
unsigned int index_;
char letter_;
// TODO: Is this member still needed at this level?
unsigned int jmp_index_;
JumpEqualSegment jmp_segment_;
};
class StaticCodeSegment : public AssemblySegment {
public:
StaticCodeSegment(unsigned int id, const uint8_t* code, size_t code_size) noexcept :
id_(id), code_(code), code_size_(code_size) {}
void write_code(uint8_t** code) const noexcept override;
void determine_size(const OffsetInterface* offset_if) noexcept override {}
void determine_offset(const OffsetInterface* offset_if) noexcept override {}
virtual std::string debug_string() const = 0;
size_t size() const noexcept override;
size_t max_size() const noexcept override;
unsigned int id() const override {
return id_;
}
private:
unsigned int id_;
const uint8_t* code_;
const size_t code_size_;
};
class StackManagementSegment : public StaticCodeSegment {
private:
static const uint8_t kCode[];
public:
StackManagementSegment(unsigned int id);
std::string debug_string() const override;
};
class SuccessSegment : public StaticCodeSegment {
private:
static const uint8_t kCode[];
public:
SuccessSegment(unsigned int id);
std::string debug_string() const override;
};
class FailureSegment : public StaticCodeSegment {
private:
static const uint8_t kCode[];
public:
FailureSegment(unsigned int id);
std::string debug_string() const override;
};
} // end namespace assembly
} // end namespace gnossen