logo
class

compiler::Loop

sys::Obj
  compiler::Loop
    1  //
    2  // Copyright (c) 2006, Brian Frank and Andy Frank
    3  // Licensed under the Academic Free License version 3.0
    4  //
    5  // History:
    6  //   15 Sep 05  Brian Frank  Creation
    7  //
    8  
    9  **
   10  ** CodeAsm is used to assemble the fcode instructions of an Expr or Block.
   11  **
   12  class CodeAsm : CompilerSupport
   13  {
   14  
   15  //////////////////////////////////////////////////////////////////////////
   16  // Construction
   17  //////////////////////////////////////////////////////////////////////////
   18  
   19    new make(Compiler compiler, Location location, FPod fpod)
   20      : super(compiler)
   21    {
   22      this.location  = location
   23      this.fpod      = fpod
   24      this.code      = Buf.make
   25      this.errTable  = Buf.make; errTable.writeI2(-1)
   26      this.errCount  = 0
   27      this.lines     = Buf.make; lines.writeI2(-1)
   28      this.lineCount = 0
   29      this.loopStack = Loop[,]
   30    }
   31  
   32  //////////////////////////////////////////////////////////////////////////
   33  // Statements
   34  //////////////////////////////////////////////////////////////////////////
   35  
   36    Void block(Block block)
   37    {
   38      block.stmts.each |Stmt s| { stmt(s) }
   39    }
   40  
   41    Void stmt(Stmt stmt)
   42    {
   43      switch (stmt.id)
   44      {
   45        case StmtId.nop:           return
   46        case StmtId.expr:          expr(((ExprStmt)stmt).expr)
   47        case StmtId.localDef:      localVarDefStmt((LocalDefStmt)stmt)
   48        case StmtId.ifStmt:        ifStmt((IfStmt)stmt)
   49        case StmtId.returnStmt:    returnStmt((ReturnStmt)stmt)
   50        case StmtId.throwStmt:     throwStmt((ThrowStmt)stmt)
   51        case StmtId.forStmt:       forStmt((ForStmt)stmt)
   52        case StmtId.whileStmt:     whileStmt((WhileStmt)stmt)
   53        case StmtId.breakStmt:     breakOrContinueStmt(stmt)
   54        case StmtId.continueStmt:  breakOrContinueStmt(stmt)
   55        case StmtId.switchStmt:    switchStmt((SwitchStmt)stmt)
   56        case StmtId.tryStmt:       tryStmt((TryStmt)stmt)
   57        default:                   throw Err.make(stmt.id.toStr)
   58      }
   59    }
   60  
   61    private Void ifStmt(IfStmt stmt)
   62    {
   63      endLabel := -1
   64      c := Cond.make
   65  
   66      // optimize: if (true)
   67      if (stmt.condition.id == ExprId.trueLiteral)
   68      {
   69        block(stmt.trueBlock)
   70        return
   71      }
   72  
   73      // optimize: if (false)
   74      if (stmt.condition.id == ExprId.falseLiteral)
   75      {
   76        if (stmt.falseBlock != null)
   77          block(stmt.falseBlock)
   78        return
   79      }
   80  
   81      // check condition - if the condition is itself a CondExpr
   82      // then we just have it branch directly to the true/false
   83      // block rather than wasting instructions to push true/false
   84      // onto the stack
   85      if (stmt.condition is CondExpr)
   86      {
   87        cond((CondExpr)stmt.condition, c)
   88      }
   89      else
   90      {
   91        expr(stmt.condition)
   92        c.jumpFalses.add(jump(FOp.JumpFalse))
   93      }
   94  
   95      // true block
   96      c.jumpTrues.each |Int pos| { backpatch(pos) }
   97      block(stmt.trueBlock)
   98      if (!stmt.trueBlock.isExit && stmt.falseBlock != null)
   99        endLabel = jump(FOp.Jump)
  100  
  101      // false block
  102      c.jumpFalses.each |Int pos| { backpatch(pos) }
  103      if (stmt.falseBlock != null)
  104        block(stmt.falseBlock)
  105  
  106      // end
  107      if (endLabel !== -1) backpatch(endLabel)
  108    }
  109  
  110    private Void returnStmt(ReturnStmt stmt)
  111    {
  112      // if we are in a protected region, then we can't return immediately,
  113      // rather we need to save the result into a temporary local; and use
  114      // a "leave" instruction which we will backpatch in finishCode() with
  115      // the actual return sequence;
  116      if (inProtectedRegion)
  117      {
  118        // if returning a result then stash in temp local
  119        if (stmt.expr != null)
  120        {
  121          expr(stmt.expr)
  122          returnLocal = stmt.leaveVar
  123          op(FOp.StoreVar, returnLocal.register)
  124        }
  125  
  126        // jump to any finally blocks we are inside
  127        protectedRegions.eachr |ProtectedRegion region|
  128        {
  129          if (region.hasFinally)
  130            region.jumpFinallys.add(jump(FOp.JumpFinally))
  131        }
  132  
  133        // generate leave instruction and register for backpatch
  134        if (leavesToReturn == null) leavesToReturn = Int[,]
  135        leavesToReturn.add(jump(FOp.Leave))
  136        return
  137      }
  138  
  139      // process as normal return
  140      if (stmt.expr != null)
  141      {
  142        expr(stmt.expr)
  143        op(FOp.ReturnObj)
  144      }
  145      else
  146      {
  147        op(FOp.ReturnVoid)
  148      }
  149    }
  150  
  151    private Void throwStmt(ThrowStmt stmt)
  152    {
  153      expr(stmt.exception)
  154      op(FOp.Throw)
  155    }
  156  
  157    private Void localVarDefStmt(LocalDefStmt stmt)
  158    {
  159      if (stmt.isCatchVar)
  160      {
  161        op(FOp.CatchErrStart, fpod.addTypeRef(stmt.ctype))
  162        op(FOp.StoreVar,      stmt.var.register)
  163      }
  164      else if (stmt.init != null)
  165      {
  166        expr(stmt.init)
  167      }
  168    }
  169  
  170  //////////////////////////////////////////////////////////////////////////
  171  // Loops
  172  //////////////////////////////////////////////////////////////////////////
  173  
  174    private Void whileStmt(WhileStmt stmt)
  175    {
  176      // push myself onto the loop stack so that breaks
  177      // and continues can register for backpatching
  178      loop := Loop.make(stmt)
  179      loopStack.push(loop)
  180  
  181      // assemble the while loop code
  182      continueLabel := mark
  183      expr(stmt.condition)
  184      breakJump := jump(FOp.JumpFalse)
  185      block(stmt.block)
  186      jump(FOp.Jump, continueLabel)
  187      breakLabel := mark
  188      backpatch(breakJump)
  189  
  190      // backpatch continues/breaks
  191      loop.continues.each |Int pos| { backpatch(pos, continueLabel) }
  192      loop.breaks.each |Int pos| { backpatch(pos, breakLabel) }
  193  
  194      // pop loop from stack
  195      loopStack.pop
  196  
  197      // TODO - the fcode will often contain Jumps to Jumps which can be optimized
  198    }
  199  
  200    private Void forStmt(ForStmt stmt)
  201    {
  202      breakJump := -1
  203  
  204      // push myself onto the loop stack so that breaks
  205      // and continues can register for backpatching
  206      loop := Loop.make(stmt)
  207      loopStack.push(loop)
  208  
  209      // assemble init if available
  210      if (stmt.init != null) this.stmt(stmt.init)
  211  
  212      // assemble the for loop code
  213      condLabel := mark
  214      if (stmt.condition != null)
  215      {
  216        expr(stmt.condition)
  217        breakJump = jump(FOp.JumpFalse)
  218      }
  219      block(stmt.block)
  220      updateLabel := mark
  221      if (stmt.update != null) expr(stmt.update)
  222      jump(FOp.Jump, condLabel)
  223      endLabel := mark
  224      if (breakJump != -1) backpatch(breakJump, endLabel)
  225  
  226      // backpatch continues/breaks
  227      loop.continues.each |Int pos| { backpatch(pos, updateLabel) }
  228      loop.breaks.each |Int pos| { backpatch(pos, endLabel) }
  229  
  230      // pop loop from stack
  231      loopStack.pop
  232  
  233      // TODO - the fcode will often contain Jumps to Jumps which can be optimized
  234    }
  235  
  236    private Void breakOrContinueStmt(Stmt stmt)
  237    {
  238      // associated loop should be top of stack
  239      loop := loopStack.peek
  240      if (loop.stmt !== stmt->loop)
  241        throw err("Internal compiler error", stmt.location)
  242  
  243      // if we are inside a protection region which was pushed onto
  244      // my loop's own stack that means this break or continue
  245      // needs to jump out of the protected region - that requires
  246      // calling each region's finally block and using a "leave"
  247      // instruction rather than a standard "jump"
  248      Int toBackpatch := null
  249      if (!loop.protectedRegions.isEmpty)
  250      {
  251        // jump to any finally blocks we are inside
  252        loop.protectedRegions.eachr |ProtectedRegion region|
  253        {
  254          if (region.hasFinally)
  255            region.jumpFinallys.add(jump(FOp.JumpFinally))
  256        }
  257  
  258        // generate leave instruction
  259        toBackpatch = jump(FOp.Leave)
  260      }
  261      else
  262      {
  263        // generate standard jump instruction
  264        toBackpatch = jump(FOp.Jump)
  265      }
  266  
  267      // register for backpatch
  268      if (stmt.id === StmtId.breakStmt)
  269        loop.breaks.add(toBackpatch)
  270      else
  271        loop.continues.add(toBackpatch)
  272    }
  273  
  274  //////////////////////////////////////////////////////////////////////////
  275  // Switch
  276  //////////////////////////////////////////////////////////////////////////
  277  
  278    private Void switchStmt(SwitchStmt stmt)
  279    {
  280      // A table switch is a series of contiguous (or near contiguous)
  281      // cases which can be represented an offset into a jump table.
  282      minMax := computeTableRange(stmt)
  283      if (minMax != null)
  284        tableSwitchStmt(stmt, minMax[0], minMax[1])
  285      else
  286        equalsSwitchStmt(stmt)
  287    }
  288  
  289    **
  290    ** Compute the range of this switch and return as a list of '[min, max]'
  291    ** if the switch is a candidate for a table switch as a series of
  292    ** contiguous (or near contiguous) cases which can be represented an
  293    ** offset into a jump table.  Return null if the switch is not numeric
  294    ** or too sparse to use as a table switch.
  295    **
  296    private Int[] computeTableRange(SwitchStmt stmt)
  297    {
  298      // we only compute ranges for Ints and Enums
  299      ctype := stmt.condition.ctype
  300      if (!ctype.isInt && !ctype.isEnum)
  301        return null
  302  
  303      // now we need to determine contiguous range
  304      min := 2147483647
  305      max := -2147483648
  306      count := 0
  307      try
  308      {
  309        stmt.cases.each |Case c|
  310        {
  311          for (i:=0; i<c.cases.size; ++i)
  312          {
  313            count++
  314            expr := c.cases[i]
  315            literal := expr.asTableSwitchCase
  316            if (literal == null) throw CompilerErr.make("return null", c.location)
  317            if (literal < min) min = literal
  318            if (literal > max) max = literal
  319          }
  320        }
  321      }
  322      catch (CompilerErr e)
  323      {
  324        return null
  325      }
  326  
  327      // if no cases, then don't use tableswitch
  328      if (count == 0) return null
  329  
  330      // enums and anything with less than 32 jumps is immediately
  331      // allowed, otherwise base the table on a percentage of count
  332      delta := max - min
  333      if (ctype.isEnum || delta < 32 || count*32 > delta)
  334        return [min,max]
  335      else
  336        return null
  337    }
  338  
  339    private Void tableSwitchStmt(SwitchStmt stmt, Int min, Int max)
  340    {
  341      stmt.isTableswitch = true
  342      isEnum := stmt.condition.ctype.isEnum
  343  
  344      // push condition onto the stack
  345      expr(stmt.condition)
  346  
  347      // if this is an enum get ordinal on the stack
  348      if (isEnum)
  349        op(FOp.CallVirtual, fpod.addMethodRef(ns.enumOrdinal))
  350  
  351      // if min is not zero, then do a subtraction so that
  352      // our condition is a zero based index into the jump table
  353      if (min != 0)
  354      {
  355        op(FOp.LoadInt, fpod.ints.add(-min))
  356        op(FOp.CallVirtual, fpod.addMethodRef(ns.intPlus))
  357      }
  358  
  359      // now allocate our jump table
  360      count := max - min + 1
  361      jumps := Case[,]
  362      jumps.size = count
  363  
  364      // walk thru each case, and map the jump offset to a block
  365      stmt.cases.each |Case c|
  366      {
  367        for (i:=0; i<c.cases.size; ++i)
  368        {
  369          expr    := c.cases[i]
  370          literal := expr.asTableSwitchCase
  371          offset  := literal - min
  372          jumps[offset] = c
  373        }
  374      }
  375  
  376      // now write the switch bytecodes
  377      op(FOp.Switch)
  378      code.writeI2(count)
  379      jumpStart := code.size
  380      fill := count*2
  381      fill.times |,| { code.write(0xff) }  // we'll backpatch the jump offsets last
  382  
  383      // default block goes first - it's the switch fall
  384      // thru, save offset to back patch jump
  385      defaultStart := mark
  386      defaultEnd := switchBlock(stmt.defaultBlock)
  387  
  388      // now write each case block
  389      caseEnds := Int[,]
  390      caseEnds.size = stmt.cases.size
  391      stmt.cases.each |Case c, Int i|
  392      {
  393        c.startOffset = code.size
  394        caseEnds[i] = switchBlock(c.block)
  395      }
  396  
  397      // backpatch the jump table
  398      end := code.size
  399      code.seek(jumpStart)
  400      jumps.each |Case c, Int i|
  401      {
  402        if (c == null)
  403          code.writeI2(defaultStart)
  404        else
  405          code.writeI2(c.startOffset)
  406      }
  407      code.seek(end)
  408  
  409      // backpatch all the case blocks to jump here when done
  410      if (defaultEnd != -1) backpatch(defaultEnd)
  411      caseEnds.each |Int pos|
  412      {
  413        if (pos != -1) backpatch(pos)
  414      }
  415    }
  416  
  417    private Void equalsSwitchStmt(SwitchStmt stmt)
  418    {
  419      stmt.isTableswitch = false
  420  
  421      // push condition onto the stack
  422      expr(stmt.condition)
  423  
  424      // walk thru each case, keeping track of all the
  425      // places we need to backpatch when cases match
  426      jumpPositions := Int[,]
  427      jumpCases := Case[,]
  428      stmt.cases.each |Case c|
  429      {
  430        for (i:=0; i<c.cases.size; ++i)
  431        {
  432          op(FOp.Dup)
  433          expr(c.cases[i])
  434          op(FOp.CmpEQ) // TODO eq/jump combo?
  435          jumpPositions.add(jump(FOp.JumpTrue))
  436          jumpCases.add(c)
  437        }
  438      }
  439  
  440      // default block goes first - it's the switch fall
  441      // thru, save offset to back patch jump
  442      defaultStart := mark
  443      defaultEnd := switchBlock(stmt.defaultBlock, true)
  444  
  445      // now write each case block
  446      caseEnds := Int[,]
  447      caseEnds.size = stmt.cases.size
  448      stmt.cases.each |Case c, Int i|
  449      {
  450        c.startOffset = code.size
  451        caseEnds[i] = switchBlock(c.block, true)
  452      }
  453  
  454      // backpatch the jump table
  455      end := code.size
  456      jumpPositions.each |Int pos, Int i|
  457      {
  458        backpatch(pos, jumpCases[i].startOffset)
  459      }
  460      code.seek(end)
  461  
  462      // backpatch all the case blocks to jump here when done
  463      if (defaultEnd != -1) backpatch(defaultEnd)
  464      caseEnds.each |Int pos|
  465      {
  466        if (pos != -1) backpatch(pos)
  467      }
  468    }
  469  
  470    private Int switchBlock(Block block, Bool pop := false)
  471    {
  472      if (pop) op(FOp.Pop);
  473      if (block != null)
  474      {
  475        this.block(block)
  476        if (block.isExit) return -1
  477      }
  478      return jump(FOp.Jump)
  479    }
  480  
  481  //////////////////////////////////////////////////////////////////////////
  482  // Try
  483  //////////////////////////////////////////////////////////////////////////
  484  
  485    private Bool inProtectedRegion()
  486    {
  487      return protectedRegions != null && !protectedRegions.isEmpty
  488    }
  489  
  490    private Void tryStmt(TryStmt stmt)
  491    {
  492      // enter a "protected region" which means that we can't
  493      // jump or return out of this region directly - we have to
  494      // use a special "leave" jump of the protected region
  495      if (protectedRegions == null) protectedRegions = ProtectedRegion[,]
  496      region := ProtectedRegion.make(stmt)
  497      protectedRegions.push(region)
  498      if (!loopStack.isEmpty) loopStack.peek.protectedRegions.push(region)
  499  
  500      // assemble body of try block
  501      start := mark
  502      block(stmt.block)
  503      end := mark
  504  
  505      // if the block isn't guaranteed to exit:
  506      //  1) if we have a finally, then jump to finally
  507      //  2) jump over catch blocks
  508      tryDone := -1
  509      finallyStart := -1
  510      if (!stmt.block.isExit)
  511      {
  512        if (region.hasFinally)
  513        {
  514          region.jumpFinallys.add(jump(FOp.JumpFinally))
  515          end = mark
  516        }
  517        tryDone = jump(FOp.Leave)
  518      }
  519  
  520      // assemble catch blocks
  521      catchDones := Int[,]
  522      catchDones.size = stmt.catches.size
  523      stmt.catches.each |Catch c, Int i|
  524      {
  525        catchDones[i] = tryCatch(c, start, end, region)
  526      }
  527  
  528      // assemble finally block
  529      if (region.hasFinally)
  530      {
  531        // wrap try block and each catch block with catch all to finally
  532        addToErrTable(start, end, mark, null)
  533        stmt.catches.each |Catch c|
  534        {
  535          addToErrTable(c.start, c.end, mark, null)
  536        }
  537  
  538        // handler code
  539        region.jumpFinallys.each |Int pos| { backpatch(pos) }
  540        op(FOp.FinallyStart)
  541        block(stmt.finallyBlock)
  542        op(FOp.FinallyEnd)
  543      }
  544  
  545      // mark next statement as jump destination for try block
  546      if (tryDone != -1) backpatch(tryDone)
  547      catchDones.each |Int pos| { if (pos != -1) backpatch(pos) }
  548  
  549      // leave protected region
  550      if (!loopStack.isEmpty) loopStack.peek.protectedRegions.pop
  551      protectedRegions.pop
  552    }
  553  
  554    private Int tryCatch(Catch c, Int start, Int end, ProtectedRegion region)
  555    {
  556      // assemble catch block - if there isn't a local variable
  557      // we emit the CatchAllStart, otherwise the block will
  558      // start off with a LocalVarDef which will write out the
  559      // CatchErrStart opcode
  560      handler := mark
  561      c.start = mark
  562      if (c.errVariable == null) op(FOp.CatchAllStart)
  563      block(c.block)
  564      done := -1
  565      if (!c.block.isExit)
  566      {
  567        if (region.hasFinally)
  568          region.jumpFinallys.add(jump(FOp.JumpFinally))
  569  
  570        done = jump(FOp.Leave)
  571      }
  572      c.end = mark
  573      op(FOp.CatchEnd)
  574  
  575      // fill in err table
  576      addToErrTable(start, end, handler, c.errType)
  577  
  578      // return position to backpatch
  579      return done
  580    }
  581  
  582    private Void addToErrTable(Int start, Int end, Int handler, CType errType)
  583    {
  584      // catch all is implicitly a catch for sys::Err
  585      if (errType == null) errType = ns.errType
  586  
  587      // add to err table buffer
  588      errCount++
  589      errTable.writeI2(start)
  590      errTable.writeI2(end)
  591      errTable.writeI2(handler)
  592      errTable.writeI2(fpod.addTypeRef(errType))
  593    }
  594  
  595  //////////////////////////////////////////////////////////////////////////
  596  // Expressions
  597  //////////////////////////////////////////////////////////////////////////
  598  
  599    Void expr(Expr expr)
  600    {
  601      line(expr.location)
  602      switch (expr.id)
  603      {
  604        case ExprId.nullLiteral:     nullLiteral
  605        case ExprId.trueLiteral:
  606        case ExprId.falseLiteral:    boolLiteral((LiteralExpr)expr)
  607        case ExprId.intLiteral:      intLiteral((LiteralExpr)expr)
  608        case ExprId.floatLiteral:    floatLiteral((LiteralExpr)expr)
  609        case ExprId.strLiteral:      strLiteral((LiteralExpr)expr)
  610        case ExprId.durationLiteral: durationLiteral((LiteralExpr)expr)
  611        case ExprId.uriLiteral:      uriLiteral((LiteralExpr)expr)
  612        case ExprId.typeLiteral:     typeLiteral((LiteralExpr)expr)
  613        case ExprId.rangeLiteral:    rangeLiteral((RangeLiteralExpr)expr)
  614        case ExprId.listLiteral:     listLiteral((ListLiteralExpr)expr)
  615        case ExprId.mapLiteral:      mapLiteral((MapLiteralExpr)expr)
  616        case ExprId.simpleLiteral:   simpleLiteral((SimpleLiteralExpr)expr)
  617        case ExprId.boolNot:         not((UnaryExpr)expr)
  618        case ExprId.cmpNull:         cmpNull((UnaryExpr)expr)
  619        case ExprId.cmpNotNull:      cmpNotNull((UnaryExpr)expr)
  620        case ExprId.assign:          assign((BinaryExpr)expr)
  621        case ExprId.same:            same((BinaryExpr)expr)
  622        case ExprId.notSame:         notSame((BinaryExpr)expr)
  623        case ExprId.boolOr:          or((CondExpr)expr, null)
  624        case ExprId.boolAnd:         and((CondExpr)expr, null)
  625        case ExprId.isExpr:          isExpr((TypeCheckExpr)expr)
  626        case ExprId.asExpr:          asExpr((TypeCheckExpr)expr)
  627        case ExprId.localVar:
  628        case ExprId.thisExpr:
  629        case ExprId.superExpr:       loadLocalVar((LocalVarExpr)expr)
  630        case ExprId.call:            call((CallExpr)expr)
  631        case ExprId.shortcut:        shortcut((ShortcutExpr)expr)
  632        case ExprId.field:           loadField((FieldExpr)expr)
  633        case ExprId.cast:            cast((TypeCheckExpr)expr)
  634        case ExprId.closure:         this.expr(((ClosureExpr)expr).substitute)
  635        case ExprId.ternary:         ternary((TernaryExpr)expr)
  636        case ExprId.withBlock:       withBlock((WithBlockExpr)expr)
  637        case ExprId.withBase:        return
  638        case ExprId.staticTarget:    return
  639        default:                     throw Err.make(expr.id.toStr)
  640      }
  641    }
  642  
  643  //////////////////////////////////////////////////////////////////////////
  644  // Literals
  645  //////////////////////////////////////////////////////////////////////////
  646  
  647    private Void nullLiteral()
  648    {
  649      op(FOp.LoadNull)
  650    }
  651  
  652    private Void boolLiteral(LiteralExpr expr)
  653    {
  654      if (expr.val == true)
  655        op(FOp.LoadTrue)
  656      else
  657        op(FOp.LoadFalse)
  658    }
  659  
  660    private Void intLiteral(LiteralExpr expr)
  661    {
  662      op(FOp.LoadInt, fpod.ints.add(expr.val))
  663    }
  664  
  665    private Void floatLiteral(LiteralExpr expr)
  666    {
  667      op(FOp.LoadFloat, fpod.floats.add(expr.val))
  668    }
  669  
  670    private Void strLiteral(LiteralExpr expr)
  671    {
  672      op(FOp.LoadStr, fpod.strs.add(expr.val))
  673    }
  674  
  675    private Void durationLiteral(LiteralExpr expr)
  676    {
  677      op(FOp.LoadDuration, fpod.durations.add(expr.val))
  678    }
  679  
  680    private Void uriLiteral(LiteralExpr expr)
  681    {
  682      op(FOp.LoadUri, fpod.uris.add(expr.val))
  683    }
  684  
  685    private Void typeLiteral(LiteralExpr expr)
  686    {
  687      val := (CType)expr.val
  688      op(FOp.LoadType, fpod.addTypeRef(val));
  689    }
  690  
  691    private Void rangeLiteral(RangeLiteralExpr r)
  692    {
  693      expr(r.start);
  694      expr(r.end);
  695      if (r.exclusive)
  696        op(FOp.CallNew, fpod.addMethodRef(ns.rangeMakeExclusive))
  697      else
  698        op(FOp.CallNew, fpod.addMethodRef(ns.rangeMakeInclusive))
  699    }
  700  
  701    private Void listLiteral(ListLiteralExpr list)
  702    {
  703      v := ((ListType)list.ctype).v
  704  
  705      op(FOp.LoadType, fpod.addTypeRef(v));
  706      op(FOp.LoadInt,  fpod.ints.add(list.vals.size))
  707      op(FOp.CallNew,  fpod.addMethodRef(ns.listMake))
  708  
  709      add := fpod.addMethodRef(ns.listAdd)
  710      for (i:=0; i<list.vals.size; ++i)
  711      {
  712        expr(list.vals[i])
  713        op(FOp.CallVirtual, add)
  714      }
  715    }
  716  
  717    private Void mapLiteral(MapLiteralExpr map)
  718    {
  719      op(FOp.LoadType, fpod.addTypeRef(map.ctype))
  720      op(FOp.CallNew,  fpod.addMethodRef(ns.mapMake))
  721  
  722      set := fpod.addMethodRef(ns.mapSet)
  723      for (i:=0; i<map.keys.size; ++i)
  724      {
  725        expr(map.keys[i])
  726        expr(map.vals[i])
  727        op(FOp.CallVirtual, set)
  728      }
  729    }
  730  
  731    private Void simpleLiteral(SimpleLiteralExpr simple)
  732    {
  733      if (!simple.method.isStatic)
  734        throw err("parse not static", simple.location)
  735  
  736      expr(simple.arg)
  737      op(FOp.CallStatic, fpod.addMethodRef(simple.method))
  738    }
  739  
  740  //////////////////////////////////////////////////////////////////////////
  741  // UnaryExpr
  742  //////////////////////////////////////////////////////////////////////////
  743  
  744    private Void not(UnaryExpr unary)
  745    {
  746      expr(unary.operand)
  747      op(FOp.CallVirtual, fpod.addMethodRef(ns.boolNot))
  748    }
  749  
  750    private Void cmpNull(UnaryExpr unary)
  751    {
  752      expr(unary.operand)
  753      op(FOp.CmpNull)
  754    }
  755  
  756    private Void cmpNotNull(UnaryExpr unary)
  757    {
  758      expr(unary.operand)
  759      op(FOp.CmpNotNull)
  760    }
  761  
  762  //////////////////////////////////////////////////////////////////////////
  763  // BinaryExpr
  764  //////////////////////////////////////////////////////////////////////////
  765  
  766    private Void same(BinaryExpr binary)
  767    {
  768      if (binary.lhs.id === ExprId.nullLiteral)
  769      {
  770        expr(binary.rhs)
  771        op(FOp.CmpNull)
  772      }
  773      else if (binary.rhs.id === ExprId.nullLiteral)
  774      {
  775        expr(binary.lhs)
  776        op(FOp.CmpNull)
  777      }
  778      else
  779      {
  780        expr(binary.lhs)
  781        expr(binary.rhs)
  782        op(FOp.CmpSame)
  783      }
  784    }
  785  
  786    private Void notSame(BinaryExpr binary)
  787    {
  788      if (binary.lhs.id === ExprId.nullLiteral)
  789      {
  790        expr(binary.rhs)
  791        op(FOp.CmpNotNull)
  792      }
  793      else if (binary.rhs.id === ExprId.nullLiteral)
  794      {
  795        expr(binary.lhs)
  796        op(FOp.CmpNotNull)
  797      }
  798      else
  799      {
  800        expr(binary.lhs)
  801        expr(binary.rhs)
  802        op(FOp.CmpNotSame)
  803      }
  804    }
  805  
  806  //////////////////////////////////////////////////////////////////////////
  807  // CondExpr
  808  //////////////////////////////////////////////////////////////////////////
  809  
  810    private Void cond(CondExpr expr, Cond cond)
  811    {
  812      switch (expr.id)
  813      {
  814        case ExprId.boolOr:  or(expr, cond)
  815        case ExprId.boolAnd: and(expr, cond)
  816        default:             throw Err.make(expr.id.toStr)
  817      }
  818    }
  819  
  820    private Void or(CondExpr expr, Cond cond)
  821    {
  822      // if cond is null this is a top level expr which means
  823      // the result is to push true or false onto the stack;
  824      // otherwise our only job is to do the various jumps if
  825      // true or fall-thru if true (used with if statement)
  826      // NOTE: this code could be further optimized because
  827      //   it doesn't optimize "a && b || c && c"
  828      topLevel := cond == null
  829      if (topLevel) cond = Cond.make
  830  
  831      // perform short circuit logical-or
  832      expr.operands.each |Expr operand, Int i|
  833      {
  834        this.expr(operand)
  835        if (i < expr.operands.size-1)
  836          cond.jumpTrues.add(jump(FOp.JumpTrue))
  837        else
  838          cond.jumpFalses.add(jump(FOp.JumpFalse))
  839      }
  840  
  841      // if top level push true/false onto stack
  842      if (topLevel) condEnd(cond)
  843    }
  844  
  845    private Void and(CondExpr expr, Cond cond)
  846    {
  847      // if cond is null this is a top level expr which means
  848      // the result is to push true or false onto the stack;
  849      // otherwise our only job is to do the various jumps if
  850      // true or fall-thru if true (used with if statement)
  851      // NOTE: this code could be further optimized because
  852      //   it doesn't optimize "a && b || c && c"
  853      topLevel := cond == null
  854      if (topLevel) cond = Cond.make
  855  
  856      // perform short circuit logical-and
  857      expr.operands.each |Expr operand|
  858      {
  859        this.expr(operand)
  860        cond.jumpFalses.add(jump(FOp.JumpFalse))
  861      }
  862  
  863      // if top level push true/false onto stack
  864      if (topLevel) condEnd(cond)
  865    }
  866  
  867    private Void condEnd(Cond cond)
  868    {
  869      // true if always fall-thru
  870      cond.jumpTrues.each |Int pos| { backpatch(pos) }
  871      op(FOp.LoadTrue)
  872      end := jump(FOp.Jump)
  873  
  874      // false
  875      cond.jumpFalses.each |Int pos| { backpatch(pos) }
  876      op(FOp.LoadFalse)
  877  
  878      backpatch(end)
  879    }
  880  
  881  //////////////////////////////////////////////////////////////////////////
  882  // Type Checks
  883  //////////////////////////////////////////////////////////////////////////
  884  
  885    private Void isExpr(TypeCheckExpr tc)
  886    {
  887      expr(tc.target)
  888      op(FOp.Is, fpod.addTypeRef(tc.check))
  889    }
  890  
  891    private Void asExpr(TypeCheckExpr tc)
  892    {
  893      expr(tc.target)
  894      op(FOp.As, fpod.addTypeRef(tc.check))
  895    }
  896  
  897    private Void cast(TypeCheckExpr tc)
  898    {
  899      expr(tc.target)
  900      op(FOp.Cast, fpod.addTypeRef(tc.check))
  901    }
  902  
  903  //////////////////////////////////////////////////////////////////////////
  904  // Ternary
  905  //////////////////////////////////////////////////////////////////////////
  906  
  907    private Void ternary(TernaryExpr ternary)
  908    {
  909      expr(ternary.condition)
  910      falseLabel := jump(FOp.JumpFalse)
  911      expr(ternary.trueExpr)
  912      endLabel := jump(FOp.Jump)
  913      backpatch(falseLabel)
  914      expr(ternary.falseExpr)
  915      backpatch(endLabel)
  916    }
  917  
  918  //////////////////////////////////////////////////////////////////////////
  919  // WithBlock
  920  //////////////////////////////////////////////////////////////////////////
  921  
  922    private Void withBlock(WithBlockExpr withBlock)
  923    {
  924      expr(withBlock.base)
  925      withBlock.subs.each |Expr sub|
  926      {
  927        op(FOp.Dup)
  928        expr(sub)
  929        if (sub.leave) throw Err.make("should never leave with expr " + sub.location)
  930      }
  931    }
  932  
  933  //////////////////////////////////////////////////////////////////////////
  934  // Assign
  935  //////////////////////////////////////////////////////////////////////////
  936  
  937    **
  938    ** Simple assignment using =
  939    **
  940    private Void assign(BinaryExpr expr)
  941    {
  942      switch (expr.lhs.id)
  943      {
  944        case ExprId.localVar: assignLocalVar(expr)
  945        case ExprId.field:    assignField(expr)
  946        defaultthrow err("Internal compiler error", expr.location)
  947      }
  948    }
  949  
  950  //////////////////////////////////////////////////////////////////////////
  951  // Local Var
  952  //////////////////////////////////////////////////////////////////////////
  953  
  954    private Void loadLocalVar(LocalVarExpr var)
  955    {
  956      op(FOp.LoadVar, var.register)
  957    }
  958  
  959    private Void storeLocalVar(LocalVarExpr var)
  960    {
  961      op(FOp.StoreVar, var.register);
  962    }
  963  
  964    private Void assignLocalVar(BinaryExpr assign)
  965    {
  966      expr(assign.rhs)
  967      if (assign.leave) op(FOp.Dup)
  968      storeLocalVar((LocalVarExpr)assign.lhs)
  969    }
  970  
  971  //////////////////////////////////////////////////////////////////////////
  972  // Field
  973  //////////////////////////////////////////////////////////////////////////
  974  
  975    private Void loadField(FieldExpr fexpr, Bool dupTarget := false)
  976    {
  977      field := fexpr.field
  978  
  979      if (fexpr.target != null)
  980      {
  981        expr(fexpr.target);
  982        if (dupTarget) op(FOp.Dup)
  983      }
  984  
  985      if (fexpr.useAccessor)
  986      {
  987        getter := field.getter // if null then bug in useAccessor
  988        index := fpod.addMethodRef(getter)
  989        if (field.parent.isMixin)
  990        {
  991          if (getter.isStatic)
  992            op(FOp.CallMixinStatic, index)
  993          else
  994            op(FOp.CallMixinVirtual, index)
  995        }
  996        else
  997        {
  998          if (getter.isStatic)
  999            op(FOp.CallStatic, index)
 1000          else if (fexpr.target.id == ExprId.superExpr)
 1001            op(FOp.CallNonVirtual, index)
 1002          else
 1003            op(FOp.CallVirtual, index)
 1004        }
 1005  
 1006        if (field.isCovariant)
 1007        {
 1008          op(FOp.Cast, fpod.addTypeRef(field.fieldType))
 1009        }
 1010      }
 1011      else
 1012      {
 1013        index := fpod.addFieldRef(field)
 1014        if (field.parent.isMixin)
 1015        {
 1016          if (field.isStatic)
 1017            op(FOp.LoadMixinStatic, index)
 1018          else
 1019            throw err("LoadMixinInstance", fexpr.location)
 1020        }
 1021        else
 1022        {
 1023          if (field.isStatic)
 1024            op(FOp.LoadStatic, index)
 1025          else
 1026            op(FOp.LoadInstance, index)
 1027        }
 1028      }
 1029    }
 1030  
 1031    private Void assignField(BinaryExpr assign)
 1032    {
 1033      lhs := (FieldExpr)assign.lhs
 1034      isInstanceField := !lhs.field.isStatic;  // used to determine how to duplicate
 1035  
 1036      if (lhs .target != null) expr(lhs.target)
 1037      expr(assign.rhs);
 1038      if (assign.leave)
 1039      {
 1040        op(FOp.Dup)
 1041        if (isInstanceField)
 1042          op(FOp.StoreVar, assign.tempVar.register)
 1043      }
 1044      storeField(lhs)
 1045      if (assign.leave && isInstanceField)
 1046      {
 1047        op(FOp.LoadVar, assign.tempVar.register)
 1048      }
 1049    }
 1050  
 1051    private Void storeField(FieldExpr fexpr)
 1052    {
 1053      field := fexpr.field
 1054      if (fexpr.useAccessor)
 1055      {
 1056        setter := field.setter  // if null then bug in useAccessor
 1057        index := fpod.addMethodRef(setter)
 1058  
 1059        if (field.parent.isMixin) // TODO
 1060        {
 1061          if (setter.isStatic)
 1062            op(FOp.CallMixinStatic, index)
 1063          else
 1064            op(FOp.CallMixinVirtual, index)
 1065        }
 1066        else
 1067        {
 1068          if (setter.isStatic)
 1069            op(FOp.CallStatic, index)
 1070          else if (fexpr.target.id == ExprId.superExpr)
 1071            op(FOp.CallNonVirtual, index)
 1072          else
 1073            op(FOp.CallVirtual, index)
 1074        }
 1075      }
 1076      else
 1077      {
 1078        index := fpod.addFieldRef(field)
 1079  
 1080        if (field.parent.isMixin)
 1081        {
 1082          if (field.isStatic)
 1083            op(FOp.StoreMixinStatic, index)
 1084          else
 1085            throw err("StoreMixinInstance", fexpr.location)
 1086        }
 1087        else
 1088        {
 1089          if (field.isStatic)
 1090            op(FOp.StoreStatic, index)
 1091          else
 1092            op(FOp.StoreInstance, index)
 1093        }
 1094      }
 1095    }
 1096  
 1097  //////////////////////////////////////////////////////////////////////////
 1098  // Call
 1099  //////////////////////////////////////////////////////////////////////////
 1100  
 1101    private Void call(CallExpr call, Bool leave := call.leave)
 1102    {
 1103      if (call.isDynamic)
 1104      {
 1105        dynamicCall(call)
 1106      }
 1107      else
 1108      {
 1109        prepareCall(call)
 1110        invokeCall(call, leave)
 1111      }
 1112    }
 1113  
 1114    private Void dynamicCall(CallExpr call)
 1115    {
 1116      // target
 1117      expr(call.target)
 1118  
 1119      // name str literal
 1120      op(FOp.LoadStr, fpod.strs.add(call.name))
 1121  
 1122      // args Obj[]
 1123      op(FOp.LoadInt,  fpod.ints.add(call.args.size))
 1124      op(FOp.CallNew,  fpod.addMethodRef(ns.listMakeObj))
 1125      add := fpod.addMethodRef(ns.listAdd)
 1126      call.args.each |Expr arg|
 1127      {
 1128        expr(arg)
 1129        op(FOp.CallVirtual, add)
 1130      }
 1131  
 1132      // Obj.trap
 1133      op(FOp.CallVirtual, fpod.addMethodRef(ns.objTrap))
 1134  
 1135      // pop return if no leave
 1136      if (!call.leave) op(FOp.Pop)
 1137    }
 1138  
 1139    private Void prepareCall(CallExpr call)
 1140    {
 1141      if (call.target != null) expr(call.target)
 1142      call.args.each |Expr arg| { expr(arg) }
 1143    }
 1144  
 1145    private Void invokeCall(CallExpr call, Bool leave := call.leave)
 1146    {
 1147      m := call.method
 1148      index := fpod.addMethodRef(m, call.args.size)
 1149  
 1150      // write CallVirtual, CallNonVirtual, CallStatic, CallNew, or CallCtor;
 1151      // note that if a constructor call has a target (this or super), then it
 1152      // is a CallCtor instance call because we don't want to allocate
 1153      // a new instance
 1154      if (m.parent.isMixin)
 1155      {
 1156        if (m.isStatic)
 1157          op(FOp.CallMixinStatic, index)
 1158        else if (call.target.id == ExprId.superExpr)
 1159          op(FOp.CallMixinNonVirtual, index)
 1160        else
 1161          op(FOp.CallMixinVirtual, index)
 1162      }
 1163      else if (m.isStatic)
 1164      {
 1165        op(FOp.CallStatic, index)
 1166      }
 1167      else if (m.isCtor)
 1168      {
 1169        if (call.target == null || call.target.id == ExprId.staticTarget)
 1170          op(FOp.CallNew, index)
 1171        else
 1172          op(FOp.CallCtor, index)
 1173      }
 1174      else
 1175      {
 1176        // because CallNonVirtual maps to Java's invokespecial, we can't
 1177        // use it for calls outside of the class (consider it like calling
 1178        // protected method)
 1179        targetId := call.target.id
 1180        if (targetId == ExprId.superExpr || (targetId == ExprId.thisExpr && !m.isVirtual))
 1181          op(FOp.CallNonVirtual, index)
 1182        else
 1183          op(FOp.CallVirtual, index)
 1184      }
 1185  
 1186      // if we are leaving a value on the stack of a method which
 1187      // has a parameterized return value or is covariant, then we
 1188      // need to insert a cast operation
 1189      //   Int.toStr    -> non-generic - no cast
 1190      //   Str[].toStr  -> return isn't parameterized - no cast
 1191      //   Str[].get()  -> actual return is Obj, but we want Str - cast
 1192      //   covariant    -> actual call is against inheritedReturnType
 1193      if (leave)
 1194      {
 1195        if (m.isParameterized)
 1196        {
 1197          ret := m.generic.returnType
 1198          if (ret.isGenericParameter)
 1199            op(FOp.Cast, fpod.addTypeRef(m.returnType))
 1200        }
 1201        else if (m.isCovariant)
 1202        {
 1203          op(FOp.Cast, fpod.addTypeRef(m.returnType))
 1204        }
 1205      }
 1206  
 1207      // if the method left a value on the stack, and we
 1208      // aren't going to use it, then pop it off
 1209      if (!leave)
 1210      {
 1211        // note we need to use the actual method signature (not parameterized)
 1212        x := m.isParameterized ? m.generic : m
 1213        if (!x.returnType.isVoid || x.isCtor)
 1214          op(FOp.Pop)
 1215      }
 1216    }
 1217  
 1218  //////////////////////////////////////////////////////////////////////////
 1219  // Shortcut
 1220  //////////////////////////////////////////////////////////////////////////
 1221  
 1222    private Void shortcut(ShortcutExpr call)
 1223    {
 1224      // handle comparisions as special opcodes
 1225      switch (call.opToken)
 1226      {
 1227        case Token.eq:     prepareCall(call); op(FOp.CmpEQ)return
 1228        case Token.notEq:  prepareCall(call); op(FOp.CmpNE)return
 1229        case Token.cmp:    prepareCall(call); op(FOp.Cmp);   return
 1230        case Token.lt:     prepareCall(call); op(FOp.CmpLT)return
 1231        case Token.ltEq:   prepareCall(call); op(FOp.CmpLE)return
 1232        case Token.gt:     prepareCall(call); op(FOp.CmpGT)return
 1233        case Token.gtEq:   prepareCall(call); op(FOp.CmpGE)return
 1234      }
 1235  
 1236      // always check string concat first since it can
 1237      // have string on either left or right hand side
 1238      if (call.isStrConcat)
 1239      {
 1240        addStr(call, true)
 1241        return
 1242      }
 1243  
 1244      // if assignment we need to do a bunch of special processing
 1245      if (call.isAssign)
 1246      {
 1247        shortcutAssign(call)
 1248        return
 1249      }
 1250  
 1251      // just process as normal call
 1252      this.call(call)
 1253    }
 1254  
 1255    **
 1256    ** This method is used for complex assignments: prefix/postfix
 1257    ** increment and special dual assignment operators like "+=".
 1258    **
 1259    private Void shortcutAssign(ShortcutExpr c)
 1260    {
 1261      var := c.target
 1262      leaveUsingTemp := false
 1263  
 1264      // load the variable
 1265      switch (var.id)
 1266      {
 1267        case ExprId.localVar:
 1268          loadLocalVar((LocalVarExpr)var)
 1269        case ExprId.field:
 1270          fexpr := (FieldExpr)var
 1271          loadField(fexpr, true) // dup target on stack for upcoming set
 1272          leaveUsingTemp = !fexpr.field.isStatic  // used to determine how to duplicate
 1273        case ExprId.shortcut:
 1274          // since .NET sucks when it comes to stack manipulation,
 1275          // we use two scratch locals to get the stack into the
 1276          // following format:
 1277          //   index  \  used for get
 1278          //   target /
 1279          //   index  \  used for set
 1280          //   target /
 1281          index := (IndexedAssignExpr)c
 1282          get := (ShortcutExpr)index.target
 1283          expr(get.target)  // target
 1284          op(FOp.Dup)
 1285          op(FOp.StoreVar, index.scratchA.register)
 1286          expr(get.args[0]) // index expr
 1287          op(FOp.Dup)
 1288          op(FOp.StoreVar, index.scratchB.register)
 1289          op(FOp.LoadVar, index.scratchA.register)
 1290          op(FOp.LoadVar, index.scratchB.register)
 1291          invokeCall(get, true)
 1292          leaveUsingTemp = true
 1293        default:
 1294          throw err("Internal error", var.location)
 1295      }
 1296  
 1297      // if postfix leave, duplicate value before we preform computation
 1298      if (c.leave && c.isPostfixLeave)
 1299      {
 1300        op(FOp.Dup)
 1301        if (leaveUsingTemp)
 1302          op(FOp.StoreVar, c.tempVar.register)
 1303      }
 1304  
 1305      // load args and invoke call
 1306      c.args.each |Expr arg| { expr(arg) }
 1307      invokeCall(c, true)
 1308  
 1309      // if prefix, duplicate after we've done computation
 1310      if (c.leave && !c.isPostfixLeave)
 1311      {
 1312        op(FOp.Dup)
 1313        if (leaveUsingTemp)
 1314          op(FOp.StoreVar, c.tempVar.register)
 1315      }
 1316  
 1317      // save the variable back
 1318      switch (var.id)
 1319      {
 1320        case ExprId.localVar:
 1321          storeLocalVar((LocalVarExpr)var)
 1322        case ExprId.field:
 1323          storeField((FieldExpr)var)
 1324        case ExprId.shortcut:
 1325          set := (CMethod)c->setMethod
 1326          op(FOp.CallVirtual, fpod.addMethodRef(set, 2))
 1327          if (!set.returnType.isVoid) op(FOp.Pop)
 1328      }
 1329  
 1330      // if field leave, then load back from temp local
 1331      if (c.leave && leaveUsingTemp)
 1332        op(FOp.LoadVar, c.tempVar.register)
 1333    }
 1334  
 1335  //////////////////////////////////////////////////////////////////////////
 1336  // Strings
 1337  //////////////////////////////////////////////////////////////////////////
 1338  
 1339    **
 1340    ** Assemble code to build a string using sys::StrBuf.
 1341    **
 1342    private Void addStr(ShortcutExpr expr, Bool topLevel)
 1343    {
 1344      if (topLevel)
 1345        op(FOp.CallNew, fpod.addMethodRef(ns.strBufMake, 0))
 1346  
 1347      lhs := expr.target
 1348      rhs := expr.args.first
 1349  
 1350      lhsShortcut := lhs as ShortcutExpr
 1351      if (lhsShortcut != null && lhsShortcut.isStrConcat)
 1352      {
 1353        addStr(lhsShortcut, false)
 1354      }
 1355      else
 1356      {
 1357        if (!isEmptyStrLiteral(lhs))
 1358        {
 1359          this.expr(lhs)
 1360          op(FOp.CallVirtual, fpod.addMethodRef(ns.strBufAdd))
 1361        }
 1362      }
 1363  
 1364      if (!isEmptyStrLiteral(rhs))
 1365      {
 1366        this.expr(rhs)
 1367        op(FOp.CallVirtual, fpod.addMethodRef(ns.strBufAdd))
 1368      }
 1369  
 1370      if (topLevel) op(FOp.CallVirtual, fpod.addMethodRef(ns.strBufToStr))
 1371    }
 1372  
 1373    private Bool isEmptyStrLiteral(Expr expr)
 1374    {
 1375      return expr.id === ExprId.strLiteral && expr->val == ""
 1376    }
 1377  
 1378  //////////////////////////////////////////////////////////////////////////
 1379  // Code Buffer
 1380  //////////////////////////////////////////////////////////////////////////
 1381  
 1382    **
 1383    ** Append a opcode with option two byte argument.
 1384    **
 1385    Void op(FOp op, Int arg := null)
 1386    {
 1387      code.write(op.ordinal)
 1388      if (arg != null) code.writeI2(arg)
 1389    }
 1390  
 1391  //////////////////////////////////////////////////////////////////////////
 1392  // Jumps
 1393  //////////////////////////////////////////////////////////////////////////
 1394  
 1395    **
 1396    ** Get the current location as a mark to use for backwards jump.
 1397    **
 1398    private Int mark()
 1399    {
 1400      return code.size
 1401    }
 1402  
 1403    **
 1404    ** Add the specified jump opcode and two bytes for the jump
 1405    ** location.  If a backward jump then pass the mark; if a
 1406    ** a forward jump we return the code pos to backpatch the
 1407    ** mark later.
 1408    **
 1409    private Int jump(FOp op, Int mark := 0xffff)
 1410    {
 1411      this.op(op, mark)
 1412      return code.size-2
 1413    }
 1414  
 1415    **
 1416    ** Backpacth the mark of forward jump using the given
 1417    ** pos which was returned by jump().  If mark is defaulted,
 1418    ** then we use the current instruction as the mark.
 1419    **
 1420    private Void backpatch(Int pos, Int mark := code.size)
 1421    {
 1422      orig := code.pos
 1423      code.seek(pos).writeI2(mark)
 1424      code.seek(orig)
 1425    }
 1426  
 1427  //////////////////////////////////////////////////////////////////////////
 1428  // Utils
 1429  //////////////////////////////////////////////////////////////////////////
 1430  
 1431    **
 1432    ** Finish writing out the exception handling table
 1433    **
 1434    Buf finishCode()
 1435    {
 1436      // if we had to return from a protected region, then now we
 1437      // need to generate the actual return instructions and backpatch
 1438      // all the leaves
 1439      if (leavesToReturn != null)
 1440      {
 1441        leavesToReturn.each |Int pos| { backpatch(pos) }
 1442        if (returnLocal != null)
 1443        {
 1444          op(FOp.LoadVar, returnLocal.register)
 1445          op(FOp.ReturnObj)
 1446        }
 1447        else
 1448        {
 1449          op(FOp.ReturnVoid)
 1450        }
 1451      }
 1452  
 1453      // check final size
 1454      if (code.size >= 0x7fff) throw err("Method too big", location)
 1455      return code
 1456    }
 1457  
 1458    **
 1459    ** Finish writing out the exception handling table
 1460    **
 1461    Buf finishErrTable()
 1462    {
 1463      errTable.seek(0).writeI2(errCount)
 1464      return errTable
 1465    }
 1466  
 1467    **
 1468    ** Finish writing out the line number table
 1469    **
 1470    Buf finishLines()
 1471    {
 1472      lines.seek(0).writeI2(lineCount)
 1473      return lines
 1474    }
 1475  
 1476    **
 1477    ** Map the opcode we are getting ready to add to the specified line number
 1478    **
 1479    private Void line(Location loc)
 1480    {
 1481      line := loc.line
 1482      if (line == null || lastLine == line) return
 1483      lineCount++
 1484      lines.writeI2(code.size)
 1485      lines.writeI2(line)
 1486      lastLine = line
 1487    }
 1488  
 1489  //////////////////////////////////////////////////////////////////////////
 1490  // Fields
 1491  //////////////////////////////////////////////////////////////////////////
 1492  
 1493    Location location
 1494    FPod fpod
 1495    Buf code
 1496    Buf errTable
 1497    Int errCount
 1498    Buf lines
 1499    Int lineCount
 1500    Int lastLine := -1
 1501    Loop[] loopStack
 1502  
 1503    // protected region fields
 1504    ProtectedRegion[] protectedRegions // stack of protection regions
 1505    Int[] leavesToReturn     // list of Leave positions to backpatch
 1506    MethodVar returnLocal    // where we stash return value
 1507  }
 1508  
 1509  **************************************************************************
 1510  ** Loop
 1511  **************************************************************************
 1512  
 1513  class Loop
 1514  {
 1515    new make(Stmt stmt) { this.stmt = stmt }
 1516  
 1517    Stmt stmt                  // WhileStmt or ForStmt
 1518    Int[] breaks := Int[,]     // backpatch positions
 1519    Int[] continues := Int[,]  // backpatch positions
 1520    ProtectedRegion[] protectedRegions := ProtectedRegion[,] // stack
 1521  }
 1522  
 1523  **************************************************************************
 1524  ** ProtectedRegion
 1525  **************************************************************************
 1526  
 1527  class ProtectedRegion
 1528  {
 1529    new make(TryStmt stmt)
 1530    {
 1531      hasFinally = stmt.finallyBlock != null
 1532      if (hasFinally) jumpFinallys = Int[,]
 1533    }
 1534  
 1535    Bool hasFinally      // does this region have a finally
 1536    Int[] jumpFinallys  // list of JumpFinally positions to backpatch
 1537  }
 1538  
 1539  **************************************************************************
 1540  ** Cond
 1541  **************************************************************************
 1542  
 1543  class Cond
 1544  {
 1545    Int[] jumpTrues  := Int[,]   // backpatch positions
 1546    Int[] jumpFalses := Int[,]   // backpatch positions
 1547  }