Handle-ize BytecodeEmitter.
1 /* vim: set ts=4 sw=4 tw=99 et:
3 * Copyright (C) 2012 David Anderson
5 * This file is part of JITCraft.
7 * JITCraft is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation, either version 3 of the License, or (at your option)
12 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with
17 * JITCraft. If not, see http://www.gnu.org/licenses/.
19 #ifndef _include_bytecode_emitter_h_
20 #define _include_bytecode_emitter_h_
26 #include "NameBinding.h"
34 typedef std::vector<ScopedRoot<Object> *> ObjectList;
39 bytecode *buffer_pos_;
40 bytecode *buffer_end_;
41 bytecode inline_buffer_[512];
43 unsigned maxStackDepth_;
48 static const size_t MAX_LENGTH = INT_MAX / 2;
51 bool growBuffer(size_t bytes);
53 // Checking the return value is not necessary, since the inline buffer is
55 bool ensure(size_t bytes) {
56 if (buffer_pos_ + bytes > buffer_end_)
57 return growBuffer(bytes);
61 void writeUint8(unsigned value) {
62 assert(value >= 0 && value <= UCHAR_MAX);
63 *buffer_pos_++ = value;
65 void writeFloat(float value) {
66 *(float *)buffer_pos_ = value;
67 buffer_pos_ += sizeof(float);
69 void writeInt32(int value) {
70 *(int *)buffer_pos_ = value;
71 buffer_pos_ += sizeof(int);
73 void writeUint32(unsigned value) {
74 *(unsigned *)buffer_pos_ = value;
75 buffer_pos_ += sizeof(unsigned);
77 void writeOp(Opcode op) {
79 stackDepth_ += OpcodeInfo[op].ndefs;
80 if (OpcodeInfo[op].nuses != -1)
81 stackDepth_ -= OpcodeInfo[op].nuses;
82 if (stackDepth_ > maxStackDepth_)
83 maxStackDepth_ = stackDepth_;
85 void writeObject(Object *obj) {
86 writeUint32(objects_.size());
87 ScopedRoot<Object> *root = new ScopedRoot<Object>(obj);
90 objects_.push_back(root);
93 int position() const {
94 assert(buffer_pos_ - buffer_ < INT_MAX / 2);
95 return int(buffer_pos_ - buffer_);
101 bytecode *buffer() const {
104 unsigned length() const {
105 return unsigned(buffer_pos_ - buffer_);
110 unsigned stackDepth() const {
113 unsigned maxStackDepth() const {
114 return maxStackDepth_;
116 const ObjectList *objects() const {
121 // A label represents a local control-flow target in the bytecode. A label
122 // may be in three states:
124 // (1) Empty - the label is not used yet.
125 // (2) Pending - the label has incoming jumps, but the actual location of
126 // is not yet known. The list of jumps is threaded through
127 // the four bytes reserved for each emitted jump target. In
128 // this case, the offset field is the location of the most
129 // recently emitted incoming jump.
130 // (3) Bound - the label's position has been pinned.
132 // A jump can be emitted to a label in any state. Binding a label
133 // immediately backpatches all pending incoming jumps. A label must be
134 // bound if it is used.
140 void setOffset(int offset) {
142 assert(offset_ == offset);
146 static const int INVALID_OFFSET = -1;
150 : offset_(INVALID_OFFSET),
156 assert(bound_ || offset_ == INVALID_OFFSET);
165 void bind(int offset) {
170 int link(int offset) {
179 int patchJumpAt(int at, int to) {
180 assert(at >= 0 && at < position());
181 int old = *(int *)(buffer_ + at + 1);
182 *(int *)(buffer_ + at + 1) = (to - at);
186 void singleByteOp(Opcode op) {
187 assert(OpcodeInfo[op].length == 1);
188 ensure(OpcodeInfo[op].length);
193 void int_(int value) {
202 void getlocal(unsigned local) {
203 ensure(OP_GETLOCAL_LENGTH);
204 writeOp(OP_GETLOCAL);
207 void setlocal(unsigned local) {
208 ensure(OP_SETLOCAL_LENGTH);
209 writeOp(OP_SETLOCAL);
212 void getarg(unsigned slot) {
213 ensure(OP_GETARG_LENGTH);
217 void setarg(unsigned slot) {
218 ensure(OP_SETARG_LENGTH);
223 void j(Opcode op, Label *label) {
224 // Since jumps can skip around the stream, do not try to do anything
225 // in a potentially truncated state.
229 assert(op >= OP_JL && op <= OP_JUMP);
230 ensure(OP_JUMP_LENGTH);
232 if (label->bound()) {
233 // We can immediately emit this jump.
234 int distance = label->offset() - position();
236 writeInt32(distance);
238 // We can't emit this jump yet. Add a placeholder.
239 int old = label->link(position());
244 void bind(Label *label) {
248 assert(!label->bound());
250 int offset = label->offset();
251 while (offset != Label::INVALID_OFFSET)
252 offset = patchJumpAt(offset, position());
254 label->bind(position());
256 void jump(Label *label) {
260 void binary(Opcode op) {
265 singleByteOp(OP_POP);
268 singleByteOp(OP_STOP);
270 void callglobal(unsigned nargs, unsigned index) {
271 ensure(OP_CALLGLOBAL_LENGTH);
272 writeOp(OP_CALLGLOBAL);
276 stackDepth_ -= nargs;
278 void getarray(PrimitiveType type) {
280 case PrimitiveType_Float:
281 singleByteOp(OP_GETARRAY_F);
283 case PrimitiveType_Int32:
284 singleByteOp(OP_GETARRAY_I);
287 assert(type == PrimitiveType_Char);
288 singleByteOp(OP_GETARRAY_C);
292 void setarray(PrimitiveType type) {
294 case PrimitiveType_Float:
295 singleByteOp(OP_SETARRAY_F);
297 case PrimitiveType_Int32:
298 singleByteOp(OP_SETARRAY_I);
301 assert(type == PrimitiveType_Char);
302 singleByteOp(OP_SETARRAY_C);
307 singleByteOp(OP_GETARRAY_O);
310 singleByteOp(OP_BOUNDS);
312 void float_(float value) {
313 ensure(OP_FLOAT_LENGTH);
318 singleByteOp(OP_CVT_I2F);
320 void newarray_lifo(Handle<Map> map) {
321 ensure(OP_NEWARRAY_LIFO_LENGTH);
322 writeOp(OP_NEWARRAY_LIFO);
325 void savelifo(unsigned slot) {
326 ensure(OP_SAVELIFO_LENGTH);
327 writeOp(OP_SAVELIFO);
330 void unwindlifo(unsigned slot) {
331 ensure(OP_UNWINDLIFO_LENGTH);
332 writeOp(OP_UNWINDLIFO);
335 void newref(Variable *var) {
336 if (var->location() == Variable::STACK) {
337 ensure(OP_VARREF_LIFO_LENGTH);
338 writeOp(OP_VARREF_LIFO);
340 assert(var->location() == Variable::PARAMETER);
341 ensure(OP_ARGREF_LIFO_LENGTH);
342 writeOp(OP_ARGREF_LIFO);
344 writeUint32(var->slot());
346 void get(Variable *var) {
347 if (var->location() == Variable::STACK) {
348 getlocal(var->slot());
350 assert(var->location() == Variable::PARAMETER);
354 void set(Variable *var) {
355 assert(!var->type()->isReference());
356 if (var->location() == Variable::STACK) {
357 setlocal(var->slot());
359 assert(var->location() == Variable::PARAMETER);
363 void slotref_lifo() {
364 singleByteOp(OP_SLOTREF_LIFO);
366 void arrayref_lifo() {
367 singleByteOp(OP_ARRAYREF_LIFO);
369 void getref(PrimitiveType type) {
370 if (type == PrimitiveType_Int32) {
371 singleByteOp(OP_GETREF_I);
373 assert(type == PrimitiveType_Float);
374 singleByteOp(OP_GETREF_F);
377 void setref(PrimitiveType type) {
378 if (type == PrimitiveType_Int32) {
379 singleByteOp(OP_SETREF_I);
381 assert(type == PrimitiveType_Float);
382 singleByteOp(OP_SETREF_F);
385 void object(Handle<Object> obj) {
386 ensure(OP_OBJECT_LENGTH);
391 singleByteOp(OP_ARRAYCOPY);
394 singleByteOp(OP_DUP);
397 singleByteOp(OP_DUP2);
400 singleByteOp(OP_SWAP);
403 singleByteOp(OP_ROLL3);
406 singleByteOp(OP_PICK2);
409 singleByteOp(OP_PICK3);
415 #endif // _include_bytecode_emitter_h_