# HG changeset patch # User David Anderson # Date 1340788916 25200 # Node ID 633176823669b45b7ae5fe06523ce13514e0a93a # Parent f43e399ab62254bf47f05ffc006238fcc80808cd Add support for float variables and basic math. diff -r f43e399ab622 -r 633176823669 jitcraft/AST.h --- a/jitcraft/AST.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/AST.h Wed Jun 27 02:21:56 2012 -0700 @@ -29,7 +29,8 @@ _(VariableDeclaration) \ _(ForStatement) \ _(ReturnStatement) \ - _(IntegerLiteralExpression) \ + _(IntegerLiteral) \ + _(FloatLiteral) \ _(BinaryExpression) \ _(BlockStatement) \ _(Assignment) \ @@ -141,15 +142,20 @@ Variable *variable_; Expression *initialization_; ExpressionList *postDimensions_; + Name *tag_; public: - VariableDeclaration(Variable *var, ExpressionList *postDimensions, Expression *initialization) + VariableDeclaration(Variable *var, Name *tag, ExpressionList *postDimensions, Expression *initialization) : variable_(var), postDimensions_(postDimensions), - initialization_(initialization) + initialization_(initialization), + tag_(tag) { } + Name *tag() const { + return tag_; + } Kind kind() const { return kVariableDeclaration; } @@ -197,18 +203,18 @@ } }; -class IntegerLiteralExpression : public Expression +class IntegerLiteral : public Expression { int value_; public: - IntegerLiteralExpression(int value) + IntegerLiteral(int value) : value_(value) { } Kind kind() const { - return kIntegerLiteralExpression; + return kIntegerLiteral; } int value() const { return value_; @@ -218,6 +224,27 @@ } }; +class FloatLiteral : public Expression +{ + double value_; + + public: + FloatLiteral(double value) + : value_(value) + { + } + + Kind kind() const { + return kFloatLiteral; + } + double value() const { + return value_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + class BinaryExpression : public Expression { Expression *left_; diff -r f43e399ab622 -r 633176823669 jitcraft/BytecodeCompiler.cpp --- a/jitcraft/BytecodeCompiler.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/BytecodeCompiler.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -152,12 +152,22 @@ } void +BytecodeCompiler::visitForALU(Type *needed, Expression *node) +{ + node->accept(this); + if (needed->isFloat() && !node->type()->isFloat()) + __ cvt_i2f(); +} + +void BytecodeCompiler::visit(BinaryExpression *node) { - node->left()->accept(this); - node->right()->accept(this); + visitForALU(node->type(), node->left()); + visitForALU(node->type(), node->right()); Opcode op = TokenToOpcode(node->token()); + if (node->type()->isFloat()) + op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); __ binary(op); } @@ -256,12 +266,18 @@ } void -BytecodeCompiler::visit(IntegerLiteralExpression *node) +BytecodeCompiler::visit(IntegerLiteral *node) { __ int_(node->value()); } void +BytecodeCompiler::visit(FloatLiteral *node) +{ + __ float_((float)node->value()); +} + +void BytecodeCompiler::visit(VariableDeclaration *node) { Variable *var = node->variable(); diff -r f43e399ab622 -r 633176823669 jitcraft/BytecodeCompiler.h --- a/jitcraft/BytecodeCompiler.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/BytecodeCompiler.h Wed Jun 27 02:21:56 2012 -0700 @@ -57,6 +57,7 @@ void leaveScope(Scope *scope); void visitForTest(Expression *expression, BytecodeEmitter::Label *label, bool onTrue); Code *compileFunction(Module *module, FunctionStatement *fun); + void visitForALU(Type *needed, Expression *node); public: BytecodeCompiler(ParseTree *tree); @@ -71,7 +72,8 @@ void visit(ForStatement *node); void visit(BlockStatement *node); void visit(VariableDeclaration *node); - void visit(IntegerLiteralExpression *node); + void visit(IntegerLiteral *node); + void visit(FloatLiteral *node); void visit(ExpressionStatement *node); void visit(FunctionStatement *node); void visit(CallExpression *node); diff -r f43e399ab622 -r 633176823669 jitcraft/BytecodeEmitter.h --- a/jitcraft/BytecodeEmitter.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/BytecodeEmitter.h Wed Jun 27 02:21:56 2012 -0700 @@ -54,6 +54,10 @@ assert(value >= 0 && value <= UCHAR_MAX); *buffer_pos_++ = value; } + void writeFloat(float value) { + *(float *)buffer_pos_ = value; + buffer_pos_ += sizeof(float); + } void writeInt32(int value) { *(int *)buffer_pos_ = value; buffer_pos_ += sizeof(int); @@ -271,6 +275,14 @@ void bounds_sta() { singleByteOp(OP_BOUNDS_STA); } + void float_(float value) { + ensure(OP_FLOAT_LENGTH); + writeOp(OP_FLOAT); + writeFloat(value); + } + void cvt_i2f() { + singleByteOp(OP_CVT_I2F); + } }; } diff -r f43e399ab622 -r 633176823669 jitcraft/Interpreter.cpp --- a/jitcraft/Interpreter.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Interpreter.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -28,6 +28,12 @@ top.sp++; \ } while (0) +#define PUSH_F(top, value) \ + do { \ + top.sp->f = value; \ + top.sp++; \ + } while (0) + static inline int POP_I32(Top &top) { @@ -36,6 +42,14 @@ return value; } +static inline float +POP_F(Top &top) +{ + float value = top.sp[-1].f; + top.sp--; + return value; +} + static inline bool CheckDimensionSize(int size, unsigned width) { @@ -171,7 +185,7 @@ } bool -jc::Interpret(Frame *frame, int *result) +jc::Interpret(Frame *frame, LocalSlot *result) { Top &top = *ZONE()->stack().top(); LocalSlot *locals = frame->locals(); @@ -187,17 +201,19 @@ PUSH_I32(top, READ_INT32(top.pc)); break; +case OP_FLOAT: + PUSH_F(top, READ_FLOAT(top.pc)); + break; + case OP_RETURN: { - int rval = POP_I32(top); - if (!frame->prev()) { - *result = rval; + *result = *--top.sp; + if (!frame->prev()) return true; - } frame = ZONE()->stack().popFrame(frame); locals = frame->locals(); - PUSH_I32(top, rval); + *top.sp++ = *result; top.pc += OpcodeInfo[*top.pc].length; continue; } @@ -312,6 +328,44 @@ break; } +case OP_ADD_F: +{ + float right = POP_F(top); + float left = POP_F(top); + float result = left + right; + PUSH_F(top, result); + break; +} + +case OP_SUB_F: +{ + float right = POP_F(top); + float left = POP_F(top); + float result = left - right; + PUSH_F(top, result); + break; +} + +case OP_MUL_F: +{ + float right = POP_F(top); + float left = POP_F(top); + float result = left * right; + PUSH_F(top, result); + break; +} + +case OP_DIV_F: +{ + float right = POP_F(top); + float left = POP_F(top); + if (!right) + return false; + float result = left / right; + PUSH_F(top, result); + break; +} + case OP_POP: top.sp--; break; @@ -398,6 +452,14 @@ break; } +case OP_CVT_I2F: +{ + int ival = POP_I32(top); + float fval = (float)ival; + PUSH_F(top, fval); + break; +} + default: assert(false); return false; diff -r f43e399ab622 -r 633176823669 jitcraft/Interpreter.h --- a/jitcraft/Interpreter.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Interpreter.h Wed Jun 27 02:21:56 2012 -0700 @@ -22,7 +22,7 @@ namespace jc { -bool Interpret(Frame *frame, int *result); +bool Interpret(Frame *frame, LocalSlot *result); // Virtual machine helper functions. UnsafeArray *AllocateUnsafeArray(unsigned width, unsigned ndims, int *dims); diff -r f43e399ab622 -r 633176823669 jitcraft/Opcodes.cpp --- a/jitcraft/Opcodes.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Opcodes.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -55,6 +55,11 @@ case OP_SUB: case OP_MUL: case OP_DIV: + case OP_ADD_F: + case OP_SUB_F: + case OP_MUL_F: + case OP_DIV_F: + case OP_CVT_I2F: case OP_POP: case OP_LD_STA_I: case OP_LD_STA_P: @@ -70,6 +75,10 @@ fprintf(fp, "%d", READ_INT32(pc)); break; + case OP_FLOAT: + fprintf(fp, "%f", READ_FLOAT(pc)); + break; + case OP_JL: case OP_JLE: case OP_JG: diff -r f43e399ab622 -r 633176823669 jitcraft/Opcodes.h --- a/jitcraft/Opcodes.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Opcodes.h Wed Jun 27 02:21:56 2012 -0700 @@ -80,6 +80,11 @@ { return *reinterpret_cast(pc + 1); } +static inline float +READ_FLOAT(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} class Code; diff -r f43e399ab622 -r 633176823669 jitcraft/Opcodes.tbl --- a/jitcraft/Opcodes.tbl Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Opcodes.tbl Wed Jun 27 02:21:56 2012 -0700 @@ -116,3 +116,19 @@ // push(array) // push(index) OPDEF(OP_BOUNDS_STA, 28, "bounds.sta", 1, 2, 2) + +// Pushes a 32-bit IEEE-754 floating point immediate onto the stack. +OPDEF(OP_FLOAT, 29, "float", 5, 0, 1) + +// Converts an integer to a 32-bit IEEE-754 floating point immediate. +OPDEF(OP_CVT_I2F, 30, "cvt.i2f", 1, 1, 1) + +// Stack transitions for these binary operators: +// rhs = pop() +// lhs = pop() +// push(lhs OP rhs) +OPDEF(OP_ADD_F, 31, "add.f", 1, 2, 1) +OPDEF(OP_SUB_F, 32, "sub.f", 1, 2, 1) +OPDEF(OP_MUL_F, 33, "mul.f", 1, 2, 1) +OPDEF(OP_DIV_F, 34, "div.f", 1, 2, 1) + diff -r f43e399ab622 -r 633176823669 jitcraft/Parser.cpp --- a/jitcraft/Parser.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Parser.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -68,6 +68,25 @@ return proxy; } +bool +Parser::matchName(Name **name, TokenKind kind) +{ + assert(kind == TOK_NAME || kind == TOK_LABEL); + *name = NULL; + if (!match(kind)) + return false; + *name = strings_.add(scanner_.literal()); + return true; +} + +Name * +Parser::expectName() +{ + if (!expect(TOK_NAME)) + return NULL; + return strings_.add(scanner_.literal()); +} + Expression * Parser::primitive() { @@ -79,11 +98,18 @@ return bind(name); } + case TOK_FLOAT_LITERAL: + { + scanner_.scan(); + double value = Scanner::ParseDouble(scanner_.literal()); + return new (pool_) FloatLiteral(value); + } + case TOK_INTEGER_LITERAL: { scanner_.scan(); int value = atoi(scanner_.literal()); - return new (pool_) IntegerLiteralExpression(value); + return new (pool_) IntegerLiteral(value); } default: @@ -326,11 +352,13 @@ if (!expect(TOK_NEW)) return NULL; - // Get the name. - if (!expect(TOK_NAME)) + Name *tag; + if (matchName(&tag, TOK_LABEL) && !tag) return NULL; - - Name *name = strings_.add(scanner_.literal()); + + Name *name = expectName(); + if (!name) + return NULL; ExpressionList *postDimensions = NULL; if (scanner_.peek() == TOK_LBRACKET) { @@ -359,7 +387,7 @@ } Variable *var = scope_->declareVariable(name); - VariableDeclaration *decl = new (pool_) VariableDeclaration(var, postDimensions, expr); + VariableDeclaration *decl = new (pool_) VariableDeclaration(var, tag, postDimensions, expr); return decl; } @@ -549,10 +577,7 @@ if (scanner_.peek() == TOK_RBRACE) break; - if (!expect(TOK_NAME)) - return NULL; - - Name *name = strings_.add(scanner_.literal()); + Name *name = expectName(); if (!name) return NULL; @@ -582,9 +607,7 @@ Statement * Parser::function() { - expect(TOK_NAME); - - Name *name = strings_.add(scanner_.literal()); + Name *name = expectName(); if (!name) return NULL; @@ -607,10 +630,7 @@ return NULL; if (!match(TOK_RPAREN)) { while (true) { - if (!expect(TOK_NAME)) - return NULL; - - Name *name = strings_.add(scanner_.literal()); + Name *name = expectName(); if (!name) return NULL; @@ -783,9 +803,13 @@ node->initialization()->accept(this); unindent(); } - void visit(IntegerLiteralExpression *node) { + void visit(IntegerLiteral *node) { prefix(); - fprintf(fp_, "[ LiteralExpression (%d)\n", node->value()); + fprintf(fp_, "[ IntegerLiteral (%d)\n", node->value()); + } + void visit(FloatLiteral *node) { + prefix(); + fprintf(fp_, "[ FloatLiteral (%f)\n", node->value()); } void visit(IfStatement *node) { prefix(); diff -r f43e399ab622 -r 633176823669 jitcraft/Parser.h --- a/jitcraft/Parser.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Parser.h Wed Jun 27 02:21:56 2012 -0700 @@ -45,6 +45,8 @@ Scope *enterScope(Scope::Kind kind); VariableDeclaration *variableDeclaration(); + bool matchName(Name **name, TokenKind kind = TOK_NAME); + Name *expectName(); NameProxy *bind(Name *name); Expression *primitive(); Expression *call(Expression *callee); diff -r f43e399ab622 -r 633176823669 jitcraft/Scanner.cpp --- a/jitcraft/Scanner.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Scanner.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -86,21 +86,115 @@ return false; } +static inline bool +IsDigit(char c) +{ + return c >= '0' && c <= '9'; +} + +// Based off the logic in sc2.c's ftoi()... +double +Scanner::ParseDouble(const char *string) +{ + const char *ptr = string; + + double number = 0.0; + while (IsDigit(*ptr)) { + number = (number * 10) + (*ptr - '0'); + ptr++; + } + + assert(*ptr == '.'); + ptr++; + + double fraction = 0.0; + double multiplier = 1.0; + while (IsDigit(*ptr)) { + fraction = (fraction * 10) + (*ptr - '0'); + multiplier = multiplier / 10.0; + ptr++; + } + + number += fraction * multiplier; + + if (*ptr++ == 'e') { + int sign = 1; + if (*ptr == '-') { + sign = -1; + ptr++; + } + + int exponent = 0; + while (IsDigit(*ptr)) { + exponent = (exponent * 10) + (*ptr - '0'); + ptr++; + } + + multiplier = pow(10.0, exponent * sign); + number *= multiplier; + } + + return number; +} + TokenKind -Scanner::integerLiteral(char first) +Scanner::numberLiteral(char first) { literal_ = first; + char c; for (;;) { - char c = getChar(); - if (c < '0' || c > '9') { - putChar(c); + c = getChar(); + if (!IsDigit(c)) break; - } literal_ += c; } - return TOK_INTEGER_LITERAL; + if (c != '.') { + putChar(c); + return TOK_INTEGER_LITERAL; + } + literal_ += c; + + c = getChar(); + if (!IsDigit(c)) { + assert(false); + return TOK_ERROR; + } + literal_ += c; + + for (;;) { + c = getChar(); + if (!IsDigit(c)) + break; + literal_ += c; + } + + if (c != 'e') { + putChar(c); + } else { + literal_ += c; + c = getChar(); + if (c == '-') { + literal_ += c; + c = getChar(); + } + if (!IsDigit(c)) { + assert(false); + return TOK_ERROR; + } + literal_ += c; + for (;;) { + c = getChar(); + if (!IsDigit(c)) { + putChar(c); + break; + } + literal_ += c; + } + } + + return TOK_FLOAT_LITERAL; } TokenKind @@ -108,10 +202,12 @@ { literal_ = first; + char c; for (;;) { - char c = getChar(); + c = getChar(); if (!isascii(c) || (!isalnum(c) && c != '_')) { - putChar(c); + if (c != ':') + putChar(c); break; } literal_ += c; @@ -129,7 +225,7 @@ return TOK_ELSE; if (literal_ == "enum") return TOK_ENUM; - return TOK_NAME; + return (c == ':') ? TOK_LABEL : TOK_NAME; } TokenKind @@ -218,9 +314,10 @@ default: if (c >= '0' && c <= '9') - return integerLiteral(c); + return numberLiteral(c); if (c == '_' || (isascii(c) && isalpha(c))) return identifier(c); + assert(false); return TOK_ERROR; } } diff -r f43e399ab622 -r 633176823669 jitcraft/Scanner.h --- a/jitcraft/Scanner.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Scanner.h Wed Jun 27 02:21:56 2012 -0700 @@ -38,7 +38,7 @@ void putChar(char c); void eatSingleLine(); bool eatComment(); - TokenKind integerLiteral(char first); + TokenKind numberLiteral(char first); TokenKind identifier(char first); TokenKind next(); @@ -46,6 +46,8 @@ public: Scanner(const char *stream, size_t length); + static double ParseDouble(const char *string); + TokenKind scan(); TokenKind peek(); TokenKind last() const { diff -r f43e399ab622 -r 633176823669 jitcraft/SemanticAnalysis.cpp --- a/jitcraft/SemanticAnalysis.cpp Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/SemanticAnalysis.cpp Wed Jun 27 02:21:56 2012 -0700 @@ -80,8 +80,8 @@ return true; } - case AstNode::kIntegerLiteralExpression: - *out = BoxedPrimitive::Int(expression->toIntegerLiteralExpression()->value()); + case AstNode::kIntegerLiteral: + *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value()); return true; default: @@ -111,15 +111,29 @@ name->setType(name->binding()->type()); } +static inline bool +TypeCheck(Type *left, Type *right) +{ + if (!left || !right) + return false; + + if (left->isPrimitive()) { + if (!right->isPrimitive()) + return false; + return left->primitive() == right->primitive(); + } + + return false; +} + void SemanticAnalysis::visit(Assignment *node) { node->expression()->accept(this); if (node->lvalue()->isIndexExpression()) { - node->lvalue()->accept(this); - - node->setType(node->lvalue()->type()); + node->lvalue()->accept(this); + node->setType(node->lvalue()->type()->contained()); } else { NameProxy *proxy = node->lvalue()->toNameProxy(); if (!proxy->binding()->kind() == BoundName::VARIABLE) { @@ -130,11 +144,37 @@ Variable *var = proxy->binding()->toVariable(); node->setType(var->type()); } + + assert(TypeCheck(node->type(), node->expression()->type())); } + void SemanticAnalysis::visit(BinaryExpression *node) { + node->left()->accept(this); + node->right()->accept(this); + + if (node->token() >= TOK_PLUS && node->token() <= TOK_SLASH) { + // Both sides must be primitive. + Type *left = node->left()->type(); + Type *right = node->right()->type(); + 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->isInt()); + assert(right->isFloat() || right->isInt()); + node->setType(left->isFloat() ? left : right); + return; + } + + // Otherwise, both sides must be integer. + assert(left->isInt() && right->isInt()); + node->setType(left); + return; + } } void @@ -169,9 +209,7 @@ void SemanticAnalysis::visit(VariableDeclaration *node) { - Tag *tag = NULL; - - Type *type = Type::NewPrimitive(tag); + Type *type = Type::NewPrimitive(node->tag()); if (!type) return; @@ -191,8 +229,8 @@ } int size = -1; - if (expr->isIntegerLiteralExpression()) { - size = expr->toIntegerLiteralExpression()->value(); + if (expr->isIntegerLiteral()) { + size = expr->toIntegerLiteral()->value(); if (size <= 0) { error(); return; @@ -211,7 +249,7 @@ } void -SemanticAnalysis::visit(IntegerLiteralExpression *node) +SemanticAnalysis::visit(IntegerLiteral *node) { Type *type = Type::NewPrimitive(NULL); node->setType(type); @@ -259,12 +297,8 @@ void SemanticAnalysis::visit(EnumStatement *node) { - Tag *tag = NULL; - if (node->name()) { - tag = new Tag(node->name()); - if (!tag) - return; - } + // :TODO: test "enum Float { ..." + Name *tag = node->name(); Type *type = Type::NewPrimitive(tag); if (!type) @@ -288,3 +322,13 @@ value++; } } + +void +SemanticAnalysis::visit(FloatLiteral *node) +{ + Type *type = Type::NewPrimitive(PrimitiveType_Float); + if (!type) + return; + + node->setType(type); +} diff -r f43e399ab622 -r 633176823669 jitcraft/SemanticAnalysis.h --- a/jitcraft/SemanticAnalysis.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/SemanticAnalysis.h Wed Jun 27 02:21:56 2012 -0700 @@ -47,13 +47,14 @@ void visit(ForStatement *node); void visit(BlockStatement *node); void visit(VariableDeclaration *node); - void visit(IntegerLiteralExpression *node); + void visit(IntegerLiteral *node); void visit(ExpressionStatement *node); void visit(FunctionStatement *node); void visit(CallExpression *node); void visit(IfStatement *node); void visit(IndexExpression *node); void visit(EnumStatement *node); + void visit(FloatLiteral *node); }; } // namespace jc diff -r f43e399ab622 -r 633176823669 jitcraft/Stack.h --- a/jitcraft/Stack.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Stack.h Wed Jun 27 02:21:56 2012 -0700 @@ -28,6 +28,7 @@ union LocalSlot { int i32; + float f; void *p; UnsafeArray *unsafeArray; }; diff -r f43e399ab622 -r 633176823669 jitcraft/Token.h --- a/jitcraft/Token.h Tue Jun 26 00:58:55 2012 -0700 +++ b/jitcraft/Token.h Wed Jun 27 02:21:56 2012 -0700 @@ -27,9 +27,11 @@ _(RETURN, "'return'") \ _(NAME, "") \ _(INTEGER_LITERAL, "") \ + _(FLOAT_LITERAL, "") \ _(IF, "") \ _(ELSE, "") \ _(ENUM, "") \ + _(LABEL, "