src/compiler/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Sat Nov 17 18:41:01 2012 -0800 (2012-11-17)
changeset 195 c1ba166f3fc4
parent 194 src/BytecodeEmitter.h@b029d63565a2
child 220 5df4a112967d
permissions -rw-r--r--
Massive SemA/BC refactoring to support better type systems. Read on for more.

The previous compiler had a simple pipeline, best outlined as:
(1) Parsing and name binding -> AST
(2) Storage allocation -> AST
(3) Semantic Analysis (SemA) -> AST
(4) Bytecode Compilation (BC) -> Code

This pipeline, unfortunately, had two problems. First, name binding cannot be performed in one pass if we wish to have multi-file support in the form of modules. Name binding must be two-pass.

Second, because SemA was only capable of annotating AST nodes with a single type, most coercion work had to be duplicated in the BC phase, as coercion was not explicit in the SemA output. This made introducing new type rules extremely difficult, and all but precluded concepts like operator overloading (or overloading at all).

This patch rewrites most of Keima's backend. Of interest are the following changes:
(1) CompileContext has been refactored around future multi-file support.
(2) Parsing no longer performs any name binding.
(3) The grammar for type-and-name has been changed from:
(Label | Identifier)? Identifier
to:
Type? Identifier
where:
Type ::= OldType | NewType
OldType ::= Label
NewType ::= Identifier (. NewType)*


Although we do not implement the full production for Type yet, this
distinction is important. A type identifier is now affixed to the AST
as an Expression (right now, always a NameProxy), so it can fully
participate in name binding.

(4) Immediately after parsing, the AST goes through a NamePopulation phase.
This phase creates Scope objects for every scope which declares a name,
and if those names are declaring types or functions, a Symbol is created
and entered into the scope. Symbols entirely replace the old BoundName
classes.

(5) After NamePopulation comes NameBinding. This phase performs three steps:
(a) Links Scope objects together, to form a tree.
(b) Binds any free name to an existing name in the scope chain.
(c) Creates and registers any Symbols for names which have not yet been
added to the scope (for example, local variables).

In accordance, all "allocation" and "binding" concepts have been removed
from Scopes, which are now lightweight container classes.

(6) SemA now generates bytecode for each statement in the AST. Expression
compilation is performed via a separate mechanism called HIR (High-level
Intermediate Representation). To analyze an expression, SemA will walk
the AST, and produce a HIR object for each node. HIR is typed, and SemA
may insert coercion nodes, or even expand an AST node into multiple HIR
nodes. The result of evaluating an expression in SemA is therefore the
the root of a HIR tree, which SemA then sends to the HIRTranslator, which
performs bytecode generation.

As before, SemA still performs full semantic analysis. However, not it
also produces each function's bytecode.

This split allows us to decompose potentially complex semantics into
a more fine-grained, AST-like structure, which can have very simple
code-generation logic. For example, (1.0 + 5) might look like:

HAdd(HFloat(1.0), HConvert(HInteger(5), <Float>))

As part of this decomposition, many opcodes have been removed. Rather than
using typed jumps, we now expand jumps into longer tests. For example:
jge.f <label>
becomes:
ge.f
cvt.f2b
jt <label>

This simplifies the pipeline, and JITs should be able to melt the added
work away.

HIR is not used at a statement level, and HIR does not have any concept
of control-flow.

(7) The old BytecodeCompiler has been removed, as its work is now split
between SemA and HIRTranslator.


In addition, some minor refactorings have taken place:

(1) Opcodes.tbl no longer hardcodes numbers (aaah).
(2) Type has been split into smaller, typed structs.
(3) BytecodeEmitter no longer relies on Pools or RootScopes.
(4) SemA is now responsible for variable/storage allocation.
(5) Native table are now global, rather than per-module. As such, native
declarations now result in a Native object (which still requires a
CALLNATIVE opcode), which is an index into the global table. Natives
must be bound globally. This is in preparation for module support.
(6) Publics are no longer tracked, but rather registered via a global
callback upon module load. This callback can be set by embedders. This
is in preparation for multi-file support.
(7) The BytecodeEmitter now performs Symbol allocations itself, and it
also generates Code objects itself, which removes a good deal of
complexity.

Finally, a test harness has been added. This harness is a python script which finds *.test files, recursively, in the tests folder. For each file it runs the corresponding .sp. The contents of the .test file must match stdout+stderr.
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of JITCraft.
     6  *
     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)
    10  * any later version.
    11  * 
    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.
    15  *
    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/.
    18  */
    19 #ifndef _include_bytecode_emitter_h_
    20 #define _include_bytecode_emitter_h_
    21 
    22 #include <assert.h>
    23 #include <limits.h>
    24 #include "../Opcodes.h"
    25 #include "../Handles.h"
    26 #include "../CompactBuffer.h"
    27 #include "../Array.h"
    28 #include "../HashTable.h"
    29 #include "../VirtualObjectVisitor.h"
    30 
    31 namespace ke {
    32 
    33 // An overarching note on overflows: Though we should use CheckedInt, we rely
    34 // on a source-length cap covering all possible overflow checks. As long as
    35 // the source length is capped to INT_MAX, the number of AST nodes will be
    36 // strictly beneath that cap, and so to will most things generated by the
    37 // AST. For example, local and argument counts, number of functions, fields
    38 // in a struct, et cetera. Not everything can be covered by this cap: the
    39 // stack depth, number of bytecodes for example. In these cases we explicitly
    40 // test for overflow of INT_MAX and in other cases we assert.
    41 //
    42 // In the future, we should of course use a checked int wrapper.
    43 
    44 class Type;
    45 class Map;
    46 class ArrayMap;
    47 class Symbol;
    48 class VariableSymbol;
    49 
    50 struct OopRecord
    51 {
    52     unsigned depth;
    53     unsigned offset;
    54     Vector<bool> slots;
    55 
    56     OopRecord(unsigned length, unsigned offset)
    57       : depth(length),
    58         offset(offset)
    59     {
    60     }
    61 };
    62 
    63 class StringTable
    64 {
    65     struct Entry
    66     {
    67         String *string;
    68         unsigned index;
    69 
    70         Entry()
    71           : string(NULL),
    72             index(0)
    73         {
    74         }
    75 
    76         Entry(String *string, unsigned index)
    77           : string(string),
    78             index(index)
    79         {
    80         }
    81     };
    82 
    83     struct Policy
    84     {
    85         typedef Entry Payload;
    86 
    87         static uint32 hash(String *string) {
    88             return HashPointer(string);
    89         }
    90         
    91         static bool matches(String *key, Entry &e) {
    92             return key == e.string;
    93         }
    94     };
    95 
    96     typedef HashTable<Policy, ZoneAllocatorPolicy> Set;
    97 
    98   public:
    99     StringTable();
   100     bool initialize();
   101     void accept(VirtualObjectVisitor *visitor);
   102 
   103     bool add(Handle<String> name, unsigned *indexp);
   104     size_t stringCount() const {
   105         return strings_.length();
   106     }
   107     String *stringAt(size_t i) const {
   108         return strings_[i].string;
   109     }
   110 
   111   private:
   112     Set set_;
   113     Vector<Entry> strings_;
   114     bool initialized_;
   115 };
   116 
   117 // The bytecode emitter assists in building Code objects, by emitting opcodes
   118 // and tracking stack information. Since the SemA creates temporary pool/root
   119 // scopes, we cannot use ScopedRoot or PoolObjects. Instead, BCE gets a
   120 // special marking function.
   121 class BytecodeEmitter : public AutoRooted
   122 {
   123   private:
   124     PoolAllocator &pool_;
   125 
   126     bytecode *buffer_;
   127     bytecode *buffer_pos_;
   128     bytecode *buffer_end_;
   129     bytecode inline_buffer_[512];
   130     bool oom_;
   131     unsigned maxStackDepth_;
   132     Vector<bool> oopMap_;
   133     Vector<OopRecord *> oopRecords_;
   134     CompactBufferWriter sourcemap_;
   135     unsigned lastCodePosition_;
   136     unsigned firstLine_;
   137     StringTable strings_;
   138     Vector<Symbol *> locals_;
   139     Vector<Object *> objects_;
   140 
   141   private:
   142     // The maximum size of a code object must be such that the distance between
   143     // two instructions fits into a signed, 31-bit integer. This ensures that
   144     // all code-bounded buffers (like the number of object literals or
   145     // safepoints) does not need to be bounds checked. It also means we can add
   146     // any two offsets and be guaranteed that they don't overflow.
   147     static const size_t MAX_LENGTH = INT_MAX / 2;
   148 
   149   private:
   150     bool growBuffer(size_t bytes);
   151 
   152     // Checking the return value is not necessary, since the inline buffer is
   153     // used as a ballast.
   154     bool ensure(size_t bytes) {
   155         if (buffer_pos_ + bytes > buffer_end_)
   156             return growBuffer(bytes);
   157         return true;
   158     }
   159 
   160     void writeUint8(unsigned value) {
   161         assert(value <= UCHAR_MAX);
   162         *buffer_pos_++ = value;
   163     }
   164     void writeFloat(float value) {
   165         *(float *)buffer_pos_ = value;
   166         buffer_pos_ += sizeof(float);
   167     }
   168     void writeInt32(int value) {
   169         *(int *)buffer_pos_ = value;
   170         buffer_pos_ += sizeof(int);
   171     }
   172     void writeUint32(unsigned value) {
   173         *(unsigned *)buffer_pos_ = value;
   174         buffer_pos_ += sizeof(unsigned);
   175     }
   176     void writeOp(Opcode op) {
   177         if (oom_)
   178             return;
   179 
   180         writeUint8(op);
   181         for (int i = 0; i < OpcodeInfo[op].nuses; i++)
   182             oopMap_.pop();
   183         for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
   184             oopMap_.append(false);
   185         if (oopMap_.length() > maxStackDepth_)
   186             maxStackDepth_ = oopMap_.length();
   187     }
   188     void writeObject(Object *obj) {
   189         writeUint32(objects_.length());
   190         ScopedRoot<Object> root(obj);
   191         if (!objects_.append(root))
   192             oom_ = true;
   193     }
   194 
   195     int position() const {
   196         assert(buffer_pos_ - buffer_ < INT_MAX / 2);
   197         return int(buffer_pos_ - buffer_);
   198     }
   199 
   200   public:
   201     BytecodeEmitter(PoolAllocator &pool);
   202     ~BytecodeEmitter();
   203 
   204     void accept(VirtualObjectVisitor *visitor);
   205 
   206     bytecode *buffer() const {
   207         return buffer_;
   208     }
   209     unsigned length() const {
   210         return unsigned(buffer_pos_ - buffer_);
   211     }
   212     bool oom() const {
   213         return oom_ || sourcemap_.oom();
   214     }
   215     unsigned stackDepth() const {
   216         return oopMap_.length();
   217     }
   218     unsigned maxStackDepth() const {
   219         return maxStackDepth_;
   220     }
   221 
   222     void drop(unsigned amt) {
   223         assert(amt <= stackDepth());
   224         for (unsigned i = 0; i < amt; i++)
   225             oopMap_.pop();
   226     }
   227 
   228     void note_position(const SourcePosition &pos);
   229     void setFirstLine(unsigned line) {
   230         assert(!sourcemap_.length());
   231         firstLine_ = line;
   232     }
   233 
   234   public:
   235     // A label represents a local control-flow target in the bytecode. A label
   236     // may be in three states:
   237     //
   238     // (1) Empty - the label is not used yet.
   239     // (2) Pending - the label has incoming jumps, but the actual location of
   240     //               is not yet known. The list of jumps is threaded through
   241     //               the four bytes reserved for each emitted jump target. In
   242     //               this case, the offset field is the location of the most
   243     //               recently emitted incoming jump.
   244     // (3) Bound   - the label's position has been pinned.
   245     //
   246     // A jump can be emitted to a label in any state. Binding a label
   247     // immediately backpatches all pending incoming jumps. A label must be
   248     // bound if it is used.
   249     class Label
   250     {
   251         int offset_ : 31;
   252         bool bound_ : 1;
   253 
   254         void setOffset(int offset) {
   255             offset_ = offset;
   256             assert(offset_ == offset);
   257         }
   258 
   259       public:
   260         static const int INVALID_OFFSET = -1;
   261 
   262       public:
   263         Label()
   264           : offset_(INVALID_OFFSET),
   265             bound_(false)
   266         {
   267         }
   268         ~Label()
   269         {
   270             assert(bound_ || offset_ == INVALID_OFFSET);
   271         }
   272         
   273         bool bound() const {
   274             return bound_;
   275         }
   276         int offset() const {
   277             return offset_;
   278         }
   279         void bind(int offset) {
   280             assert(!bound_);
   281             setOffset(offset);
   282             bound_ = true;
   283         }
   284         int link(int offset) {
   285             assert(!bound_);
   286             int old = offset_;
   287             setOffset(offset);
   288             return old;
   289         }
   290     };
   291 
   292   private:
   293     int patchJumpAt(int at, int to) {
   294         assert(at >= 0 && at < position());
   295         int old = *(int *)(buffer_ + at + 1);
   296         *(int *)(buffer_ + at + 1) = (to - at);
   297         return old;
   298     }
   299 
   300     void singleByteOp(Opcode op) {
   301         assert(OpcodeInfo[op].length == 1);
   302         ensure(OpcodeInfo[op].length);
   303         writeOp(op);
   304     }
   305 
   306     // Mark a stack slot as having an object pointer.
   307     void markOop(unsigned depth, bool oop = true) {
   308         if (oom_)
   309             return;
   310         oopMap_[oopMap_.length() - depth] = oop;
   311     }
   312     bool getOop(unsigned depth) {
   313         if (oom_)
   314             return false;
   315         return oopMap_[oopMap_.length() - depth];
   316     }
   317 
   318     // Mark the current bytecode position as either re-entrant (meaning, it
   319     // can leave the interpreter or call to another function), or causing GC.
   320     // This will record which stack slots have objects and associate that
   321     // information with the current pc.
   322     void recordOops();
   323 
   324   public:
   325     void int_(int value) {
   326         ensure(OP_INT);
   327         writeOp(OP_INT);
   328         writeInt32(value);
   329     }
   330     void return_() {
   331         ensure(OP_RETURN);
   332         writeOp(OP_RETURN);
   333     }
   334     void getlocal(VariableSymbol *var);
   335     void setlocal(VariableSymbol *var);
   336 #if 0
   337     void getarg(Variable *var) {
   338         ensure(OP_GETARG_LENGTH);
   339         writeOp(OP_GETARG);
   340         writeUint32(var->slot());
   341 
   342         if (var->type()->isTraceable())
   343             markOop(1);
   344     }
   345     void setarg(Variable *var) {
   346         ensure(OP_SETARG_LENGTH);
   347         writeOp(OP_SETARG);
   348         writeUint32(var->slot());
   349 
   350         if (var->type()->isTraceable())
   351             markOop(1);
   352     }
   353 #endif
   354 
   355     void j(Opcode op, Label *label) {
   356         // Since jumps can skip around the stream, do not try to do anything
   357         // in a potentially truncated state.
   358         if (oom())
   359             return;
   360 
   361         assert(op == OP_JT || op == OP_JF || op == OP_JUMP);
   362         ensure(OP_JUMP_LENGTH);
   363 
   364         if (label->bound()) {
   365             // We can immediately emit this jump.
   366             int distance = label->offset() - position();
   367             writeOp(op);
   368             writeInt32(distance);
   369         } else {
   370             // We can't emit this jump yet. Add a placeholder.
   371             int old = label->link(position());
   372             writeOp(op);
   373             writeInt32(old);
   374         }
   375     }
   376     void bind(Label *label) {
   377         if (oom())
   378             return;
   379 
   380         assert(!label->bound());
   381 
   382         int offset = label->offset();
   383         while (offset != Label::INVALID_OFFSET)
   384             offset = patchJumpAt(offset, position());
   385 
   386         label->bind(position());
   387     }
   388     void jump(Label *label) {
   389         j(OP_JUMP, label);
   390     }
   391 
   392     void binary(Opcode op) {
   393         singleByteOp(op);
   394     }
   395     void unary(Opcode op) {
   396         singleByteOp(op);
   397     }
   398 
   399     void pop() {
   400         singleByteOp(OP_POP);
   401     }
   402     void stop() {
   403         singleByteOp(OP_STOP);
   404     }
   405     void call(Handle<FunctionType> fun, unsigned argc) {
   406         recordOops();
   407 
   408         if (fun->isNative()) {
   409             ensure(OP_CALLNATIVE_LENGTH);
   410             writeOp(OP_CALLNATIVE);
   411             writeUint32(argc);
   412             drop(argc + 1);
   413         } else {
   414             ensure(OP_CALL_LENGTH);
   415             writeOp(OP_CALL);
   416             drop(argc + 1);
   417 
   418             if (fun->returnType()->isVoid())
   419                 drop(1);
   420             else if (fun->returnType()->isTraceable())
   421                 markOop(1);
   422         }
   423     }
   424 
   425     void getelem(Type *type) {
   426         singleByteOp(OP_GETELEM);
   427 
   428         if (type->isTraceable())
   429             markOop(1);
   430     }
   431     void setelem(Type *type) {
   432         singleByteOp(OP_SETELEM);
   433     }
   434 
   435     void getfield(unsigned index, Type *type) {
   436         ensure(OP_GETFIELD_LENGTH);
   437         writeOp(OP_GETFIELD);
   438         writeUint32(index);
   439 
   440         if (type->isTraceable())
   441             markOop(1);
   442     }
   443     void setfield(unsigned index, Type *type) {
   444         ensure(OP_SETFIELD_LENGTH);
   445         writeOp(OP_SETFIELD);
   446         writeUint32(index);
   447 
   448         if (type->isTraceable())
   449             markOop(1);
   450     }
   451 
   452 #if 0
   453     void getglobal(Variable *var) {
   454         ensure(OP_GETGLOBAL_F_LENGTH);
   455         if (var->type()->isPrimitive()) {
   456             switch (var->type()->primitive()) {
   457               case PrimitiveType_Float:
   458                 writeOp(OP_GETGLOBAL_F);
   459                 break;
   460               default:
   461                 assert(var->type()->primitive() == PrimitiveType_Int32);
   462                 writeOp(OP_GETGLOBAL_I);
   463                 break;
   464             }
   465         } else {
   466             writeOp(OP_GETGLOBAL_O);
   467             markOop(1);
   468         }
   469         writeUint32(var->slot());
   470     }
   471     void setglobal(Variable *var) {
   472         ensure(OP_SETGLOBAL_F_LENGTH);
   473         if (var->type()->isPrimitive()) {
   474             switch (var->type()->primitive()) {
   475               case PrimitiveType_Float:
   476                 writeOp(OP_SETGLOBAL_F);
   477                 break;
   478               default:
   479                 assert(var->type()->primitive() == PrimitiveType_Int32);
   480                 writeOp(OP_SETGLOBAL_I);
   481                 break;
   482             }
   483         } else {
   484             writeOp(OP_SETGLOBAL_O);
   485         }
   486         writeUint32(var->slot());
   487     }
   488 #endif
   489 
   490     void float_(float value) {
   491         ensure(OP_FLOAT_LENGTH);
   492         writeOp(OP_FLOAT);
   493         writeFloat(value);
   494     }
   495     void cvt_i2f() {
   496         singleByteOp(OP_CVT_I2F);
   497     }
   498     void newarray(Opcode op, Handle<ArrayMap> map, Heap::Tenure tenure) {
   499         assert(op == OP_NEWFIXED ||
   500                op == OP_NEWEMPTY ||
   501                op == OP_NEWSIZED);
   502 
   503         recordOops();
   504         ensure(OpcodeInfo[op].length);
   505         writeOp(op);
   506         writeUint8(tenure);
   507         writeObject(map);
   508 
   509         if (op == OP_NEWSIZED)
   510             drop(map->type()->levels());
   511 
   512         markOop(1);
   513     }
   514     void newstruct(Handle<Map> map, Heap::Tenure tenure) {
   515         recordOops();
   516         ensure(OP_NEWSTRUCT_LENGTH);
   517         writeOp(OP_NEWSTRUCT);
   518         writeUint8(tenure);
   519         writeObject(map);
   520         markOop(1);
   521     }
   522     void savelifo(unsigned slot) {
   523         ensure(OP_SAVELIFO_LENGTH);
   524         writeOp(OP_SAVELIFO);
   525         writeUint32(slot);
   526     }
   527     void unwindlifo(unsigned slot) {
   528         ensure(OP_UNWINDLIFO_LENGTH);
   529         writeOp(OP_UNWINDLIFO);
   530         writeUint32(slot);
   531     }
   532 #if 0
   533     void newref(Variable *var) {
   534         recordOops();
   535         if (var->location() == Variable::STACK) {
   536             ensure(OP_VARREF_LIFO_LENGTH);
   537             writeOp(OP_VARREF_LIFO);
   538         } else {
   539             assert(var->location() == Variable::PARAMETER);
   540             ensure(OP_ARGREF_LIFO_LENGTH);
   541             writeOp(OP_ARGREF_LIFO);
   542         }
   543         writeUint32(var->slot());
   544         markOop(1);
   545     }
   546 #endif
   547 
   548     void slotref_lifo() {
   549         recordOops();
   550         singleByteOp(OP_SLOTREF_LIFO);
   551         markOop(1);
   552     }
   553     void getref(Type *type) {
   554         if (type->pod() == PrimitiveType_Int32) {
   555             singleByteOp(OP_GETREF_I);
   556         } else {
   557             assert(type->pod() == PrimitiveType_Float);
   558             singleByteOp(OP_GETREF_F);
   559         }
   560     }
   561     void setref(Type *type) {
   562         if (type->pod() == PrimitiveType_Int32) {
   563             singleByteOp(OP_SETREF_I);
   564         } else {
   565             assert(type->pod() == PrimitiveType_Float);
   566             singleByteOp(OP_SETREF_F);
   567         }
   568     }
   569     void object(Handle<Object> obj) {
   570         ensure(OP_OBJECT_LENGTH);
   571         writeOp(OP_OBJECT);
   572         writeObject(obj);
   573         markOop(1);
   574     }
   575     void copyarray() {
   576         singleByteOp(OP_COPYARRAY);
   577         markOop(1);
   578     }
   579     void copystruct() {
   580         singleByteOp(OP_COPYSTRUCT);
   581         markOop(1);
   582     }
   583     void sizeof_() {
   584         singleByteOp(OP_SIZEOF);
   585     }
   586 
   587     void dup() {
   588         bool oop = getOop(1);
   589         singleByteOp(OP_DUP);
   590         markOop(1, oop);
   591     }
   592     void dup2() {
   593         bool oop2 = getOop(2);
   594         bool oop1 = getOop(1);
   595 
   596         singleByteOp(OP_DUP2);
   597 
   598         markOop(2, oop2);
   599         markOop(1, oop1);
   600     }
   601     void swap() {
   602         bool oop2 = getOop(2);
   603         bool oop1 = getOop(1);
   604 
   605         singleByteOp(OP_SWAP);
   606 
   607         markOop(2, oop1);
   608         markOop(1, oop2);
   609     }
   610     void roll3() {
   611         bool oop3 = getOop(3);
   612         bool oop2 = getOop(2);
   613         bool oop1 = getOop(1);
   614 
   615         singleByteOp(OP_ROLL3);
   616 
   617         markOop(3, oop2);
   618         markOop(2, oop1);
   619         markOop(1, oop3);
   620     }
   621     void pick2() {
   622         bool oop2 = getOop(2);
   623         bool oop1 = getOop(1);
   624 
   625         singleByteOp(OP_PICK2);
   626 
   627         markOop(3, oop2);
   628         markOop(2, oop1);
   629         markOop(1, oop2);
   630     }
   631     void pick3() {
   632         bool oop3 = getOop(3);
   633         bool oop2 = getOop(2);
   634         bool oop1 = getOop(1);
   635 
   636         singleByteOp(OP_PICK3);
   637 
   638         markOop(4, oop3);
   639         markOop(3, oop2);
   640         markOop(2, oop1);
   641         markOop(1, oop3);
   642     }
   643 
   644     void returnvoid() {
   645         singleByteOp(OP_RETURNVOID);
   646     }
   647     void bitnot() {
   648         singleByteOp(OP_BITNOT);
   649     }
   650     void not_() {
   651         singleByteOp(OP_NOT);
   652     }
   653     void not_f() {
   654         singleByteOp(OP_NOT_F);
   655     }
   656     void neg() {
   657         singleByteOp(OP_NEG);
   658     }
   659     void neg_f() {
   660         singleByteOp(OP_NEG_F);
   661     }
   662     void bits_i2f() {
   663         singleByteOp(OP_BITS_I2F);
   664     }
   665     void bits_f2i() {
   666         singleByteOp(OP_BITS_F2I);
   667     }
   668     void true_() {
   669         singleByteOp(OP_TRUE);
   670     }
   671     void false_() {
   672         singleByteOp(OP_FALSE);
   673     }
   674     
   675     unsigned addString(Handle<String> name) {
   676         unsigned index;
   677         if (!strings_.add(name, &index)) {
   678             oom_ |= true;
   679             return 0;
   680         }
   681         return index;
   682     }
   683 
   684     void getglobal(Symbol *sym);
   685     void setglobal(Symbol *sym);
   686 
   687     void public_() {
   688         recordOops();
   689         singleByteOp(OP_PUBLIC);
   690     }
   691 
   692     void tableswitch(int low, int high, Label *def, Label **labels) {
   693         unsigned tablesize = TableSwitchEntries(low, high);
   694         assert(tablesize <= MAX_TABLESWITCH_ENTRIES);
   695 
   696         ensure(OP_TABLESWITCH_LENGTH);
   697         writeOp(OP_TABLESWITCH);
   698         writeInt32(low);
   699         writeInt32(high);
   700         jump(def);
   701 
   702         // When labels are patched, we assume each position is a jump. Rather
   703         // than hack position offsets to make labels work without actual
   704         // incoming jumps, we emit a jump inline.
   705         for (unsigned i = 0; i < tablesize; i++)
   706             jump(labels[i]);
   707     }
   708 
   709     bool allocate(VariableSymbol *sym);
   710 
   711     Code *finish();
   712 
   713     // Helpers.
   714 #if 0
   715     void get(Variable *var) {
   716         if (var->location() == Variable::STACK) {
   717             getlocal(var);
   718         } else {
   719             assert(var->location() == Variable::PARAMETER);
   720             getarg(var);
   721         }
   722     }
   723     void set(Variable *var) {
   724         assert(!var->type()->isReference());
   725         if (var->location() == Variable::STACK) {
   726             setlocal(var);
   727         } else {
   728             assert(var->location() == Variable::PARAMETER);
   729             setarg(var);
   730         }
   731     }
   732 #endif
   733 };
   734 
   735 }
   736 
   737 #endif // _include_bytecode_emitter_h_