Add support for SP1-style fixed array initializers.
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"
36 typedef PoolList<ScopedRoot<Object> > ObjectList;
38 struct OopRecord : public PoolObject
44 OopRecord(unsigned length, unsigned offset)
58 bytecode *buffer_pos_;
59 bytecode *buffer_end_;
60 bytecode inline_buffer_[512];
62 unsigned maxStackDepth_;
64 PoolList<bool> oopMap_;
65 PoolList<OopRecord *> oopRecords_;
66 CompactBufferWriter sourcemap_;
67 unsigned lastCodePosition_;
71 // The maximum size of a code object must be such that the distance between
72 // two instructions fits into a signed, 31-bit integer. This ensures that
73 // all code-bounded buffers (like the number of object literals or
74 // safepoints) does not need to be bounds checked. It also means we can add
75 // any two offsets and be guaranteed that they don't overflow.
76 static const size_t MAX_LENGTH = INT_MAX / 2;
79 bool growBuffer(size_t bytes);
81 // Checking the return value is not necessary, since the inline buffer is
83 bool ensure(size_t bytes) {
84 if (buffer_pos_ + bytes > buffer_end_)
85 return growBuffer(bytes);
89 void writeUint8(unsigned value) {
90 assert(value <= UCHAR_MAX);
91 *buffer_pos_++ = value;
93 void writeFloat(float value) {
94 *(float *)buffer_pos_ = value;
95 buffer_pos_ += sizeof(float);
97 void writeInt32(int value) {
98 *(int *)buffer_pos_ = value;
99 buffer_pos_ += sizeof(int);
101 void writeUint32(unsigned value) {
102 *(unsigned *)buffer_pos_ = value;
103 buffer_pos_ += sizeof(unsigned);
105 void writeOp(Opcode op) {
110 for (int i = 0; i < OpcodeInfo[op].nuses; i++)
112 for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
113 oopMap_.append(false);
114 if (oopMap_.length() > maxStackDepth_)
115 maxStackDepth_ = oopMap_.length();
117 void writeObject(Object *obj) {
118 writeUint32(objects_.length());
119 ScopedRoot<Object> root(obj);
120 if (!objects_.append(root))
124 int position() const {
125 assert(buffer_pos_ - buffer_ < INT_MAX / 2);
126 return int(buffer_pos_ - buffer_);
130 BytecodeEmitter(PoolAllocator &pool);
132 bytecode *buffer() const {
135 unsigned length() const {
136 return unsigned(buffer_pos_ - buffer_);
139 return oom_ || sourcemap_.oom();
141 unsigned stackDepth() const {
142 return oopMap_.length();
144 unsigned maxStackDepth() const {
145 return maxStackDepth_;
147 const ObjectList *objects() const {
150 PoolList<OopRecord *> *oopRecords() {
154 void drop(unsigned amt) {
155 assert(amt <= stackDepth());
156 for (unsigned i = 0; i < amt; i++)
160 void note_position(const SourcePosition &pos);
161 uint8 *sourcemap() const {
162 return sourcemap_.buffer();
164 size_t sourcemapLength() const {
165 return sourcemap_.length();
167 void setFirstLine(unsigned line) {
168 assert(!sourcemap_.length());
173 // A label represents a local control-flow target in the bytecode. A label
174 // may be in three states:
176 // (1) Empty - the label is not used yet.
177 // (2) Pending - the label has incoming jumps, but the actual location of
178 // is not yet known. The list of jumps is threaded through
179 // the four bytes reserved for each emitted jump target. In
180 // this case, the offset field is the location of the most
181 // recently emitted incoming jump.
182 // (3) Bound - the label's position has been pinned.
184 // A jump can be emitted to a label in any state. Binding a label
185 // immediately backpatches all pending incoming jumps. A label must be
186 // bound if it is used.
192 void setOffset(int offset) {
194 assert(offset_ == offset);
198 static const int INVALID_OFFSET = -1;
202 : offset_(INVALID_OFFSET),
208 assert(bound_ || offset_ == INVALID_OFFSET);
217 void bind(int offset) {
222 int link(int offset) {
231 int patchJumpAt(int at, int to) {
232 assert(at >= 0 && at < position());
233 int old = *(int *)(buffer_ + at + 1);
234 *(int *)(buffer_ + at + 1) = (to - at);
238 void singleByteOp(Opcode op) {
239 assert(OpcodeInfo[op].length == 1);
240 ensure(OpcodeInfo[op].length);
244 // Mark a stack slot as having an object pointer.
245 void markOop(unsigned depth, bool oop = true) {
248 oopMap_[oopMap_.length() - depth] = oop;
250 bool getOop(unsigned depth) {
253 return oopMap_[oopMap_.length() - depth];
256 // Mark the current bytecode position as either re-entrant (meaning, it
257 // can leave the interpreter or call to another function), or causing GC.
258 // This will record which stack slots have objects and associate that
259 // information with the current pc.
263 void int_(int value) {
272 void getlocal(Variable *var) {
273 ensure(OP_GETLOCAL_LENGTH);
274 writeOp(OP_GETLOCAL);
275 writeUint32(var->slot());
277 if (var->type()->isTraceable())
280 void setlocal(Variable *var) {
281 ensure(OP_SETLOCAL_LENGTH);
282 writeOp(OP_SETLOCAL);
283 writeUint32(var->slot());
285 if (var->type()->isTraceable())
288 void getarg(Variable *var) {
289 ensure(OP_GETARG_LENGTH);
291 writeUint32(var->slot());
293 if (var->type()->isTraceable())
296 void setarg(Variable *var) {
297 ensure(OP_SETARG_LENGTH);
299 writeUint32(var->slot());
301 if (var->type()->isTraceable())
305 void j(Opcode op, Label *label) {
306 // Since jumps can skip around the stream, do not try to do anything
307 // in a potentially truncated state.
311 assert(op >= OP_JL && op <= OP_JUMP);
312 ensure(OP_JUMP_LENGTH);
314 if (label->bound()) {
315 // We can immediately emit this jump.
316 int distance = label->offset() - position();
318 writeInt32(distance);
320 // We can't emit this jump yet. Add a placeholder.
321 int old = label->link(position());
326 void bind(Label *label) {
330 assert(!label->bound());
332 int offset = label->offset();
333 while (offset != Label::INVALID_OFFSET)
334 offset = patchJumpAt(offset, position());
336 label->bind(position());
338 void jump(Label *label) {
342 void binary(Opcode op) {
347 singleByteOp(OP_POP);
350 singleByteOp(OP_STOP);
352 void call(Handle<Type> fun) {
354 ensure(OP_CALL_LENGTH);
357 if (fun->returnType()->isVoid())
359 else if (fun->returnType()->isTraceable())
362 void callnative(unsigned index, unsigned argc) {
364 ensure(OP_CALLNATIVE_LENGTH);
365 writeOp(OP_CALLNATIVE);
370 void getelem(Type *type) {
371 singleByteOp(OP_GETELEM);
373 if (type->isTraceable())
376 void setelem(Type *type) {
377 singleByteOp(OP_SETELEM);
380 void getfield(unsigned index, Type *type) {
381 ensure(OP_GETFIELD_LENGTH);
382 writeOp(OP_GETFIELD);
385 if (type->isTraceable())
388 void setfield(unsigned index, Type *type) {
389 ensure(OP_SETFIELD_LENGTH);
390 writeOp(OP_SETFIELD);
393 if (type->isTraceable())
397 void getglobal(Variable *var) {
398 ensure(OP_GETGLOBAL_F_LENGTH);
399 if (var->type()->isPrimitive()) {
400 switch (var->type()->primitive()) {
401 case PrimitiveType_Float:
402 writeOp(OP_GETGLOBAL_F);
405 assert(var->type()->primitive() == PrimitiveType_Int32);
406 writeOp(OP_GETGLOBAL_I);
410 writeOp(OP_GETGLOBAL_O);
413 writeUint32(var->slot());
415 void setglobal(Variable *var) {
416 ensure(OP_SETGLOBAL_F_LENGTH);
417 if (var->type()->isPrimitive()) {
418 switch (var->type()->primitive()) {
419 case PrimitiveType_Float:
420 writeOp(OP_SETGLOBAL_F);
423 assert(var->type()->primitive() == PrimitiveType_Int32);
424 writeOp(OP_SETGLOBAL_I);
428 writeOp(OP_SETGLOBAL_O);
430 writeUint32(var->slot());
433 void float_(float value) {
434 ensure(OP_FLOAT_LENGTH);
439 singleByteOp(OP_CVT_I2F);
441 void newarray(Opcode op, Handle<ArrayMap> map, Heap::Tenure tenure) {
442 assert(op == OP_NEWFIXED ||
447 ensure(OpcodeInfo[op].length);
452 if (op == OP_NEWSIZED)
453 drop(map->type()->levels());
457 void newstruct(Handle<Map> map, Heap::Tenure tenure) {
459 ensure(OP_NEWSTRUCT_LENGTH);
460 writeOp(OP_NEWSTRUCT);
465 void savelifo(unsigned slot) {
466 ensure(OP_SAVELIFO_LENGTH);
467 writeOp(OP_SAVELIFO);
470 void unwindlifo(unsigned slot) {
471 ensure(OP_UNWINDLIFO_LENGTH);
472 writeOp(OP_UNWINDLIFO);
475 void newref(Variable *var) {
477 if (var->location() == Variable::STACK) {
478 ensure(OP_VARREF_LIFO_LENGTH);
479 writeOp(OP_VARREF_LIFO);
481 assert(var->location() == Variable::PARAMETER);
482 ensure(OP_ARGREF_LIFO_LENGTH);
483 writeOp(OP_ARGREF_LIFO);
485 writeUint32(var->slot());
489 void slotref_lifo() {
491 singleByteOp(OP_SLOTREF_LIFO);
494 void arrayref_lifo() {
496 singleByteOp(OP_ARRAYREF_LIFO);
499 void fieldref_lifo(unsigned index) {
501 ensure(OP_FIELDREF_LIFO_LENGTH);
502 writeOp(OP_FIELDREF_LIFO);
506 void getref(Type *type) {
507 if (type->pod() == PrimitiveType_Int32) {
508 singleByteOp(OP_GETREF_I);
510 assert(type->pod() == PrimitiveType_Float);
511 singleByteOp(OP_GETREF_F);
514 void setref(Type *type) {
515 if (type->pod() == PrimitiveType_Int32) {
516 singleByteOp(OP_SETREF_I);
518 assert(type->pod() == PrimitiveType_Float);
519 singleByteOp(OP_SETREF_F);
522 void object(Handle<Object> obj) {
523 ensure(OP_OBJECT_LENGTH);
529 singleByteOp(OP_COPYARRAY);
533 singleByteOp(OP_COPYSTRUCT);
538 bool oop = getOop(1);
539 singleByteOp(OP_DUP);
543 bool oop2 = getOop(2);
544 bool oop1 = getOop(1);
546 singleByteOp(OP_DUP2);
552 bool oop2 = getOop(2);
553 bool oop1 = getOop(1);
555 singleByteOp(OP_SWAP);
561 bool oop3 = getOop(3);
562 bool oop2 = getOop(2);
563 bool oop1 = getOop(1);
565 singleByteOp(OP_ROLL3);
572 bool oop2 = getOop(2);
573 bool oop1 = getOop(1);
575 singleByteOp(OP_PICK2);
582 bool oop3 = getOop(3);
583 bool oop2 = getOop(2);
584 bool oop1 = getOop(1);
586 singleByteOp(OP_PICK3);
595 singleByteOp(OP_RETURNVOID);
598 singleByteOp(OP_MODULE);
601 void getmodule_fn(unsigned index) {
602 ensure(OP_GETMODULE_FN_LENGTH);
603 writeOp(OP_GETMODULE_FN);
608 singleByteOp(OP_BITNOT);
611 singleByteOp(OP_NOT);
614 singleByteOp(OP_NOT_F);
617 singleByteOp(OP_NEG);
620 singleByteOp(OP_NEG_F);
623 singleByteOp(OP_BITS_I2F);
626 singleByteOp(OP_BITS_F2I);
629 singleByteOp(OP_TRUE);
632 singleByteOp(OP_FALSE);
635 void tableswitch(int low, int high, Label *def, Label **labels) {
636 unsigned tablesize = TableSwitchEntries(low, high);
637 assert(tablesize <= MAX_TABLESWITCH_ENTRIES);
639 ensure(OP_TABLESWITCH_LENGTH);
640 writeOp(OP_TABLESWITCH);
645 // When labels are patched, we assume each position is a jump. Rather
646 // than hack position offsets to make labels work without actual
647 // incoming jumps, we emit a jump inline.
648 for (unsigned i = 0; i < tablesize; i++)
653 void get(Variable *var) {
654 if (var->location() == Variable::STACK) {
657 assert(var->location() == Variable::PARAMETER);
661 void set(Variable *var) {
662 assert(!var->type()->isReference());
663 if (var->location() == Variable::STACK) {
666 assert(var->location() == Variable::PARAMETER);
674 #endif // _include_bytecode_emitter_h_