# HG changeset patch # User David Anderson # Date 1341185799 25200 # Node ID 765ab1b672a373d4b3de0ae6827e99e6da7e67d3 # Parent af5d8e985502967ff2a334fa3a4b02ce2303c972 Added support for fused math-assign ops (+=, etc). diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/AST.h --- a/jitcraft/AST.h Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/AST.h Sun Jul 01 16:36:39 2012 -0700 @@ -381,16 +381,16 @@ class ForStatement : public Statement { Scope *scope_; - VariableDeclaration *declaration_; + Statement *initialization_; Expression *condition_; Statement *update_; Statement *body_; public: - ForStatement(Scope *scope, VariableDeclaration *declaration, + ForStatement(Scope *scope, Statement *initialization, Expression *condition, Statement *update, Statement *body) : scope_(scope), - declaration_(declaration), + initialization_(initialization), condition_(condition), update_(update), body_(body) @@ -400,8 +400,8 @@ Kind kind() const { return kForStatement; } - VariableDeclaration *declaration() const { - return declaration_; + Statement *initialization() const { + return initialization_; } Scope *scope() const { return scope_; @@ -504,12 +504,14 @@ class Assignment : public Expression { + TokenKind token_; Expression *lvalue_; Expression *expression_; public: - Assignment(Expression *left, Expression *right) - : lvalue_(left), + Assignment(TokenKind token, Expression *left, Expression *right) + : token_(token), + lvalue_(left), expression_(right) { } @@ -517,6 +519,9 @@ Kind kind() const { return kAssignment; } + TokenKind token() const { + return token_; + } Expression *lvalue() const { return lvalue_; } diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/BytecodeCompiler.cpp --- a/jitcraft/BytecodeCompiler.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/BytecodeCompiler.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -140,14 +140,8 @@ __ getarg(var->slot()); // If this argument is actually a reference, dereference it now. - if (var->type()->isReference()) { - if (var->type()->contained()->isFloat()) { - __ getref_f(); - } else { - assert(var->type()->contained()->isInt32()); - __ getref_i(); - } - } + if (var->type()->isReference()) + __ getref(var->type()->contained()->primitive()); } break; } @@ -173,16 +167,33 @@ void BytecodeCompiler::visit(Assignment *node) { - // First compute the left-hand side. + // The first step is three sub-steps: + // + // (1) Bind the left-hand side. + // -- If a cumulative operation (+=, etc): + // (2) Save a copy of the binding on the stack. + // (3) Dereference the binding to get a value. + // if (node->lvalue()->isNameProxy()) { NameProxy *proxy = node->lvalue()->toNameProxy(); Variable *var = proxy->binding()->toVariable(); - // Make sure to bind the left-hand side first. + // Bind. if (var->type()->isReference() || var->type()->isArray()) __ get(var); - node->expression()->accept(this); + // Get the value if needed. + if (node->token() != TOK_ASSIGN) { + // += etc is not allowed on arrays. + assert(!var->type()->isArray()); + + if (var->type()->isReference()) { + __ dup(); + __ getref(var->type()->contained()->primitive()); + } else { + __ get(var); + } + } } else if (node->lvalue()->isIndexExpression()) { IndexExpression *index = node->lvalue()->toIndexExpression(); @@ -195,7 +206,24 @@ // decision. index->left()->accept(this); index->right()->accept(this); + + if (node->token() != TOK_ASSIGN) { + __ dup2(); + __ bounds(); + if (node->type()->isArray()) + __ getarray_o(); + else + __ getarray(node->type()->primitive()); + } + } + + if (node->token() == TOK_ASSIGN) { node->expression()->accept(this); + } else { + visitForALU(node->type(), node->expression()); + + TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); + __ binary(TokenToOpcode(alu)); } // Emit the store. @@ -207,19 +235,15 @@ NameProxy *proxy = node->lvalue()->toNameProxy(); Variable *var = proxy->binding()->toVariable(); - if (var->type()->isReference()) { - if (var->type()->contained()->isFloat()) { - __ setref_f(); - } else { - assert(var->type()->contained()->isInt32()); - __ setref_i(); - } - } else { + if (var->type()->isReference()) + __ setref(var->type()->contained()->primitive()); + else __ set(var); - } } else { IndexExpression *index = node->lvalue()->toIndexExpression(); - __ bounds(); + // :TODO: get rid of the BOUNDS instruction, embed it in setarray*? + // We can't emit it here because the stack is slightly out of position. + // __ bounds(); __ setarray(index->left()->type()->contained()->primitive()); } } @@ -230,6 +254,8 @@ node->accept(this); if (needed->isFloat() && !node->type()->isFloat()) __ cvt_i2f(); + else + assert(needed->primitive() == node->type()->primitive()); } void @@ -302,8 +328,8 @@ Label join, update; LoopScope loop(scope_, &join, &update, &loop_); - if (node->declaration()) - node->declaration()->accept(this); + if (node->initialization()) + node->initialization()->accept(this); Label header, test; if (node->condition()) diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/BytecodeEmitter.h --- a/jitcraft/BytecodeEmitter.h Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/BytecodeEmitter.h Sun Jul 01 16:36:39 2012 -0700 @@ -359,17 +359,21 @@ void arrayref_lifo() { singleByteOp(OP_ARRAYREF_LIFO); } - void getref_i() { - singleByteOp(OP_GETREF_I); - } - void setref_i() { - singleByteOp(OP_SETREF_I); + void getref(PrimitiveType type) { + if (type == PrimitiveType_Int32) { + singleByteOp(OP_GETREF_I); + } else { + assert(type == PrimitiveType_Float); + singleByteOp(OP_GETREF_F); + } } - void getref_f() { - singleByteOp(OP_GETREF_F); - } - void setref_f() { - singleByteOp(OP_SETREF_F); + void setref(PrimitiveType type) { + if (type == PrimitiveType_Int32) { + singleByteOp(OP_SETREF_I); + } else { + assert(type == PrimitiveType_Float); + singleByteOp(OP_SETREF_F); + } } void object(Object *obj) { ensure(OP_OBJECT_LENGTH); @@ -379,6 +383,12 @@ void arraycopy() { singleByteOp(OP_ARRAYCOPY); } + void dup() { + singleByteOp(OP_DUP); + } + void dup2() { + singleByteOp(OP_DUP2); + } }; } diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Interpreter.cpp --- a/jitcraft/Interpreter.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Interpreter.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -245,7 +245,7 @@ int right = POP_I32(top); int left = POP_I32(top); int result = left * right; - PUSH_I32(top, result); + PUSH_I32(top, result); break; } @@ -586,6 +586,19 @@ break; } +case OP_DUP: +{ + *top.sp++ = *top.sp; + break; +} + +case OP_DUP2: +{ + *top.sp++ = top.sp[-2]; + *top.sp++ = top.sp[-2]; + break; +} + default: assert(false); return false; diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Opcodes.cpp --- a/jitcraft/Opcodes.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Opcodes.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -104,6 +104,8 @@ case OP_ARRAYREF_LIFO: case OP_OBJECT: case OP_ARRAYCOPY: + case OP_DUP: + case OP_DUP2: break; case OP_STOP: diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Opcodes.tbl --- a/jitcraft/Opcodes.tbl Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Opcodes.tbl Sun Jul 01 16:36:39 2012 -0700 @@ -157,3 +157,9 @@ // copy(Left, Right) // push(Left) OPDEF(OP_ARRAYCOPY, 55, "arraycopy", 1, 2, 1) + +// Duplicates the top item on the stack. +OPDEF(OP_DUP, 56, "dup", 1, 1, 2) + +// Duplicates the top two items on the stack. +OPDEF(OP_DUP2, 57, "dup2", 1, 2, 4) diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Parser.cpp --- a/jitcraft/Parser.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Parser.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -356,9 +356,27 @@ } Expression * +Parser::assignment() +{ + Expression *left = equals(); + while (left) { + TokenKind token = scanner_.peek(); + if (token != TOK_ASSIGN && (token < TOK_ASSIGN_ADD || token > TOK_ASSIGN_SHL)) + break; + scanner_.scan(); + Expression *expr = equals(); + if (!expr) + return NULL; + + left = new (pool_) Assignment(token, left, expr); + } + return left; +} + +Expression * Parser::expression() { - return equals(); + return assignment(); } Statement * @@ -411,12 +429,17 @@ if (!expect(TOK_LPAREN)) return NULL; - VariableDeclaration *decl = NULL; + Statement *decl = NULL; if (!match(TOK_SEMICOLON)) { - if (!enterScope(Scope::BLOCK)) - return NULL; - if ((decl = variableDeclaration()) == NULL) - return NULL; + if (scanner_.peek() == TOK_NEW) { + if (!enterScope(Scope::BLOCK)) + return NULL; + if ((decl = variableDeclaration()) == NULL) + return NULL; + } else { + if ((decl = expressionStatement()) == NULL) + return NULL; + } if (!expect(TOK_SEMICOLON)) return NULL; } @@ -442,7 +465,7 @@ return NULL; Scope *blockScope = NULL; - if (decl) { + if (decl && decl->isVariableDeclaration()) { blockScope = scope_->unlinkIfEmpty(); scope_ = scope_->enclosing(); } @@ -511,18 +534,10 @@ Statement * Parser::expressionStatement() { - Expression *left = primary(); + Expression *left = assignment(); if (!left) return NULL; - if (match(TOK_ASSIGN)) { - Expression *expr = expression(); - if (!expr) - return NULL; - - left = new (pool_) Assignment(left, expr); - } - return new (pool_) ExpressionStatement(left); } @@ -908,8 +923,8 @@ prefix(); fprintf(fp_, "[ ForStatement\n"); indent(); - if (node->declaration()) - node->declaration()->accept(this); + if (node->initialization()) + node->initialization()->accept(this); if (node->condition()) node->condition()->accept(this); if (node->update()) diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Parser.h --- a/jitcraft/Parser.h Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Parser.h Sun Jul 01 16:36:39 2012 -0700 @@ -62,6 +62,7 @@ Expression *bitor(); Expression *relational(); Expression *equals(); + Expression *assignment(); Expression *expression(); Statement *enum_(); diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Scanner.cpp --- a/jitcraft/Scanner.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Scanner.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -391,15 +391,33 @@ return TOK_PLUS; case '&': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITAND; + putChar(c); return TOK_BITAND; case '|': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITOR; + putChar(c); return TOK_BITOR; case '^': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITXOR; + putChar(c); return TOK_BITXOR; - case '-': return TOK_MINUS; + case '-': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_SUB; + putChar(c); + return TOK_MINUS; + case ',': return TOK_COMMA; case '\0': return TOK_EOF; @@ -429,8 +447,13 @@ return TOK_GE; if (c == '>') { c = getChar(); - if (c == '>') + if (c == '>') { + c = getChar(); + if (c == '=') + return TOK_ASSIGN_USHR; + putChar(c); return TOK_USHR; + } return TOK_SHR; } putChar(c); diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/SemanticAnalysis.cpp --- a/jitcraft/SemanticAnalysis.cpp Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/SemanticAnalysis.cpp Sun Jul 01 16:36:39 2012 -0700 @@ -189,6 +189,28 @@ return false; } +static inline Type * +TypeCheckBinary(TokenKind token, Type *left, Type *right) +{ + if (!left || !right) + return NULL; + + if ((token >= TOK_PLUS && token <= TOK_SLASH) || + (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV)) + { + // If either side is a float, the other side must be a float or int. + if (left->isFloat() || right->isFloat()) { + assert(left->isFloat() || left->isInt32()); + assert(right->isFloat() || right->isInt32()); + return left->isFloat() ? left : right; + } + } + + assert(left->isInt32() && right->isInt32()); + assert(!left->tag() && !right->tag()); + return left; +} + void SemanticAnalysis::visit(Assignment *node) { @@ -214,34 +236,21 @@ node->setType(var->type()); } + if (node->token() != TOK_ASSIGN) + assert(TypeCheckBinary(node->token(), node->type(), node->expression()->type())); + assert(TypeCheckAssignment(node->type(), node->expression()->type())); } - void SemanticAnalysis::visit(BinaryExpression *node) { + // :TODO: use Local Type *left = visitForLoad(node->left()); Type *right = visitForLoad(node->right()); - if (node->token() >= TOK_PLUS && node->token() <= TOK_SLASH) { - // Both sides must be primitive. - if (!left || !right) - return; - - // If either side is a float, the other side must be a float or int. - if (left->isFloat() || right->isFloat()) { - assert(left->isFloat() || left->isInt32()); - assert(right->isFloat() || right->isInt32()); - node->setType(left->isFloat() ? left : right); - return; - } - } - - // For now, assert everything else is integer and untagged. - assert(left->isInt32() && right->isInt32()); - assert(!left->tag() && !right->tag()); - node->setType(left); + Type *result = TypeCheckBinary(node->token(), left, right); + node->setType(result); } void @@ -258,8 +267,8 @@ loopDepth_++; if (node->scope()) scope_ = node->scope(); - if (node->declaration()) - node->declaration()->accept(this); + if (node->initialization()) + node->initialization()->accept(this); if (node->condition()) visitForLoad(node->condition()); if (node->update()) diff -r af5d8e985502 -r 765ab1b672a3 jitcraft/Token.h --- a/jitcraft/Token.h Sun Jul 01 14:52:57 2012 -0700 +++ b/jitcraft/Token.h Sun Jul 01 16:36:39 2012 -0700 @@ -20,6 +20,11 @@ namespace jc { +// List of tokens the scanner can recognize. +// +// Some restrictions: +// * (ASSIGN_ADD - TOK_PLUS) must be the delta between an operator and its +// op-and-assign equivalent. #define TOKENMAP(_) \ _(ERROR, "") \ _(NEW, "'new'") \ @@ -56,7 +61,14 @@ _(LBRACKET, "[") \ _(RBRACKET, "]") \ _(ASSIGN_ADD, "+=") \ + _(ASSIGN_SUB, "-=") \ _(ASSIGN_MUL, "*=") \ + _(ASSIGN_DIV, "/=") \ + _(ASSIGN_BITAND, "&=") \ + _(ASSIGN_BITOR, "|=") \ + _(ASSIGN_BITXOR, "^=") \ + _(ASSIGN_SHR, ">>=") \ + _(ASSIGN_USHR, ">>>=") \ _(ASSIGN_SHL, "<<=") \ _(EQUALS, "==") \ _(NOTEQUALS, "!=") \ diff -r af5d8e985502 -r 765ab1b672a3 tests/prototype/assignadd.sp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/prototype/assignadd.sp Sun Jul 01 16:36:39 2012 -0700 @@ -0,0 +1,8 @@ +main() +{ + new x[3]; + for (new i = 0; i < 100; i += 3) { + x[2] += i; + } + return x[2]; +}