Added support for fused math-assign ops (+=, etc).
authorDavid Anderson <dvander@alliedmods.net>
Sun Jul 01 16:36:39 2012 -0700 (2012-07-01)
changeset 22765ab1b672a3
parent 21 af5d8e985502
child 23 8a31742781c1
Added support for fused math-assign ops (+=, etc).
jitcraft/AST.h
jitcraft/BytecodeCompiler.cpp
jitcraft/BytecodeEmitter.h
jitcraft/Interpreter.cpp
jitcraft/Opcodes.cpp
jitcraft/Opcodes.tbl
jitcraft/Parser.cpp
jitcraft/Parser.h
jitcraft/Scanner.cpp
jitcraft/SemanticAnalysis.cpp
jitcraft/Token.h
tests/prototype/assignadd.sp
     1.1 --- a/jitcraft/AST.h	Sun Jul 01 14:52:57 2012 -0700
     1.2 +++ b/jitcraft/AST.h	Sun Jul 01 16:36:39 2012 -0700
     1.3 @@ -381,16 +381,16 @@
     1.4  class ForStatement : public Statement
     1.5  {
     1.6      Scope *scope_;
     1.7 -    VariableDeclaration *declaration_;
     1.8 +    Statement *initialization_;
     1.9      Expression *condition_;
    1.10      Statement *update_;
    1.11      Statement *body_;
    1.12  
    1.13    public:
    1.14 -    ForStatement(Scope *scope, VariableDeclaration *declaration,
    1.15 +    ForStatement(Scope *scope, Statement *initialization,
    1.16                   Expression *condition, Statement *update, Statement *body)
    1.17        : scope_(scope),
    1.18 -        declaration_(declaration),
    1.19 +        initialization_(initialization),
    1.20          condition_(condition),
    1.21          update_(update),
    1.22          body_(body)
    1.23 @@ -400,8 +400,8 @@
    1.24      Kind kind() const {
    1.25          return kForStatement;
    1.26      }
    1.27 -    VariableDeclaration *declaration() const {
    1.28 -        return declaration_;
    1.29 +    Statement *initialization() const {
    1.30 +        return initialization_;
    1.31      }
    1.32      Scope *scope() const {
    1.33          return scope_;
    1.34 @@ -504,12 +504,14 @@
    1.35  
    1.36  class Assignment : public Expression
    1.37  {
    1.38 +    TokenKind token_;
    1.39      Expression *lvalue_;
    1.40      Expression *expression_;
    1.41  
    1.42    public:
    1.43 -    Assignment(Expression *left, Expression *right)
    1.44 -      : lvalue_(left),
    1.45 +    Assignment(TokenKind token, Expression *left, Expression *right)
    1.46 +      : token_(token),
    1.47 +        lvalue_(left),
    1.48          expression_(right)
    1.49      {
    1.50      }
    1.51 @@ -517,6 +519,9 @@
    1.52      Kind kind() const {
    1.53          return kAssignment;
    1.54      }
    1.55 +    TokenKind token() const {
    1.56 +        return token_;
    1.57 +    }
    1.58      Expression *lvalue() const {
    1.59          return lvalue_;
    1.60      }
     2.1 --- a/jitcraft/BytecodeCompiler.cpp	Sun Jul 01 14:52:57 2012 -0700
     2.2 +++ b/jitcraft/BytecodeCompiler.cpp	Sun Jul 01 16:36:39 2012 -0700
     2.3 @@ -140,14 +140,8 @@
     2.4              __ getarg(var->slot());
     2.5  
     2.6              // If this argument is actually a reference, dereference it now.
     2.7 -            if (var->type()->isReference()) {
     2.8 -                if (var->type()->contained()->isFloat()) {
     2.9 -                    __ getref_f();
    2.10 -                } else {
    2.11 -                    assert(var->type()->contained()->isInt32());
    2.12 -                    __ getref_i();
    2.13 -                }
    2.14 -            }
    2.15 +            if (var->type()->isReference())
    2.16 +                __ getref(var->type()->contained()->primitive());
    2.17          }
    2.18          break;
    2.19        }
    2.20 @@ -173,16 +167,33 @@
    2.21  void
    2.22  BytecodeCompiler::visit(Assignment *node)
    2.23  {
    2.24 -    // First compute the left-hand side.
    2.25 +    // The first step is three sub-steps:
    2.26 +    //
    2.27 +    //   (1) Bind the left-hand side.
    2.28 +    //   -- If a cumulative operation (+=, etc):
    2.29 +    //   (2) Save a copy of the binding on the stack.
    2.30 +    //   (3) Dereference the binding to get a value.
    2.31 +    //
    2.32      if (node->lvalue()->isNameProxy()) {
    2.33          NameProxy *proxy = node->lvalue()->toNameProxy();
    2.34          Variable *var = proxy->binding()->toVariable();
    2.35  
    2.36 -        // Make sure to bind the left-hand side first.
    2.37 +        // Bind.
    2.38          if (var->type()->isReference() || var->type()->isArray())
    2.39              __ get(var);
    2.40  
    2.41 -        node->expression()->accept(this);
    2.42 +        // Get the value if needed.
    2.43 +        if (node->token() != TOK_ASSIGN) {
    2.44 +            // += etc is not allowed on arrays.
    2.45 +            assert(!var->type()->isArray());
    2.46 +
    2.47 +            if (var->type()->isReference()) {
    2.48 +                __ dup();
    2.49 +                __ getref(var->type()->contained()->primitive());
    2.50 +            } else {
    2.51 +                __ get(var);
    2.52 +            }
    2.53 +        }
    2.54      } else if (node->lvalue()->isIndexExpression()) {
    2.55          IndexExpression *index = node->lvalue()->toIndexExpression();
    2.56  
    2.57 @@ -195,7 +206,24 @@
    2.58          // decision.
    2.59          index->left()->accept(this);
    2.60          index->right()->accept(this);
    2.61 +
    2.62 +        if (node->token() != TOK_ASSIGN) {
    2.63 +            __ dup2();
    2.64 +            __ bounds();
    2.65 +            if (node->type()->isArray())
    2.66 +                __ getarray_o();
    2.67 +            else
    2.68 +                __ getarray(node->type()->primitive());
    2.69 +        }
    2.70 +    }
    2.71 +
    2.72 +    if (node->token() == TOK_ASSIGN) {
    2.73          node->expression()->accept(this);
    2.74 +    } else {
    2.75 +        visitForALU(node->type(), node->expression());
    2.76 +
    2.77 +        TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS));
    2.78 +        __ binary(TokenToOpcode(alu));
    2.79      }
    2.80  
    2.81      // Emit the store.
    2.82 @@ -207,19 +235,15 @@
    2.83          NameProxy *proxy = node->lvalue()->toNameProxy();
    2.84          Variable *var = proxy->binding()->toVariable();
    2.85  
    2.86 -        if (var->type()->isReference()) {
    2.87 -            if (var->type()->contained()->isFloat()) {
    2.88 -                __ setref_f();
    2.89 -            } else {
    2.90 -                assert(var->type()->contained()->isInt32());
    2.91 -                __ setref_i();
    2.92 -            }
    2.93 -        } else {
    2.94 +        if (var->type()->isReference())
    2.95 +            __ setref(var->type()->contained()->primitive());
    2.96 +        else
    2.97              __ set(var);
    2.98 -        }
    2.99      } else {
   2.100          IndexExpression *index = node->lvalue()->toIndexExpression();
   2.101 -        __ bounds();
   2.102 +        // :TODO: get rid of the BOUNDS instruction, embed it in setarray*?
   2.103 +        // We can't emit it here because the stack is slightly out of position.
   2.104 +        // __ bounds();
   2.105          __ setarray(index->left()->type()->contained()->primitive());
   2.106      }
   2.107  }
   2.108 @@ -230,6 +254,8 @@
   2.109      node->accept(this);
   2.110      if (needed->isFloat() && !node->type()->isFloat())
   2.111          __ cvt_i2f();
   2.112 +    else
   2.113 +        assert(needed->primitive() == node->type()->primitive());
   2.114  }
   2.115  
   2.116  void
   2.117 @@ -302,8 +328,8 @@
   2.118      Label join, update;
   2.119      LoopScope loop(scope_, &join, &update, &loop_);
   2.120  
   2.121 -    if (node->declaration())
   2.122 -        node->declaration()->accept(this);
   2.123 +    if (node->initialization())
   2.124 +        node->initialization()->accept(this);
   2.125  
   2.126      Label header, test;
   2.127      if (node->condition())
     3.1 --- a/jitcraft/BytecodeEmitter.h	Sun Jul 01 14:52:57 2012 -0700
     3.2 +++ b/jitcraft/BytecodeEmitter.h	Sun Jul 01 16:36:39 2012 -0700
     3.3 @@ -359,17 +359,21 @@
     3.4      void arrayref_lifo() {
     3.5          singleByteOp(OP_ARRAYREF_LIFO);
     3.6      }
     3.7 -    void getref_i() {
     3.8 -        singleByteOp(OP_GETREF_I);
     3.9 -    }
    3.10 -    void setref_i() {
    3.11 -        singleByteOp(OP_SETREF_I);
    3.12 +    void getref(PrimitiveType type) {
    3.13 +        if (type == PrimitiveType_Int32) {
    3.14 +            singleByteOp(OP_GETREF_I);
    3.15 +        } else {
    3.16 +            assert(type == PrimitiveType_Float);
    3.17 +            singleByteOp(OP_GETREF_F);
    3.18 +        }
    3.19      }
    3.20 -    void getref_f() {
    3.21 -        singleByteOp(OP_GETREF_F);
    3.22 -    }
    3.23 -    void setref_f() {
    3.24 -        singleByteOp(OP_SETREF_F);
    3.25 +    void setref(PrimitiveType type) {
    3.26 +        if (type == PrimitiveType_Int32) {
    3.27 +            singleByteOp(OP_SETREF_I);
    3.28 +        } else {
    3.29 +            assert(type == PrimitiveType_Float);
    3.30 +            singleByteOp(OP_SETREF_F);
    3.31 +        }
    3.32      }
    3.33      void object(Object *obj) {
    3.34          ensure(OP_OBJECT_LENGTH);
    3.35 @@ -379,6 +383,12 @@
    3.36      void arraycopy() {
    3.37          singleByteOp(OP_ARRAYCOPY);
    3.38      }
    3.39 +    void dup() {
    3.40 +        singleByteOp(OP_DUP);
    3.41 +    }
    3.42 +    void dup2() {
    3.43 +        singleByteOp(OP_DUP2);
    3.44 +    }
    3.45  };
    3.46  
    3.47  }
     4.1 --- a/jitcraft/Interpreter.cpp	Sun Jul 01 14:52:57 2012 -0700
     4.2 +++ b/jitcraft/Interpreter.cpp	Sun Jul 01 16:36:39 2012 -0700
     4.3 @@ -245,7 +245,7 @@
     4.4      int right = POP_I32(top);
     4.5      int left = POP_I32(top);
     4.6      int result = left * right;
     4.7 -   PUSH_I32(top, result);
     4.8 +    PUSH_I32(top, result);
     4.9      break;
    4.10  }
    4.11  
    4.12 @@ -586,6 +586,19 @@
    4.13      break;
    4.14  }
    4.15  
    4.16 +case OP_DUP:
    4.17 +{
    4.18 +    *top.sp++ = *top.sp;
    4.19 +    break;
    4.20 +}
    4.21 +
    4.22 +case OP_DUP2:
    4.23 +{
    4.24 +    *top.sp++ = top.sp[-2];
    4.25 +    *top.sp++ = top.sp[-2];
    4.26 +    break;
    4.27 +}
    4.28 +
    4.29  default:
    4.30      assert(false);
    4.31      return false;
     5.1 --- a/jitcraft/Opcodes.cpp	Sun Jul 01 14:52:57 2012 -0700
     5.2 +++ b/jitcraft/Opcodes.cpp	Sun Jul 01 16:36:39 2012 -0700
     5.3 @@ -104,6 +104,8 @@
     5.4            case OP_ARRAYREF_LIFO:
     5.5            case OP_OBJECT:
     5.6            case OP_ARRAYCOPY:
     5.7 +          case OP_DUP:
     5.8 +          case OP_DUP2:
     5.9              break;
    5.10  
    5.11            case OP_STOP:
     6.1 --- a/jitcraft/Opcodes.tbl	Sun Jul 01 14:52:57 2012 -0700
     6.2 +++ b/jitcraft/Opcodes.tbl	Sun Jul 01 16:36:39 2012 -0700
     6.3 @@ -157,3 +157,9 @@
     6.4  //   copy(Left, Right)
     6.5  //   push(Left)
     6.6  OPDEF(OP_ARRAYCOPY,     55,         "arraycopy",    1,      2,  1)
     6.7 +
     6.8 +// Duplicates the top item on the stack.
     6.9 +OPDEF(OP_DUP,           56,         "dup",          1,      1,  2)
    6.10 +
    6.11 +// Duplicates the top two items on the stack.
    6.12 +OPDEF(OP_DUP2,          57,         "dup2",         1,      2,  4)
     7.1 --- a/jitcraft/Parser.cpp	Sun Jul 01 14:52:57 2012 -0700
     7.2 +++ b/jitcraft/Parser.cpp	Sun Jul 01 16:36:39 2012 -0700
     7.3 @@ -356,9 +356,27 @@
     7.4  }
     7.5  
     7.6  Expression *
     7.7 +Parser::assignment()
     7.8 +{
     7.9 +    Expression *left = equals();
    7.10 +    while (left) {
    7.11 +        TokenKind token = scanner_.peek();
    7.12 +        if (token != TOK_ASSIGN && (token < TOK_ASSIGN_ADD || token > TOK_ASSIGN_SHL))
    7.13 +            break;
    7.14 +        scanner_.scan();
    7.15 +        Expression *expr = equals();
    7.16 +        if (!expr)
    7.17 +            return NULL;
    7.18 +
    7.19 +        left = new (pool_) Assignment(token, left, expr);
    7.20 +    }
    7.21 +    return left;
    7.22 +}
    7.23 +
    7.24 +Expression *
    7.25  Parser::expression()
    7.26  {
    7.27 -    return equals();
    7.28 +    return assignment();
    7.29  }
    7.30  
    7.31  Statement *
    7.32 @@ -411,12 +429,17 @@
    7.33      if (!expect(TOK_LPAREN))
    7.34          return NULL;
    7.35  
    7.36 -    VariableDeclaration *decl = NULL;
    7.37 +    Statement *decl = NULL;
    7.38      if (!match(TOK_SEMICOLON)) {
    7.39 -        if (!enterScope(Scope::BLOCK))
    7.40 -            return NULL;
    7.41 -        if ((decl = variableDeclaration()) == NULL)
    7.42 -            return NULL;
    7.43 +        if (scanner_.peek() == TOK_NEW) {
    7.44 +            if (!enterScope(Scope::BLOCK))
    7.45 +                return NULL;
    7.46 +            if ((decl = variableDeclaration()) == NULL)
    7.47 +                return NULL;
    7.48 +        } else {
    7.49 +            if ((decl = expressionStatement()) == NULL)
    7.50 +                return NULL;
    7.51 +        }
    7.52          if (!expect(TOK_SEMICOLON))
    7.53              return NULL;
    7.54      }
    7.55 @@ -442,7 +465,7 @@
    7.56          return NULL;
    7.57  
    7.58      Scope *blockScope = NULL;
    7.59 -    if (decl) {
    7.60 +    if (decl && decl->isVariableDeclaration()) {
    7.61          blockScope = scope_->unlinkIfEmpty();
    7.62          scope_ = scope_->enclosing();
    7.63      }
    7.64 @@ -511,18 +534,10 @@
    7.65  Statement *
    7.66  Parser::expressionStatement()
    7.67  {
    7.68 -    Expression *left = primary();
    7.69 +    Expression *left = assignment();
    7.70      if (!left)
    7.71          return NULL;
    7.72  
    7.73 -    if (match(TOK_ASSIGN)) {
    7.74 -        Expression *expr = expression();
    7.75 -        if (!expr)
    7.76 -            return NULL;
    7.77 -
    7.78 -        left = new (pool_) Assignment(left, expr);
    7.79 -    }
    7.80 -
    7.81      return new (pool_) ExpressionStatement(left);
    7.82  }
    7.83  
    7.84 @@ -908,8 +923,8 @@
    7.85          prefix();
    7.86          fprintf(fp_, "[ ForStatement\n");
    7.87          indent();
    7.88 -        if (node->declaration())
    7.89 -            node->declaration()->accept(this);
    7.90 +        if (node->initialization())
    7.91 +            node->initialization()->accept(this);
    7.92          if (node->condition())
    7.93              node->condition()->accept(this);
    7.94          if (node->update())
     8.1 --- a/jitcraft/Parser.h	Sun Jul 01 14:52:57 2012 -0700
     8.2 +++ b/jitcraft/Parser.h	Sun Jul 01 16:36:39 2012 -0700
     8.3 @@ -62,6 +62,7 @@
     8.4      Expression *bitor();
     8.5      Expression *relational();
     8.6      Expression *equals();
     8.7 +    Expression *assignment();
     8.8      Expression *expression();
     8.9  
    8.10      Statement *enum_();
     9.1 --- a/jitcraft/Scanner.cpp	Sun Jul 01 14:52:57 2012 -0700
     9.2 +++ b/jitcraft/Scanner.cpp	Sun Jul 01 16:36:39 2012 -0700
     9.3 @@ -391,15 +391,33 @@
     9.4              return TOK_PLUS;
     9.5  
     9.6            case '&':
     9.7 +            c = getChar();
     9.8 +            if (c == '=')
     9.9 +                return TOK_ASSIGN_BITAND;
    9.10 +            putChar(c);
    9.11              return TOK_BITAND;
    9.12  
    9.13            case '|':
    9.14 +            c = getChar();
    9.15 +            if (c == '=')
    9.16 +                return TOK_ASSIGN_BITOR;
    9.17 +            putChar(c);
    9.18              return TOK_BITOR;
    9.19  
    9.20            case '^':
    9.21 +            c = getChar();
    9.22 +            if (c == '=')
    9.23 +                return TOK_ASSIGN_BITXOR;
    9.24 +            putChar(c);
    9.25              return TOK_BITXOR;
    9.26  
    9.27 -          case '-': return TOK_MINUS;
    9.28 +          case '-':
    9.29 +            c = getChar();
    9.30 +            if (c == '=')
    9.31 +                return TOK_ASSIGN_SUB;
    9.32 +            putChar(c);
    9.33 +            return TOK_MINUS;
    9.34 +
    9.35            case ',': return TOK_COMMA;
    9.36            case '\0': return TOK_EOF;
    9.37  
    9.38 @@ -429,8 +447,13 @@
    9.39                  return TOK_GE;
    9.40              if (c == '>') {
    9.41                  c = getChar();
    9.42 -                if (c == '>')
    9.43 +                if (c == '>') {
    9.44 +                    c = getChar();
    9.45 +                    if (c == '=')
    9.46 +                        return TOK_ASSIGN_USHR;
    9.47 +                    putChar(c);
    9.48                      return TOK_USHR;
    9.49 +                }
    9.50                  return TOK_SHR;
    9.51              }
    9.52              putChar(c);
    10.1 --- a/jitcraft/SemanticAnalysis.cpp	Sun Jul 01 14:52:57 2012 -0700
    10.2 +++ b/jitcraft/SemanticAnalysis.cpp	Sun Jul 01 16:36:39 2012 -0700
    10.3 @@ -189,6 +189,28 @@
    10.4      return false;
    10.5  }
    10.6  
    10.7 +static inline Type *
    10.8 +TypeCheckBinary(TokenKind token, Type *left, Type *right)
    10.9 +{
   10.10 +    if (!left || !right)
   10.11 +        return NULL;
   10.12 +
   10.13 +    if ((token >= TOK_PLUS && token <= TOK_SLASH) ||
   10.14 +        (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV))
   10.15 +    {
   10.16 +        // If either side is a float, the other side must be a float or int.
   10.17 +        if (left->isFloat() || right->isFloat()) {
   10.18 +            assert(left->isFloat() || left->isInt32());
   10.19 +            assert(right->isFloat() || right->isInt32());
   10.20 +            return left->isFloat() ? left : right;
   10.21 +        }
   10.22 +    }
   10.23 +
   10.24 +    assert(left->isInt32() && right->isInt32());
   10.25 +    assert(!left->tag() && !right->tag());
   10.26 +    return left;
   10.27 +}
   10.28 +
   10.29  void
   10.30  SemanticAnalysis::visit(Assignment *node)
   10.31  {
   10.32 @@ -214,34 +236,21 @@
   10.33              node->setType(var->type());
   10.34      }
   10.35  
   10.36 +    if (node->token() != TOK_ASSIGN)
   10.37 +        assert(TypeCheckBinary(node->token(), node->type(), node->expression()->type()));
   10.38 +
   10.39      assert(TypeCheckAssignment(node->type(), node->expression()->type()));
   10.40  }
   10.41  
   10.42 -
   10.43  void
   10.44  SemanticAnalysis::visit(BinaryExpression *node)
   10.45  {
   10.46 +    // :TODO: use Local
   10.47      Type *left = visitForLoad(node->left());
   10.48      Type *right = visitForLoad(node->right());
   10.49  
   10.50 -    if (node->token() >= TOK_PLUS && node->token() <= TOK_SLASH) {
   10.51 -        // Both sides must be primitive.
   10.52 -        if (!left || !right)
   10.53 -            return;
   10.54 -
   10.55 -        // If either side is a float, the other side must be a float or int.
   10.56 -        if (left->isFloat() || right->isFloat()) {
   10.57 -            assert(left->isFloat() || left->isInt32());
   10.58 -            assert(right->isFloat() || right->isInt32());
   10.59 -            node->setType(left->isFloat() ? left : right);
   10.60 -            return;
   10.61 -        }
   10.62 -    }
   10.63 -
   10.64 -    // For now, assert everything else is integer and untagged.
   10.65 -    assert(left->isInt32() && right->isInt32());
   10.66 -    assert(!left->tag() && !right->tag());
   10.67 -    node->setType(left);
   10.68 +    Type *result = TypeCheckBinary(node->token(), left, right);
   10.69 +    node->setType(result);
   10.70  }
   10.71  
   10.72  void
   10.73 @@ -258,8 +267,8 @@
   10.74      loopDepth_++;
   10.75      if (node->scope())
   10.76          scope_ = node->scope();
   10.77 -    if (node->declaration())
   10.78 -        node->declaration()->accept(this);
   10.79 +    if (node->initialization())
   10.80 +        node->initialization()->accept(this);
   10.81      if (node->condition())
   10.82          visitForLoad(node->condition());
   10.83      if (node->update())
    11.1 --- a/jitcraft/Token.h	Sun Jul 01 14:52:57 2012 -0700
    11.2 +++ b/jitcraft/Token.h	Sun Jul 01 16:36:39 2012 -0700
    11.3 @@ -20,6 +20,11 @@
    11.4  
    11.5  namespace jc {
    11.6  
    11.7 +// List of tokens the scanner can recognize.
    11.8 +//
    11.9 +// Some restrictions:
   11.10 +//   * (ASSIGN_ADD - TOK_PLUS) must be the delta between an operator and its
   11.11 +//     op-and-assign equivalent.
   11.12  #define TOKENMAP(_)                                     \
   11.13      _(ERROR,              "<error>")                    \
   11.14      _(NEW,                "'new'")                      \
   11.15 @@ -56,7 +61,14 @@
   11.16      _(LBRACKET,           "[")                          \
   11.17      _(RBRACKET,           "]")                          \
   11.18      _(ASSIGN_ADD,         "+=")                         \
   11.19 +    _(ASSIGN_SUB,         "-=")                         \
   11.20      _(ASSIGN_MUL,         "*=")                         \
   11.21 +    _(ASSIGN_DIV,         "/=")                         \
   11.22 +    _(ASSIGN_BITAND,      "&=")                         \
   11.23 +    _(ASSIGN_BITOR,       "|=")                         \
   11.24 +    _(ASSIGN_BITXOR,      "^=")                         \
   11.25 +    _(ASSIGN_SHR,         ">>=")                        \
   11.26 +    _(ASSIGN_USHR,        ">>>=")                       \
   11.27      _(ASSIGN_SHL,         "<<=")                        \
   11.28      _(EQUALS,             "==")                         \
   11.29      _(NOTEQUALS,          "!=")                         \
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/tests/prototype/assignadd.sp	Sun Jul 01 16:36:39 2012 -0700
    12.3 @@ -0,0 +1,8 @@
    12.4 +main()
    12.5 +{
    12.6 +    new x[3];
    12.7 +    for (new i = 0; i < 100; i += 3) {
    12.8 +        x[2] += i;
    12.9 +    }
   12.10 +    return x[2];
   12.11 +}