Added support for declaring empty-length arrays.
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_
25 #include "NameBinding.h"
27 #include "CompactBuffer.h"
34 typedef PoolList<ScopedRoot<Object> > ObjectList;
36 struct OopRecord : public PoolObject
42 OopRecord(unsigned length, unsigned offset)
56 bytecode *buffer_pos_;
57 bytecode *buffer_end_;
58 bytecode inline_buffer_[512];
60 unsigned maxStackDepth_;
62 PoolList<bool> oopMap_;
63 PoolList<OopRecord *> oopRecords_;
64 CompactBufferWriter sourcemap_;
65 unsigned lastCodePosition_;
69 // The maximum size of a code object must be such that the distance between
70 // two instructions fits into a signed, 31-bit integer. This ensures that
71 // all code-bounded buffers (like the number of object literals or
72 // safepoints) does not need to be bounds checked. It also means we can add
73 // any two offsets and be guaranteed that they don't overflow.
74 static const size_t MAX_LENGTH = INT_MAX / 2;
77 bool growBuffer(size_t bytes);
79 // Checking the return value is not necessary, since the inline buffer is
81 bool ensure(size_t bytes) {
82 if (buffer_pos_ + bytes > buffer_end_)
83 return growBuffer(bytes);
87 void writeUint8(unsigned value) {
88 assert(value <= UCHAR_MAX);
89 *buffer_pos_++ = value;
91 void writeFloat(float value) {
92 *(float *)buffer_pos_ = value;
93 buffer_pos_ += sizeof(float);
95 void writeInt32(int value) {
96 *(int *)buffer_pos_ = value;
97 buffer_pos_ += sizeof(int);
99 void writeUint32(unsigned value) {
100 *(unsigned *)buffer_pos_ = value;
101 buffer_pos_ += sizeof(unsigned);
103 void writeOp(Opcode op) {
108 for (int i = 0; i < OpcodeInfo[op].nuses; i++)
110 for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
111 oopMap_.append(false);
112 if (oopMap_.length() > maxStackDepth_)
113 maxStackDepth_ = oopMap_.length();
115 void writeObject(Object *obj) {
116 writeUint32(objects_.length());
117 ScopedRoot<Object> root(obj);
118 if (!objects_.append(root))
122 int position() const {
123 assert(buffer_pos_ - buffer_ < INT_MAX / 2);
124 return int(buffer_pos_ - buffer_);
128 BytecodeEmitter(PoolAllocator &pool);
130 bytecode *buffer() const {
133 unsigned length() const {
134 return unsigned(buffer_pos_ - buffer_);
137 return oom_ || sourcemap_.oom();
139 unsigned stackDepth() const {
140 return oopMap_.length();
142 unsigned maxStackDepth() const {
143 return maxStackDepth_;
145 const ObjectList *objects() const {
148 PoolList<OopRecord *> *oopRecords() {
152 void drop(unsigned amt) {
153 assert(amt <= stackDepth());
154 for (unsigned i = 0; i < amt; i++)
158 void note_position(const SourcePosition &pos);
159 uint8 *sourcemap() const {
160 return sourcemap_.buffer();
162 size_t sourcemapLength() const {
163 return sourcemap_.length();
165 void setFirstLine(unsigned line) {
166 assert(!sourcemap_.length());
171 // A label represents a local control-flow target in the bytecode. A label
172 // may be in three states:
174 // (1) Empty - the label is not used yet.
175 // (2) Pending - the label has incoming jumps, but the actual location of
176 // is not yet known. The list of jumps is threaded through
177 // the four bytes reserved for each emitted jump target. In
178 // this case, the offset field is the location of the most
179 // recently emitted incoming jump.
180 // (3) Bound - the label's position has been pinned.
182 // A jump can be emitted to a label in any state. Binding a label
183 // immediately backpatches all pending incoming jumps. A label must be
184 // bound if it is used.
190 void setOffset(int offset) {
192 assert(offset_ == offset);
196 static const int INVALID_OFFSET = -1;
200 : offset_(INVALID_OFFSET),
206 assert(bound_ || offset_ == INVALID_OFFSET);
215 void bind(int offset) {
220 int link(int offset) {
229 int patchJumpAt(int at, int to) {
230 assert(at >= 0 && at < position());
231 int old = *(int *)(buffer_ + at + 1);
232 *(int *)(buffer_ + at + 1) = (to - at);
236 void singleByteOp(Opcode op) {
237 assert(OpcodeInfo[op].length == 1);
238 ensure(OpcodeInfo[op].length);
242 // Mark a stack slot as having an object pointer.
243 void markOop(unsigned depth, bool oop = true) {
246 oopMap_[oopMap_.length() - depth] = oop;
248 bool getOop(unsigned depth) {
251 return oopMap_[oopMap_.length() - depth];
254 // Mark the current bytecode position as either re-entrant (meaning, it
255 // can leave the interpreter or call to another function), or causing GC.
256 // This will record which stack slots have objects and associate that
257 // information with the current pc.
261 void int_(int value) {
270 void getlocal(Variable *var) {
271 ensure(OP_GETLOCAL_LENGTH);
272 writeOp(OP_GETLOCAL);
273 writeUint32(var->slot());
275 if (var->type()->isTraceable())
278 void setlocal(Variable *var) {
279 ensure(OP_SETLOCAL_LENGTH);
280 writeOp(OP_SETLOCAL);
281 writeUint32(var->slot());
283 if (var->type()->isTraceable())
286 void getarg(Variable *var) {
287 ensure(OP_GETARG_LENGTH);
289 writeUint32(var->slot());
291 if (var->type()->isTraceable())
294 void setarg(Variable *var) {
295 ensure(OP_SETARG_LENGTH);
297 writeUint32(var->slot());
299 if (var->type()->isTraceable())
303 void j(Opcode op, Label *label) {
304 // Since jumps can skip around the stream, do not try to do anything
305 // in a potentially truncated state.
309 assert(op >= OP_JL && op <= OP_JUMP);
310 ensure(OP_JUMP_LENGTH);
312 if (label->bound()) {
313 // We can immediately emit this jump.
314 int distance = label->offset() - position();
316 writeInt32(distance);
318 // We can't emit this jump yet. Add a placeholder.
319 int old = label->link(position());
324 void bind(Label *label) {
328 assert(!label->bound());
330 int offset = label->offset();
331 while (offset != Label::INVALID_OFFSET)
332 offset = patchJumpAt(offset, position());
334 label->bind(position());
336 void jump(Label *label) {
340 void binary(Opcode op) {
345 singleByteOp(OP_POP);
348 singleByteOp(OP_STOP);
350 void call(Handle<Type> fun) {
352 ensure(OP_CALL_LENGTH);
355 if (fun->returnType()->isVoid())
357 else if (fun->returnType()->isTraceable())
360 void callnative(unsigned index, unsigned argc) {
362 ensure(OP_CALLNATIVE_LENGTH);
363 writeOp(OP_CALLNATIVE);
368 void getelem(Type *type) {
369 singleByteOp(OP_GETELEM);
371 if (type->isTraceable())
374 void setelem(Type *type) {
375 singleByteOp(OP_SETELEM);
378 void getfield(unsigned index, Type *type) {
379 ensure(OP_GETFIELD_LENGTH);
380 writeOp(OP_GETFIELD);
383 if (type->isTraceable())
386 void setfield(unsigned index, Type *type) {
387 ensure(OP_SETFIELD_LENGTH);
388 writeOp(OP_SETFIELD);
391 if (type->isTraceable())
395 void getglobal(Variable *var) {
396 ensure(OP_GETGLOBAL_F_LENGTH);
397 if (var->type()->isPrimitive()) {
398 switch (var->type()->primitive()) {
399 case PrimitiveType_Float:
400 writeOp(OP_GETGLOBAL_F);
403 assert(var->type()->primitive() == PrimitiveType_Int32);
404 writeOp(OP_GETGLOBAL_I);
408 writeOp(OP_GETGLOBAL_O);
411 writeUint32(var->slot());
413 void setglobal(Variable *var) {
414 ensure(OP_SETGLOBAL_F_LENGTH);
415 if (var->type()->isPrimitive()) {
416 switch (var->type()->primitive()) {
417 case PrimitiveType_Float:
418 writeOp(OP_SETGLOBAL_F);
421 assert(var->type()->primitive() == PrimitiveType_Int32);
422 writeOp(OP_SETGLOBAL_I);
426 writeOp(OP_SETGLOBAL_O);
428 writeUint32(var->slot());
431 void float_(float value) {
432 ensure(OP_FLOAT_LENGTH);
437 singleByteOp(OP_CVT_I2F);
439 void newarray(Opcode op, Handle<Map> map, Heap::Tenure tenure) {
440 assert(op == OP_NEWFIXED ||
445 ensure(OpcodeInfo[op].length);
451 void newstruct(Handle<Map> map, Heap::Tenure tenure) {
453 ensure(OP_NEWSTRUCT_LENGTH);
454 writeOp(OP_NEWSTRUCT);
459 void savelifo(unsigned slot) {
460 ensure(OP_SAVELIFO_LENGTH);
461 writeOp(OP_SAVELIFO);
464 void unwindlifo(unsigned slot) {
465 ensure(OP_UNWINDLIFO_LENGTH);
466 writeOp(OP_UNWINDLIFO);
469 void newref(Variable *var) {
471 if (var->location() == Variable::STACK) {
472 ensure(OP_VARREF_LIFO_LENGTH);
473 writeOp(OP_VARREF_LIFO);
475 assert(var->location() == Variable::PARAMETER);
476 ensure(OP_ARGREF_LIFO_LENGTH);
477 writeOp(OP_ARGREF_LIFO);
479 writeUint32(var->slot());
483 void slotref_lifo() {
485 singleByteOp(OP_SLOTREF_LIFO);
488 void arrayref_lifo() {
490 singleByteOp(OP_ARRAYREF_LIFO);
493 void fieldref_lifo(unsigned index) {
495 ensure(OP_FIELDREF_LIFO_LENGTH);
496 writeOp(OP_FIELDREF_LIFO);
500 void getref(Type *type) {
501 if (type->pod() == PrimitiveType_Int32) {
502 singleByteOp(OP_GETREF_I);
504 assert(type->pod() == PrimitiveType_Float);
505 singleByteOp(OP_GETREF_F);
508 void setref(Type *type) {
509 if (type->pod() == PrimitiveType_Int32) {
510 singleByteOp(OP_SETREF_I);
512 assert(type->pod() == PrimitiveType_Float);
513 singleByteOp(OP_SETREF_F);
516 void object(Handle<Object> obj) {
517 ensure(OP_OBJECT_LENGTH);
523 singleByteOp(OP_COPYARRAY);
527 singleByteOp(OP_COPYSTRUCT);
532 bool oop = getOop(1);
533 singleByteOp(OP_DUP);
537 bool oop2 = getOop(2);
538 bool oop1 = getOop(1);
540 singleByteOp(OP_DUP2);
546 bool oop2 = getOop(2);
547 bool oop1 = getOop(1);
549 singleByteOp(OP_SWAP);
555 bool oop3 = getOop(3);
556 bool oop2 = getOop(2);
557 bool oop1 = getOop(1);
559 singleByteOp(OP_ROLL3);
566 bool oop2 = getOop(2);
567 bool oop1 = getOop(1);
569 singleByteOp(OP_PICK2);
576 bool oop3 = getOop(3);
577 bool oop2 = getOop(2);
578 bool oop1 = getOop(1);
580 singleByteOp(OP_PICK3);
589 singleByteOp(OP_RETURNVOID);
592 singleByteOp(OP_MODULE);
595 void getmodule_fn(unsigned index) {
596 ensure(OP_GETMODULE_FN_LENGTH);
597 writeOp(OP_GETMODULE_FN);
602 singleByteOp(OP_BITNOT);
605 singleByteOp(OP_NOT);
608 singleByteOp(OP_NOT_F);
611 singleByteOp(OP_NEG);
614 singleByteOp(OP_NEG_F);
617 singleByteOp(OP_BITS_I2F);
620 singleByteOp(OP_BITS_F2I);
623 singleByteOp(OP_TRUE);
626 singleByteOp(OP_FALSE);
629 void tableswitch(int low, int high, Label *def, Label **labels) {
630 unsigned tablesize = TableSwitchEntries(low, high);
631 assert(tablesize <= MAX_TABLESWITCH_ENTRIES);
633 ensure(OP_TABLESWITCH_LENGTH);
634 writeOp(OP_TABLESWITCH);
639 // When labels are patched, we assume each position is a jump. Rather
640 // than hack position offsets to make labels work without actual
641 // incoming jumps, we emit a jump inline.
642 for (unsigned i = 0; i < tablesize; i++)
647 void get(Variable *var) {
648 if (var->location() == Variable::STACK) {
651 assert(var->location() == Variable::PARAMETER);
655 void set(Variable *var) {
656 assert(!var->type()->isReference());
657 if (var->location() == Variable::STACK) {
660 assert(var->location() == Variable::PARAMETER);
668 #endif // _include_bytecode_emitter_h_