# HG changeset patch # User David Anderson # Date 1343199214 25200 # Node ID 32ad0523b3095bdc60f2c8d4e78b0bdea7d6fc7f # Parent 06a0015b6a5c6e44993a0f00bbebe284bc6c6381 Renamed jitcraft/ to src/, fixed some /Wp64 warnings. diff -r 06a0015b6a5c -r 32ad0523b309 src/API.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/API.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,483 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "../include/keima.h" +#include "Zone.h" +#include "RootScopes.h" +#include "CompileContext.h" +#include "Parser.h" +#include "Scopes.h" +#include "SemanticAnalysis.h" +#include "BytecodeCompiler.h" +#include "Modules.h" +#include "GlobalRoots.h" +#include "Reference.h" +#include "Array.h" +#include "Interpreter.h" +#include "Stack.h" +#include "Strings.h" + +using namespace ke; + +bool +Ke::Keima::Initialize() +{ + SingleThreadedZone = new Zone(); + if (!SingleThreadedZone || !SingleThreadedZone->initialize()) + return false; + return true; +} + +void +Ke::Keima::Shutdown() +{ + delete SingleThreadedZone; + SingleThreadedZone = NULL; +} + +Ke::HandleScope::HandleScope() + : zone_(ke::ZONE()), + position_(zone_->rootScopes().position()), + prev_(*zone_->rootScopes().scope()) +{ + *zone_->rootScopes().scope() = this; +} + +Ke::HandleScope::~HandleScope() +{ + zone_->rootScopes().unwind(position_); + *zone_->rootScopes().scope() = prev_; +} + +ke::Object ** +Ke::HandleScope::CreateHandle(ke::Object *obj) +{ + if (!obj) + return NULL; + + return ZONE()->rootScopes().make(obj); +} + +template static inline To * +Open(Ke::Handle from) +{ + return *reinterpret_cast(*from); +} + +template static inline To * +Open(From *from) +{ + return *reinterpret_cast(from); +} + +template static inline Ke::Local +ToLocal(From *from) +{ + return Ke::Local(reinterpret_cast(Ke::HandleScope::CreateHandle(from))); +} + +static ke::Module * +CompileFromSource(CompileContext &cc, const char *path, const char *stream, size_t length) +{ + Parser parser(ZONE(), cc, stream, length); + ParseTree *tree = parser.parse(); + if (!tree) + return NULL; + tree->dump(stdout); + + if (!Scope::Bind(cc, tree)) + return NULL; + + SemanticAnalysis semantics(ZONE(), cc, tree); + if (!semantics.analyze()) + return NULL; + + Scope::Allocate(tree); + + BytecodeCompiler compiler(ZONE(), tree, NULL); + ke::Local module(ke::ZONE(), compiler.compile()); + if (!module) + return NULL; + + ke::InvokeArgs args; + if (!ke::ZONE()->stack().pushModuleFrame(module, args)) + return NULL; + if (!ke::Interpret(ke::ZONE(), NULL, NULL)) + return NULL; + + return module; +} + +Ke::Local +Ke::Compile(const char *path) +{ + RootScope roots(ZONE()); + CompileContext cc(ZONE(), path); + + char *bytes = NULL; + size_t length; + { + FILE *fp = fopen(path, "rb"); + if (!fp) { + cc.reportError(SourcePosition(), Message_CantOpenFile); + return Ke::Local(); + } + + if (fseek(fp, 0, SEEK_END)) { + cc.reportError(SourcePosition(), Message_ErrorSeekingFile); + fclose(fp); + return Ke::Local();; + } + long size = ftell(fp); + if (size < 0) { + cc.reportError(SourcePosition(), Message_ErrorSeekingFile); + fclose(fp); + return Ke::Local();; + } + rewind(fp); + length = size_t(size); + bytes = new char[length + 1]; + if (fread(bytes, 1, length, fp) != length) { + cc.reportError(SourcePosition(), Message_ErrorReadingFile); + fclose(fp); + delete [] bytes; + return Ke::Local();; + } + fclose(fp); + } + + ke::Local module(ZONE(), CompileFromSource(cc, path, bytes, length)); + delete [] bytes; + if (!module) + return Ke::Local(); + + ke::Local pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old)); + if (!pathstr) + return Ke::Local(); + + module->setPath(pathstr); + return ToLocal(module); +} + +void +Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter) +{ + ZONE()->installErrorReporter(reporter); +} + +class StackFrameIterator : public Ke::IStackFrameIterator +{ + StackIter iter_; + + public: + StackFrameIterator() + : iter_(ZONE()) + { + } + + virtual bool done() { + return iter_.done(); + } + + const char *function() { + return iter_.function(); + } + + const char *source() { + return iter_.source(); + } + + unsigned lineNumber() { + return iter_.lineNumber(); + } + + void prev() { + iter_.prev(); + } +}; + +Ke::IStackFrameIterator * +Ke::Keima::NewStackFrameIterator() +{ + return new StackFrameIterator(); +} + +static inline ke::HeapRoot * +FromAPI(ke::HeapRoot *from) +{ + ke::Address address = reinterpret_cast(from); + address -= offsetof(ke::HeapRoot, obj); + return reinterpret_cast(address); +} + +ke::HeapRoot * +Ke::Internals::NewHeapRoot(ke::Object **ptr) +{ + ke::Local obj(ZONE(), *ptr); + ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(obj); + if (!root) + return NULL; + + // Compute an interior pointer, so it looks like a Handle to an object. + return reinterpret_cast(&root->obj); +} + +void +Ke::Internals::IncRef(ke::HeapRoot *root) +{ + ZONE()->globalRoots()->incref(FromAPI(root)); +} + +void +Ke::Internals::DecRef(ke::HeapRoot *root) +{ + ZONE()->globalRoots()->decref(FromAPI(root)); +} + +bool +Ke::Keima::IsInCallStack(Handle argModule) +{ + ke::Local module(ke::ZONE(), Open(argModule)); + return ZONE()->stack().isInCallStack(module); +} + +void +Ke::Keima::CollectGarbage() +{ + ke::ZONE()->heap().reclaim(); +} + +unsigned +Ke::Module::numPublics() +{ + ke::Local module(ke::ZONE(), Open(this)); + if (!module->publics()) + return 0; + return module->publics()->length(); +} + +Ke::Local +Ke::Module::getPublic(unsigned index) +{ + ke::Local module(ke::ZONE(), Open(this)); + ke::Local fun(ke::ZONE(), ke::Function::cast(module->publics()->at(index))); + return ToLocal(fun); +} + +unsigned +Ke::Module::numNatives() +{ + ke::Local module(ke::ZONE(), Open(this)); + if (!module->natives()) + return 0; + return module->natives()->length(); +} + +Ke::Local +Ke::Module::getNative(unsigned index) +{ + ke::Local module(ke::ZONE(), Open(this)); + ke::Local fun(ke::ZONE(), ke::Native::cast(module->natives()->at(index))); + return ToLocal(fun); +} + +void +Ke::Module::setNativeIdentity(void *identity) +{ + ke::Local module(ke::ZONE(), Open(this)); + module->setNativeIdentity(identity); +} + +unsigned +Ke::String::length() +{ + ke::Local obj(ke::ZONE(), Open(this)); + return obj->length(); +} + +const char * +Ke::String::chars() +{ + ke::Local obj(ke::ZONE(), Open(this)); + return obj->chars(); +} + +Ke::Local +Ke::Function::name() +{ + ke::Local fun(ke::ZONE(), Open(this)); + return ToLocal(fun->name()); +} + +Ke::Local +Ke::Native::name() +{ + ke::Local native(ke::ZONE(), Open(this)); + return ToLocal(native->name()); +} + +void +Ke::Native::bind(void *function) +{ + ke::Local native(ke::ZONE(), Open(this)); + native->bind(function); +} + +static inline bool +CheckInvokeType(const Ke::Parameter ¶m, Type *type) +{ + switch (param.type()) { + case Ke::Parameter::Array: + return type->isArray() && + type->levels() == 1 && + type->contained()->isPrimitive() && + (type->contained()->primitive() != PrimitiveType_Char); + case Ke::Parameter::CharArray: + return type->isCharArray(); + case Ke::Parameter::Float: + return type->isFloat(); + case Ke::Parameter::FloatRef: + return type->isReference() && type->contained()->isFloat(); + case Ke::Parameter::Int: + return type->isInt32(); + case Ke::Parameter::IntRef: + return type->isReference() && type->contained()->isInt32(); + default: + return false; + } +} + +bool +Ke::Keima::CheckInvokeTypes(Ke::Handle argFun, const Ke::Parameter *params, unsigned nparams) +{ + ke::Local fun(ke::ZONE(), Open(argFun)); + + if (fun->nargs() != nparams) + return false; + + for (unsigned i = 0; i < nparams; i++) { + if (!CheckInvokeType(params[i], fun->argType(i))) + return false; + } + + // Functions can only return tagged cells, for ISPE1-compatible Invoke(). + ke::Local returnType(ke::ZONE(), fun->type()->returnType()); + if ((!returnType->isPrimitive() && !returnType->isVoid()) || returnType->isTraceable()) + return false; + + return true; +} + +bool +Ke::Keima::Invoke(Handle argFun, const Parameter *params, unsigned nparams, int *retval) +{ + assert(CheckInvokeTypes(argFun, params, nparams)); + + ke::Local fun(ke::ZONE(), Open(argFun)); + + if (fun->nargs() != nparams) { + // :TODO: report error + assert(false); + return false; + } + + ke::InvokeArgs args; + if (!ke::ZONE()->stack().pushInvokeFrame(fun, args)) + return false; + + for (unsigned i = 0; i < nparams; i++) { + ke::LocalSlot &slot = args.arg(i); + switch (params[i].type()) { + case Ke::Parameter::Int: + slot.i32 = params[i].i32(); + break; + + case Ke::Parameter::Float: + slot.f = params[i].f(); + break; + + case Ke::Parameter::IntRef: + if (params[i].writeback()) { + slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack); + } else { + LocalSlot other; + other.i32 = *params[i].i32p(); + slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack); + } + if (!slot.ref) + return false; + break; + + case Ke::Parameter::FloatRef: + if (params[i].writeback()) { + slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack); + } else { + LocalSlot other; + other.f = *params[i].fp(); + slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack); + } + if (!slot.ref) + return false; + break; + + case Ke::Parameter::Array: + { + ke::Local map(ke::ZONE(), ke::ZONE()->genericArrayMap()); + if (params[i].writeback()) { + slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(), + params[i].buffer(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + } else { + Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(int)); + } + break; + } + + case Ke::Parameter::CharArray: + { + ke::Local map(ke::ZONE(), ke::ZONE()->charArrayMap()); + if (params[i].writeback()) { + slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(), + params[i].buffer(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + } else { + Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(char)); + } + break; + } + } + } + + LocalSlot result; + if (!Interpret(ke::ZONE(), NULL, &result)) + return false; + + if (fun->type()->returnType()->isVoid()) + result.i32 = 0; + + *retval = result.i32; + return true; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/AST.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/AST.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,873 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_ast_h_ +#define _include_jitcraft_ast_h_ + +#include "PoolAllocator.h" +#include "Token.h" +#include "NameBinding.h" +#include "Vector.h" + +namespace ke { + +#define ASTKINDS(_) \ + _(VariableDeclaration) \ + _(ForStatement) \ + _(ReturnStatement) \ + _(IntegerLiteral) \ + _(FloatLiteral) \ + _(StringLiteral) \ + _(BinaryExpression) \ + _(BlockStatement) \ + _(Assignment) \ + _(NameProxy) \ + _(ExpressionStatement) \ + _(FunctionStatement) \ + _(CallExpression) \ + _(IfStatement) \ + _(IndexExpression) \ + _(EnumStatement) \ + _(WhileStatement) \ + _(BreakStatement) \ + _(ContinueStatement) \ + _(IncDecExpression) \ + _(UnaryExpression) \ + _(TernaryExpression) + +// Forward declarations. +#define _(name) class name; +ASTKINDS(_) +#undef _ + +class AstVisitor; + +// Interface for AST nodes. +class AstNode : public PoolObject +{ + SourcePosition pos_; + + public: + enum Kind { +# define _(name) k##name, + ASTKINDS(_) +# undef _ + AstKind_Invalid + }; + + AstNode(const SourcePosition &pos) + : pos_(pos) + { + } + + virtual Kind kind() const = 0; + +#define _(name) bool is##name() { return kind() == k##name; } \ + name *to##name() { assert(is##name()); return (name *)this; } + ASTKINDS(_) +#undef _ + + virtual void accept(AstVisitor *visitor) = 0; + + const SourcePosition &pos() const { + return pos_; + } +}; + +class AstVisitor +{ + public: +#define _(name) virtual void visit(name *node) = 0; + ASTKINDS(_) +#undef _ +}; + +class NameLocation +{ + public: + enum Kind { + LOCAL + }; + + static NameLocation Local(VariableDeclaration *decl) { + NameLocation loc; + loc.kind_ = LOCAL; + loc.u_.decl = decl; + return loc; + } + + Kind kind() const { + return kind_; + } + + VariableDeclaration *local() const { + assert(kind() == LOCAL); + return u_.decl; + } + + private: + Kind kind_; + union { + VariableDeclaration *decl; + } u_; +}; + +class Statement : public AstNode +{ + public: + Statement(const SourcePosition &pos) + : AstNode(pos) + { + } +}; + +class Expression : public AstNode +{ + ScopedRoot type_; + + public: + Expression(const SourcePosition &pos) + : AstNode(pos), + type_(NULL) + { + } + + void setType(Type *type) { + type_ = type; + } + Type *type() const { + return type_; + } +}; + +typedef PoolList StatementList; +typedef PoolList ExpressionList; + +class VariableDeclaration : public Statement +{ + Variable *variable_; + Expression *initialization_; + ExpressionList *postDimensions_; + ScopedRoot tag_; + + public: + VariableDeclaration(const SourcePosition &pos, Variable *var, Handle tag, + ExpressionList *postDimensions, Expression *initialization) + : Statement(pos), + variable_(var), + initialization_(initialization), + postDimensions_(postDimensions), + tag_(tag) + { + } + + String *tag() const { + return tag_; + } + Kind kind() const { + return kVariableDeclaration; + } + Variable *variable() const { + return variable_; + } + Expression *initialization() const { + return initialization_; + } + ExpressionList *postDimensions() const { + return postDimensions_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class Parameter : public PoolObject +{ + Variable *variable_; + ScopedRoot tag_; + bool reference_; + bool const_; + ExpressionList *dimensions_; + + public: + Parameter(Variable *var, Handle tag, bool reference, bool isConst, ExpressionList *dimensions) + : variable_(var), + tag_(tag), + reference_(reference), + const_(isConst), + dimensions_(dimensions) + { + } + + String *tag() const { + return tag_; + } + Variable *variable() const { + return variable_; + } + bool reference() const { + return reference_; + } + ExpressionList *dimensions() const { + return dimensions_; + } + bool isConst() const { + return const_; + } +}; + +class NameProxy : public Expression +{ + ScopedRoot name_; + BoundName *binding_; + + public: + NameProxy(const SourcePosition &pos, Handle name, BoundName *binding) + : Expression(pos), + name_(name), + binding_(binding) + { + } + + Kind kind() const { + return kNameProxy; + } + String *name() const { + return name_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } + BoundName *binding() const { + return binding_; + } + void bind(BoundName *name) { + assert(!binding()); + binding_ = name; + } +}; + +class IntegerLiteral : public Expression +{ + int value_; + + public: + IntegerLiteral(const SourcePosition &pos, int value) + : Expression(pos), + value_(value) + { + } + + Kind kind() const { + return kIntegerLiteral; + } + int value() const { + return value_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class FloatLiteral : public Expression +{ + double value_; + + public: + FloatLiteral(const SourcePosition &pos, double value) + : Expression(pos), + value_(value) + { + } + + Kind kind() const { + return kFloatLiteral; + } + double value() const { + return value_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class StringLiteral : public Expression +{ + ScopedRoot string_; + + public: + StringLiteral(const SourcePosition &pos, ByteArray *array) + : Expression(pos), + string_(array) + { + } + + Kind kind() const { + return kStringLiteral; + } + ByteArray *string() const { + return string_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class UnaryExpression : public Expression +{ + Expression *expression_; + TokenKind token_; + + public: + UnaryExpression(const SourcePosition &pos, TokenKind token, Expression *expr) + : Expression(pos), + expression_(expr), + token_(token) + { + } + + Kind kind() const { + return kUnaryExpression; + } + Expression *expression() const { + return expression_; + } + TokenKind token() const { + return token_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class BinaryExpression : public Expression +{ + Expression *left_; + Expression *right_; + TokenKind token_; + + public: + BinaryExpression(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) + : Expression(pos), + left_(left), + right_(right), + token_(token) + { + } + + Kind kind() const { + return kBinaryExpression; + } + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } + TokenKind token() const { + return token_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class TernaryExpression : public Expression +{ + Expression *condition_; + Expression *left_; + Expression *right_; + + public: + TernaryExpression(const SourcePosition &pos, Expression *condition, Expression *left, Expression *right) + : Expression(pos), + condition_(condition), + left_(left), + right_(right) + { + } + + Kind kind() const { + return kTernaryExpression; + } + Expression *condition() const { + return condition_; + } + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class IndexExpression : public Expression +{ + Expression *left_; + Expression *right_; + + public: + IndexExpression(const SourcePosition &pos, Expression *left, Expression *right) + : Expression(pos), + left_(left), + right_(right) + { + } + + Kind kind() const { + return kIndexExpression; + } + Expression *left() const { + return left_; + } + Expression *right() const { + return right_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class CallExpression : public Expression +{ + Expression *callee_; + ExpressionList *arguments_; + + public: + CallExpression(const SourcePosition &pos, Expression *callee, ExpressionList *arguments) + : Expression(pos), + callee_(callee), + arguments_(arguments) + { + } + + Kind kind() const { + return kCallExpression; + } + Expression *callee() const { + return callee_; + } + ExpressionList *arguments() const { + return arguments_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class ForStatement : public Statement +{ + Scope *scope_; + Statement *initialization_; + Expression *condition_; + Statement *update_; + Statement *body_; + + public: + ForStatement(const SourcePosition &pos, Scope *scope, Statement *initialization, + Expression *condition, Statement *update, Statement *body) + : Statement(pos), + scope_(scope), + initialization_(initialization), + condition_(condition), + update_(update), + body_(body) + { + } + + Kind kind() const { + return kForStatement; + } + Statement *initialization() const { + return initialization_; + } + Scope *scope() const { + return scope_; + } + Expression *condition() const { + return condition_; + } + Statement *update() const { + return update_; + } + Statement *body() const { + return body_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class WhileStatement : public Statement +{ + TokenKind token_; + Expression *condition_; + Statement *body_; + + public: + WhileStatement(const SourcePosition &pos, TokenKind kind, Expression *condition, Statement *body) + : Statement(pos), + token_(kind), + condition_(condition), + body_(body) + { + } + + Kind kind() const { + return kWhileStatement; + } + TokenKind token() const { + return token_; + } + Expression *condition() const { + return condition_; + } + Statement *body() const { + return body_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class ReturnStatement : public Statement +{ + Expression *expression_; + + public: + ReturnStatement(const SourcePosition &pos, Expression *expression) + : Statement(pos), + expression_(expression) + { + } + + Kind kind() const { + return kReturnStatement; + } + Expression *expression() const { + return expression_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class BreakStatement : public Statement +{ + public: + BreakStatement(const SourcePosition &pos) + : Statement(pos) + { + } + + Kind kind() const { + return kBreakStatement; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class ContinueStatement : public Statement +{ + public: + ContinueStatement(const SourcePosition &pos) + : Statement(pos) + { + } + + Kind kind() const { + return kContinueStatement; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class Assignment : public Expression +{ + TokenKind token_; + Expression *lvalue_; + Expression *expression_; + + public: + Assignment(const SourcePosition &pos, TokenKind token, Expression *left, Expression *right) + : Expression(pos), + token_(token), + lvalue_(left), + expression_(right) + { + } + + Kind kind() const { + return kAssignment; + } + TokenKind token() const { + return token_; + } + Expression *lvalue() const { + return lvalue_; + } + Expression *expression() const { + return expression_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class ExpressionStatement : public Statement +{ + Expression *expression_; + + public: + ExpressionStatement(Expression *expression) + : Statement(expression->pos()), + expression_(expression) + { + } + + Kind kind() const { + return kExpressionStatement; + } + Expression *expression() const { + return expression_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class BlockStatement : public Statement +{ + Scope *scope_; + StatementList *statements_; + + public: + BlockStatement(const SourcePosition &pos, Scope *scope, StatementList *statements) + : Statement(pos), + scope_(scope), + statements_(statements) + { + } + + Kind kind() const { + return kBlockStatement; + } + Scope *scope() const { + return scope_; + } + StatementList *statements() const { + return statements_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +typedef PoolList ParameterList; + +class FunctionStatement : public Statement +{ + Scope *scope_; + NamedFunction *fun_; + Statement *body_; + ParameterList *parameters_; + bool explicitReturn_; + + public: + FunctionStatement(const SourcePosition &pos, Scope *scope, NamedFunction *fun, Statement *body, + ParameterList *parameters, bool explicitReturn) + : Statement(pos), + scope_(scope), + fun_(fun), + body_(body), + parameters_(parameters), + explicitReturn_(explicitReturn) + { + } + + Kind kind() const { + return kFunctionStatement; + } + Scope *scope() const { + return scope_; + } + NamedFunction *fun() const { + return fun_; + } + Statement *body() const { + return body_; + } + ParameterList *parameters() const { + return parameters_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } + bool hasExplicitReturn() const { + return explicitReturn_; + } +}; + +class IfStatement : public Statement +{ + Expression *condition_; + Statement *ifTrue_; + Statement *ifFalse_; + + public: + IfStatement(const SourcePosition &pos, Expression *condition, Statement *ifTrue) + : Statement(pos), + condition_(condition), + ifTrue_(ifTrue), + ifFalse_(NULL) + { + } + + Kind kind() const { + return kIfStatement; + } + Expression *condition() const { + return condition_; + } + Statement *ifTrue() const { + return ifTrue_; + } + Statement *ifFalse() const { + return ifFalse_; + } + void setIfFalse(Statement *ifFalse) { + ifFalse_ = ifFalse; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class EnumStatement : public Statement +{ + ScopedRoot name_; + + public: + struct Entry { + NamedConstant *name; + Expression *expr; + + Entry(NamedConstant *name, Expression *expr) + : name(name), + expr(expr) + { + } + }; + + typedef PoolList EntryList; + + public: + EnumStatement(const SourcePosition &pos, Handle name, EntryList *entries) + : Statement(pos), + name_(name), + entries_(entries) + { + } + + Kind kind() const { + return kEnumStatement; + } + String *name() const { + return name_; + } + EntryList *entries() const { + return entries_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } + + private: + EntryList *entries_; +}; + +class IncDecExpression : public Expression +{ + TokenKind token_; + Expression *expression_; + bool postfix_; + + public: + IncDecExpression(const SourcePosition &pos, TokenKind token, Expression *expression, bool postfix) + : Expression(pos), + token_(token), + expression_(expression), + postfix_(postfix) + { + } + + Kind kind() const { + return kIncDecExpression; + } + TokenKind token() const { + return token_; + } + Expression *expression() const { + return expression_; + } + bool postfix() const { + return postfix_; + } + void accept(AstVisitor *visitor) { + visitor->visit(this); + } +}; + +class Scope; + +typedef PoolList NameProxyList; + +class ParseTree : public PoolObject +{ + StatementList *statements_; + Scope *scope_; + NameProxyList *unbound_; + + public: + ParseTree(Scope *scope, StatementList *statements, NameProxyList *unbound) + : statements_(statements), + scope_(scope), + unbound_(unbound) + { + } + + void dump(FILE *fp); + + StatementList *statements() const { + return statements_; + } + Scope *scope() const { + return scope_; + } + NameProxyList *unbound() const { + return unbound_; + } +}; + +} + +#endif // _include_jitcraft_ast_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/AllocatorPolicies.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/AllocatorPolicies.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_allocators_h_ +#define _include_jc_allocators_h_ + +#include + +namespace ke { + +class ZoneAllocatorPolicy +{ + protected: + void reportOutOfMemory(); + void reportAllocationOverflow(); + + public: + void free(void *memory) { + ::free(memory); + } + void *malloc(size_t bytes) { + void *ptr = ::malloc(bytes); + if (!ptr) + reportOutOfMemory(); + return ptr; + } +}; + +} + +#endif // _include_jc_allocators_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Array.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Array.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Array.h" +#include "Heap.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +ArrayMap * +ArrayMap::New(Zone *zone, Handle type, Handle contained) +{ + Local obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray)); + if (!obj) + return NULL; + + Local map(zone, ArrayMap::cast(obj)); + map->type_ = type; + map->contained_ = contained; + return map; +} + +static inline size_t +SizeFor(Type *type, unsigned elements) +{ + if (type->levels() > 1) + return sizeof(Array) + elements * sizeof(ke::Object *); + if (type->contained()->isInt32()) + return sizeof(Array) + elements * sizeof(int); + if (type->contained()->primitive() == PrimitiveType_Char) + return sizeof(Array) + elements * sizeof(char); + assert(type->contained()->isFloat()); + return elements * sizeof(float); +} + +Array * +Array::New(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure) +{ + size_t elementBytes = SizeFor(map->type(), elements); + size_t bytes = sizeof(Array) + elementBytes; + Local array(zone, Array::cast(zone->allocate(map, bytes, tenure))); + if (!array) + return NULL; + + array->nelements_ = elements; + array->elements_ = (Array *)array + 1; + memset(array->elements_, 0, elementBytes); + return array; +} + +Array * +Array::NewExternal(Zone *zone, Handle map, uint32 nelements, void *buffer, + Heap::Tenure tenure) +{ + Local array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure))); + if (!array) + return NULL; + + array->nelements_ = nelements; + array->elements_ = buffer; + return array; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Array.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Array.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_array_h_ +#define _include_jitcraft_array_h_ + +#include "Heap.h" +#include "Handles.h" +#include "Types.h" + +namespace ke { + +class ArrayMap; + +class Array : public Object +{ + friend class ArrayMap; + + uint32 nelements_; + void *elements_; + + public: + static Array *New(Zone *zone, Handle map, uint32 elements, + Heap::Tenure = Heap::Tenure_Default); + static Array *NewExternal(Zone *zone, Handle map, uint32 nelements, + void *elements, Heap::Tenure = Heap::Tenure_Default); + + uint32 nelements() { + return nelements_; + } + void *vec() { + return elements_; + } + + inline int *int32Vec(); + inline float *floatVec(); + inline Object **objVec(); + inline char *chars(); + + inline Type *type(); + inline size_t elementSize(); + + int readInt32(size_t index) { + assert(index < nelements()); + return int32Vec()[index]; + } + void writeInt32(size_t index, int i) { + assert(index < nelements()); + int32Vec()[index] = i; + } + char readChar(size_t index) { + assert(index < nelements()); + return chars()[index]; + } + void writeChar(size_t index, char c) { + assert(index < nelements()); + chars()[index] = c; + } + float readFloat(size_t index) { + assert(index < nelements()); + return floatVec()[index]; + } + void writeFloat(size_t index, float f) { + assert(index < nelements()); + floatVec()[index] = f; + } + inline void *slotPtr(size_t index); + Object *readObject(size_t index) { + assert(index < nelements()); + return objVec()[index]; + } + void writeObject(Zone *zone, size_t index, Object *ptr) { + assert(index < nelements()); + write(zone, &objVec()[index], ptr); + } + + bool isExternal() { + // Inline arrays always have their elements pointing at the next slot. + Address buffer = reinterpret_cast
(elements_); + return buffer != reinterpret_cast
(this + 1); + } + + static inline size_t SizeOf(Array *array) { + if (array->isExternal()) + return sizeof(Array); + return sizeof(Array) + array->nelements() * array->elementSize(); + } + + static Array *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Array)); + return reinterpret_cast(obj); + } +}; + +class ArrayMap : public Map +{ + Barriered type_; + Barriered contained_; + + public: + static ArrayMap *New(Zone *zone, Handle type, Handle contained); + + Type *type() { + return type_; + } + ArrayMap *contained() { + return contained_; + } + + // GC Descriptor. + static inline size_t offsetOfType() { + return offsetof(ArrayMap, type_); + } + static inline size_t offsetOfContained() { + return offsetof(ArrayMap, contained_); + } + // End GC Descriptor. + + static ArrayMap *cast(Object *obj) { + assert(!obj || obj->is(MapKind_MetaArray)); + return reinterpret_cast(obj); + } +}; + +inline int * +Array::int32Vec() +{ + assert(type()->levels() == 1); + assert(type()->contained()->isInt32()); + return reinterpret_cast(vec()); +} + +inline float * +Array::floatVec() +{ + assert(type()->levels() == 1); + assert(type()->contained()->isFloat()); + return reinterpret_cast(vec()); +} + +inline Object ** +Array::objVec() +{ + assert(type()->levels() > 1); + return reinterpret_cast(vec()); +} + +inline void * +Array::slotPtr(size_t index) +{ + assert(type()->contained()->primitive() == PrimitiveType_Int32 || + type()->contained()->primitive() == PrimitiveType_Float); + assert(index < nelements()); + return reinterpret_cast(vec()) + index; +} + +inline char * +Array::chars() +{ + assert(type()->levels() == 1); + assert(type()->contained()->primitive() == PrimitiveType_Char); + return reinterpret_cast(vec()); +} + +inline Type * +Array::type() +{ + return ArrayMap::cast(map())->type(); +} + +inline size_t +Array::elementSize() +{ + assert(type()->isArray()); + if (type()->contained()->isArray()) + return sizeof(Object *); + switch (type()->contained()->primitive()) { + case PrimitiveType_Float: + return sizeof(float); + case PrimitiveType_Int32: + return sizeof(int); + default: + assert(type()->contained()->primitive() == PrimitiveType_Char); + return sizeof(char); + } +} + +} + +#endif // _include_jitcraft_array_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Barriers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Barriers.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_barriers_h_ +#define _include_jitcraft_barriers_h_ + +namespace ke { + +class Object; + +// Used to store unbarriered heap locations. +template +class Unbarriered +{ + T *obj_; + + public: + Unbarriered() + : obj_(NULL) + { + } + + T *operator ->() const { + return operator *(); + } + T *operator *() const { + return static_cast(obj_); + } + Unbarriered &operator =(T *obj) { + obj_ = obj; + return *this; + } + Object **address() { + return reinterpret_cast(&obj_); + } + operator bool () const { + return !!obj_; + } + operator T*() const { + return static_cast(obj_); + } +}; + +// Used to store unbarriered heap locations. +template +class Barriered +{ + T *obj_; + + public: + Barriered() + : obj_(NULL) + { + } + + T *operator ->() const { + return operator *(); + } + T *operator *() const { + return static_cast(obj_); + } + Barriered &operator =(T *obj) { + obj_ = obj; + return *this; + } + Object **addr() { + return reinterpret_cast(&obj_); + } + operator bool () const { + return !!obj_; + } + operator T*() const { + return static_cast(obj_); + } +}; + +} + +#endif // _include_jitcraft_barriers_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/BytecodeCompiler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BytecodeCompiler.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,1096 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "BytecodeCompiler.h" +#include "Scopes.h" +#include "Modules.h" +#include "Types.h" +#include "Array.h" +#include "Strings.h" +#include "Zone.h" +#include "Environments.h" +#include "Heap-inl.h" + +using namespace ke; + +typedef BytecodeEmitter::Label Label; + +#define __ emitter_. + +BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) + : zone_(zone), + tree_(tree), + emitter_(zone->pool()), + moduleData_(module), + locals_(NULL), + scope_(NULL), + loop_(NULL) +{ +} + +static inline TokenKind +ToCompare(Expression *expr) +{ + if (expr->isBinaryExpression()) { + BinaryExpression *b = expr->toBinaryExpression(); + if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) + return b->token(); + } else if (expr->isUnaryExpression()) { + UnaryExpression *u = expr->toUnaryExpression(); + if (u->token() == TOK_NOT) + return TOK_NOT; + } + return TOK_EOF; +} + +static inline Opcode +CompareTokenToJump(TokenKind kind) +{ + switch (kind) { + case TOK_EQUALS: + return OP_JEQ; + case TOK_NOTEQUALS: + return OP_JNE; + case TOK_GT: + return OP_JG; + case TOK_GE: + return OP_JGE; + case TOK_LT: + return OP_JL; + default: + assert(kind == TOK_LE); + return OP_JLE; + } +} + +static inline Opcode +TokenToOpcode(TokenKind kind) +{ + switch (kind) { + case TOK_PLUS: + return OP_ADD; + case TOK_MINUS: + return OP_SUB; + case TOK_STAR: + return OP_MUL; + case TOK_SLASH: + return OP_DIV; + case TOK_PERCENT: + return OP_MOD; + case TOK_SHR: + return OP_SHR; + case TOK_USHR: + return OP_USHR; + case TOK_SHL: + return OP_SHL; + case TOK_BITAND: + return OP_BITAND; + case TOK_BITOR: + return OP_BITOR; + case TOK_GT: + return OP_GT; + case TOK_GE: + return OP_GE; + case TOK_LT: + return OP_LT; + case TOK_LE: + return OP_LE; + case TOK_EQUALS: + return OP_EQ; + case TOK_NOTEQUALS: + return OP_NE; + default: + assert(kind == TOK_BITXOR); + return OP_BITXOR; + } +} + +void +BytecodeCompiler::recordLifoSlot(Scope *scope) +{ + if (!scope) + return; + + if (Variable *var = scope->lifoSlot()) { + locals_->set(zone_, var->slot(), var->type()); + __ savelifo(var->slot()); + } +} + +void +BytecodeCompiler::unwindTo(Scope *scope) +{ + // Note: we only need to unwind to the outermost scope's LIFO slot. + Variable *var = NULL; + for (Scope *current = scope_; current != scope; current = current->enclosing()) { + if (Variable *slot = current->lifoSlot()) + var = slot; + } + + if (var) + __ unwindlifo(var->slot()); +} + +void +BytecodeCompiler::visit(NameProxy *name) +{ + switch (name->binding()->kind()) { + case BoundName::VARIABLE: + { + Variable *var = name->binding()->toVariable(); + + if (var->location() == Variable::STACK) { + __ getlocal(var); + assert(!name->type()->isReference()); + } else if (var->location() == Variable::ENVIRONMENT) { + getEnvSlot(var); + } else { + assert(var->location() == Variable::PARAMETER); + __ getarg(var); + + // If this argument is actually a reference, dereference it now. + if (var->type()->isReference()) + __ getref(var->type()->contained()->primitive()); + } + break; + } + + case BoundName::FUNCTION: + { + NamedFunction *fun = name->binding()->toNamedFunction(); + __ module(); + __ getmodule_fn(fun->slot()); + break; + } + + default: + { + NamedConstant *constant = name->binding()->toNamedConstant(); + __ int_(constant->value().toInt()); + break; + } + } +} + +void +BytecodeCompiler::visit(ExpressionStatement *node) +{ + // Expressions leave their value on the top of the stack, so we make sure + // to balance the stack if they're used as a statement. + node->expression()->accept(this); + if (!node->expression()->type()->isVoid()) + __ pop(); +} + +void +BytecodeCompiler::visit(Assignment *node) +{ + // 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(); + + // Bind. + if (var->type()->isReference() || var->type()->isArray()) + __ get(var); + + // 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 { + IndexExpression *index = node->lvalue()->toIndexExpression(); + __ note_position(index->pos()); + + // Note: Eventually, watch out for a whole here. An expression like: + // + // (a[4] = (5, a = [])) + // + // Could shrink array, so we need to either bind a[i] to a Reference, + // or keep the bounds check near the set. This is just a semantic + // 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)); + __ note_position(node->pos()); + __ binary(TokenToOpcode(alu)); + } + + // Emit the store. + if (node->expression()->type()->isArray()) { + // Unlike references, which can only be computed from a NamePRoxy, + // arrays can be computed from multiple lvalue types. + __ note_position(node->pos()); + __ arraycopy(); + } else if (node->lvalue()->isNameProxy()) { + NameProxy *proxy = node->lvalue()->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(var->type()->contained()->primitive()); + else + __ set(var); + } else { + IndexExpression *index = node->lvalue()->toIndexExpression(); + // :TODO: get rid of the BOUNDS instruction, embed it in g/setarray*? + // We can't emit it here because the stack is slightly out of position. + // __ bounds(); + __ setarray(index->left()->type()->contained()->primitive()); + } +} + +void +BytecodeCompiler::visit(IncDecExpression *node) +{ + // In the postfix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // (4) Swap -> VAL REF + // (5) Pick -> VAL REF VAL + // (6) Add -> VAL REF VAL+1 + // (7) Store -> VAL VAL+1 + // (8) Drop -> VAL + // + // In the prefix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // -- + // -- + // (6) Add -> REF VAL+1 + // (7) Store -> VAL+1 + // -- + // + // This is squirrely enough such that we don't bother sharing too much + // with the above. + // + // Note: We simulate a "Pick" operation with a dup2+pop. + + // This is steps (1) to (5). + Expression *expr = node->expression(); + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + // Bind. + if (var->type()->isReference() || var->type()->isArray()) + __ get(var); + + // Dup and fetch. + if (var->type()->isReference()) { + __ dup(); + __ getref(node->type()->primitive()); + if (node->postfix()) { + __ swap(); + __ pick2(); + } + } else { + assert(!var->type()->isArray()); + __ get(var); + if (node->postfix()) + __ dup(); + } + } else { + IndexExpression *index = expr->toIndexExpression(); + __ note_position(index->pos()); + + index->left()->accept(this); + index->right()->accept(this); + + assert(!node->type()->isArray()); + __ bounds(); + __ dup2(); + __ getarray(node->type()->primitive()); + + if (node->postfix()) { + // The stack looks like this now: + // ARRAY INDEX VALUE + // + // So we must use a PICK operation to rotate the stack into position. + __ roll3(); // INDEX VALUE ARRAY + __ roll3(); // VALUE ARRAY INDEX + __ pick3(); // VALUE ARRAY INDEX VALUE + } + } + + // Perform step (6). + if (node->type()->isFloat()) { + __ float_(1.0); + } else { + assert(node->type()->isInt32()); + __ int_(1); + } + + __ note_position(node->pos()); + if (node->token() == TOK_INCREMENT) + __ binary(OP_ADD); + else + __ binary(OP_SUB); + + // Perform step (7). + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(node->type()->primitive()); + else + __ set(var); + } else { + __ setarray(node->type()->primitive()); + } + + // Perform step (8). + if (node->postfix()) + __ pop(); +} + +void +BytecodeCompiler::visitForALU(Type *needed, Expression *node) +{ + node->accept(this); + if (needed->isFloat() && !node->type()->isFloat()) + __ cvt_i2f(); + else + assert(needed->primitive() == node->type()->primitive()); +} + +static inline Type * +GetCoercionType(TokenKind tok, Type *left, Type *right) +{ + if (left->isFloat()) + return left; + return right; +} + +void +BytecodeCompiler::visit(UnaryExpression *node) +{ + switch (node->token()) { + case TOK_MINUS: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (node->type()->isFloat()) + __ neg_f(); + else + __ neg(); + break; + } + + case TOK_NOT: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (node->type()->isFloat()) + __ not_f(); + else + __ not_(); + break; + } + + default: + assert(node->token() == TOK_TILDE); + node->expression()->accept(this); + __ bitnot(); + break; + } +} + +void +BytecodeCompiler::visit(BinaryExpression *node) +{ + if (node->token() == TOK_AND || node->token() == TOK_OR) { + Label success, failure, done; + visitForTest(node, &success, &failure, &success); + __ bind(&success); + __ int_(1); + __ drop(1); // Otherwise, the stack depth will be +1 too much. + __ jump(&done); + __ bind(&failure); + __ int_(0); + __ bind(&done); + return; + } + + Local leftType(zone_, node->left()->type()); + Local rightType(zone_, node->right()->type()); + Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); + + visitForALU(coerce, node->left()); + visitForALU(coerce, node->right()); + + Opcode op = TokenToOpcode(node->token()); + if (node->type()->isFloat()) + op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); + + __ note_position(node->pos()); + __ binary(op); +} + +void +BytecodeCompiler::visit(IndexExpression *node) +{ + node->left()->accept(this); + node->right()->accept(this); + __ note_position(node->pos()); + __ bounds(); + + if (node->type()->isArray()) + __ getarray_o(); + else + __ getarray(node->type()->primitive()); +} + +void +BytecodeCompiler::visit(ReturnStatement *node) +{ + if (node->expression()) { + node->expression()->accept(this); + __ return_(); + } else { + __ returnvoid(); + } +} + +void +BytecodeCompiler::visitForTest(Expression *expression, + Label *trueBranch, + Label *falseBranch, + Label *fallthrough) +{ + TokenKind token = ToCompare(expression); + if (token == TOK_EOF || token == TOK_NOT) { + if (token == TOK_EOF) + expression->accept(this); + else + expression->toUnaryExpression()->expression()->accept(this); + + bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); + + Opcode op = jumpOnTrue ? OP_JT : OP_JF; + if (expression->type()->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == falseBranch) + __ j(op, trueBranch); + else + __ j(op, falseBranch); + return; + } + + BinaryExpression *binary = expression->toBinaryExpression(); + + if (token == TOK_AND || token == TOK_OR) { + Label next; + if (token == TOK_AND) + visitForTest(binary->left(), &next, falseBranch, &next); + else + visitForTest(binary->left(), trueBranch, &next, &next); + __ bind(&next); + visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); + return; + } + + Opcode op = CompareTokenToJump(token); + if (fallthrough == trueBranch) + op = Invert(op); + + Local leftType(zone_, binary->left()->type()); + Local rightType(zone_, binary->right()->type()); + Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); + + visitForALU(coerce, binary->left()); + visitForALU(coerce, binary->right()); + if (coerce->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == trueBranch) + __ j(op, falseBranch); + else + __ j(op, trueBranch); +} + +void +BytecodeCompiler::visit(ForStatement *node) +{ + AutoEnterScope forScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + Label join, update; + LoopScope loop(scope_, &join, &update, &loop_); + + if (node->initialization()) + node->initialization()->accept(this); + + Label header, test; + if (node->condition()) + __ jump(&test); + + __ bind(&header); + node->body()->accept(this); + + __ bind(&update); + if (node->update()) + node->update()->accept(this); + + if (node->condition()) { + __ bind(&test); + visitForTest(node->condition(), &header, &join, &join); + } else { + __ jump(&header); + } + + __ bind(&join); + + if (node->scope()) + unwindTo(node->scope()->enclosing()); + assert(__ stackDepth() == 0); +} + +void +BytecodeCompiler::visit(WhileStatement *node) +{ + Label header, join; + + __ bind(&header); + + LoopScope loop(scope_, &join, &header, &loop_); + + if (node->token() == TOK_WHILE) { + Label body; + + visitForTest(node->condition(), &body, &join, &body); + __ bind(&body); + node->body()->accept(this); + __ jump(&header); + } else { + node->body()->accept(this); + visitForTest(node->condition(), &header, &join, &join); + } + + __ bind(&join); +} + +void +BytecodeCompiler::visit(BreakStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->break_()); +} + +void +BytecodeCompiler::visit(ContinueStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->continue_()); +} + +void +BytecodeCompiler::visit(BlockStatement *node) +{ + AutoEnterScope blockScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *statement = node->statements()->at(i); + statement->accept(this); + assert(__ stackDepth() == 0); + } + + // We don't bother emitting an "unwind" instruction for function scopes, + // since this is taken care of by the calling convention. + if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) + unwindTo(node->scope()->enclosing()); +} + +void +BytecodeCompiler::visit(IntegerLiteral *node) +{ + __ int_(node->value()); +} + +void +BytecodeCompiler::visit(FloatLiteral *node) +{ + __ float_((float)node->value()); +} + +static inline ArrayMap * +BuildArrayMap(Zone *zone, Handle type) +{ + Local contained(zone); + if (type->levels() > 1) { + Local containedType(zone, type->contained()); + contained = BuildArrayMap(zone, containedType); + if (!contained) + return NULL; + } + + return ArrayMap::New(zone, type, contained); +} + +void +BytecodeCompiler::visit(StringLiteral *node) +{ + Local type(zone_, node->type()); + Local map(zone_, BuildArrayMap(zone_, type)); + if (!map) + return; + + Local array(zone_, Array::New(zone_, map, node->string()->length(), Heap::Tenure_Old)); + if (!array) + return; + + assert(array->nelements() >= node->string()->length()); + memcpy(array->chars(), node->string()->raw(), node->string()->length() * sizeof(char)); + + __ note_position(node->pos()); + __ object(array); +} + +void +BytecodeCompiler::getEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ getglobal(var); +} + +void +BytecodeCompiler::setEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ setglobal(var); +} + +void +BytecodeCompiler::visit(VariableDeclaration *node) +{ + Variable *var = node->variable(); + + Local type(zone_, var->type()); + if (type->isArray()) { + Local map(zone_, BuildArrayMap(zone_, type)); + __ note_position(node->pos()); + if (var->scope()->kind() == Scope::GLOBAL) + __ newarray(map, Heap::Tenure_Old); + else if (var->needsHeapAllocation()) + __ newarray(map, Heap::Tenure_Default); + else + __ newarray(map, Heap::Tenure_Stack); + + if (node->initialization()) { + node->initialization()->accept(this); + __ note_position(node->pos()); + __ arraycopy(); + } + } else { + if (node->initialization()) { + node->initialization()->accept(this); + } else { + if (!node->postDimensions()) + __ int_(0); + } + } + + if (var->location() == Variable::STACK) { + __ setlocal(var); + locals_->set(zone_, var->slot(), var->type()); + } else { + assert(var->location() == Variable::ENVIRONMENT); + setEnvSlot(var); + } + + __ pop(); +} + +void +BytecodeCompiler::visit(CallExpression *node) +{ + Local funType(zone_, node->callee()->type()); + + // Sniff out natives, which have a specific opcode and are guaranteed to + // be called via name proxy. + NamedFunction *native = NULL; + if (funType->isNative()) { + native = node->callee()->toNameProxy()->binding()->toNamedFunction(); + } + + for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { + Expression *arg = node->arguments()->at(i); + + // We need to compute a reference if the parameter was in the form: + // f(&a) + // Note this form does not allow: f({}) + // + // We also need to compute references for all variadic native + // arguments, but arrays and references are already in the + // appropriate form. + Local given(zone_, arg->type()); + bool mustPushReference = false; + if (i >= funType->parameters()->length()) { + assert(funType->isNative() && funType->isNativeVariadic()); + mustPushReference = !given->isArray() && !given->isReference(); + } else if (funType->parameterAt(i)->isReference()) { + mustPushReference = !given->isReference(); + } + + // If a native is passed to a native, it must only be one level deep. + assert(!funType->isNative() || !given->isArray() || given->levels() == 1); + + if (mustPushReference) { + if (arg->isNameProxy()) { + // :TODO: test with functions and globals + // Compute a reference to the name. + Variable *var = arg->toNameProxy()->binding()->toVariable(); + __ note_position(node->pos()); + __ newref(var); + } else if (arg->isIndexExpression()) { + IndexExpression *expr = arg->toIndexExpression(); + expr->left()->accept(this); + expr->right()->accept(this); + __ note_position(expr->pos()); + __ bounds(); + __ arrayref_lifo(); + } else { + // We don't know how to, or can't, build a direct reference to + // this type. Make an indirect one, by taking the top of the + // stack and boxing it up. + arg->accept(this); + __ note_position(node->pos()); + __ slotref_lifo(); + } + } else { + arg->accept(this); + } + } + + __ note_position(node->pos()); + if (native) { + __ callnative(native->slot(), node->arguments()->length()); + __ drop(node->arguments()->length()); + } else { + node->callee()->accept(this); + __ call(funType); + __ drop(node->arguments()->length() + 1); + } +} + +void +BytecodeCompiler::visit(TernaryExpression *node) +{ + Label success, failure, join; + + visitForTest(node->condition(), &success, &failure, &success); + __ bind(&success); + node->left()->accept(this); + __ drop(1); + __ jump(&join); + __ bind(&failure); + node->right()->accept(this); + __ bind(&join); +} + +void +BytecodeCompiler::visit(IfStatement *node) +{ + Label join; + if (!node->ifFalse()) { + Label success; + + visitForTest(node->condition(), &success, &join, &success); + __ bind(&success); + node->ifTrue()->accept(this); + } else { + Label success, next; + + visitForTest(node->condition(), &success, &next, &success); + __ bind(&success); + node->ifTrue()->accept(this); + __ jump(&join); + __ bind(&next); + node->ifFalse()->accept(this); + } + + __ bind(&join); +} + +void +BytecodeCompiler::visit(FunctionStatement *node) +{ + if (node->fun()->isNative()) { + assert(!moduleData_->natives->at(node->fun()->slot())); + + Local type(zone_, node->fun()->type()); + Local name(zone_, node->fun()->name()); + Local native(zone_, Native::New(zone_, type, name)); + if (!native) + return; + + moduleData_->natives->set(zone_, node->fun()->slot(), native); + return; + } + + // If the function has no body, then it's just a declaration. + if (!node->body()) { + assert(node->fun()->isForward() || node->fun()->isPublic()); + return; + } + + BytecodeCompiler child(zone_, tree_, moduleData_); + Local fun(zone_, child.compileFunction(moduleData_, node)); + if (!fun) + return; + + assert(!moduleData_->module->getFunction(node->fun()->slot())); + moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); +} + +void +BytecodeCompiler::visit(EnumStatement *node) +{ + // :TODO: eventually, we'll export these. +} + +Code * +BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) +{ + // STOP doubles as RETURNVOID. + __ stop(); + + if (__ oom() || zone_->hasPendingException()) + return NULL; + + Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); + if (!bytes) + return NULL; + memcpy(bytes->raw(), __ buffer(), __ length()); + + for (size_t i = 0; i < scope->maxContainedLocals(); i++) + assert(locals_->at(i)); + + Local objects(zone_, FixedArray::New(zone_, __ objects()->length(), Heap::Tenure_Old)); + if (!objects) + return NULL; + + for (size_t i = 0; i < __ objects()->length(); i++) + objects->set(zone_, i, *(__ objects()->at(i))); + + Local safepoints(zone_); + + // If any stack slots hold gc pointers at potential failure points in the + // bytecode, record them now. We do this by building a list of tuples; + // each tuple contains the pc offset, and a bitset containing which stack + // slots must be scanned by the gc. + if (__ oopRecords()->length()) { + size_t bytesPerSafepoint = Code::BytesPerSafepoint(__ maxStackDepth()); + size_t gcmapSize = bytesPerSafepoint * __ oopRecords()->length(); + safepoints = ByteArray::New(zone_, gcmapSize, Heap::Tenure_Old); + if (!safepoints) + return NULL; + + Address cursor = safepoints->raw(); + for (size_t i = 0; i < __ oopRecords()->length(); i++) { + OopRecord *record = __ oopRecords()->at(i); + assert(record->depth <= __ maxStackDepth()); + + *reinterpret_cast(cursor) = record->offset; + + unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); + for (unsigned i = 0; i < record->depth; i++) + bits[i / 8] |= (1 << (i % 8)); + + cursor += bytesPerSafepoint; + } + + assert(cursor == safepoints->raw() + gcmapSize); + } + + Local sourcemap(zone_); + if (__ sourcemapLength()) { + sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); + if (!sourcemap) + return NULL; + + memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); + } + + Local code(zone_); + + code = Code::New(zone_, + bytes, + locals_, + objects, + __ maxStackDepth(), + moduleData_->module, + safepoints, + sourcemap, + lineno); + return code; +} + +Function * +BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) +{ + // Note: No need to record a life slot here, since it's part of the Frame. + AutoEnterScope functionScope(this, node->scope()); + + // Set the initial line, because line mappings are deltas. + __ setFirstLine(node->pos().line); + locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); + + node->body()->accept(this); + + unsigned nargs = node->scope()->numArguments(); + Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); + if (!args) + return NULL; + + for (size_t i = 0; i < node->scope()->numArguments(); i++) + args->set(zone_, i, node->scope()->getArgument(i)->type()); + + + Local code(zone_, compileCode(node->scope(), node->pos().line)); + if (!code) + return NULL; + + Local type(zone_, node->fun()->type()); + Local name(zone_, node->fun()->name()); + Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); + if (!fun) + return NULL; + + // If this function had a forward, export it as a named public. + if (node->fun()->isPublic()) { + assert(node->fun()->isPublic()); + ScopedRoot root(fun); + if (!root) + return NULL; + if (!moduleData_->publics.append(root)) + return NULL; + } + + Disassemble(code, stdout); + + return fun; +} + +Module * +BytecodeCompiler::compile() +{ + AutoEnterScope scope(this, tree_->scope()); + + ModuleData moduleData; + + moduleData_ = &moduleData; + moduleData_->module = Module::New(zone_, tree_->scope()->numFunctions()); + + if (!moduleData_) + return NULL; + + moduleData_->natives = FixedArray::New(zone_, tree_->scope()->numNatives(), Heap::Tenure_Old); + if (!moduleData_->natives) + return NULL; + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + statement->accept(this); + } + + if (zone_->hasPendingException()) + return NULL; + +#ifndef NDEBUG + for (size_t i = 0; i < tree_->scope()->numFunctions(); i++) + assert(moduleData_->module->getFunction(i)); + for (size_t i = 0; i < tree_->scope()->numNatives(); i++) + assert(moduleData_->natives->at(i)); +#endif + + if (moduleData_->publics.length()) { + Local fun(zone_); + Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), + Heap::Tenure_Old)); + if (!list) + return NULL; + + for (size_t i = 0; i < moduleData_->publics.length(); i++) + list->set(zone_, i, moduleData_->publics[i]); + + moduleData_->module->setPublics(list); + } + + moduleData_->module->setNatives(moduleData_->natives); + + if (scope_->numEnvironmentVars()) { + Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); + if (!globalMap) + return NULL; + + Local environment(zone_, Environment::New(zone_, globalMap)); + if (!environment) + return NULL; + + moduleData_->module->setEnvironment(environment); + } + + Local code(zone_, compileCode(tree_->scope(), 1)); + if (!code) + return NULL; + + Disassemble(code, stdout); + + moduleData_->module->setCode(code); + + return moduleData_->module; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/BytecodeCompiler.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BytecodeCompiler.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,149 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_bytecode_compiler_h_ +#define _include_bytecode_compiler_h_ + +#include "AST.h" +#include "BytecodeEmitter.h" +#include "Functions.h" + +namespace ke { + +class Module; + +struct ModuleData +{ + ScopedRoot module; + PoolList > publics; + ScopedRoot natives; +}; + +class BytecodeCompiler : public AstVisitor +{ + Zone *zone_; + ParseTree *tree_; + BytecodeEmitter emitter_; + ScopedRoot locals_; + Scope *scope_; + ModuleData *moduleData_; + + class LoopScope + { + Scope *scope_; + BytecodeEmitter::Label *bk_; + BytecodeEmitter::Label *ct_; + LoopScope **prevp_; + LoopScope *prev_; + + public: + LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct, + LoopScope **prevp) + : scope_(scope), + bk_(bk), + ct_(ct), + prevp_(prevp), + prev_(*prevp) + { + *prevp_ = this; + } + + ~LoopScope() + { + *prevp_ = prev_; + } + + BytecodeEmitter::Label *break_() { + return bk_; + } + BytecodeEmitter::Label *continue_() { + return ct_; + } + Scope *scope() { + return scope_; + } + }; + + LoopScope *loop_; + + public: + class AutoEnterScope + { + BytecodeCompiler *bc_; + Scope *saved_; + + public: + AutoEnterScope(BytecodeCompiler *bc, Scope *scope) + : bc_(bc), + saved_(bc->scope_) + { + if (scope) + bc->scope_ = scope; + } + ~AutoEnterScope() { + bc_->scope_ = saved_; + } + }; + + private: + void recordLifoSlot(Scope *scope); + void unwindTo(Scope *scope); + void visitForTest(Expression *expression, + BytecodeEmitter::Label *trueBranch, + BytecodeEmitter::Label *falseBranch, + BytecodeEmitter::Label *fallthrough); + Code *compileCode(Scope *scope, unsigned lineno); + Function *compileFunction(ModuleData *module, FunctionStatement *fun); + void visitForALU(Type *needed, Expression *node); + + void getEnvSlot(Variable *var); + void setEnvSlot(Variable *var); + + public: + BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module); + + Module *compile(); + + public: + void visit(NameProxy *name); + void visit(Assignment *node); + void visit(BinaryExpression *node); + void visit(ReturnStatement *node); + void visit(ForStatement *node); + void visit(BlockStatement *node); + void visit(VariableDeclaration *node); + void visit(IntegerLiteral *node); + void visit(FloatLiteral *node); + void visit(StringLiteral *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(WhileStatement *node); + void visit(BreakStatement *node); + void visit(ContinueStatement *node); + void visit(IncDecExpression *node); + void visit(UnaryExpression *node); + void visit(TernaryExpression *node); +}; + +} + +#endif // _include_bytecode_generator_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/BytecodeEmitter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BytecodeEmitter.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,114 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "BytecodeEmitter.h" +#include "Utility.h" +#include "Types.h" +#include "Zone.h" + +using namespace ke; + +BytecodeEmitter::BytecodeEmitter(PoolAllocator &pool) + : pool_(pool), + buffer_(inline_buffer_), + buffer_pos_(buffer_), + buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), + oom_(false), + maxStackDepth_(0) +{ + lastCodePosition_ = 0; + firstLine_ = 1; +} + +bool +BytecodeEmitter::growBuffer(size_t nbytes) +{ + if (oom_) { + buffer_pos_ = buffer_; + return false; + } + + size_t newSize = size_t((buffer_end_ - buffer_) * 2); + if (newSize >= MAX_LENGTH) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + bytecode *newBuffer; + if (buffer_ == inline_buffer_) + newBuffer = (bytecode *)malloc(nbytes); + else + newBuffer = (bytecode *)realloc(buffer_, newSize); + + // If we can't allocate a buffer, reset the buffer to the start and set + // a flag to indicate out-of-memory. + if (!newBuffer) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_); + buffer_ = newBuffer; + buffer_end_ = buffer_ + newSize; + return true; +} + +void +BytecodeEmitter::recordOops() +{ + OopRecord *record = new (pool_) OopRecord(oopMap_.length(), position()); + + record->slots = (bool *)pool_.allocate(record->depth); + if (!record->slots) { + oom_ = true; + return; + } + + bool hadOops = false; + for (size_t i = 0; i < oopMap_.length(); i++) { + bool bit = oopMap_[i]; + record->slots[i] = bit; + hadOops |= bit; + } + + if (!hadOops) + return; + + oom_ &= oopRecords_.append(record); +} + +void +BytecodeEmitter::note_position(const SourcePosition &pos) +{ + assert(pos.line > 0); + assert(unsigned(pos.line) >= firstLine_); + + unsigned lineDelta = unsigned(pos.line) - firstLine_; + unsigned pcDelta = unsigned(position()) - lastCodePosition_; + + sourcemap_.writeUint32(pcDelta); + sourcemap_.writeUint32(lineDelta); + sourcemap_.writeUint32(pos.col); + + lastCodePosition_ = unsigned(position()); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/BytecodeEmitter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BytecodeEmitter.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,626 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_bytecode_emitter_h_ +#define _include_bytecode_emitter_h_ + +#include +#include +#include "Opcodes.h" +#include "NameBinding.h" +#include "Handles.h" +#include "CompactBuffer.h" + +namespace ke { + +class Type; +class Map; + +typedef PoolList> ObjectList; + +struct OopRecord : public PoolObject +{ + unsigned depth; + unsigned offset; + bool *slots; + + OopRecord(unsigned length, unsigned offset) + : depth(length), + offset(offset), + slots(NULL) + { + } +}; + +class BytecodeEmitter +{ + private: + PoolAllocator &pool_; + + bytecode *buffer_; + bytecode *buffer_pos_; + bytecode *buffer_end_; + bytecode inline_buffer_[512]; + bool oom_; + unsigned maxStackDepth_; + ObjectList objects_; + PoolList oopMap_; + PoolList oopRecords_; + CompactBufferWriter sourcemap_; + unsigned lastCodePosition_; + unsigned firstLine_; + + private: + // The maximum size of a code object must be such that the distance between + // two instructions fits into a signed, 31-bit integer. This ensures that + // all code-bounded buffers (like the number of object literals or + // safepoints) does not need to be bounds checked. It also means we can add + // any two offsets and be guaranteed that they don't overflow. + static const size_t MAX_LENGTH = INT_MAX / 2; + + private: + bool growBuffer(size_t bytes); + + // Checking the return value is not necessary, since the inline buffer is + // used as a ballast. + bool ensure(size_t bytes) { + if (buffer_pos_ + bytes > buffer_end_) + return growBuffer(bytes); + return true; + } + + void writeUint8(unsigned value) { + assert(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); + } + void writeUint32(unsigned value) { + *(unsigned *)buffer_pos_ = value; + buffer_pos_ += sizeof(unsigned); + } + void writeOp(Opcode op) { + if (oom_) + return; + + writeUint8(op); + for (int i = 0; i < OpcodeInfo[op].nuses; i++) + oopMap_.pop(); + for (int i = 0; i < OpcodeInfo[op].ndefs; i++) + oopMap_.append(false); + if (oopMap_.length() > maxStackDepth_) + maxStackDepth_ = oopMap_.length(); + } + void writeObject(Object *obj) { + writeUint32(objects_.length()); + ScopedRoot root(obj); + if (!objects_.append(root)) + oom_ = true; + } + + int position() const { + assert(buffer_pos_ - buffer_ < INT_MAX / 2); + return int(buffer_pos_ - buffer_); + } + + public: + BytecodeEmitter(PoolAllocator &pool); + + bytecode *buffer() const { + return buffer_; + } + unsigned length() const { + return unsigned(buffer_pos_ - buffer_); + } + bool oom() const { + return oom_ || sourcemap_.oom(); + } + unsigned stackDepth() const { + return oopMap_.length(); + } + unsigned maxStackDepth() const { + return maxStackDepth_; + } + const ObjectList *objects() const { + return &objects_; + } + PoolList *oopRecords() { + return &oopRecords_; + } + + void drop(unsigned amt) { + assert(amt <= stackDepth()); + for (unsigned i = 0; i < amt; i++) + oopMap_.pop(); + } + + void note_position(const SourcePosition &pos); + uint8 *sourcemap() const { + return sourcemap_.buffer(); + } + size_t sourcemapLength() const { + return sourcemap_.length(); + } + void setFirstLine(unsigned line) { + assert(!sourcemap_.length()); + firstLine_ = line; + } + + public: + // A label represents a local control-flow target in the bytecode. A label + // may be in three states: + // + // (1) Empty - the label is not used yet. + // (2) Pending - the label has incoming jumps, but the actual location of + // is not yet known. The list of jumps is threaded through + // the four bytes reserved for each emitted jump target. In + // this case, the offset field is the location of the most + // recently emitted incoming jump. + // (3) Bound - the label's position has been pinned. + // + // A jump can be emitted to a label in any state. Binding a label + // immediately backpatches all pending incoming jumps. A label must be + // bound if it is used. + class Label + { + int offset_ : 31; + bool bound_ : 1; + + void setOffset(int offset) { + offset_ = offset; + assert(offset_ == offset); + } + + public: + static const int INVALID_OFFSET = -1; + + public: + Label() + : offset_(INVALID_OFFSET), + bound_(false) + { + } + ~Label() + { + assert(bound_ || offset_ == INVALID_OFFSET); + } + + bool bound() const { + return bound_; + } + int offset() const { + return offset_; + } + void bind(int offset) { + assert(!bound_); + setOffset(offset); + bound_ = true; + } + int link(int offset) { + assert(!bound_); + int old = offset_; + setOffset(offset); + return old; + } + }; + + private: + int patchJumpAt(int at, int to) { + assert(at >= 0 && at < position()); + int old = *(int *)(buffer_ + at + 1); + *(int *)(buffer_ + at + 1) = (to - at); + return old; + } + + void singleByteOp(Opcode op) { + assert(OpcodeInfo[op].length == 1); + ensure(OpcodeInfo[op].length); + writeOp(op); + } + + // Mark a stack slot as having an object pointer. + void markOop(unsigned depth, bool oop = true) { + if (oom_) + return; + oopMap_[oopMap_.length() - depth] = oop; + } + bool getOop(unsigned depth) { + if (oom_) + return false; + return oopMap_[oopMap_.length() - depth]; + } + + // Mark the current bytecode position as either re-entrant (meaning, it + // can leave the interpreter or call to another function), or causing GC. + // This will record which stack slots have objects and associate that + // information with the current pc. + void recordOops(); + + public: + void int_(int value) { + ensure(OP_INT); + writeOp(OP_INT); + writeInt32(value); + } + void return_() { + ensure(OP_RETURN); + writeOp(OP_RETURN); + } + void getlocal(Variable *var) { + ensure(OP_GETLOCAL_LENGTH); + writeOp(OP_GETLOCAL); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); + } + void setlocal(Variable *var) { + ensure(OP_SETLOCAL_LENGTH); + writeOp(OP_SETLOCAL); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); + } + void getarg(Variable *var) { + ensure(OP_GETARG_LENGTH); + writeOp(OP_GETARG); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); + } + void setarg(Variable *var) { + ensure(OP_SETARG_LENGTH); + writeOp(OP_SETARG); + writeUint32(var->slot()); + + if (var->type()->isTraceable()) + markOop(1); + } + + void j(Opcode op, Label *label) { + // Since jumps can skip around the stream, do not try to do anything + // in a potentially truncated state. + if (oom()) + return; + + assert(op >= OP_JL && op <= OP_JUMP); + ensure(OP_JUMP_LENGTH); + + if (label->bound()) { + // We can immediately emit this jump. + int distance = label->offset() - position(); + writeOp(op); + writeInt32(distance); + } else { + // We can't emit this jump yet. Add a placeholder. + int old = label->link(position()); + writeOp(op); + writeInt32(old); + } + } + void bind(Label *label) { + if (oom()) + return; + + assert(!label->bound()); + + int offset = label->offset(); + while (offset != Label::INVALID_OFFSET) + offset = patchJumpAt(offset, position()); + + label->bind(position()); + } + void jump(Label *label) { + j(OP_JUMP, label); + } + + void binary(Opcode op) { + singleByteOp(op); + } + + void pop() { + singleByteOp(OP_POP); + } + void stop() { + singleByteOp(OP_STOP); + } + void call(Handle fun) { + recordOops(); + ensure(OP_CALL_LENGTH); + writeOp(OP_CALL); + + if (fun->returnType()->isVoid()) + drop(1); + else if (fun->returnType()->isTraceable()) + markOop(1); + } + void callnative(unsigned index, unsigned argc) { + recordOops(); + ensure(OP_CALLNATIVE_LENGTH); + writeOp(OP_CALLNATIVE); + writeUint32(index); + writeUint32(argc); + } + + void getarray(PrimitiveType type) { + switch (type) { + case PrimitiveType_Float: + singleByteOp(OP_GETARRAY_F); + break; + case PrimitiveType_Int32: + singleByteOp(OP_GETARRAY_I); + break; + default: + assert(type == PrimitiveType_Char); + singleByteOp(OP_GETARRAY_C); + break; + } + } + void setarray(PrimitiveType type) { + switch (type) { + case PrimitiveType_Float: + singleByteOp(OP_SETARRAY_F); + break; + case PrimitiveType_Int32: + singleByteOp(OP_SETARRAY_I); + break; + default: + assert(type == PrimitiveType_Char); + singleByteOp(OP_SETARRAY_C); + break; + } + } + + void getglobal(Variable *var) { + ensure(OP_GETGLOBAL_F_LENGTH); + if (var->type()->isPrimitive()) { + switch (var->type()->primitive()) { + case PrimitiveType_Float: + writeOp(OP_GETGLOBAL_F); + break; + default: + assert(var->type()->primitive() == PrimitiveType_Int32); + writeOp(OP_GETGLOBAL_I); + break; + } + } else { + writeOp(OP_GETGLOBAL_O); + markOop(1); + } + writeUint32(var->slot()); + } + void setglobal(Variable *var) { + ensure(OP_SETGLOBAL_F_LENGTH); + if (var->type()->isPrimitive()) { + switch (var->type()->primitive()) { + case PrimitiveType_Float: + writeOp(OP_SETGLOBAL_F); + break; + default: + assert(var->type()->primitive() == PrimitiveType_Int32); + writeOp(OP_SETGLOBAL_I); + break; + } + } else { + writeOp(OP_SETGLOBAL_O); + } + writeUint32(var->slot()); + } + + void getarray_o() { + singleByteOp(OP_GETARRAY_O); + markOop(1); + } + void bounds() { + singleByteOp(OP_BOUNDS); + markOop(2); + } + void float_(float value) { + ensure(OP_FLOAT_LENGTH); + writeOp(OP_FLOAT); + writeFloat(value); + } + void cvt_i2f() { + singleByteOp(OP_CVT_I2F); + } + void newarray(Handle map, Heap::Tenure tenure) { + recordOops(); + ensure(OP_NEWARRAY_LENGTH); + writeOp(OP_NEWARRAY); + writeUint8(tenure); + writeObject(map); + markOop(1); + } + void savelifo(unsigned slot) { + ensure(OP_SAVELIFO_LENGTH); + writeOp(OP_SAVELIFO); + writeUint32(slot); + } + void unwindlifo(unsigned slot) { + ensure(OP_UNWINDLIFO_LENGTH); + writeOp(OP_UNWINDLIFO); + writeUint32(slot); + } + void newref(Variable *var) { + recordOops(); + if (var->location() == Variable::STACK) { + ensure(OP_VARREF_LIFO_LENGTH); + writeOp(OP_VARREF_LIFO); + } else { + assert(var->location() == Variable::PARAMETER); + ensure(OP_ARGREF_LIFO_LENGTH); + writeOp(OP_ARGREF_LIFO); + } + writeUint32(var->slot()); + markOop(1); + } + + void slotref_lifo() { + recordOops(); + singleByteOp(OP_SLOTREF_LIFO); + markOop(1); + } + void arrayref_lifo() { + recordOops(); + singleByteOp(OP_ARRAYREF_LIFO); + markOop(1); + } + void getref(PrimitiveType type) { + if (type == PrimitiveType_Int32) { + singleByteOp(OP_GETREF_I); + } else { + assert(type == PrimitiveType_Float); + singleByteOp(OP_GETREF_F); + } + } + void setref(PrimitiveType type) { + if (type == PrimitiveType_Int32) { + singleByteOp(OP_SETREF_I); + } else { + assert(type == PrimitiveType_Float); + singleByteOp(OP_SETREF_F); + } + } + void object(Handle obj) { + ensure(OP_OBJECT_LENGTH); + writeOp(OP_OBJECT); + writeObject(obj); + markOop(1); + } + void arraycopy() { + singleByteOp(OP_ARRAYCOPY); + markOop(1); + } + + void dup() { + bool oop = getOop(1); + singleByteOp(OP_DUP); + markOop(1, oop); + } + void dup2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_DUP2); + + markOop(2, oop2); + markOop(1, oop1); + } + void swap() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_SWAP); + + markOop(2, oop1); + markOop(1, oop2); + } + void roll3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_ROLL3); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); + } + void pick2() { + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_PICK2); + + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop2); + } + void pick3() { + bool oop3 = getOop(3); + bool oop2 = getOop(2); + bool oop1 = getOop(1); + + singleByteOp(OP_PICK3); + + markOop(4, oop3); + markOop(3, oop2); + markOop(2, oop1); + markOop(1, oop3); + } + + void returnvoid() { + singleByteOp(OP_RETURNVOID); + } + void module() { + singleByteOp(OP_MODULE); + markOop(1); + } + void getmodule_fn(unsigned index) { + ensure(OP_GETMODULE_FN_LENGTH); + writeOp(OP_GETMODULE_FN); + writeUint32(index); + markOop(1); + } + void bitnot() { + singleByteOp(OP_BITNOT); + } + void not_() { + singleByteOp(OP_NOT); + } + void not_f() { + singleByteOp(OP_NOT_F); + } + void neg() { + singleByteOp(OP_NEG); + } + void neg_f() { + singleByteOp(OP_NEG_F); + } + + // Helpers. + void get(Variable *var) { + if (var->location() == Variable::STACK) { + getlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + getarg(var); + } + } + void set(Variable *var) { + assert(!var->type()->isReference()); + if (var->location() == Variable::STACK) { + setlocal(var); + } else { + assert(var->location() == Variable::PARAMETER); + setarg(var); + } + } +}; + +} + +#endif // _include_bytecode_emitter_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Code.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Code.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,127 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_code_h_ +#define _include_jc_code_h_ + +#include "Opcodes.h" +#include "Barriers.h" +#include "Heap.h" +#include "Types.h" +#include "FixedArray.h" +#include "Token.h" + +namespace ke { + +class Module; +class Type; + +// A code object contains the bytecode compiled from a function (or global +// script), as well as all information needed to interpret or JIT-compile +// the bytecode. +class Code : public Object +{ + Barriered module_; + Barriered bytecode_; + Barriered locals_; + Barriered objects_; + Barriered safepoints_; + Barriered sourcemap_; + unsigned maxStackDepth_; + unsigned lineNumber_; + + public: + static inline size_t BytesPerSafepoint(size_t stackDepth) { + return (Align(stackDepth, 8) / 8) + sizeof(unsigned); + } + + public: + static Code *New(Zone *zone, Handle bytecode, Handle locals, + Handle objects, unsigned maxStackDepth, Handle module, + Handle safepoints, Handle sourcemap, + unsigned lineNumber); + + Module *module() { + return module_; + } + bytecode *start() { + return bytecode_->raw(); + } + unsigned length() { + return bytecode_->length(); + } + Type *localType(size_t index) { + return Type::cast(locals_->at(index)); + } + unsigned nlocals() { + return locals_ ? locals_->length() : 0; + } + unsigned maxStackDepth() { + return maxStackDepth_; + } + Map *map(size_t index) { + return Map::cast(objects_->at(index)); + } + Object *object(size_t index) { + return objects_->at(index); + } + ByteArray *safepoints() { + return safepoints_; + } + uint8 *safepointAt(unsigned pcOffset); + + unsigned pcToOffset(bytecode *pc) { + assert(pc >= start() && pc < start() + length()); + return unsigned(pc - start()); + } + + SourcePosition pcToSource(unsigned pc); + unsigned lineNumber() { + return lineNumber_; + } + + // GC Descriptor. + static inline size_t offsetOfModule() { + return offsetof(Code, module_); + } + static inline size_t offsetOfBytecode() { + return offsetof(Code, bytecode_); + } + static inline size_t offsetOfLocals() { + return offsetof(Code, locals_); + } + static inline size_t offsetOfObjects() { + return offsetof(Code, objects_); + } + static inline size_t offsetOfSafepoints() { + return offsetof(Code, safepoints_); + } + static inline size_t offsetOfSourceMap() { + return offsetof(Code, sourcemap_); + } + // End GC Descriptor. + + static Code *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Code)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jc_code_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/CompactBuffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CompactBuffer.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,100 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_compact_buffer_h_ +#define _include_jc_compact_buffer_h_ + +#include "AllocatorPolicies.h" +#include "Utility.h" +#include "Vector.h" + +namespace ke { + +class CompactBufferReader +{ + const uint8 *pos_; + const uint8 *end_; + + public: + CompactBufferReader(const uint8 *start, const uint8 *end) + : pos_(start), + end_(end) + { + } + + uint8 readUint8() { + assert(pos_ < end_); + return *pos_++; + } + + uint32 readUint32() { + uint32 value = 0; + uint32 shift = 0; + + uint8 byte; + do { + byte = readUint8(); + value |= (byte >> 1) << shift; + shift += 7; + } while (byte & 1); + + assert(shift < 32); + return value; + } + + bool done() const { + return pos_ == end_; + } +}; + +class CompactBufferWriter +{ + Vector buffer_; + bool enoughMemory_; + + public: + CompactBufferWriter() + : enoughMemory_(true) + { + } + + void writeUint8(unsigned value) { + enoughMemory_ &= buffer_.append(value); + } + void writeUint32(unsigned value) { + do { + writeUint8(((value & 0x7F) << 1) | (value > 0x7F)); + value >>= 7; + } while (value); + } + + uint8 *buffer() const { + return buffer_.buffer(); + } + size_t length() const { + return buffer_.length(); + } + + bool oom() const { + return !enoughMemory_; + } +}; + +} + +#endif // _include_jc_compact_buffer_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/CompileContext.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CompileContext.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Scanner.h" +#include "CompileContext.h" +#include "Zone.h" + +using namespace ke; + +CompileContext::CompileContext(Zone *zone, const char *file) + : zone_(zone), + failures_(0), + file_(file) +{ +} + +CompileContext::~CompileContext() +{ + if (failures_) + zone_->endCompilationErrors(); +} + +void +CompileContext::reportErrorVa(const SourcePosition &pos, Message msg, va_list ap) +{ + if (!failures_) + zone_->beginCompilationErrors(file_); + + zone_->reportCompilationError(msg, pos.line, pos.col, ap); + + failures_++; +} + +void +CompileContext::reportError(const SourcePosition &pos, Message msg, ...) +{ + va_list ap; + va_start(ap, msg); + reportErrorVa(pos, msg, ap); + va_end(ap); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/CompileContext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CompileContext.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_compile_context_h_ +#define _include_jitcraft_compile_context_h_ + +#include +#include "Messages.h" + +namespace ke { + +class Zone; +struct SourcePosition; + +class CompileContext +{ + Zone *zone_; + unsigned failures_; + const char *file_; + + public: + CompileContext(Zone *zone, const char *file); + ~CompileContext(); + + void reportErrorVa(const SourcePosition &pos, Message msg, va_list ap); + void reportError(const SourcePosition &pos, Message msg, ...); + + unsigned failures() const { + return failures_; + } +}; + +} + +#endif // _include_jitcraft_compile_context_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Environments.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Environments.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Utility.h" +#include "Environments.h" +#include "Structures.h" +#include "FixedArray.h" +#include "Zone.h" +#include "NameBinding.h" + +using namespace ke; + +EnvironmentSlotDescriptorArray * +EnvironmentSlotDescriptorArray::New(Zone *zone, unsigned nslots) +{ + size_t bytesNeeded = sizeof(EnvironmentSlotDescriptorArray) + + sizeof(EnvironmentSlotDescriptor) * nslots; + + Local array(zone); + Local map(zone, zone->getMap(MapKind_EnvironmentSlotDescriptorArray)); + + array = EnvironmentSlotDescriptorArray::cast(zone->allocate(map, bytesNeeded, Heap::Tenure_Old)); + if (!array) + return NULL; + + // We zero the array for safety, since we could GC or fail before initialization. + array->length_ = nslots; + memset(array->slots(), 0, sizeof(EnvironmentSlotDescriptor) * nslots); + return array; +} + +Environment * +Environment::New(Zone *zone, Handle map, Heap::Tenure tenure) +{ + size_t bytesNeeded = sizeof(Environment) + map->structureSize(); + Local env(zone, Environment::cast(zone->allocate(map, bytesNeeded, tenure))); + if (!env) + return NULL; + + // Initialize when called from C++, to make sure object slots are NULL. + memset((Object *)env + 1, 0, map->structureSize()); + return env; +} + +EnvironmentMap * +EnvironmentMap::New(Zone *zone, unsigned nvars, Variable **vars) +{ + StructureBufferBuilder structure; + + Local slots(zone); + slots = EnvironmentSlotDescriptorArray::New(zone, nvars); + if (!slots) + return NULL; + + // Build the descriptor table, mapping each slot to an offset and type. + Local type(zone); + for (unsigned i = 0; i < nvars; i++) { + assert(vars[i]->slot() == i); + type = vars[i]->type(); + + EnvironmentSlotDescriptor &desc = slots->slots()[i]; + if (!structure.add(type, &desc.offset)) + return NULL; + + desc.type = type; + } + + // Allocate the actual map now. + size_t bytesNeeded = sizeof(EnvironmentMap) + structure.traceableOffsetCount() * sizeof(unsigned); + Local map(zone); + + map = EnvironmentMap::cast(zone->allocateMap(MapKind_Environment, bytesNeeded, MapKind_MetaEnvironment)); + if (!map) + return NULL; + + // Fill in the map fields, and copy the traceable offsets structure. + map->structureSize_ = structure.structureSize(); + map->traceOffsetCount_ = structure.traceableOffsetCount(); + map->slots_ = slots; + memcpy(const_cast(map->traceOffsets()), structure.offsets(), sizeof(unsigned) * structure.traceableOffsetCount()); + + return map; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Environments.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Environments.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_ke_environment_h_ +#define _include_ke_environment_h_ + +#include "Objects.h" +#include "Handles.h" +#include "Heap.h" +#include "FixedArray.h" + +namespace ke { + +class EnvironmentMap; +class Variable; +class ByteArray; +class Type; + +// An environment represents the lexical environment of a code or function +// object. It used for variables which may be accessed outside a function's +// normal activation (i.e., upvars). +// +// A module uses an Environment to store its global variables. +class Environment : public Object +{ + template + inline T *pointerToSlot(unsigned slot); + + public: + static Environment *New(Zone *zone, Handle map, Heap::Tenure tenure = Heap::Tenure_Default); + + inline EnvironmentMap *map(); + inline size_t size(); + + float getFloat(unsigned slot) { + return *pointerToSlot(slot); + } + int getInt32(unsigned slot) { + return *pointerToSlot(slot); + } + Object *getObject(unsigned slot) { + return *pointerToSlot(slot); + } + void setFloat(unsigned slot, float f) { + *pointerToSlot(slot) = f; + } + void setInt32(unsigned slot, int i) { + *pointerToSlot(slot) = i; + } + void setObject(unsigned slot, Object *obj) { + // :TODO: write barrier + *pointerToSlot(slot) = obj; + } + + static inline size_t offsetOfSlotOffset(unsigned offset) { + return sizeof(Environment) + offset; + } + + static inline Environment *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Environment)); + return reinterpret_cast(obj); + } +}; + +struct EnvironmentSlotDescriptor +{ + Barriered type; + unsigned offset; +}; + +class EnvironmentSlotDescriptorArray : public Object +{ + unsigned length_; + + public: + static EnvironmentSlotDescriptorArray *New(Zone *zone, unsigned length); + + EnvironmentSlotDescriptor *slots() { + return reinterpret_cast(this + 1); + } + EnvironmentSlotDescriptor &at(unsigned slot) { + assert(slot < length_); + return slots()[slot]; + } + unsigned length() { + return length_; + } + unsigned size() { + return Align(sizeof(*this) + length() * sizeof(EnvironmentSlotDescriptorArray), + kObjectAlignment); + } + + static inline EnvironmentSlotDescriptorArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_EnvironmentSlotDescriptorArray)); + return reinterpret_cast(obj); + } +}; + +class EnvironmentMap : public Map +{ + unsigned structureSize_; + unsigned traceOffsetCount_; + Barriered slots_; + + public: + static EnvironmentMap *New(Zone *zone, unsigned nvars, Variable **vars); + + unsigned structureSize() { + return structureSize_; + } + unsigned traceOffsetCount() { + return traceOffsetCount_; + } + const unsigned *traceOffsets() { + return reinterpret_cast(this + 1); + } + size_t size() { + return Align(sizeof(EnvironmentMap) + sizeof(unsigned) * traceOffsetCount(), kObjectAlignment); + } + EnvironmentSlotDescriptor &slotDescriptor(unsigned slot) { + return slots_->at(slot); + } + + static inline size_t offsetOfSlots() { + return offsetof(EnvironmentMap, slots_); + } + + static inline EnvironmentMap *cast(Object *obj) { + assert(!obj || obj->is(MapKind_MetaEnvironment)); + return reinterpret_cast(obj); + } +}; + +size_t +Environment::size() +{ + return Align(sizeof(Environment) + map()->structureSize(), kObjectAlignment); +} + +EnvironmentMap * +Environment::map() +{ + return EnvironmentMap::cast(Object::map()); +} + +template T * +Environment::pointerToSlot(unsigned slot) +{ + return reinterpret_cast(reinterpret_cast(this + 1) + map()->slotDescriptor(slot).offset); +} + +} + +#endif // _include_ke_environment_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Exception.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Exception.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,31 @@ + +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Utility.h" +#include "Messages.h" + +using namespace ke; + +const MessageInfo ke::Messages[] = +{ +#define MSG(Name, Type, String) \ + { MessageType_##Type, String }, +# include "Messages.tbl" +#undef MSG + { MessageType_SyntaxError, NULL } +}; diff -r 06a0015b6a5c -r 32ad0523b309 src/FixedArray.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/FixedArray.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "FixedArray.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +static const unsigned kMaxFixedArrayLength = ((INT_MAX - sizeof(FixedArray)) / sizeof(ke::Object *)); + +FixedArray * +FixedArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, FixedArray::cast(zone->allocate(MapKind_FixedArray, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->vec(), 0, length * sizeof(Object *)); + return array; +} + +ByteArray * +ByteArray::New(Zone *zone, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, ByteArray::cast(zone->allocate(MapKind_ByteArray, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->raw(), 0, length); + return array; +} + +ByteArray * +ByteArray::New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure) +{ + if (length >= kMaxFixedArrayLength) { + zone->reportError(Message_ArrayTooBig); + return NULL; + } + + Local array(zone, ByteArray::cast(zone->allocate(map, SizeFor(length), tenure))); + if (!array) + return NULL; + + array->length_ = length; + memset(array->raw(), 0, length); + return array; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/FixedArray.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/FixedArray.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,89 @@ +/* vim: set ts=4 sw=4 tw=99 et + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_fixedarray_h_ +#define _include_jitcraft_fixedarray_h_ + +#include "Heap.h" + +namespace ke { + +// A fixed-length array that contains Object * slots. +class FixedArray : public Object +{ + uint32 length_; + + private: + Object **vec() { + return reinterpret_cast(this + 1); + } + + public: + static FixedArray *New(Zone *zone, unsigned length, Heap::Tenure tenure); + + unsigned length() { + return length_; + } + Object *at(size_t i) { + assert(i < length()); + return vec()[i]; + } + void set(Zone *zone, size_t i, Object *obj) { + assert(i < length()); + write(zone, &vec()[i], obj); + } + + static size_t SizeFor(unsigned length) { + return sizeof(FixedArray) + sizeof(Object *) * length; + } + + static inline FixedArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_FixedArray)); + return reinterpret_cast(obj); + } +}; + +// A fixed-length array containing raw bytes. +class ByteArray : public Object +{ + uint32 length_; + + public: + static ByteArray *New(Zone *zone, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); + static ByteArray *New(Zone *zone, Handle map, unsigned length, Heap::Tenure tenure = Heap::Tenure_Default); + + unsigned length() { + return length_; + } + Address raw() { + return reinterpret_cast(this + 1); + } + + static size_t SizeFor(unsigned length) { + return Align(sizeof(ByteArray) + sizeof(uint8) * length, kObjectAlignment); + } + + static inline ByteArray *cast(Object *obj) { + assert(!obj || obj->is(MapKind_ByteArray)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_fixedarray_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Functions.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Functions.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Functions.h" +#include "Modules.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +Function * +Function::New(Zone *zone, Handle code, Handle type, Handle name, + const SourcePosition &pos) +{ + Function *fun = Function::cast(zone->allocate(MapKind_Function, sizeof(Function), Heap::Tenure_Old)); + if (!fun) + return NULL; + + fun->code_ = code; + fun->pos_ = pos; + fun->type_ = type; + fun->name_ = name; + return fun; +} + +Native * +Native::New(Zone *zone, Handle type, Handle name) +{ + Native *fun = Native::cast(zone->allocate(MapKind_Native, sizeof(Native), Heap::Tenure_Old)); + if (!fun) + return NULL; + + fun->type_ = type; + fun->name_ = name; + fun->function_ = NULL; + return fun; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Functions.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Functions.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_function_h_ +#define _include_jitcraft_function_h_ + +#include "Heap.h" +#include "Barriers.h" +#include "Token.h" +#include "Types.h" + +namespace ke { + +class Code; +class Module; +class Type; +class String; + +class Function : public Object +{ + Barriered code_; + Barriered type_; + Barriered name_; + SourcePosition pos_; + + public: + static Function *New(Zone *zone, Handle code, Handle type, Handle name, + const SourcePosition &pos); + + Code *code() const { + return code_; + } + Type *type() const { + return type_; + } + String *name() const { + return name_; + } + const SourcePosition &pos() { + return pos_; + } + unsigned nargs() { + return type()->parameters()->length(); + } + Type *argType(size_t index) { + return type()->parameterAt(index); + } + + // Begin GC Descriptor. + static inline size_t offsetOfCode() { + return offsetof(Function, code_); + } + static inline size_t offsetOfType() { + return offsetof(Function, type_); + } + static inline size_t offsetOfName() { + return offsetof(Function, name_); + } + // End GC Descriptor. + + static inline Function *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Function)); + return reinterpret_cast(obj); + } +}; + +// A Native is a function with a very specialized calling convention, intended +// for earlier ISourcePawnEngine API compatibility. +class Native : public Object +{ + Barriered type_; + Barriered name_; + void *function_; + + public: + static Native *New(Zone *zone, Handle type, Handle name); + + void *function() { + return function_; + } + Type *type() { + return type_; + } + String *name() { + return name_; + } + void bind(void *function) { + function_ = function; + } + + // Begin GC Descriptor. + static inline size_t offsetOfType() { + return offsetof(Native, type_); + } + static inline size_t offsetOfName() { + return offsetof(Native, name_); + } + // End GC Descriptor. + + static inline Native *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Native)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_function_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/GlobalRoots.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/GlobalRoots.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2004-2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#include "GlobalRoots.h" +#include "Zone.h" + +using namespace ke; + +GlobalRoots::GlobalRoots(Zone *zone) + : zone_(zone) +{ +} + +GlobalRoots::~GlobalRoots() +{ + InlineList::iterator iter = roots_.begin(); + while (iter != roots_.end()) { + HeapRoot *root = *iter; + iter = roots_.erase(iter); + delete root; + } +} + +bool +GlobalRoots::initialize() +{ + return true; +} + +void +GlobalRoots::accept(VirtualObjectVisitor *visitor) +{ + for (InlineList::iterator iter = roots_.begin(); iter != roots_.end(); iter++) + visitor->visit(&iter->obj); +} + +HeapRoot * +GlobalRoots::create(Handle obj) +{ + HeapRoot *root = new HeapRoot(); + if (!root) { + ZONE()->reportOutOfMemory(); + return NULL; + } + + root->obj = obj; + root->refcount = 1; + + roots_.append(root); + return root; +} + +void +GlobalRoots::incref(HeapRoot *root) +{ + root->refcount++; +} + +void +GlobalRoots::decref(HeapRoot *root) +{ + assert(root->refcount > 0); + if (--root->refcount == 0) { + roots_.remove(root); + delete root; + } +} diff -r 06a0015b6a5c -r 32ad0523b309 src/GlobalRoots.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/GlobalRoots.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004-2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_ke_global_roots_h_ +#define _include_ke_global_roots_h_ + +#include "Handles.h" +#include "InlineList.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class Zone; +class Object; + +class HeapRoot : public InlineListNode +{ + public: + Object *obj; + intptr_t refcount; +}; + +class GlobalRoots +{ + Zone *zone_; + + public: + GlobalRoots(Zone *zone); + ~GlobalRoots(); + + bool initialize(); + + // The caller must immediately incref this. + HeapRoot *create(Handle ptr); + + void incref(HeapRoot *root); + void decref(HeapRoot *root); + + void accept(VirtualObjectVisitor *visitor); + + private: + InlineList roots_; +}; + +} + +#endif // _include_ke_global_roots_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Handles-inl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Handles-inl.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_rooting_inl_h_ +#define _include_jitcraft_rooting_inl_h_ + +#include "Heap.h" + +namespace ke { + +template +Local::Local(Environment *env) + : zone_(env->zone()), thing_(NULL) +{ + prev_ = zone_->registerThingLocal(toRaw()); +} + +template +Local::Local(Environment *env, T *thing) + : zone_(env->zone()), thing_(thing) +{ + prev_ = zone_->registerThingLocal(toRaw()); +} + +template +Local::Local(Zone *zone) + : zone_(zone), thing_(NULL) +{ + prev_ = zone_->registerThingLocal(toRaw()); +} + +template +Local::Local(Zone *zone, T *thing) + : zone_(zone), thing_(thing) +{ + prev_ = zone_->registerThingLocal(toRaw()); +} + + +template +Local::~Local() +{ + zone_->deregisterThingLocal(toRaw(), prev_); +} + +} + +#endif // _include_jitcraft_rooting_inl_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Handles.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Handles.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,204 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_rooting_h_ +#define _include_jitcraft_rooting_h_ + +#include + +namespace ke { + +class Zone; +class Object; + +template class Local; +template class ScopedRoot; + +/* + * Internal handles work differently from API handles. An API handle is a + * root, whereas as an internal handle points to a rooted location. This + * subtle difference makes handles easy for embedders, but somewhat + * cumbersome internally. + */ +template +class Handle +{ + T * const *thingp_; + + public: + Handle(const Handle &other) { + thingp_ = other.address(); + } + template Handle(const Handle &handle) { + typeCheck(); + thingp_ = reinterpret_cast(handle.address()); + } + template inline Handle(const Local &local); + template inline Handle(const ScopedRoot &root); + + public: + T *operator ->() { + return *thingp_; + } + T *operator *() { + return *thingp_; + } + operator T *() { return *thingp_; } + operator bool() const { + return thingp_ && *thingp_; + } + T * const *address() const { + return thingp_; + } + + private: + template + void typeCheck() { +#ifndef NDEBUG + T *v1 = NULL; + S *v2 = NULL; + v1 = v2; + (void)v1; +#endif + } +}; + +template +class Local +{ + friend class Zone; + + Zone *zone_; + T *thing_; + Local *prev_; + + Local *prev() { + return prev_; + } + Local *toRaw() { + return reinterpret_cast*>(this); + } + + public: + // These are implemented in Zone.h. + inline explicit Local(Zone *zone, T *thing = NULL); + inline ~Local(); + + const Local &operator =(T *thing) { + thing_ = thing; + return *this; + } + T *operator ->() const { + return operator *(); + } + T *operator *() const { + return thing_; + } + operator bool () const { + return !!thing_; + } + bool operator !=(const Local &other) { + return thing_ != other.thing_; + } + + Object **address() { + return (Object **)&thing_; + } + Object * const *address() const { + return (Object * const *)&thing_; + } + T **ref() { + return &thing_; + } + operator T *() { + return thing_; + } + + private: + // Disallow heap allocation. + void *operator new(size_t size); + void *operator new[](size_t size); + + // Disallow copy construction. + Local(const Local &other); + Local &operator =(const Local &other); +}; + +// A ScopedRoot is a special kind of handle for storing references on the +// heap in bulk. +template +class ScopedRoot +{ + T **thingp_; + + private: + // This is implemented in Zone.h. + inline T **make(T *other); + + void assign(T *other) { + *thingp_ = other; + } + + public: + ScopedRoot(); + + // This is implemented in Zone.h. + inline ScopedRoot(T *thing); + + ScopedRoot &operator =(const ScopedRoot &other) { + assign(*other); + return *this; + } + ScopedRoot &operator =(T *other) { + assign(other); + return *this; + } + T *operator ->() const { + return operator *(); + } + T *operator *() const { + return *thingp_; + } + operator T *() const { + return *thingp_; + } + operator bool () const { + return !!*thingp_; + } + T **address() const { + return thingp_; + } +}; + +template template inline +Handle::Handle(const Local &local) +{ + typeCheck(); + thingp_ = reinterpret_cast(local.address()); +} + +template template inline +Handle::Handle(const ScopedRoot &root) +{ + typeCheck(); + thingp_ = reinterpret_cast(root.address()); +} + +} + +#endif // _include_jitcraft_rooting_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/HashTable.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/HashTable.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _INCLUDE_KEIMA_HASHTABLE_H_ +#define _INCLUDE_KEIMA_HASHTABLE_H_ + +#include +#include +#include "Utility.h" + +namespace ke { + +template +class HashTableEntry +{ + uint32 hash_; + T t_; + + public: + static const uint32 kFreeHash = 0; + static const uint32 kRemovedHash = 1; + + public: + void setHash(uint32 hash) { + hash_ = hash; + } + uint32 hash() const { + return hash_; + } + void setRemoved() { + hash_ = kRemovedHash; + t_ = T(); + } + void construct() { + hash_ = kFreeHash; + new (&t_) T(); + } + void destruct() { + t_.~T(); + } + bool removed() const { + return hash_ == kRemovedHash; + } + bool free() const { + return hash_ == kFreeHash; + } + bool isLive() const { + return hash_ > kRemovedHash; + } + T &payload() { + return t_; + } + bool sameHash(uint32 hash) const { + return hash_ == hash; + } +}; + +template +class HashTable : public AllocPolicy +{ + friend class iterator; + + typedef typename HashPolicy::Payload Payload; + typedef HashTableEntry Entry; + + uint32 capacity_; + uint32 nelements_; + uint32 ndeleted_; + Entry *table_; + uint32 minCapacity_; + + private: + static const uint32 kMinCapacity = 16; + static const uint32 kMaxCapacity = INT_MAX / sizeof(Entry); + + template + uint32 computeHash(const Key &key) { + // Multiply by golden ratio. + uint32 hash = HashPolicy::hash(key) * 0x9E3779B9; + if (hash == Entry::kFreeHash || hash == Entry::kRemovedHash) + hash += 2; + return hash; + } + + Entry *createTable(uint32 capacity) { + assert(capacity <= kMaxCapacity); + + Entry *table = (Entry *)this->malloc(capacity * sizeof(Entry)); + if (!table) + return NULL; + + for (size_t i = 0; i < capacity; i++) + table[i].construct(); + + return table; + } + + public: + class Result + { + friend class HashTable; + + Entry *entry_; + + Entry &entry() { + return *entry_; + } + + public: + Result(Entry *entry) + : entry_(entry) + { } + + Payload * operator ->() { + return entry_->payload(); + } + Payload & operator *() { + return entry_->payload(); + } + + bool found() const { + return entry_->isLive(); + } + }; + + class Insert : public Result + { + uint32 hash_; + + public: + Insert(Entry *entry, uint32 hash) + : Result(entry), + hash_(hash) + { + } + + uint32 hash() const { + return hash_; + } + }; + + private: + class Probulator { + uint32 hash_; + uint32 capacity_; + + public: + Probulator(uint32 hash, uint32 capacity) + : hash_(hash), + capacity_(capacity) + { + assert(IsPowerOfTwo(capacity_)); + } + + uint32 entry() const { + return hash_ & (capacity_ - 1); + } + uint32 next() { + hash_++; + return entry(); + } + }; + + bool underloaded() const { + // Check if the table is underloaded: < 25% entries used. + return (capacity_ > kMinCapacity) && (nelements_ + ndeleted_ < capacity_ / 4); + } + bool overloaded() const { + // Grow if the table is overloaded: > 75% entries used. + return (nelements_ + ndeleted_) > ((capacity_ / 2) + (capacity_ / 4)); + } + + bool shrink() { + if ((capacity_ >> 1) < minCapacity_) + return true; + return changeCapacity(capacity_ >> 1); + } + + bool grow() { + assert(IsPowerOfTwo(kMaxCapacity)); + if (capacity_ >= kMaxCapacity) { + this->reportAllocationOverflow(); + return false; + } + return changeCapacity(capacity_ << 1); + } + + bool changeCapacity(uint32 newCapacity) { + assert(newCapacity <= kMaxCapacity); + + Entry *newTable = createTable(newCapacity); + if (!newTable) + return false; + + Entry *oldTable = table_; + uint32 oldCapacity = capacity_; + + table_ = newTable; + capacity_ = newCapacity; + ndeleted_ = 0; + + for (uint32 i = 0; i < oldCapacity; i++) { + Entry &oldEntry = oldTable[i]; + if (oldEntry.isLive()) { + Insert p = insertUnique(oldEntry.hash()); + p.entry().setHash(p.hash()); + p.entry().payload() = oldEntry.payload(); + } + oldEntry.destruct(); + } + this->free(oldTable); + + return true; + } + + // For use when the key is known to be unique. + Insert insertUnique(uint32 hash) { + Probulator probulator(hash, capacity_); + + Entry *e = &table_[probulator.entry()]; + for (;;) { + if (e->free() || e->removed()) + break; + e = &table_[probulator.next()]; + } + + return Insert(e, hash); + } + + template + Result lookup(const Key &key) { + uint32 hash = computeHash(key); + Probulator probulator(hash, capacity_); + + Entry *e = &table_[probulator.entry()]; + for (;;) { + if (e->free()) + break; + if (e->isLive() && + e->sameHash(hash) && + HashPolicy::matches(key, e->payload())) + { + return Result(e); + } + e = &table_[probulator.next()]; + } + + return Result(e); + } + + template + Insert lookupForAdd(const Key &key) { + uint32 hash = computeHash(key); + Probulator probulator(hash, capacity_); + + Entry *e = &table_[probulator.entry()]; + for (;;) { + if (!e->isLive()) + break; + if (e->sameHash(hash) && HashPolicy::matches(key, e->payload())) + break; + e = &table_[probulator.next()]; + } + + return Insert(e, hash); + } + + bool add(Insert &i) { + assert(!i.found()); + + // If the entry is deleted, just re-use the slot. + if (i.entry().removed()) { + ndeleted_--; + return true; + } + + // Otherwise, see if we're at max capacity. + if (nelements_ == kMaxCapacity) { + this->reportAllocationOverflow(); + return false; + } + + // Check if the table is over or underloaded. The table is always at + // least 25% free, so this check is enough to guarantee one free slot. + // (Without one free slot, insertion search could infinite loop.) + uint32 oldCapacity = capacity_; + if (!checkDensity()) + return false; + + // If the table changed size, we need to find a new insertion point. + // Note that a removed entry is impossible: either we caught it above, + // or we just resized and no entries are removed. + if (capacity_ != oldCapacity) + i = insertUnique(i.hash()); + + i.entry().setHash(i.hash()); + nelements_++; + + return true; + } + + void remove(Entry &e) { + assert(e.isLive()); + e.setRemoved(); + ndeleted_++; + nelements_--; + } + + public: + HashTable(AllocPolicy ap) + : AllocPolicy(ap), + capacity_(0), + nelements_(0), + ndeleted_(0), + table_(NULL), + minCapacity_(kMinCapacity) + { + } + + ~HashTable() + { + for (uint32 i = 0; i < capacity_; i++) + table_[i].destruct(); + this->free(table_); + } + + bool init(uint32 capacity = 0) { + if (capacity < kMinCapacity) + capacity = kMinCapacity; + else if (capacity > kMaxCapacity) + capacity = kMaxCapacity; + + minCapacity_ = capacity; + + assert(IsPowerOfTwo(capacity)); + capacity_ = capacity; + + table_ = createTable(capacity_); + if (!table_) + return false; + + return true; + } + + template + Result find(const Key &key) { + return lookup(key); + } + + template + Insert findForAdd(const Key &key) { + return lookupForAdd(key); + } + + bool add(Insert &i, const Payload &payload) { + if (!add(i)) + return false; + *i = payload; + return true; + } + + bool checkDensity() { + if (underloaded()) + return shrink(); + if (overloaded()) + return grow(); + return true; + } + + public: + class iterator + { + HashTable *table_; + Entry *i_; + Entry *end_; + + public: + iterator(HashTable *table) + : table_(table), + i_(table->table_), + end_(table->table_ + table->capacity_) + { + while (i_ < end_ && !i_->isLive()) + i_++; + } + + bool empty() const { + return i_ == end_; + } + + void erase() { + assert(!empty()); + table_->remove(*i_); + } + + const Payload &operator *() const { + return i_->payload(); + } + + void next() { + do { + i_++; + } while (i_ < end_ && !i_->isLive()); + } + }; +}; + +} + +#endif // _INCLUDE_KEIMA_HASHTABLE_H_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Heap-inl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Heap-inl.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,118 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_heap_inl_h_ +#define _include_jitcraft_heap_inl_h_ + +#include "Heap.h" +#include "Spaces-inl.h" + +namespace ke { + +static inline Object * +InitAddressIntoObject(Address address, Map *map) +{ + uintptr_t untagged = reinterpret_cast(address); + assert(IsAligned(untagged, kObjectAlignment)); + + Object *obj = reinterpret_cast(address); + obj->init(map); + return obj; +} + +inline Address +Heap::newObject(size_t bytes, Tenure age) +{ + if (bytes >= HeapChunk::kLargeObjectThreshold) + return newLargeObject(bytes); + + if (age == Tenure_Stack) { + if (Address addr = newStackObject(bytes)) + return addr; + } + +#if 0 + if (age == Tenure_Default) { + if (Address addr = newSpace_.allocate(bytes)) + return addr; + + scavenge(); + if (Address addr = newSpace_.allocate(bytes)) + return addr; + } +#endif + + // Try an old-space allocation. + if (Address addr = newOldObject(bytes)) + return addr; + + // Uh-oh - perform a last-ditch collection. + reclaim(); + + return newOldObject(bytes); +} + +inline Object * +Heap::allocate(Handle map, size_t bytes, Tenure age) +{ + size_t actualBytes = Align(bytes, kObjectAlignment); + + // All objects must be at least two words. + assert(actualBytes >= sizeof(Object *) * 2); + + Address address = newObject(actualBytes, age); + if (!address) { + reportOutOfMemory(); + return NULL; + } + return InitAddressIntoObject(address, map); +} + +inline Address +Heap::newStackObject(size_t bytes) +{ + return lifoSpace_.allocate(bytes); +} + +inline Address +Heap::newOldObject(size_t bytes) +{ + return oldSpace_.allocate(bytes); +} + +inline Address +Heap::newLargeObject(size_t bytes) +{ + if (Address address = largeSpace_.allocate(bytes)) + return address; + + reclaim(); + + return largeSpace_.allocate(bytes); +} + +inline void +Object::write(Zone *zone, Object **ptr, Object *obj) +{ + *ptr = obj; +} + +} + +#endif // _include_jitcraft_heap_inl_h_ + diff -r 06a0015b6a5c -r 32ad0523b309 src/Heap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Heap.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,372 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Heap.h" +#include "Heap-inl.h" +#include "Spaces-inl.h" +#include "VirtualObjectVisitor.h" +#include "ObjectVisitor.h" +#include "Zone.h" +#include "StringPool.h" + +using namespace ke; + +static const size_t kMarkStackSize = 256 * kKB; + +Heap::Heap(Zone *zone) + : zone_(zone), + markStack_(NULL), + oldSpace_(zone), + mapSpace_(zone) +{ +} + +bool +Heap::initialize() +{ + if (!lifoSpace_.initialize()) + return false; + if (!oldSpace_.initialize()) + return false; + if (!mapSpace_.initialize()) + return false; + if (!largeSpace_.initialize()) + return false; + + markStack_ = malloc(kMarkStackSize); + if (!markStack_) + return false; + + if ((metaMap_ = allocateMap(MapKind_Meta, sizeof(Map))) == NULL) + return false; + metaMap_->init(metaMap_); + + return true; +} + +Heap::~Heap() +{ + free(markStack_); +} + +Map * +Heap::allocateMap(MapKind kind, size_t bytes) +{ + size_t actualBytes = Align(bytes, kObjectAlignment); + + Address addr = mapSpace_.allocate(actualBytes); + if (!addr) { + reportOutOfMemory(); + return NULL; + } + + // Note that we use a NULL type initially, and reinterpret-cast to get + // the Type pointer, so we can allocate the self-referencing meta type. + Object *obj = InitAddressIntoObject(addr, NULL); + Map *map = reinterpret_cast(obj); + map->setKind(kind); + return map; +} + +// To avoid marking recursively, we use a worklist to manage the marking +// process. Algorithm: +// +// (1) While the mark stack is not empty: +// (a) Remove object |o| from the mark stack. +// (b) For each reference |p| in object |o|, +// (i) Mark |p| as Black. +// (ii) Push |p| onto the mark stack. +// (iv) If the mark stack overflowde, mark |p| as Grey. +// +// We run this algorithm after pushing every root object onto the mark stack. +// After all roots have been processed, it may be that the mark stack +// overflowed. In this case, we use the following algorithm: +// +// (1) while the mark stack has overflowed: +// (a) Search the heap for grey objects, adding as many as possible to the +// mark stack. +// (b) Run the mark-stack-emptying algorithm above. +// +// This algorithm is guaranteed to terminate, because even if there are more +// grey objects than mark stack capacity, we eventually all objects will +// become grey, and grey objects are not re-pushed onto the mark stack. Thus, +// eventually, all objects will become black. +// +namespace ke { +class MarkStack +{ + Object **base_; + Object **top_; + Object **end_; + bool overflowed_; + + public: + MarkStack(Object **base, Object **end) + : base_(base), + top_(base), + end_(end), + overflowed_(false) + { + } + + void pushBlack(Object *ptr) { + assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(ptr))); + if (top_ >= end_) { + MarkBit bit = HeapChunk::GetMarkBit(ptr); + HeapChunk::BlackToGrey(bit); + overflowed_ = true; + return; + } + *top_++ = ptr; + } + + Object *pop() { + assert(!empty()); + return *--top_; + } + bool overflowed() const { + return overflowed_; + } + bool empty() const { + return base_ == top_; + } + void reset() { + assert(empty()); + overflowed_ = true; + } +}; +} // namespace ke + +// Iterates over an object's body, pushing its referenced objects onto the +// mark stack. +class MarkingVisitor : public ObjectVisitor +{ + MarkStack &stack_; + + public: + MarkingVisitor(MarkStack &stack) + : stack_(stack) + { + } + + void visit(Object **ptr) { + if (!*ptr) + return; + + Object *obj = *ptr; + + // If the object is grey, we'll find it later when searching the heap. + // If the object is black, it's already been scsanned. + MarkBit bit = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsBlackOrGrey(bit)) + return; + + // Place the object in the stack and optimistically mark it black. If + // the stack overflows, it'll become grey instead. + HeapChunk::WhiteToBlack(bit); + stack_.pushBlack(obj); + } +}; + +static void +ProcessMarkStack(MarkStack &stack) +{ + MarkingVisitor visitor(stack); + + while (!stack.empty()) { + Object *obj = stack.pop(); + assert(HeapChunk::IsBlack(HeapChunk::GetMarkBit(obj))); + + visitor.iterate(obj); + } +} + +class RootMarkingVisitor : public VirtualObjectVisitor +{ + MarkStack &stack_; + MarkingVisitor visitor_; + + public: + RootMarkingVisitor(MarkStack &stack) + : stack_(stack), + visitor_(stack) + { + } + + void visit(Object **ptr) { + // Flush the stack after every new root. Note that the root gets pushed + // as black; that's okay. If the mark stack overflows, its children are + // guaranteed to be at least grey. + visitor_.visit(ptr); + ProcessMarkStack(stack_); + } +}; + +typedef ChunkObjectIteratorBase ChunkObjectIterator; + +static inline bool +EnqueueGreyObject(MarkStack &stack, Object *obj) +{ + assert(!stack.overflowed()); + MarkBit bit = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsGrey(bit)) { + HeapChunk::GreyToBlack(bit); + stack.pushBlack(obj); + return !stack.overflowed(); + } + return true; +} + +static void +EnqueueGreyObjectsInChunks(MarkStack &stack, ChunkObjectIterator iter) +{ + for (Object *obj = iter.next(); obj; obj = iter.next()) { + if (!EnqueueGreyObject(stack, obj)) + return; + } +} + +static void +EnqueueGreyObjectsInLargeChunks(MarkStack &stack, HeapChunk *first) +{ + for (HeapChunk *chunk = first; chunk; chunk = chunk->next()) { + Object *obj = reinterpret_cast(chunk->first()); + if (!EnqueueGreyObject(stack, obj)) + return; + } +} + +void +Heap::findGreyObjects(MarkStack &stack) +{ + EnqueueGreyObjectsInChunks(stack, lifoSpace_.begin(ObjectSizeEvaluator())); + if (stack.overflowed()) + return; + + EnqueueGreyObjectsInChunks(stack, mapSpace_.begin(ObjectSizeEvaluator())); + if (stack.overflowed()) + return; + + EnqueueGreyObjectsInChunks(stack, oldSpace_.begin(ObjectSizeEvaluator())); + if (stack.overflowed()) + return; + + EnqueueGreyObjectsInLargeChunks(stack, largeSpace_.first()); + if (stack.overflowed()) + return; +} + +void +Heap::mark() +{ + Object **qbase = reinterpret_cast(markStack_); + Object **qend = qbase + (kMarkStackSize / sizeof(Object *)); + MarkStack stack(qbase, qend); + RootMarkingVisitor visitor(stack); + + // Attempt to mark all objects reachable from the root set. + zone_->accept(&visitor); + assert(stack.empty()); + + // If the stack overflowed, we need to look through the entire heap for + // grey objects to rescan. We keep doing this until there are no grey + // objects left. + while (stack.overflowed()) { + stack.reset(); + findGreyObjects(stack); + ProcessMarkStack(stack); + } +} + +// Use the mark bits to scan for live objects, and mark the regions in +// between them as free. +static void +SweepOldSpaceChunk(OldSpace *space, HeapChunk *chunk) +{ + uint32 *markbits = chunk->markBits(); + uint32 *markbitsEnd = markbits + HeapChunk::kMarkWords; + Address freeStart = chunk->first(); + + // Scoot up to the first usable markword. Since memory is reserved for + // mark bits, not all addresses are allocatable, so quite a few of the + // initial mark bits are wasted. (1KB of mark bytes on x86.) + markbits = HeapChunk::GetMarkBit(reinterpret_cast(chunk->first())).word(); + + uint32 bits = *markbits; + for (;;) { + while (bits) { + uint32 bit = FindRightmostBit(bits); + Address object = HeapChunk::FromMarkBit(chunk, markbits, bit); + + assert(object >= freeStart); + if (freeStart != object) + space->markRegionFree(freeStart, object - freeStart); + + Object *obj = reinterpret_cast(object); + freeStart = object + SizeOfObject(obj, obj->map()); + + // Since black is 10, we never need to clear the next bit. + bits &= ~(1 << bit); + } + markbits++; + bits = *markbits; + if (markbits >= markbitsEnd) + break; + } + + if (freeStart == chunk->base()) + chunk->setEmpty(); + else if (freeStart != chunk->end()) + space->markRegionFree(freeStart, chunk->end() - freeStart); +} + +static void +SweepOldSpace(OldSpace *space) +{ + space->resetFreeList(); + for (HeapChunk *chunk = space->first(); chunk; chunk = chunk->next()) { + SweepOldSpaceChunk(space, chunk); + chunk->resetMarkBits(); + } + space->freeUnusedPages(); +} + +void +Heap::sweep() +{ + // Before destroying mark bits, sweep weak roots. + zone_->symbols()->sweep(); + + // Note: we don't bother sweeping the LIFO space. + SweepOldSpace(&mapSpace_); + SweepOldSpace(&oldSpace_); + largeSpace_.sweep(); +} + +void +Heap::reclaim() +{ + mark(); + sweep(); +} + +void +Heap::reportOutOfMemory() +{ + zone_->reportOutOfMemory(); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Heap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Heap.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,143 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_heap_h_ +#define _include_jitcraft_heap_h_ + +#include "Utility.h" +#include "Spaces.h" +#include "Barriers.h" +#include "Objects.h" +#include "Handles.h" + +namespace ke { + +class Zone; +class MarkStack; + +// Heap-allocated objects go here. Heaps are thread-local. The heap contains +// four spaces: +// +// LIFO Space: Used for objects that may be stack-allocated. +// Old Space: Used for objects that are expected to be long-lived, or that +// survive one collection. +// Large Space: Used for objects that are so large they should never be +// moved between spaces. +// +// ALLOCATING AN OBJECT DOES NOT ZERO ITS MEMORY. +// ALLOCATING AN OBJECT DOES NOT ZERO ITS MEMORY! +// +// --------------------------------------------------------------------------- +// +// The following is a high-level overview of how garbage collection works in +// Keima. Detailed notes are present in Heap.cpp. +// +// THE TRI-COLOR INVARIANT: Via an object's mark bits, we maintain the +// property that an object that has been fully marked may never reference a +// dead object. Objects exist in three states during collection: unmarked +// (white), scheduled for marking (grey), and fully marked (black). +// +// When initiating collection, all objects are white. Objects in the root +// set are made grey. The marking process takes grey objects and marks that +// each child object is live: if the child is white, it becomes grey. Once +// all children are grey or black, the parent grey object can become black, +// as it has been fully traversed. +// +// The result of this process is that when there are no more grey objects, +// then we are guaranteed that all black objects are live, and all white +// objects are dead. +// +// MAJOR COLLECTIONS are performed periodically, when either a given memory +// threshold has been reached, or when requested by the host application. +// They are also performed if no memory can be allocated. +// +// Major collections are extremely slow; they require full heap traversal, +// which can take more than a second on very large heaps. However, they +// have the greatest chance at reclaiming all syntactically dead objects. +// +// SWEEPING is performed after major collections. The free space in between +// live objects is added to a free list. Tenured allocations are then +// performed in the free space, if possible. +// +class Heap +{ + Zone *zone_; + HeapStack lifoSpace_; + OldSpace mapSpace_; + OldSpace oldSpace_; + LargeObjectSpace largeSpace_; + + Map *metaMap_; + + // This is a big hunk o' memory used for marking iteratively. Eventually, + // this will be replaced by the inactive semispace in the nursery. + void *markStack_; + + public: + enum Tenure { + // Default, objects go into the nursery. + Tenure_Default, + + // If objects are proven to not escape the stack, they can go into + // a LIFO arena. Allocation on the LIFO arena is not guaranteed to + // succeed; thus, objects may still be allocated in other arenas. + Tenure_Stack, + + // Objects that should not go into the nursery (i.e. are expected + // to be long-living) go here. + Tenure_Old + }; + + private: + inline Address newLargeObject(size_t bytes); + inline Address newOldObject(size_t bytes); + inline Address newStackObject(size_t bytes); + inline Address newObject(size_t bytes, Tenure pretenure); + + private: + void findGreyObjects(MarkStack &stack); + void mark(); + void sweep(); + + void reportOutOfMemory(); + + public: + Heap(Zone *zone); + ~Heap(); + bool initialize(); + + inline Object *allocate(Handle map, size_t byes, Tenure pretenure); + Map *allocateMap(MapKind kind, size_t bytes); + + Map *metaMap() { + return metaMap_; + } + inline Address lifoPosition() const { + return lifoSpace_.position(); + } + inline void unwindLifo(Address to) { + lifoSpace_.unwind(to); + } + + // Perform a full garbage collection. + void reclaim(); +}; + +} + +#endif //_include_jitcraft_heap_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/InlineList.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/InlineList.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2004-2012 David Anderson + * + * This file is part of SourcePawn. + * + * SourcePawn is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * SourcePawn. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_ke_inline_list_h_ +#define _include_ke_inline_list_h_ + +#include + +namespace ke { + +template class InlineList; + +template +class InlineListNode +{ + friend class InlineList; + + public: + InlineListNode() + : next_(NULL), + prev_(NULL) + { + } + + InlineListNode(InlineListNode *next, InlineListNode *prev) + : next_(next), + prev_(prev) + { + } + + protected: + InlineListNode *next_; + InlineListNode *prev_; +}; + +template +class InlineList +{ + typedef InlineListNode Node; + + Node head_; + + public: + InlineList() + : head_(&head_, &head_) + { + } + + public: + class iterator + { + friend class InlineList; + Node *iter_; + + public: + iterator(Node *iter) + : iter_(iter) + { + } + + iterator & operator++() { + iter_ = iter_->next_; + return *this; + } + iterator operator ++(int) { + iterator old(*this); + iter_ = iter_->next_; + return old; + } + T * operator *() { + return static_cast(iter_); + } + T * operator ->() { + return static_cast(iter_); + } + bool operator !=(const iterator &where) const { + return iter_ != where.iter_; + } + bool operator ==(const iterator &where) const { + return iter_ == where.iter_; + } + iterator prev() const { + return iterator(iter_->prev_); + } + iterator next() const { + return iterator(iter_->next_); + } + }; + + iterator begin() { + return iterator(head_.next_); + } + iterator end() { + return iterator(&head_); + } + void append(Node *t) { + t->prev_ = head_.prev_; + t->next_ = &head_; + head_.prev_->next_ = t; + head_.prev_ = t; + } + iterator erase(iterator i) { + iterator next = ++i; + remove(i.iter_); + return next; + } + void remove(Node *t) { + t->prev_->next_ = t->next_; + t->next_->prev_ = t->prev_; + } +}; + +} + +#endif // _include_ke_inline_list_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Interpreter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Interpreter.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,937 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Interpreter.h" +#include "Modules.h" +#include "Zone.h" +#include "Types.h" +#include "Array.h" +#include "Reference.h" +#include "Strings.h" +#include "Environments.h" +#include "Heap-inl.h" + +using namespace ke; + +#define PUSH_I32(top, value) \ + do { \ + top.sp->i32 = value; \ + 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) +{ + int value = top.sp[-1].i32; + top.sp--; + return value; +} + +static inline float +POP_F(Top &top) +{ + float value = top.sp[-1].f; + top.sp--; + return value; +} + +// Pawn's original semantics specify some kind of floor operation, so +// (8 / -3 == -3) and (-8 / 3 == -1). The SourcePawn 1 compiler (spcomp) uses +// the upstream algorithm when constant folding. However the SourcePawn 1 JIT +// does not. My guess is that Borja freaked out seeing the 13-instruction x86 +// sequence necessary to get this functionality (I vaguely recall a thread +// where we discussed this with Mark Peter? But the ITB forums are gone...), +// and we just decided to break the damn thing. So SP1 was inconsistent, but +// we are not. And for completeness, we assert that the C compiler does the +// right thing, since C89 leaves it unspecified. +STATIC_ASSERT(8 / -3 == -2); +STATIC_ASSERT(-8 / 3 == -2); + +static inline bool +PushNewRef(Zone *zone, Top &top, void *location, Heap::Tenure tenure) +{ + Reference *ref = Reference::NewStack(zone, location, tenure); + if (!ref) + return false; + top.sp->ref = ref; + top.sp++; + return true; +} + +// Generate an array with completely static sizes. +static Array * +GenerateArray(Zone *zone, Handle map, Heap::Tenure tenure) +{ + assert(map->type()->elements() != -1); + + Local array(zone, Array::New(zone, map, map->type()->elements(), tenure)); + if (!array) + return NULL; + + if (map->type()->levels() == 1) + return array; + + Local contained(zone, map->contained()); + Local sub(zone); + for (unsigned i = 0; i < unsigned(map->type()->elements()); i++) { + sub = GenerateArray(zone, contained, tenure); + if (!sub) + return NULL; + array->writeObject(zone, i, sub); + } + + return array; +} + +static bool +CopyArray(Zone *zone, Array *source, Array *dest) +{ + assert(source->type()->levels() == 1); + assert(dest->type()->levels() == 1); + assert(source->type()->contained()->primitive() == + dest->type()->contained()->primitive()); + assert(source->nelements() >= dest->nelements()); + assert(dest->elementSize() == source->elementSize()); + + memcpy(source->vec(), dest->vec(), dest->nelements() * dest->elementSize()); + return true; +} + +static bool +InvokeNative(Zone *zone, Handle native, void *identity, LocalSlot *args, unsigned argc, + int *result) +{ + NativeArgs guard; + + if (!zone->stack().pushNativeFrame(native, guard, argc + 1)) + return false; + + intptr_t *cells = guard.args(); + + // params[0] == argc, in ISPE + cells[0] = argc; + + // Pass the formal arguments. + Local type(zone, native->type()); + Local param(zone); + for (unsigned i = 0; i < type->parameters()->length(); i++) { + param = type->parameterAt(i); + + // For floats, we rely on the aliasing of |i32| and |f|. + LocalSlot &arg = args[-int(i) - 1]; + if (param->isInt32() || param->isFloat()) { + cells[i + 1] = arg.i32; + } else if (param->isReference()) { + cells[i + 1] = reinterpret_cast(arg.ref->location()); + } else { + assert(param->isArray()); + cells[i + 1] = reinterpret_cast(arg.array->vec()); + } + } + + // For variadic arguments, the compiler left either arrays or references. + for (unsigned i = type->parameters()->length(); i < argc; i++) { + LocalSlot &arg = args[-int(i) - 1]; + + if (arg.obj->is(MapKind_Reference)) { + cells[i + 1] = reinterpret_cast(arg.ref->location()); + } else { + assert(arg.obj->is(MapKind_Array)); + cells[i + 1] = reinterpret_cast(arg.array->vec()); + } + } + + typedef intptr_t (*INVOKE)(void *, intptr_t *); + INVOKE invoke = (INVOKE)native->function(); + *result = invoke(identity, cells); + + return !zone->hasPendingException(); +} + +bool +ke::Interpret(Zone *zone, Frame *entryFrame, LocalSlot *result) +{ + Top &top = *zone->stack().top(); + if (!entryFrame) + entryFrame = top.fp; + + LocalSlot *locals = entryFrame->locals(); + + for (;;) { + Opcode op = Opcode(*top.pc); + switch (op) { + +case OP_NOP: + break; + +case OP_INT: + PUSH_I32(top, READ_INT32(top.pc)); + break; + +case OP_FLOAT: + PUSH_F(top, READ_FLOAT(top.pc)); + break; + +case OP_RETURN: +case OP_RETURNVOID: +case OP_STOP: +{ + if (op == OP_RETURN) + *result = *--top.sp; + + if (top.fp == entryFrame) + goto done; + + // This must not GC. + zone->stack().popInlineFrame(); + locals = top.fp->locals(); + + if (op == OP_RETURN) + *top.sp++ = *result; + + top.pc += OpcodeInfo[*top.pc].length; + continue; +} + +case OP_POP: +{ + top.sp--; + break; +} + +#define RELATIONAL_JUMP(op) \ + { \ + int right = POP_I32(top); \ + int left = POP_I32(top); \ + if (left op right) { \ + top.pc += READ_INT32(top.pc); \ + continue; \ + } \ + } + +case OP_JL: + RELATIONAL_JUMP(<); + break; + +case OP_JLE: + RELATIONAL_JUMP(<=); + break; + +case OP_JG: + RELATIONAL_JUMP(>); + break; + +case OP_JGE: + RELATIONAL_JUMP(>=); + break; + +case OP_JEQ: + RELATIONAL_JUMP(==); + break; + +case OP_JNE: + RELATIONAL_JUMP(!=); + break; + +#undef RELATIONAL_JUMP + +case OP_JT: +{ + int value = POP_I32(top); + if (value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +case OP_JF: +{ + int value = POP_I32(top); + if (!value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +#define RELATIONAL_JUMP_F(op) \ + { \ + float right = POP_F(top); \ + float left = POP_F(top); \ + if (left op right) { \ + top.pc += READ_INT32(top.pc); \ + continue; \ + } \ + } + +case OP_JL_F: + RELATIONAL_JUMP_F(<); + break; + +case OP_JLE_F: + RELATIONAL_JUMP_F(<=); + break; + +case OP_JG_F: + RELATIONAL_JUMP_F(>); + break; + +case OP_JGE_F: + RELATIONAL_JUMP_F(>=); + break; + +case OP_JEQ_F: + RELATIONAL_JUMP_F(==); + break; + +case OP_JNE_F: + RELATIONAL_JUMP_F(!=); + break; + +case OP_JT_F: +{ + float value = POP_F(top); + if (value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +case OP_JF_F: +{ + float value = POP_F(top); + if (!value) { + top.pc += READ_INT32(top.pc); + continue; + } + break; +} + +case OP_JUMP: + top.pc += READ_INT32(top.pc); + continue; + +case OP_GETLOCAL: +{ + int slot = READ_INT32(top.pc); + *top.sp++ = locals[slot]; + break; +} + +case OP_SETLOCAL: +{ + int slot = READ_INT32(top.pc); + locals[slot] = top.sp[-1]; + break; +} + +case OP_ADD: +{ + top.sp[-2].i32 = top.sp[-2].i32 + top.sp[-1].i32; + top.sp--; + break; +} + +case OP_SUB: +{ + top.sp[-2].i32 = top.sp[-2].i32 - top.sp[-1].i32; + top.sp--; + break; +} + +case OP_MUL: +{ + top.sp[-2].i32 = top.sp[-2].i32 * top.sp[-1].i32; + top.sp--; + break; +} + +case OP_DIV: +{ + if (top.sp[-1].i32 == 0) { + zone->reportError(Message_IntegerDivideByZero); + goto error; + } + top.sp[-2].i32 = top.sp[-2].i32 / top.sp[-1].i32; + top.sp--; + break; +} + +case OP_MOD: +{ + if (top.sp[-1].i32 == 0) { + zone->reportError(Message_IntegerDivideByZero); + goto error; + } + top.sp[-2].i32 = top.sp[-2].i32 % top.sp[-1].i32; + top.sp--; + break; +} + +case OP_LT: +{ + top.sp[-2].i32 = top.sp[-2].i32 < top.sp[-1].i32; + top.sp--; + break; +} + +case OP_LE: +{ + top.sp[-2].i32 = top.sp[-2].i32 <= top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GT: +{ + top.sp[-2].i32 = top.sp[-2].i32 > top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GE: +{ + top.sp[-2].i32 = top.sp[-2].i32 >= top.sp[-1].i32; + top.sp--; + break; +} + +case OP_EQ: +{ + top.sp[-2].i32 = (top.sp[-2].i32 == top.sp[-1].i32); + top.sp--; + break; +} + +case OP_NE: +{ + top.sp[-2].i32 = (top.sp[-2].i32 != top.sp[-1].i32); + top.sp--; + break; +} + +case OP_ADD_F: +{ + top.sp[-2].f = top.sp[-2].f + top.sp[-1].f; + top.sp--; + break; +} + +case OP_SUB_F: +{ + top.sp[-2].f = top.sp[-2].f - top.sp[-1].f; + top.sp--; + break; +} + +case OP_MUL_F: +{ + top.sp[-2].f = top.sp[-2].f * top.sp[-1].f; + top.sp--; + break; +} + +case OP_DIV_F: +{ + top.sp[-2].f = top.sp[-2].f / top.sp[-1].f; + top.sp--; + break; +} + +case OP_LT_F: +{ + top.sp[-2].f = top.sp[-2].f < top.sp[-1].f; + top.sp--; + break; +} + +case OP_LE_F: +{ + top.sp[-2].f = top.sp[-2].f <= top.sp[-1].f; + top.sp--; + break; +} + +case OP_GT_F: +{ + top.sp[-2].f = top.sp[-2].f > top.sp[-1].f; + top.sp--; + break; +} + +case OP_GE_F: +{ + top.sp[-2].f = top.sp[-2].f >= top.sp[-1].f; + top.sp--; + break; +} + +case OP_EQ_F: +{ + top.sp[-2].i32 = (top.sp[-2].f == top.sp[-1].f); + top.sp--; + break; +} + +case OP_NE_F: +{ + top.sp[-2].i32 = (top.sp[-2].f != top.sp[-1].f); + top.sp--; + break; +} + +case OP_SHL: +{ + top.sp[-2].i32 = top.sp[-2].i32 << top.sp[-1].i32; + top.sp--; + break; +} + +case OP_SHR: +{ + top.sp[-2].i32 = top.sp[-2].i32 >> top.sp[-1].i32; + top.sp--; + break; +} + +case OP_USHR: +{ + top.sp[-2].i32 = unsigned(top.sp[-2].i32) << unsigned(top.sp[-1].i32); + top.sp--; + break; +} + +case OP_BITAND: +{ + top.sp[-2].i32 = top.sp[-2].i32 & top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITOR: +{ + top.sp[-2].i32 = top.sp[-2].i32 | top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITXOR: +{ + top.sp[-2].i32 = top.sp[-2].i32 ^ top.sp[-1].i32; + top.sp--; + break; +} + +case OP_BITNOT: +{ + top.sp[-1].i32 = ~top.sp[-1].i32; + break; +} + +case OP_NOT: +{ + top.sp[-1].i32 = top.sp[-1].i32 ? 0 : 1; + break; +} + +case OP_NOT_F: +{ + top.sp[-1].i32 = top.sp[-1].f ? 0 : 1; + break; +} + +case OP_NEG: +{ + top.sp[-1].i32 = -top.sp[-1].i32; + break; +} + +case OP_NEG_F: +{ + top.sp[-1].f = -top.sp[-1].f; + break; +} + +case OP_CALL: +{ + Local newFun(zone, top.sp[-1].fun); + top.sp--; + + if (!zone->stack().pushInlineFrame(newFun)) + goto error; + + locals = top.fp->locals(); + continue; +} + +case OP_GETARG: +{ + unsigned slot = READ_UINT32(top.pc); + *top.sp++ = top.fp->arg(slot); + break; +} + +case OP_SETARG: +{ + unsigned slot = READ_UINT32(top.pc); + top.fp->arg(slot) = *--top.sp; + break; +} + +case OP_BOUNDS: +{ + Array *array = top.sp[-2].array; + int index = top.sp[-1].i32; + if (index < 0 || unsigned(index) >= array->nelements()) { + zone->reportError(Message_ArrayIndexOutOfBounds, index, array->nelements()); + goto error; + } + break; +} + +case OP_GETARRAY_I: +{ + Array *array = top.sp[-2].array; + int index = top.sp[-1].i32; + top.sp[-2].i32 = array->readInt32(index); + top.sp--; + break; +} + +case OP_GETARRAY_F: +{ + Array *array = top.sp[-2].array; + int index = top.sp[-1].i32; + top.sp[-2].f = array->readFloat(index); + top.sp--; + break; +} + +case OP_GETARRAY_C: +{ + Array *array = top.sp[-2].array; + int index = top.sp[-1].i32; + top.sp[-2].i32 = array->readChar(index); + top.sp--; + break; +} + +case OP_SETARRAY_I: +{ + Array *array = top.sp[-3].array; + int index = top.sp[-2].i32; + array->writeInt32(index, top.sp[-1].i32); + top.sp[-3] = top.sp[-1]; + top.sp -= 2; + break; +} + +case OP_SETARRAY_F: +{ + Array *array = top.sp[-3].array; + int index = top.sp[-2].i32; + array->writeFloat(index, top.sp[-1].f); + top.sp[-3] = top.sp[-1]; + top.sp -= 2; + break; +} + +case OP_SETARRAY_C: +{ + Array *array = top.sp[-3].array; + int index = top.sp[-2].i32; + array->writeChar(index, top.sp[-1].i32); + top.sp[-3] = top.sp[-1]; + top.sp -= 2; + break; +} + +case OP_GETARRAY_O: +{ + Array *array = top.sp[-2].array; + int index = top.sp[-1].i32; + top.sp[-2].array = Array::cast(array->readObject(index)); + top.sp--; + break; +} + +case OP_CVT_I2F: +{ + int ival = POP_I32(top); + float fval = (float)ival; + PUSH_F(top, fval); + break; +} + +case OP_NEWARRAY: +{ + Heap::Tenure tenure = (Heap::Tenure)top.pc[1]; + Local map(zone, ArrayMap::cast(top.fp->code()->map(READ_UINT32(top.pc + 1)))); + Array *array = GenerateArray(zone, map, tenure); + if (!array) + goto error; + top.sp->obj = array; + top.sp++; + break; +} + +case OP_SAVELIFO: +{ + unsigned slot = READ_UINT32(top.pc); + locals[slot].address = zone->heap().lifoPosition(); + break; +} + +case OP_UNWINDLIFO: +{ + unsigned slot = READ_UINT32(top.pc); + zone->heap().unwindLifo(locals[slot].address); + break; +} + +case OP_VARREF_LIFO: +{ + unsigned slot = READ_UINT32(top.pc); + if (!PushNewRef(zone, top, &locals[slot], Heap::Tenure_Stack)) + goto error; + break; +} + +case OP_ARGREF_LIFO: +{ + unsigned slot = READ_UINT32(top.pc); + if (!PushNewRef(zone, top, &top.fp->arg(slot), Heap::Tenure_Stack)) + goto error; + break; +} + +case OP_ARRAYREF_LIFO: +{ + Local array(zone, top.sp[-2].array); + Local ref(zone, Reference::NewArray(zone, array, top.sp[-1].i32, Heap::Tenure_Stack)); + if (!ref) + goto error; + top.sp[-2].ref = ref; + top.sp--; + break; +} + +case OP_SLOTREF_LIFO: +{ + Reference *ref = Reference::NewStack(zone, top.sp[-1], Heap::Tenure_Stack); + if (!ref) + goto error; + top.sp[-1].ref = ref; + break; +} + +case OP_GETREF_I: +{ + top.sp[-1].i32 = top.sp[-1].ref->getInt(); + break; +} + +case OP_SETREF_I: +{ + top.sp[-2].ref->setInt(top.sp[-1].i32); + top.sp[-2].i32 = top.sp[-1].i32; + top.sp--; + break; +} + +case OP_GETREF_F: +{ + top.sp[-1].f = top.sp[-1].ref->getFloat(); + break; +} + +case OP_SETREF_F: +{ + top.sp[-2].ref->setFloat(top.sp[-1].f); + top.sp[-2].f = top.sp[-1].f; + break; +} + +case OP_OBJECT: +{ + top.sp->obj = top.fp->code()->object(READ_UINT32(top.pc)); + top.sp++; + break; +} + +case OP_ARRAYCOPY: +{ + // Note for this assignment opcode, we return the left-hand side, not the + // right, since in this case the result of the expression could be different. + if (!CopyArray(zone, top.sp[-2].array, top.sp[-1].array)) + goto error; + top.sp--; + break; +} + +case OP_DUP: +{ + top.sp++; + top.sp[-1] = top.sp[-2]; + break; +} + +case OP_DUP2: +{ + top.sp += 2; + top.sp[-2] = top.sp[-4]; + top.sp[-1] = top.sp[-3]; + break; +} + +case OP_SWAP: +{ + LocalSlot slot = top.sp[-2]; + top.sp[-2] = top.sp[-1]; + top.sp[-1] = slot; + break; +} + +case OP_ROLL3: +{ + LocalSlot slot = top.sp[-3]; + top.sp[-3] = top.sp[-2]; + top.sp[-2] = top.sp[-1]; + top.sp[-1] = slot; + break; +} + +case OP_PICK2: +{ + top.sp++; + top.sp[-1] = top.sp[-3]; + break; +} + +case OP_PICK3: +{ + top.sp++; + top.sp[-1] = top.sp[-4]; + break; +} + +case OP_CALLNATIVE: +{ + unsigned index = READ_UINT32(top.pc); + unsigned argc = READ_UINT32(top.pc + 4); + + Local native(zone, Native::cast(top.fp->code()->module()->natives()->at(index))); + if (!native->function()) { + zone->reportError(Message_NativeNotBound, native->name()->chars()); + goto error; + } + + int result; + void *nativeIdentity = top.fp->code()->module()->nativeIdentity(); + if (!InvokeNative(zone, native, nativeIdentity, top.sp, argc, &result)) + goto error; + + top.sp -= argc; + PUSH_I32(top, result); + break; +} + +case OP_MODULE: +{ + top.sp++; + top.sp[-1].module = top.fp->code()->module(); + break; +} + +case OP_GETMODULE_FN: +{ + top.sp[-1].fun = top.sp[-1].module->getFunction(READ_UINT32(top.pc)); + break; +} + +case OP_GETGLOBAL_I: +{ + unsigned slot = READ_UINT32(top.pc); + top.sp++; + top.sp[-1].i32 = top.fp->code()->module()->environment()->getInt32(slot); + break; +} + +case OP_GETGLOBAL_F: +{ + unsigned slot = READ_UINT32(top.pc); + top.sp++; + top.sp[-1].f = top.fp->code()->module()->environment()->getFloat(slot); + break; +} + +case OP_GETGLOBAL_O: +{ + unsigned slot = READ_UINT32(top.pc); + top.sp++; + top.sp[-1].obj = top.fp->code()->module()->environment()->getObject(slot); + break; +} + +case OP_SETGLOBAL_I: +{ + unsigned slot = READ_UINT32(top.pc); + top.fp->code()->module()->environment()->setInt32(slot, top.sp[-1].i32); + break; +} + +case OP_SETGLOBAL_F: +{ + unsigned slot = READ_UINT32(top.pc); + top.fp->code()->module()->environment()->setFloat(slot, top.sp[-1].f); + break; +} + +case OP_SETGLOBAL_O: +{ + unsigned slot = READ_UINT32(top.pc); + top.fp->code()->module()->environment()->setObject(slot, top.sp[-1].obj); + break; +} + +default: + assert(false); + return false; + + } + + top.pc += OpcodeInfo[op].length; + } + +done: + return true; + +error: + // Unwind frames until we can continue. + while (top.fp != entryFrame) + zone->stack().popInlineFrame(); + return false; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Interpreter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Interpreter.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_interpreter_h_ +#define _include_jc_interpreter_h_ + +#include "Stack.h" + +namespace ke { + +bool Interpret(Zone *zone, Frame *frame, LocalSlot *result); + +} + +#endif // _include_jc_interpreter_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.in Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,96 @@ +#vim: set ft=make: + +CXX=@CXX@ +CXXFLAGS=@CXXFLAGS@ +srcdir=@srcdir@ +VPATH=${srcdir} +TOOLS=${srcdir}/../build +CXXVENDOR=@CXXVENDOR@ +PLATFORM="@PLATFORM@" +NULL = + +INCLUDES += +CXXFLAGS += $(INCLUDES) +BASECXXFLAGS = -Wall -Werror -Wno-null-arithmetic -Wno-invalid-offsetof -g3 -O0 + +CPPSRCS = \ + Array.cpp \ + BytecodeCompiler.cpp \ + BytecodeEmitter.cpp \ + CompileContext.cpp \ + Exception.cpp \ + FixedArray.cpp \ + Functions.cpp \ + Heap.cpp \ + Interpreter.cpp \ + Modules.cpp \ + NameBinding.cpp \ + Parser.cpp \ + Opcodes.cpp \ + PoolAllocator.cpp \ + Reference.cpp \ + RootScopes.cpp \ + Scanner.cpp \ + Scopes.cpp \ + SemanticAnalysis.cpp \ + Spaces.cpp \ + Stack.cpp \ + TypeManager.cpp \ + Types.cpp \ + Zone.cpp \ + main.cpp \ + $(NULL) + +ifeq ("darwin", $(PLATFORM)) +CPPSRCS += \ + Platform-Posix.cpp \ + $(NULL) +endif +ifeq ("linux", $(PLATFORM)) +CPPSRCS += \ + Platform-Posix.cpp \ + $(NULL) +endif + +default: all + +OBJECTS := $(CPPSRCS:%.cpp=%.o) + +ifeq ($(CXXVENDOR), microsoft) + +CXXFLAGS += /DWIN32 /D_WINDOWS + +%.o: %.cpp + $(CXX) $(CXXFLAGS) /Fo$@ /c /TP /nologo $< +else + +%.o: %.cpp + $(CXX) $(BASECXXFLAGS) $(CXXFLAGS) -c -o $@ -Wp,-MD,.deps/$(patsubst %.o,%.d,$@) $< + +endif + +#$(CXX) $(BASECXXFLAGS) $(CXXFLAGS) -c -o $@ -Wp,-MD,.deps/$(basename $(@F)).pp $< + +DEPEND_FILES := $(strip $(wildcard .deps/*.d)) + +ifneq (,$(DEPEND_FILES)) +DEPEND_RESULTS = $(shell $(TOOLS)/mddepend.pl - $(DEPEND_FILES)) +$(eval $(DEPEND_RESULTS)) +endif + +all: foobar + +foobar: libfoobar.a main.o + $(CXX) main.o libfoobar.a -lpthread -o foobar + +libfoobar.a: $(OBJECTS) + $(AR) -crs libfoobar.a $(OBJECTS) + +FORCE: + +clean: + rm -f .deps/*.d + rm -f *.o + rm -f libfoobar.a + rm -f foobar + diff -r 06a0015b6a5c -r 32ad0523b309 src/Messages.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Messages.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_messages_h_ +#define _include_jitcraft_messages_h_ + +namespace ke { + +enum MessageType +{ + MessageType_SyntaxError, + MessageType_TypeError, + MessageType_SystemError +}; + +enum Message +{ +#define MSG(Name, Type, String) Message_##Name, +# include "Messages.tbl" +#undef MSG + MessageType_Total +}; + +struct MessageInfo +{ + MessageType type; + const char *format; +}; + +extern const MessageInfo Messages[]; + +} + +#endif // _include_jitcraft_messages_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Messages.tbl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Messages.tbl Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,62 @@ +MSG(UnterminatedComment, SyntaxError, "unterminated comment") +MSG(UnexpectedCharacter, SyntaxError, "unexpected character '%c' (code %02X)") +MSG(UnterminatedString, SyntaxError, "unterminated string") +MSG(UnknownEscapeCode, SyntaxError, "unknown escape code '%c'") +MSG(ExpectedDigitForFloat, SyntaxError, "expected digit in float literal, got '%c'") +MSG(WrongToken, SyntaxError, "expected token '%s', got '%s'") +MSG(ExpectedExpression, SyntaxError, "expected expression, got '%s'") +MSG(RedeclaredName, SyntaxError, "name '%s' was redeclared (previous at line %d, col %d)") +MSG(ExpectedLValue, SyntaxError, "impossible assignment, expected l-value") +MSG(MaximumArrayDepth, SyntaxError, "maximum array dimension depth exceeded") +MSG(ArrayInitializerDepth, SyntaxError, "array declaration and initializer have different dimension counts") +MSG(BadArraySize, SyntaxError, "array size must be positive") +MSG(ArraySizeTooBig, SyntaxError, "array size is too big") +MSG(EnumFieldNotConstant, SyntaxError, "enum fields must be constant integers") +MSG(BreakOutsideLoop, SyntaxError, "break occurs outside any loop") +MSG(ContinueOutsideLoop, SyntaxError, "continue occurs outside any loop") +MSG(InvalidEnumName, SyntaxError, "invalid enumeration name") +MSG(IndexMustBeInteger, SyntaxError, "array index must be integer") +MSG(IndexBaseMustBeArray, SyntaxError, "cannot index into non-array") +MSG(ArrayIsNotBoolean, SyntaxError, "cannot convert an array to boolean") +MSG(CalleeNotFunction, SyntaxError, "callee is not a function") +MSG(ArgumentCountMismatch, SyntaxError, "argument counts do not match") +MSG(CannotAssignArrayToScalar, SyntaxError, "cannot coerce an array to a scalar") +MSG(UnsupportedType, SyntaxError, "unsupported type") +MSG(TagMismatch, SyntaxError, "tag mismatch (expected '%s', got '%s')") +MSG(ArrayDimensionsMismatch, SyntaxError, "number of array dimensions does not match") +MSG(ExpectedArray, SyntaxError, "expected array") +MSG(ArrayDimensionSizeMismatch, SyntaxError, "array dimension sizes do not match (level %d)") +MSG(ArrayDimensionNotInteger, SyntaxError, "array dimension size must be an integer expression") +MSG(ArrayNotSupported, SyntaxError, "array declaration not yet supported") +MSG(ArrayInitializerUnknown, SyntaxError, "array initialized size is not known") +MSG(DynamicArrayHasInitializer, SyntaxError, "dynamic arrays cannot have initializers") +MSG(ArraySizeNotSpecified, SyntaxError, "array size was not specified") +MSG(ArraySizeCannotBeDetermined, SyntaxError, "array size cannot be determined from initializer") +MSG(ArrayAssignmentTooBig, SyntaxError, "destination array is too small") +MSG(CannotDeclareStringCell, SyntaxError, "cannot declare a String cell") +MSG(BinaryTypeError, SyntaxError, "operator '%s' does not support types '%s' and '%s'") +MSG(ArraySizeNotKnown, SyntaxError, "array size is not known") +MSG(UnaryTypeError, SyntaxError, "operator '%s' does not support type '%s'") +MSG(CantOpenFile, SyntaxError, "cannot open file") +MSG(ErrorSeekingFile, SyntaxError, "error seeking file") +MSG(ErrorReadingFile, SyntaxError, "error reading file") +MSG(ExpectedGlobal, SyntaxError, "expected global variable or function") +MSG(ForwardNotImplemented, SyntaxError, "forward '%s' not implemented") +MSG(ForwardDeclareMismatches, SyntaxError, "declaration does not match original forward signature") +MSG(ArrayCantBeReference, SyntaxError, "a parameter cannot be both an array and a reference") +MSG(ArraySizeMustBeConstant, SyntaxError, "array size must be a constant integer") +MSG(ReferenceIsConstant, SyntaxError, "cannot assign to a constant lvalue") +MSG(MultiArrayInNative, SyntaxError, "multidimensional arrays cannot be passed to natives") +MSG(InvalidVarArgs, SyntaxError, "varargs cannot be const or ref") +MSG(VarArgsOutsideNative, SyntaxError, "variadic arguments can only be specified on natives") +MSG(ArrayTooBig, TypeError, "array size is too large") +MSG(IntegerDivideByZero, TypeError, "integer divide by zero") +MSG(ArrayIndexOutOfBounds, TypeError, "array index %d is out of bounds [0, %d)") +MSG(NativeNotBound, TypeError, "attempted to call unbound native \"%s\"") +MSG(AllocationOverflow, SystemError, "allocation request overflowed") +MSG(TooMuchRecursion, SystemError, "stack overflow: too much recursion") +MSG(MissingReturnValue, SyntaxError, "tagged function must return a value") +MSG(NotAllPathsReturnValue, SyntaxError, "not all paths through function return a value") +MSG(UsedVoidReturn, SyntaxError, "return value expected, but function does not return a value") +MSG(FunctionDoesNotReturnValue, SyntaxError, "cannot use the result of a function with no return value") +MSG(IdentifierNotFound, SyntaxError, "identifier '%s' could not be found") \ No newline at end of file diff -r 06a0015b6a5c -r 32ad0523b309 src/Modules.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Modules.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Modules.h" +#include "Zone.h" +#include "Heap.h" +#include "Heap-inl.h" + +using namespace ke; + +Module * +Module::New(Zone *zone, unsigned nfunctions) +{ + Local module(zone, Module::cast(zone->allocate(MapKind_Module, sizeof(Module), Heap::Tenure_Old))); + if (!module) + return NULL; + + Local functions(zone, FixedArray::New(zone, nfunctions, Heap::Tenure_Old)); + if (!functions) + return NULL; + + module->functions_ = functions; + module->file_ = NULL; + module->publics_ = NULL; + module->natives_ = NULL; + module->environment_ = NULL; + module->nativeIdentity_ = NULL; + return module; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Modules.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Modules.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_module_h_ +#define _include_jc_module_h_ + +#include "Heap.h" +#include "Code.h" +#include "Functions.h" + +namespace ke { + +class Environment; + +class Module : public Object +{ + Barriered functions_; + Barriered publics_; + Barriered natives_; + Barriered file_; + Barriered environment_; + Barriered code_; + void *nativeIdentity_; + + public: + static Module *New(Zone *zone, unsigned nfunctions); + + Function *getFunction(size_t i) { + return Function::cast(functions_->at(i)); + } + void setFunction(Zone *zone, size_t i, Function *fun) { + assert(!functions_->at(i)); + assert(fun); + functions_->set(zone, i, fun); + } + + FixedArray *publics() { + return publics_; + } + FixedArray *natives() { + return natives_; + } + String *file() { + return file_; + } + Environment *environment() { + return environment_; + } + Code *code() { + return code_; + } + + void setPublics(Handle publics) { + publics_ = publics; + } + void setNatives(Handle natives) { + natives_ = natives; + } + void setPath(Handle path) { + file_ = path; + } + void setEnvironment(Handle environment) { + environment_ = environment; + } + void setCode(Handle code) { + code_ = code; + } + + void *nativeIdentity() { + return nativeIdentity_; + } + void setNativeIdentity(void *identity) { + nativeIdentity_ = identity; + } + + // Begin GC Descriptor. + static inline size_t offsetOfFunctions() { + return offsetof(Module, functions_); + } + static inline size_t offsetOfPublics() { + return offsetof(Module, publics_); + } + static inline size_t offsetOfNatives() { + return offsetof(Module, natives_); + } + static inline size_t offsetOfFile() { + return offsetof(Module, file_); + } + static inline size_t offsetOfEnvironment() { + return offsetof(Module, environment_); + } + static inline size_t offsetOfCode() { + return offsetof(Module, code_); + } + // End GC Descriptor. + + static inline Module *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Module)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jc_module_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/NameBinding.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/NameBinding.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "NameBinding.h" +#include "Strings.h" +#include "Zone.h" +#include "Scopes.h" + +using namespace ke; + +BoundName::BoundName(Scope *scope, Handle name, const SourcePosition &pos) + : scope_(scope), + name_(name), + type_(NULL), + pos_(pos) +{ +} + +NamedFunction::NamedFunction(Scope *scope, Handle name, const SourcePosition &pos, + TokenKind token) + : BoundName(scope, name, pos), + token_(token), + isVariadicNative_(false) +{ +} + +Variable::Variable(Scope *scope, Handle name, const SourcePosition &pos) + : BoundName(scope, name, pos), + location_(UNALLOCATED) +{ +} + +bool +Variable::needsHeapAllocation() +{ + return scope()->kind() == Scope::GLOBAL; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/NameBinding.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/NameBinding.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_name_binding_h_ +#define _include_name_binding_h_ + +#include "PoolAllocator.h" +#include "Types.h" +#include "Scanner.h" + +namespace ke { + +class Variable; +class NamedFunction; +class Scope; +class Type; +class NamedEnum; +class NamedConstant; +class String; + +class BoundName : public PoolObject +{ + Scope *scope_; + ScopedRoot name_; + ScopedRoot type_; + + protected: + SourcePosition pos_; + + public: + enum Kind { + VARIABLE, + FUNCTION, + ENUM, + CONSTANT + }; + + BoundName(Scope *scope, Handle name, const SourcePosition &pos); + + virtual Kind kind() const = 0; + + String *name() const { + return name_; + } + Scope *scope() const { + return scope_; + } + Variable *toVariable() { + assert(kind() == VARIABLE); + return reinterpret_cast(this); + } + NamedFunction *toNamedFunction() { + assert(kind() == FUNCTION); + return reinterpret_cast(this); + } + NamedConstant *toNamedConstant() { + assert(kind() == CONSTANT); + return reinterpret_cast(this); + } + Type *type() const { + return type_; + } + void setType(Handle type) { + type_ = type; + } + const SourcePosition &pos() { + return pos_; + } +}; + +class NamedFunction : public BoundName +{ + unsigned slot_; + TokenKind token_; + ScopedRoot forwardType_; + bool isVariadicNative_; + + public: + NamedFunction(Scope *scope, Handle name, const SourcePosition &pos, TokenKind token); + + Kind kind() const { + return FUNCTION; + } + void allocate(unsigned slot) { + slot_ = slot; + } + bool isForward() const { + return token_ == TOK_FORWARD; + } + bool isPublic() const { + return token_ == TOK_PUBLIC; + } + bool isNative() const { + return token_ == TOK_NATIVE; + } + void setForwardType(Handle type) { + forwardType_ = type; + } + Type *forwardType() { + return forwardType_; + } + void setImplemented(const SourcePosition &at) { + token_ = TOK_PUBLIC; + pos_ = at; + } + void markNativeVariadic() { + assert(token_ == TOK_NATIVE); + isVariadicNative_ = true; + } + bool isNativeVariadic() { + assert(token_ == TOK_NATIVE); + return isVariadicNative_; + } + unsigned slot() const { + return slot_; + } +}; + +class NamedEnum : public BoundName +{ + public: + NamedEnum(Scope *scope, Handle name, const SourcePosition &pos) + : BoundName(scope, name, pos) + { + } + + Kind kind() const { + return ENUM; + } +}; + +class NamedConstant : public BoundName +{ + BoxedPrimitive value_; + Type *type_; + + public: + NamedConstant(Scope *scope, Handle name, const SourcePosition &pos) + : BoundName(scope, name, pos), + type_(NULL) + { + } + + Kind kind() const { + return CONSTANT; + } + void set(Type *type, const BoxedPrimitive &value) { + value_ = value; + type_ = type; + } + Type *type() const { + return type_; + } + BoxedPrimitive value() const { + return value_; + } +}; + +class Variable : public BoundName +{ + public: + enum Location { + UNALLOCATED, + STACK, + PARAMETER, + ENVIRONMENT + }; + + Variable(Scope *scope, Handle name, const SourcePosition &pos); + + Kind kind() const { + return VARIABLE; + } + bool unallocated() const { + return location() == UNALLOCATED; + } + void allocate(Location location, unsigned slot) { + location_ = location; + slot_ = slot; + } + Location location() const { + return location_; + } + unsigned slot() const { + assert(!unallocated()); + return slot_; + } + bool needsHeapAllocation(); + + private: + Location location_; + unsigned slot_; +}; + +} + +#endif // _include_name_binding_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/ObjectVisitor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ObjectVisitor.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,269 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_object_visitor_h_ +#define _include_jc_object_visitor_h_ + +#include "Objects.h" +#include "Types.h" +#include "FixedArray.h" +#include "Code.h" +#include "Reference.h" +#include "Modules.h" +#include "Functions.h" +#include "Array.h" +#include "Strings.h" +#include "Environments.h" + +namespace ke { + +// An ObjectVisitor is used for traversing object references in the heap. Since +// this pattern must be lightning fast, we avoid virtual dispatch by forcing +// visitors to compose themselves with the visiting logic. +template +class ObjectVisitor : public T +{ + private: + void visitField(Object *obj, size_t offset) { + this->visit(reinterpret_cast(Address(obj) + offset)); + } + + size_t iterateType(Object *obj) { + visitField(obj, Type::offsetOfContained()); + visitField(obj, Type::offsetOfReturnType()); + visitField(obj, Type::offsetOfParameters()); + visitField(obj, Type::offsetOfTag()); + return sizeof(Type); + } + + size_t iterateFixedArray(FixedArray *array) { + unsigned length = array->length(); + Object **ptr = reinterpret_cast(array + 1); + for (unsigned i = 0; i < length; i++) + this->visit(ptr + i); + return FixedArray::SizeFor(length); + } + + size_t iterateByteArray(ByteArray *array) { + return ByteArray::SizeFor(array->length()); + } + + size_t iterateCode(Object *obj) { + visitField(obj, Code::offsetOfModule()); + visitField(obj, Code::offsetOfBytecode()); + visitField(obj, Code::offsetOfLocals()); + visitField(obj, Code::offsetOfObjects()); + visitField(obj, Code::offsetOfSafepoints()); + visitField(obj, Code::offsetOfSourceMap()); + return sizeof(Code); + } + + size_t iterateReference(Object *obj) { + visitField(obj, Reference::offsetOfBase()); + return sizeof(Reference); + } + + size_t iterateModule(Object *obj) { + visitField(obj, Module::offsetOfFunctions()); + visitField(obj, Module::offsetOfPublics()); + visitField(obj, Module::offsetOfNatives()); + visitField(obj, Module::offsetOfFile()); + visitField(obj, Module::offsetOfEnvironment()); + visitField(obj, Module::offsetOfCode()); + return sizeof(Module); + } + + size_t iterateFunction(Object *obj) { + visitField(obj, Function::offsetOfCode()); + visitField(obj, Function::offsetOfType()); + visitField(obj, Function::offsetOfName()); + return sizeof(Function); + } + + size_t iterateNative(Object *obj) { + visitField(obj, Native::offsetOfType()); + visitField(obj, Native::offsetOfName()); + return sizeof(Native); + } + + size_t iterateString(Object *obj) { + return String::SizeFor(String::cast(obj)->length()); + } + + size_t iterateArray(Array *array, ArrayMap *map) { + assert(map->type()->isArray()); + if (map->contained()) { + unsigned length = array->nelements(); + Object **vec = array->objVec(); + for (unsigned i = 0; i < length; i++) + this->visit(vec + i); + } + return Array::SizeOf(array); + } + + size_t iterateArrayMap(ArrayMap *map) { + visitField(map, ArrayMap::offsetOfType()); + visitField(map, ArrayMap::offsetOfContained()); + return sizeof(ArrayMap); + } + + size_t iterateEnvironmentSlotDescriptorArray(EnvironmentSlotDescriptorArray *env) { + unsigned length = env->length(); + EnvironmentSlotDescriptor *desc = env->slots(); + for (unsigned i = 0; i < length; i++) + this->visit(desc->type.addr()); + return env->size(); + } + + size_t iterateEnvironmentMap(EnvironmentMap *map) { + visitField(map, EnvironmentMap::offsetOfSlots()); + return map->size(); + } + + size_t iterateEnvironment(Environment *env) { + EnvironmentMap *map = env->map(); + const unsigned *offsets = map->traceOffsets(); + unsigned noffsets = map->traceOffsetCount(); + for (unsigned i = 0; i < noffsets; i++) + visitField(env, Environment::offsetOfSlotOffset(offsets[i])); + return env->size(); + } + + public: + size_t iterate(Object *obj) { + visitField(obj, Object::offsetOfMap()); + return iterate(obj, obj->map()); + } + + size_t iterate(Object *obj, Map *map) { + switch (map->kind()) { + case MapKind_Type: + return iterateType(obj); + + case MapKind_FixedArray: + return iterateFixedArray(FixedArray::cast(obj)); + + case MapKind_ByteArray: + return iterateByteArray(ByteArray::cast(obj)); + + case MapKind_Code: + return iterateCode(obj); + + case MapKind_Reference: + return iterateReference(obj); + + case MapKind_Module: + return iterateModule(obj); + + case MapKind_Function: + return iterateFunction(obj); + + case MapKind_String: + return iterateString(obj); + + case MapKind_Array: + return Align(iterateArray(Array::cast(obj), ArrayMap::cast(map)), kObjectAlignment); + + case MapKind_EnvironmentSlotDescriptorArray: + return iterateEnvironmentSlotDescriptorArray(EnvironmentSlotDescriptorArray::cast(obj)); + + case MapKind_Environment: + return iterateEnvironment(Environment::cast(obj)); + + case MapKind_Native: + return iterateNative(obj); + + case MapKind_MetaArray: + return iterateArrayMap(ArrayMap::cast(obj)); + + case MapKind_MetaEnvironment: + return iterateEnvironmentMap(EnvironmentMap::cast(obj)); + + default: + assert(map->kind() == MapKind_Meta); + return sizeof(Map); + } + } +}; + +static inline size_t +SizeOfObject(Object *obj, Map *map) +{ + switch (map->kind()) { + case MapKind_Type: + return sizeof(Type); + + case MapKind_FixedArray: + return FixedArray::SizeFor(FixedArray::cast(obj)->length()); + + case MapKind_ByteArray: + return ByteArray::SizeFor(ByteArray::cast(obj)->length()); + + case MapKind_Code: + return sizeof(Code); + + case MapKind_Reference: + return sizeof(Reference); + + case MapKind_Module: + return sizeof(Module); + + case MapKind_Function: + return sizeof(Function); + + case MapKind_String: + return String::SizeFor(String::cast(obj)->length()); + + case MapKind_Native: + return sizeof(Native); + + case MapKind_Array: + return Align(Array::SizeOf(Array::cast(obj)), kObjectAlignment); + + case MapKind_EnvironmentSlotDescriptorArray: + return EnvironmentSlotDescriptorArray::cast(obj)->size(); + + case MapKind_Environment: + return Environment::cast(obj)->size(); + + case MapKind_MetaArray: + return sizeof(ArrayMap); + + case MapKind_MetaEnvironment: + return EnvironmentMap::cast(obj)->size(); + + case MapKind_FreeRegion: + return *reinterpret_cast(reinterpret_cast
(obj + 1)); + + default: + assert(map->kind() == MapKind_Meta); + return sizeof(Map); + } +} + +class ObjectSizeEvaluator +{ + public: + size_t operator()(Object *obj, Map *map) { + return SizeOfObject(obj, map); + } +}; + +} + +#endif // _include_jc_object_visitor_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Objects.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Objects.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,119 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_objects_h_ +#define _include_jitcraft_objects_h_ + +#include "Utility.h" +#include "Spaces.h" +#include "Barriers.h" + +namespace ke { + +class Zone; + +// Garbage-collection objects require a header to describe their layout. This +// header is called a "map". The different kinds of maps are enumerated below. +class Map; + +enum MapKind +{ + // Represents Type objects. + MapKind_Type, + + // Represents basic data structures. When adding to this, don't forget + // to update Zone::initialize(), or you'll get a NULL crash trying to + // allocate, since these maps are cached. + MapKind_FixedArray, + MapKind_ByteArray, + MapKind_Code, + MapKind_Reference, + MapKind_Module, + MapKind_Function, + MapKind_String, + MapKind_Native, + MapKind_FreeRegion, // Special map denoting freed space. + MapKind_EnvironmentSlotDescriptorArray, + MapKind_Environment, + + // Used to represent Arrays. This does not have a canonical map. + MapKind_Array, + + // These represent special Map subclasses that require special tracing. + MapKinds_Special, + MapKind_Meta, // (default map class) + MapKind_MetaArray, // ArrayMap instances + MapKind_MetaEnvironment, // EnvironmentMap instances + MapKinds_EndSpecial, + + MapKinds_Total +}; + +// All garbage collected objects must inherit from GCObject. Note that using +// virtual functions is illegal, as the |map| field would be displaced. +class Object +{ + Barriered map_; + + public: + void init(Map *map) { + map_ = map; + } + + Map *map() { + return map_; + } + inline bool is(MapKind map); + + inline void write(Zone *zone, Object **ptr, Object *value); + + static inline size_t offsetOfMap() { + return offsetof(Object, map_); + } +}; + +// Maps describe the layout of GCObjects. Every GCObject requires a map. +class Map : public Object +{ + MapKind kind_; + + public: + void setKind(MapKind kind) { + kind_ = kind; + } + MapKind kind() { + return kind_; + } + + static inline Map *cast(Object *obj) { + // Note that ArrayMap is a map, but a special one: since it is non- + assert(obj->map()->kind() > MapKinds_Special && + obj->map()->kind() < MapKinds_EndSpecial); + return reinterpret_cast(obj); + } +}; + +inline bool +Object::is(MapKind kind) +{ + return map()->kind() == kind; +} + +} + +#endif //_include_jitcraft_objects_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Opcodes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Opcodes.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,311 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Opcodes.h" +#include "Utility.h" +#include "Code.h" +#include "Zone.h" +#include "Array.h" +#include "Strings.h" +#include "CompactBuffer.h" +#include "Heap-inl.h" + +using namespace ke; + +OpInfo ke::OpcodeInfo[] = +{ +#define OPDEF(name, number, text, length, uses, defs) \ + {text, length, uses, defs}, +#include "Opcodes.tbl" +#undef OPDEF +}; + +Code * +Code::New(Zone *zone, Handle bytecode, Handle locals, + Handle objects, unsigned maxStackDepth, Handle module, + Handle safepoints, Handle sourcemap, + unsigned lineNumber) +{ + Local code(zone, Code::cast(zone->allocate(MapKind_Code, sizeof(Code), Heap::Tenure_Old))); + if (!code) + return NULL; + + code->bytecode_ = bytecode; + code->locals_ = locals; + code->objects_ = objects; + code->maxStackDepth_ = maxStackDepth; + code->module_ = module; + code->safepoints_ = safepoints; + code->sourcemap_ = sourcemap; + code->lineNumber_ = lineNumber; + return code; +} + +static int +CompareSafepoints(const void *a, const void *b) +{ + const unsigned *sa = reinterpret_cast(a); + const unsigned *sb = reinterpret_cast(b); + if (*sa > *sb) + return 1; + if (*sa == *sb) + return 0; + return -1; +} + +uint8 * +Code::safepointAt(unsigned pcOffset) +{ + // Only specific opcodes may have safepoints. Asking for a safepoint at + // any other position indicates a failure to mark that position as a + // safepoint, which is a bug. + Opcode op = Opcode(*(start() + pcOffset)); + assert(op == OP_CALL || op == OP_NEWARRAY || op == OP_VARREF_LIFO || + op == OP_ARGREF_LIFO || op == OP_SLOTREF_LIFO || op == OP_ARRAYREF_LIFO || + op == OP_ARRAYCOPY); + + if (!safepoints_) + return NULL; + + size_t bytesPerSafepoint = BytesPerSafepoint(maxStackDepth()); + assert(!(safepoints_->length() % bytesPerSafepoint)); + + size_t count = safepoints_->length() / bytesPerSafepoint; + + void *result = bsearch(&pcOffset, + safepoints_->raw(), + count, + bytesPerSafepoint, + CompareSafepoints); + + if (!result) + return NULL; + + assert(*reinterpret_cast(result) == pcOffset); + + return Address(result) + sizeof(unsigned); +} + +SourcePosition +Code::pcToSource(unsigned pc) +{ + assert(pc < bytecode_->length()); + + CompactBufferReader reader(sourcemap_->raw(), sourcemap_->raw() + sourcemap_->length()); + + unsigned line = 1; + unsigned col = 0; + unsigned curPc = 0; + while (!reader.done()) { + curPc += reader.readUint32(); + if (curPc > pc) + break; + + line = reader.readUint32(); + col = reader.readUint32(); + } + + return SourcePosition(lineNumber_ + line, col); +} + +// All jumps must be the same length. +STATIC_ASSERT(OP_JL_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JLE_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JG_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JGE_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JEQ_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JNE_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JT_LENGTH == OP_JUMP_LENGTH); +STATIC_ASSERT(OP_JF_LENGTH == OP_JUMP_LENGTH); + +void +ke::Disassemble(Code *code, FILE *fp) +{ + bytecode *pc = code->start(); + for (;;) { + Opcode op = Opcode(*pc); + + fprintf(fp, "%-4d %s ", int(pc - code->start()), OpcodeInfo[op].name); + + switch (op) { + case OP_NOP: + case OP_RETURN: + case OP_RETURNVOID: + case OP_ADD: + 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_GETARRAY_I: + case OP_SETARRAY_I: + case OP_GETARRAY_F: + case OP_SETARRAY_F: + case OP_GETARRAY_C: + case OP_SETARRAY_C: + case OP_GETARRAY_O: + case OP_BOUNDS: + case OP_SHL: + case OP_SHR: + case OP_USHR: + case OP_BITAND: + case OP_BITOR: + case OP_BITXOR: + case OP_GETREF_I: + case OP_SETREF_I: + case OP_GETREF_F: + case OP_SETREF_F: + case OP_SLOTREF_LIFO: + case OP_ARRAYREF_LIFO: + case OP_ARRAYCOPY: + case OP_DUP: + case OP_DUP2: + case OP_SWAP: + case OP_ROLL3: + case OP_PICK2: + case OP_PICK3: + case OP_CALL: + case OP_MODULE: + case OP_LT: + case OP_LE: + case OP_GE: + case OP_GT: + case OP_EQ: + case OP_NE: + case OP_LT_F: + case OP_LE_F: + case OP_GE_F: + case OP_GT_F: + case OP_EQ_F: + case OP_NE_F: + case OP_BITNOT: + case OP_NOT: + case OP_NOT_F: + case OP_NEG: + case OP_NEG_F: + case OP_MOD: + break; + + case OP_STOP: + fprintf(fp, "\n"); + break; + + case OP_INT: + 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: + case OP_JGE: + case OP_JEQ: + case OP_JNE: + case OP_JT: + case OP_JF: + case OP_JL_F: + case OP_JLE_F: + case OP_JG_F: + case OP_JGE_F: + case OP_JEQ_F: + case OP_JNE_F: + case OP_JT_F: + case OP_JF_F: + case OP_JUMP: + fprintf(fp, "%u (%+d)", unsigned((pc + READ_INT32(pc)) - code->start()), READ_INT32(pc)); + break; + + case OP_NEWARRAY: + { + Heap::Tenure tenure = (Heap::Tenure)pc[1]; + ArrayMap *map = ArrayMap::cast(code->map(READ_UINT32(pc + 1))); + fprintf(fp, "%s:", map->type()->innermost()->tag() + ? map->type()->innermost()->tag()->chars() + : "_"); + for (; map; map = map->contained()) { + Type *type = map->type(); + fprintf(fp, "[%d]", type->elements()); + } + if (tenure == Heap::Tenure_Stack) + fprintf(fp, " ; lifo"); + else if (tenure == Heap::Tenure_Old) + fprintf(fp, " ; pretenured"); + break; + } + + case OP_GETLOCAL: + case OP_SETLOCAL: + case OP_GETARG: + case OP_SETARG: + case OP_SAVELIFO: + case OP_UNWINDLIFO: + case OP_VARREF_LIFO: + case OP_ARGREF_LIFO: + case OP_OBJECT: + case OP_GETMODULE_FN: + case OP_GETGLOBAL_I: + case OP_GETGLOBAL_F: + case OP_GETGLOBAL_O: + case OP_SETGLOBAL_I: + case OP_SETGLOBAL_F: + case OP_SETGLOBAL_O: + fprintf(fp, "%u", READ_UINT32(pc)); + break; + + case OP_CALLNATIVE: + fprintf(fp, "%u %u", READ_UINT32(pc), READ_UINT32(pc + 4)); + break; + + default: + // Unknown opcode! + assert(false); + return; + } + + if (op == OP_STOP) + break; + + fprintf(fp, "\n"); + pc += OpcodeInfo[op].length; + assert(pc < code->start() + code->length()); + } + + fprintf(fp, "\nSafepoints:\n"); + ByteArray *safepoints = code->safepoints(); + if (safepoints) { + size_t nsafepoints = safepoints->length() / Code::BytesPerSafepoint(code->maxStackDepth()); + for (size_t i = 0; i < nsafepoints; i++) { + Address address = safepoints->raw() + Code::BytesPerSafepoint(code->maxStackDepth()) * i; + fprintf(fp, "%4d: ", *reinterpret_cast(address)); + unsigned char *bits = (unsigned char *)(address + sizeof(unsigned)); + for (unsigned j = 0; j < code->maxStackDepth(); j++) { + if (bits[j / 8] & (1 << (j % 8))) + fprintf(fp, "%d ", j); + } + fprintf(fp, "\n"); + } + } +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Opcodes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Opcodes.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_opcodes_h_ +#define _include_opcodes_h_ + +#include +#include + +namespace ke { + +enum Opcode +{ +#define OPDEF(name, number, text, length, uses, defs) \ + name = number, +#include "Opcodes.tbl" +#undef OPDEF + OPCODES_TOTAL +}; + +#define OPDEF(name, number, text, length, uses, defs) \ +static const unsigned name##_LENGTH = length; +#include "Opcodes.tbl" +#undef OPDEF + +struct OpInfo +{ + const char *name; + int length; + int nuses; + int ndefs; +}; + +typedef unsigned char bytecode; + +extern OpInfo OpcodeInfo[]; + +static inline Opcode +Invert(Opcode op) +{ + switch (op) { + case OP_JEQ: + return OP_JNE; + case OP_JNE: + return OP_JEQ; + case OP_JG: + return OP_JLE; + case OP_JGE: + return OP_JL; + case OP_JL: + return OP_JGE; + default: + assert(op == OP_JLE); + return OP_JG; + } +} + +static inline int +READ_INT32(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} +static inline unsigned +READ_UINT32(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} +static inline float +READ_FLOAT(bytecode *pc) +{ + return *reinterpret_cast(pc + 1); +} + +class Code; + +void Disassemble(Code *code, FILE *fp); + +} + +#endif // _include_opcodes_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Opcodes.tbl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Opcodes.tbl Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,246 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +// Opcode macros: +// NAME NUMBER TEXT LENGTH USES DEFS +// +// Type suffixes: +// I = 32-bit integer +// F = 32-bit IEEE-754 float +// O = object reference + +// No operation. +OPDEF(OP_NOP, 0, "nop", 1, 0, 0) + +// Pushes a signed integer immediate onto the stack. +OPDEF(OP_INT, 1, "int", 5, 0, 1) + +// Returns from the current routine, propagating the value at the top of the +// stack to the caller. +OPDEF(OP_RETURN, 2, "return", 1, 1, 0) + +// Stack transitions for these comparison ops: +// rhs = pop() +// lhs = pop() +// if (lhs COMPARE rhs) +// goto target (immediate, relative to the beginning of the jump instruction) +OPDEF(OP_JL, 3, "jl", 5, 2, 0) +OPDEF(OP_JLE, 4, "jle", 5, 2, 0) +OPDEF(OP_JG, 5, "jg", 5, 2, 0) +OPDEF(OP_JGE, 6, "jge", 5, 2, 0) +OPDEF(OP_JEQ, 7, "jeq", 5, 2, 0) +OPDEF(OP_JNE, 8, "jne", 5, 2, 0) + +// value = pop() +// if (value == [true, false]) +// goto target +OPDEF(OP_JT, 9, "jt", 5, 1, 0) +OPDEF(OP_JF, 10, "jf", 5, 1, 0) + +OPDEF(OP_JL_F, 11, "jl.f", 5, 2, 0) +OPDEF(OP_JLE_F, 12, "jle.f", 5, 2, 0) +OPDEF(OP_JG_F, 13, "jg.f", 5, 2, 0) +OPDEF(OP_JGE_F, 14, "jge.f", 5, 2, 0) +OPDEF(OP_JEQ_F, 15, "jeq.f", 5, 2, 0) +OPDEF(OP_JNE_F, 16, "jne.f", 5, 2, 0) +OPDEF(OP_JT_F, 17, "jt.f", 5, 1, 0) +OPDEF(OP_JF_F, 18, "jf.f", 5, 1, 0) + +// Unconditional jump. +OPDEF(OP_JUMP, 19, "jump", 5, 0, 0) + +// Gets or sets the value of a local variable onto the stack. The local +// variable is given by an immediate index. +OPDEF(OP_GETLOCAL, 20, "getlocal", 5, 0, 1) +OPDEF(OP_SETLOCAL, 21, "setlocal", 5, 1, 1) + +// Stack transitions for these binary operators: +// rhs = pop() +// lhs = pop() +// push(lhs OP rhs) +OPDEF(OP_ADD, 22, "add", 1, 2, 1) +OPDEF(OP_SUB, 23, "sub", 1, 2, 1) +OPDEF(OP_MUL, 24, "mul", 1, 2, 1) +OPDEF(OP_DIV, 25, "div", 1, 2, 1) +OPDEF(OP_MOD, 26, "mod", 1, 2, 1) + +OPDEF(OP_LT, 27, "lt", 1, 2, 1) +OPDEF(OP_LE, 28, "le", 1, 2, 1) +OPDEF(OP_GE, 29, "ge", 1, 2, 1) +OPDEF(OP_GT, 30, "gt", 1, 2, 1) +OPDEF(OP_EQ, 31, "eq", 1, 2, 1) +OPDEF(OP_NE, 32, "ne", 1, 2, 1) + +OPDEF(OP_NOT, 33, "not", 1, 1, 1) +OPDEF(OP_NEG, 34, "neg", 1, 1, 1) + +OPDEF(OP_POP, 35, "pop", 1, 1, 0) + +// Unconditional terminator placed at the end of code. If a function returns +// void, this acts as a RETURNVOID. Otherwise, it must be unreachable. +OPDEF(OP_STOP, 36, "stop", 1, 0, 0) + +// Call a function with N arguments. The target function must be the last item +// on the stack. +OPDEF(OP_CALL, 37, "call", 1, -1, 1) + +// Similar to GET/LOCAL, except for arguments. +OPDEF(OP_GETARG, 38, "getarg", 5, 0, 1) +OPDEF(OP_SETARG, 39, "setarg", 5, 1, 1) + +// Reads or writes an integer element to an unsafe array. +// index = pop() +// ref = pop() +// ref[index] +// +OPDEF(OP_GETARRAY_I, 40, "getarray.i", 1, 2, 1) +OPDEF(OP_SETARRAY_I, 41, "setarray.i", 1, 3, 1) +OPDEF(OP_GETARRAY_F, 42, "getarray.f", 1, 2, 1) +OPDEF(OP_SETARRAY_F, 43, "setarray.f", 1, 3, 1) +OPDEF(OP_GETARRAY_C, 44, "getarray.c", 1, 2, 1) +OPDEF(OP_SETARRAY_C, 45, "setarray.c", 1, 3, 1) +OPDEF(OP_GETARRAY_O, 46, "getarray.o", 1, 2, 1) + +// Allocate an array. The HeapTenure is stored as a byte following pc. The +// ArrayMap to use is specified as an index into the script's Map table. +// +// Format: +// [u8 op] [u32 index] +OPDEF(OP_NEWARRAY, 47, "newarray", 6, 0, 1) + +// Performs a bounds check on an unsafe array. +// +// index = pop() +// array = pop() +// push(array) +// push(index) +OPDEF(OP_BOUNDS, 48, "bounds", 1, 2, 2) + +// Pushes a 32-bit IEEE-754 floating point immediate onto the stack. +OPDEF(OP_FLOAT, 49, "float", 5, 0, 1) + +// Converts an integer to a 32-bit IEEE-754 floating point immediate. +OPDEF(OP_CVT_I2F, 50, "cvt.i2f", 1, 1, 1) + +// Stack transitions for these binary operators: +// rhs = pop() +// lhs = pop() +// push(lhs OP rhs) +OPDEF(OP_ADD_F, 51, "add.f", 1, 2, 1) +OPDEF(OP_SUB_F, 52, "sub.f", 1, 2, 1) +OPDEF(OP_MUL_F, 53, "mul.f", 1, 2, 1) +OPDEF(OP_DIV_F, 54, "div.f", 1, 2, 1) +OPDEF(OP_UNUSED1, 55, "unused0", 1, 0, 0) + +OPDEF(OP_LT_F, 56, "lt.f", 1, 2, 1) +OPDEF(OP_LE_F, 57, "le.f", 1, 2, 1) +OPDEF(OP_GE_F, 58, "ge.f", 1, 2, 1) +OPDEF(OP_GT_F, 59, "gt.f", 1, 2, 1) +OPDEF(OP_EQ_F, 60, "eq.f", 1, 2, 1) +OPDEF(OP_NE_F, 61, "ne.f", 1, 2, 1) + +OPDEF(OP_NOT_F, 62, "not.f", 1, 1, 1) +OPDEF(OP_NEG_F, 63, "neg.f", 1, 1, 1) + +// Bitwise operators. +OPDEF(OP_SHL, 64, "shl", 1, 2, 1) +OPDEF(OP_SHR, 65, "shr", 1, 2, 1) +OPDEF(OP_USHR, 66, "ushr", 1, 2, 1) +OPDEF(OP_BITAND, 67, "bitand", 1, 2, 1) +OPDEF(OP_BITOR, 68, "bitor", 1, 2, 1) +OPDEF(OP_BITXOR, 69, "bitxor", 1, 2, 1) +OPDEF(OP_BITNOT, 70, "bitnot", 1, 1, 1) + +// Saves or restores the position of the LIFO heap to/from a local slot. +OPDEF(OP_SAVELIFO, 71, "savelifo", 5, 0, 0) +OPDEF(OP_UNWINDLIFO, 72, "unwindlifo", 5, 0, 0) + +// Composes a variable reference to a stack slot or argument. +OPDEF(OP_VARREF_LIFO, 73, "varref.lifo", 5, 0, 1) +OPDEF(OP_ARGREF_LIFO, 74, "argref.lifo", 5, 0, 1) +OPDEF(OP_SLOTREF_LIFO, 75, "slotref.lifo", 1, 1, 1) +OPDEF(OP_ARRAYREF_LIFO, 76, "arrayref.lifo",1, 2, 1) +OPDEF(OP_GETREF_I, 77, "getref.i", 1, 1, 1) +OPDEF(OP_SETREF_I, 78, "setref.i", 1, 2, 1) +OPDEF(OP_GETREF_F, 79, "getref.f", 1, 1, 1) +OPDEF(OP_SETREF_F, 80, "setref.f", 1, 2, 1) + +// Pushes an object reference onto the stack. +OPDEF(OP_OBJECT, 81, "object", 5, 0, 1) + +// Performs an array-to-array assignment. +// Left = pop() +// Right = pop() +// copy(Left, Right) +// push(Left) +OPDEF(OP_ARRAYCOPY, 82, "arraycopy", 1, 2, 1) + +// Duplicates the top item on the stack. +OPDEF(OP_DUP, 83, "dup", 1, 1, 2) + +// Duplicates the top two items on the stack. +OPDEF(OP_DUP2, 84, "dup2", 1, 2, 4) + +// Swaps the two items at the top of the stack. +OPDEF(OP_SWAP, 85, "swap", 1, 2, 2) + +// Roll operation is the same as Forth. It steals a value on the stack, +// shifting all stack values down, and then places the stolen value back at +// the top of the stack. +// +// p = sp[-3] +// sp[-3] = sp[-2] +// sp[-2] = sp[-1] +// sp[-1] = p +OPDEF(OP_ROLL3, 86, "roll3", 1, 3, 3) + +// Pick operation is the same as Forth. It pushes a copy of a slot at a +// specific depth. +// +// push(sp[-n]) +OPDEF(OP_PICK2, 87, "pick2", 1, 2, 3) +OPDEF(OP_PICK3, 88, "pick3", 1, 3, 4) + +// Calls a native function. During the course of calling a native, arguments +// on the stack are re-pushed to match the cell-based native calling +// convention expected by ISourcePawnEngine. +// +// Unlike the normal CALL instruction, natives take truly variadic arguments +// and so we must specify the number of arguments. The format is: +// +// [u8 Opcode] [u32 Index] [u32 ArgCount] +// +OPDEF(OP_CALLNATIVE, 89, "callnative", 9, -1, 1) + +// Returns from a function that does return a value. +OPDEF(OP_RETURNVOID, 90, "returnvoid", 1, 0, 0) + +// Loads the module of the running function. +OPDEF(OP_MODULE, 91, "module", 1, 0, 1) + +// Loads a function from a module object. The operand is the slot number +// in the module. +OPDEF(OP_GETMODULE_FN, 92, "getmodule.fn", 5, 1, 1) + +// Opcodes for accessing an environment slot in the global scope. +OPDEF(OP_SETGLOBAL_I, 93, "setglobal.i", 5, 1, 1) +OPDEF(OP_SETGLOBAL_F, 94, "setglobal.f", 5, 1, 1) +OPDEF(OP_SETGLOBAL_O, 95, "setglobal.o", 5, 1, 1) +OPDEF(OP_GETGLOBAL_I, 96, "getglobal.i", 5, 0, 1) +OPDEF(OP_GETGLOBAL_F, 97, "getglobal.f", 5, 0, 1) +OPDEF(OP_GETGLOBAL_O, 98, "getglobal.o", 5, 0, 1) \ No newline at end of file diff -r 06a0015b6a5c -r 32ad0523b309 src/Parser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Parser.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,1314 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Zone.h" +#include "Parser.h" +#include "FixedArray.h" +#include "Heap.h" +#include "Strings.h" +#include "Heap-inl.h" + +using namespace ke; + +Parser::Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length) + : zone_(zone), + cc_(cc), + pool_(zone_->pool()), + scanner_(cc, stream, length), + scope_(NULL), + unbound_(NULL) +{ + unbound_ = new (pool_) PoolList(); +} + +bool +Parser::match(TokenKind kind) +{ + if (scanner_.peek() != kind) + return false; + scanner_.scan(); + return true; +} + +bool +Parser::matchSemicolon() +{ + return expect(TOK_SEMICOLON); +} + +bool +Parser::expect(TokenKind kind) +{ + TokenKind got = scanner_.scan(); + if (got == kind) + return true; + cc_.reportError(scanner_.begin(), Message_WrongToken, TokenNames[kind], TokenNames[got]); + return false; +} + +Scope * +Parser::enterScope(Scope::Kind kind) +{ + scope_ = new (pool_) Scope(pool_, kind, scope_); + if (!scope_->initialize()) + return NULL; + return scope_; +} + +NameProxy * +Parser::bind(Handle name) +{ + BoundName *bound = scope_->lookup(name); + NameProxy *proxy = new (pool_) NameProxy(scanner_.begin(), name, bound); + if (!bound && !unbound_->append(proxy)) + return NULL; + return proxy; +} + +bool +Parser::matchName(String **name, TokenKind kind) +{ + assert(kind == TOK_NAME || kind == TOK_LABEL); + *name = NULL; + if (!match(kind)) + return false; + *name = zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); + return true; +} + +String * +Parser::expectName() +{ + if (!expect(TOK_NAME)) + return NULL; + return zone_->makeSymbol(scanner_.literal(), scanner_.literal_length()); +} + +void +Parser::redeclarationError(BoundName *other) +{ + cc_.reportError(scanner_.begin(), Message_RedeclaredName, other->name()->chars(), + other->pos().line, other->pos().col); +} + +Expression * +Parser::primitive() +{ + TokenKind kind = scanner_.peek(); + switch (kind) { + case TOK_NAME: + { + Local name(zone_, expectName()); + if (!name) + return NULL; + return bind(name); + } + + case TOK_FLOAT_LITERAL: + { + scanner_.scan(); + double value = Scanner::ParseDouble(scanner_.literal()); + return new (pool_) FloatLiteral(scanner_.begin(), value); + } + + case TOK_INTEGER_LITERAL: + { + scanner_.scan(); + int value = atoi(scanner_.literal()); + return new (pool_) IntegerLiteral(scanner_.begin(), value); + } + + case TOK_STRING_LITERAL: + { + scanner_.scan(); + Local array(zone_, ByteArray::New(zone_, scanner_.literal_length() + 1)); + if (!array) + return NULL; + memcpy(array->raw(), scanner_.literal(), scanner_.literal_length() + 1); + return new (pool_) StringLiteral(scanner_.begin(), array); + } + + default: + { + if (kind != TOK_ERROR) + cc_.reportError(scanner_.begin(), Message_ExpectedExpression, TokenNames[kind]); + return NULL; + } + } +} + +Expression * +Parser::prefix() +{ + switch (scanner_.peek()) { + case TOK_LPAREN: + { + scanner_.scan(); + Expression *expr = expression(); + if (!expr) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + return expr; + } + + default: + return primitive(); + } +} + +Expression * +Parser::call(Expression *callee) +{ + ExpressionList *arguments = new (pool_) ExpressionList(); + + SourcePosition pos = scanner_.begin(); + expect(TOK_LPAREN); + + if (!match(TOK_RPAREN)) { + while (true) { + Expression *expr = expression(); + if (!expr) + return NULL; + + if (!arguments->append(expr)) + return NULL; + + if (!match(TOK_COMMA)) + break; + } + + if (!expect(TOK_RPAREN)) + return NULL; + } + + return new (pool_) CallExpression(pos, callee, arguments); +} + +Expression * +Parser::index(Expression *left) +{ + expect(TOK_LBRACKET); + + SourcePosition pos = scanner_.begin(); + Expression *expr = expression(); + if (!expr) + return NULL; + + if (!expect(TOK_RBRACKET)) + return NULL; + + return new (pool_) IndexExpression(pos, left, expr); +} + +Expression * +Parser::primary() +{ + Expression *expr = prefix(); + if (!expr) + return NULL; + + for (;;) { + switch (scanner_.peek()) { + case TOK_LPAREN: + expr = call(expr); + if (!expr) + return NULL; + break; + + case TOK_LBRACKET: + expr = index(expr); + if (!expr) + return NULL; + break; + + default: + return expr; + } + } +} + +Expression * +Parser::unary() +{ + TokenKind token = scanner_.peek(); + + SourcePosition pos = scanner_.begin(); + switch (token) { + case TOK_INCREMENT: + case TOK_DECREMENT: + { + scanner_.scan(); + Expression *expr = expression(); + if (!expr) + return NULL; + return new (pool_) IncDecExpression(pos, token, expr, false); + } + + case TOK_MINUS: + case TOK_NOT: + case TOK_TILDE: + { + scanner_.scan(); + Expression *expr = expression(); + if (!expr) + return NULL; + return new (pool_) UnaryExpression(pos, token, expr); + } + + default: + break; + } + + Expression *expr = primary(); + + token = scanner_.peek(); + if (token == TOK_INCREMENT || token == TOK_DECREMENT) { + scanner_.scan(); + return new (pool_) IncDecExpression(pos, token, expr, true); + } + + return expr; +} + +Expression * +Parser::multiplication() +{ + Expression *left = unary(); + do { + if (!match(TOK_SLASH) && !match(TOK_STAR) && !match(TOK_PERCENT)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = unary(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } while (left); + return left; +} + +Expression * +Parser::addition() +{ + Expression *left = multiplication(); + while (left) { + if (!match(TOK_PLUS) && !match(TOK_MINUS)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = multiplication(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::shift() +{ + Expression *left = addition(); + while (left) { + if (!match(TOK_SHL) && !match(TOK_SHR) && !match(TOK_USHR)) + break; + SourcePosition pos = scanner_.begin(); + TokenKind kind = scanner_.last(); + Expression *right = addition(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::bitand_() +{ + Expression *left = shift(); + while (left) { + if (!match(TOK_BITAND)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITAND, left, right); + } + return left; +} + +Expression * +Parser::bitxor() +{ + Expression *left = bitand_(); + while (left) { + if (!match(TOK_BITXOR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITXOR, left, right); + } + return left; +} + +Expression * +Parser::bitor_() +{ + Expression *left = bitxor(); + while (left) { + if (!match(TOK_BITOR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = bitxor(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_BITOR, left, right); + } + return left; +} + +Expression * +Parser::relational() +{ + Expression *left = bitor_(); + while (left) { + TokenKind kind = scanner_.peek(); + if (kind < TOK_LT || kind > TOK_GE) + break; + scanner_.scan(); + SourcePosition pos = scanner_.begin(); + Expression *right = shift(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::equals() +{ + Expression *left = relational(); + while (left) { + if (!match(TOK_EQUALS) && !match(TOK_NOTEQUALS)) + break; + TokenKind kind = scanner_.last(); + SourcePosition pos = scanner_.begin(); + Expression *right = relational(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, kind, left, right); + } + return left; +} + +Expression * +Parser::and_() +{ + Expression *left = equals(); + while (left) { + if (!match(TOK_AND)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = equals(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_AND, left, right); + } + return left; +} + +Expression * +Parser::or_() +{ + Expression *left = and_(); + while (left) { + if (!match(TOK_OR)) + break; + SourcePosition pos = scanner_.begin(); + Expression *right = and_(); + if (!right) + return NULL; + left = new (pool_) BinaryExpression(pos, TOK_OR, left, right); + } + return left; +} + +Expression * +Parser::ternary() +{ + Expression *cond = or_(); + if (!cond) + return NULL; + + if (!match(TOK_QMARK)) + return cond; + + SourcePosition pos = scanner_.begin(); + Expression *left = expression(); + if (!left) + return NULL; + + if (!expect(TOK_COLON)) + return NULL; + + Expression *right = expression(); + if (!right) + return NULL; + + return new (pool_) TernaryExpression(pos, cond, left, right); +} + +Expression * +Parser::assignment() +{ + Expression *left = ternary(); + while (left) { + TokenKind token = scanner_.peek(); + if (token != TOK_ASSIGN && (token < TOK_ASSIGN_ADD || token > TOK_ASSIGN_SHL)) + break; + scanner_.scan(); + SourcePosition pos = scanner_.begin(); + Expression *expr = ternary(); + if (!expr) + return NULL; + + left = new (pool_) Assignment(pos, token, left, expr); + } + return left; +} + +Expression * +Parser::expression() +{ + return assignment(); +} + +Statement * +Parser::while_() +{ + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *condition = expression(); + if (!condition) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + // Inside a loop, we force the creation of a scope object. Normally, we + // will cull scopes if they have no variables. However inside a loop, + // function calls that take references may compute a LIFO object, and to + // avoid temporarily leaking many such objects, we ensure that a scope + // is present so a LIFO slot can be allocated. + if (!enterScope(Scope::BLOCK)) + return NULL; + + Statement *body; + if (scanner_.peek() == TOK_LBRACE) + body = block(scope_); + else + body = localStatement(); + if (!body) + return NULL; + + scope_ = scope_->enclosing(); + return new (pool_) WhileStatement(pos, TOK_WHILE, condition, body); +} + +Statement * +Parser::do_() +{ + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + + // See the comment in ::while_() as to why we force a scope. + if (!enterScope(Scope::BLOCK)) + return NULL; + + Statement *body; + if (scanner_.peek() == TOK_LBRACE) + body = block(scope_); + else + body = localStatement(); + if (!body) + return NULL; + + scope_ = scope_->enclosing(); + + if (!expect(TOK_WHILE)) + return NULL; + + if (!expect(TOK_LPAREN)) + return NULL; + Expression *condition = expression(); + if (!condition) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + + return new (pool_) WhileStatement(pos, TOK_DO, condition, body); +} + +Statement * +Parser::forStatement() +{ + SourcePosition pos = scanner_.begin(); + scanner_.scan(); + if (!expect(TOK_LPAREN)) + return NULL; + + Statement *decl = NULL; + if (!match(TOK_SEMICOLON)) { + 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; + } + + Expression *condition = NULL; + if (!match(TOK_SEMICOLON)) { + if ((condition = expression()) == NULL) + return NULL; + if (!expect(TOK_SEMICOLON)) + return NULL; + } + + Statement *update = NULL; + if (!match(TOK_RPAREN)) { + if ((update = expressionStatement()) == NULL) + return NULL; + if (!expect(TOK_RPAREN)) + return NULL; + } + + // See the comment in ::while_() as to why we force a scope. + if (!enterScope(Scope::BLOCK)) + return NULL; + + Statement *body; + if (scanner_.peek() == TOK_LBRACE) + body = block(scope_); + else + body = localStatement(); + if (!body) + return NULL; + + scope_ = scope_->enclosing(); + + Scope *declScope = NULL; + if (decl && decl->isVariableDeclaration()) { + declScope = scope_->unlinkIfEmpty(); + scope_ = scope_->enclosing(); + } + return new (pool_) ForStatement(pos, declScope, decl, condition, update, body); +} + +VariableDeclaration * +Parser::variableDeclaration() +{ + if (!expect(TOK_NEW)) + return NULL; + + Local tag(zone_); + if (matchName(tag.ref(), TOK_LABEL) && !tag) + return NULL; + + Local name(zone_, expectName()); + if (!name) + return NULL; + + if (BoundName *other = scope_->localLookup(name)) { + redeclarationError(other); + return NULL; + } + + Variable *var = scope_->declareVariable(name, scanner_.begin()); + if (!var) + return NULL; + + ExpressionList *postDimensions = NULL; + if (scanner_.peek() == TOK_LBRACKET) { + postDimensions = new (pool_) ExpressionList(); + while (match(TOK_LBRACKET)) { + Expression *dim = expression(); + if (!dim) + return NULL; + + if (!expect(TOK_RBRACKET)) + return NULL; + + if (!postDimensions->append(dim)) + return NULL; + } + } + + Expression *expr = NULL; + if (match(TOK_ASSIGN)) { + if ((expr = expression()) == NULL) + return NULL; + } + + return new (pool_) VariableDeclaration(var->pos(), var, tag, postDimensions, expr); +} + +Statement * +Parser::returnStatement() +{ + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_RETURN)) + return NULL; + + Expression *expr = NULL; + if (scanner_.peek() != TOK_SEMICOLON) { + if ((expr = expression()) == NULL) + return NULL; + + // We only care about non-void returns when determining whether a + // tagless function is non-void. + encounteredReturn_ = true; + } + + return new (pool_) ReturnStatement(pos, expr); +} + +Statement * +Parser::expressionStatement() +{ + Expression *left = assignment(); + if (!left) + return NULL; + + return new (pool_) ExpressionStatement(left); +} + +Statement * +Parser::block(Scope *scope) +{ + if (!expect(TOK_LBRACE)) + return NULL; + + SourcePosition pos = scanner_.begin(); + if (!scope && !enterScope(Scope::BLOCK)) + return NULL; + + StatementList *statements = new (pool_) StatementList(); + while (!match(TOK_RBRACE)) { + Statement *statement = localStatement(); + if (statement == NULL) + return NULL; + if (!statements->append(statement)) + return NULL; + } + + if (!scope) { + scope = scope_->unlinkIfEmpty(); + scope_ = scope_->enclosing(); + } + BlockStatement *block = new (pool_) BlockStatement(pos, scope, statements); + return block; +} + +Statement * +Parser::if_() +{ + expect(TOK_IF); + + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *cond = expression(); + if (!cond) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + Statement *ifTrue = localStatement(); + if (!ifTrue) + return NULL; + + IfStatement *outer = new (pool_) IfStatement(pos, cond, ifTrue); + + IfStatement *last = outer; + while (match(TOK_ELSE)) { + if (!match(TOK_IF)) { + Statement *ifFalse = localStatement(); + if (!ifFalse) + return NULL; + + last->setIfFalse(ifFalse); + break; + } + + SourcePosition pos = scanner_.begin(); + if (!expect(TOK_LPAREN)) + return NULL; + + Expression *otherCond = expression(); + if (!otherCond) + return NULL; + + if (!expect(TOK_RPAREN)) + return NULL; + + Statement *otherIfTrue = localStatement(); + if (!otherIfTrue) + return NULL; + + IfStatement *inner = new (pool_) IfStatement(pos, otherCond, otherIfTrue); + last->setIfFalse(inner); + last = inner; + } + + return outer; +} + +Statement * +Parser::localStatement() +{ + Statement *statement = NULL; + + // Statements which must be followed by a semicolon will break out of the + // switch. If they may end in BlockStatements, they'll immediately return. + + switch (scanner_.peek()) { + case TOK_NEW: + if ((statement = variableDeclaration()) == NULL) + return NULL; + break; + + case TOK_FOR: + return forStatement(); + + case TOK_WHILE: + return while_(); + + case TOK_BREAK: + scanner_.scan(); + statement = new (pool_) BreakStatement(scanner_.begin()); + break; + + case TOK_CONTINUE: + scanner_.scan(); + statement = new (pool_) ContinueStatement(scanner_.begin()); + break; + + case TOK_DO: + if ((statement = do_()) == NULL) + return NULL; + break; + + case TOK_RETURN: + if ((statement = returnStatement()) == NULL) + return NULL; + break; + + case TOK_ENUM: + if ((statement = enum_()) == NULL) + return NULL; + break; + + case TOK_IF: + return if_(); + + case TOK_LBRACE: + return block(); + + default: + break; + } + + if (!statement) { + if ((statement = expressionStatement()) == NULL) + return NULL; + } + + if (!expect(TOK_SEMICOLON)) + return NULL; + + return statement; +} + +Statement * +Parser::enum_() +{ + expect(TOK_ENUM); + + SourcePosition pos = scanner_.begin(); + + Local name(zone_); + if (matchName(name.ref())) { + if (!name) + return NULL; + + if (BoundName *other = scope_->localLookup(name)) { + redeclarationError(other); + return NULL; + } + + if (!scope_->declareEnum(name, scanner_.begin())) + return NULL; + } + + EnumStatement::EntryList *entries = new (pool_) EnumStatement::EntryList(); + + if (!expect(TOK_LBRACE)) + return NULL; + + do { + if (scanner_.peek() == TOK_RBRACE) + break; + + Local name(zone_, expectName()); + if (!name) + return NULL; + + if (BoundName *other = scope_->localLookup(name)) { + redeclarationError(other); + return NULL; + } + + NamedConstant *constant = scope_->declareConstant(name, scanner_.begin()); + if (!constant) + return NULL; + + Expression *expr = NULL; + if (match(TOK_ASSIGN)) { + if ((expr = expression()) == NULL) + return NULL; + } + + if (!entries->append(EnumStatement::Entry(constant, expr))) + return NULL; + } while (match(TOK_COMMA)); + if (!expect(TOK_RBRACE)) + return NULL; + + return new (pool_) EnumStatement(pos, name, entries); +} + +Statement * +Parser::function(TokenKind token) +{ + Local name(zone_, expectName()); + if (!name) + return NULL; + + SourcePosition pos = scanner_.begin(); + + // The function name goes into the outer (global) scope. + assert(!scope_->enclosing()); + + NamedFunction *fun; + if (BoundName *other = scope_->localLookup(name)) { + // We allow a redeclaration if the new type is a public, and the old + // type was a forward. However, the forward must come first. + if (other->kind() != BoundName::FUNCTION) { + redeclarationError(other); + return NULL; + } + + fun = other->toNamedFunction(); + if (!fun->isForward() || token != TOK_PUBLIC) { + redeclarationError(other); + return NULL; + } + + // We've got the same name, but the original is a forward, so let it + // slide and re-use the original declaration. + fun->setImplemented(scanner_.begin()); + } else { + fun = scope_->declareFunction(name, scanner_.begin(), token); + if (!fun) + return NULL; + } + + Scope *scope = enterScope(Scope::FUNCTION); + if (!scope) + return NULL; + + ParameterList *parameters = new (pool_) ParameterList(); + + if (!expect(TOK_LPAREN)) + return NULL; + if (!match(TOK_RPAREN)) { + while (true) { + bool isConst = match(TOK_CONST); + bool ref = match(TOK_AMPERSAND); + + SourcePosition begin = scanner_.begin(); + Local tag(zone_); + if (matchName(tag.ref(), TOK_LABEL) && !tag) + return NULL; + + if (match(TOK_ELLIPSES)) { + if (isConst || ref) { + cc_.reportError(begin, Message_InvalidVarArgs); + return NULL; + } + + if (token != TOK_NATIVE) { + cc_.reportError(begin, Message_VarArgsOutsideNative); + return NULL; + } + + fun->markNativeVariadic(); + break; + } + + Local name(zone_, expectName()); + if (!name) + return NULL; + + if (BoundName *other = scope_->localLookup(name)) { + redeclarationError(other); + return NULL; + } + Variable *arg = scope_->declareArgument(name, scanner_.begin()); + if (!arg) + return NULL; + + // Look for dimensions. + ExpressionList *postDimensions = NULL; + while (match(TOK_LBRACKET)) { + if (!postDimensions) + postDimensions = new (pool_) ExpressionList(); + + Expression *dim = NULL; + if (!match(TOK_RBRACKET)) { + if ((dim = expression()) == NULL) + return NULL; + if (!expect(TOK_RBRACKET)) + return NULL; + } + + if (!postDimensions->append(dim)) + return NULL; + } + + Parameter *parameter = new (pool_) Parameter(arg, tag, ref, isConst, postDimensions); + if (!parameters->append(parameter)) + return NULL; + + if (!match(TOK_COMMA)) + break; + } + if (!expect(TOK_RPAREN)) + return NULL; + } + + encounteredReturn_ = false; + + Statement *body = NULL; + if (token != TOK_FORWARD && token != TOK_NATIVE) { + body = block(); + if (!body) + return NULL; + } + + scope_ = scope_->enclosing(); + return new (pool_) FunctionStatement(pos, scope, fun, body, parameters, encounteredReturn_); +} + +ParseTree * +Parser::parse() +{ + StatementList *statements = new (pool_) StatementList(); + + if (!enterScope(Scope::GLOBAL)) + return NULL; + + for (;;) { + Statement *statement = NULL; + + switch (scanner_.peek()) { + case TOK_ERROR: + return NULL; + + case TOK_EOF: + break; + + case TOK_NAME: + if ((statement = function(TOK_NAME)) == NULL) + return NULL; + break; + + case TOK_FORWARD: + case TOK_PUBLIC: + case TOK_NATIVE: + { + TokenKind kind = scanner_.scan(); + if ((statement = function(kind)) == NULL) + return NULL; + if ((kind == TOK_FORWARD || kind == TOK_NATIVE) && !matchSemicolon()) + return NULL; + break; + } + + case TOK_NEW: + { + if ((statement = variableDeclaration()) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + break; + } + + case TOK_ENUM: + if ((statement = enum_()) == NULL) + return NULL; + if (!matchSemicolon()) + return NULL; + break; + + default: + cc_.reportError(scanner_.begin(), Message_ExpectedGlobal); + return NULL; + } + + if (!statement) + break; + + if (!statements->append(statement)) + return NULL; + } + + return new (pool_) ParseTree(scope_, statements, unbound_); +} + +class AstPrinter : public AstVisitor +{ + FILE *fp_; + size_t level_; + + private: + void prefix() { + for (size_t i = 0; i < level_; i++) + fprintf(fp_, " "); + } + void indent() { + level_++; + } + void unindent() { + level_--; + } + + public: + AstPrinter(FILE *fp) + : fp_(fp), + level_(0) + { + } + + void visit(NameProxy *name) { + prefix(); + fprintf(fp_, "[ NameProxy (%s)%s\n", name->name()->chars(), + (name->binding() ? "" : " unbound")); + } + void visit(CallExpression *node) { + prefix(); + fprintf(fp_, "[ CallExpression\n"); + indent(); + node->callee()->accept(this); + for (size_t i = 0; i < node->arguments()->length(); i++) + node->arguments()->at(i)->accept(this); + unindent(); + } + void visit(FunctionStatement *node) { + prefix(); + fprintf(fp_, "[ FunctionStatement (%s)\n", node->fun()->name()->chars()); + indent(); + if (node->body()) + node->body()->accept(this); + unindent(); + } + void visit(ExpressionStatement *node) { + prefix(); + fprintf(fp_, "[ ExpressionStatement\n"); + indent(); + node->expression()->accept(this); + unindent(); + } + void visit(Assignment *node) { + prefix(); + fprintf(fp_, "[ Assignment\n"); + indent(); + node->lvalue()->accept(this); + node->expression()->accept(this); + unindent(); + } + void visit(TernaryExpression *node) { + prefix(); + fprintf(fp_, "[ TernaryExpression\n"); + indent(); + node->condition()->accept(this); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visit(BinaryExpression *node) { + prefix(); + fprintf(fp_, "[ BinaryExpression (%s)\n", TokenNames[node->token()]); + indent(); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visit(UnaryExpression *node) { + prefix(); + fprintf(fp_, "[ UnaryExpression (%s)\n", TokenNames[node->token()]); + indent(); + node->expression()->accept(this); + unindent(); + } + void visit(ReturnStatement *node) { + prefix(); + fprintf(fp_, "[ ReturnStatement\n"); + indent(); + if (node->expression()) + node->expression()->accept(this); + unindent(); + } + void visit(ForStatement *node) { + prefix(); + fprintf(fp_, "[ ForStatement\n"); + indent(); + if (node->initialization()) + node->initialization()->accept(this); + if (node->condition()) + node->condition()->accept(this); + if (node->update()) + node->update()->accept(this); + node->body()->accept(this); + unindent(); + } + void visit(BlockStatement *node) { + prefix(); + fprintf(fp_, "[ BlockStatement\n"); + indent(); + for (size_t i = 0; i < node->statements()->length(); i++) + node->statements()->at(i)->accept(this); + unindent(); + } + void visit(VariableDeclaration *node) { + prefix(); + fprintf(fp_, "[ VariableDeclaration (%s)\n", node->variable()->name()->chars()); + indent(); + if (node->initialization()) + node->initialization()->accept(this); + unindent(); + } + void visit(IntegerLiteral *node) { + prefix(); + fprintf(fp_, "[ IntegerLiteral (%d)\n", node->value()); + } + void visit(FloatLiteral *node) { + prefix(); + fprintf(fp_, "[ FloatLiteral (%f)\n", node->value()); + } + void visit(IfStatement *node) { + prefix(); + fprintf(fp_, "[ IfStatement\n"); + indent(); + node->ifTrue()->accept(this); + if (node->ifFalse()) + node->ifFalse()->accept(this); + unindent(); + } + void visit(IndexExpression *node) { + prefix(); + fprintf(fp_, "[ IndexExpression\n"); + indent(); + node->left()->accept(this); + node->right()->accept(this); + unindent(); + } + void visit(EnumStatement *node) { + prefix(); + fprintf(fp_, "[ EnumStatement (%s)\n", node->name() ? node->name()->chars() : ""); + indent(); + for (size_t i = 0; i < node->entries()->length(); i++) { + prefix(); + fprintf(fp_, "%s =\n", node->entries()->at(i).name->name()->chars()); + if (node->entries()->at(i).expr) { + indent(); + node->entries()->at(i).expr->accept(this); + unindent(); + } + } + unindent(); + } + void visit(WhileStatement *node) { + prefix(); + fprintf(fp_, "[ WhileStatement (%s)\n", + (node->token() == TOK_DO) ? "do" : "while"); + indent(); + if (node->token() == TOK_DO) { + node->condition()->accept(this); + node->body()->accept(this); + } else { + node->condition()->accept(this); + node->body()->accept(this); + } + unindent(); + } + void visit(BreakStatement *node) { + prefix(); + fprintf(fp_, "[ BreakStatement\n"); + } + void visit(ContinueStatement *node) { + prefix(); + fprintf(fp_, "[ ContinueStatement\n"); + } + void visit(StringLiteral *node) { + prefix(); + fprintf(fp_, "[ StringLiteral\n"); + } + void visit(IncDecExpression *node) { + prefix(); + fprintf(fp_, "[ IncDecExpression (postfix=%d)\n", node->postfix()); + indent(); + node->expression()->accept(this); + unindent(); + } +}; + +void +ParseTree::dump(FILE *fp) +{ + AstPrinter printer(fp); + + for (size_t i = 0; i < statements_->length(); i++) + statements_->at(i)->accept(&printer); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Parser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Parser.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_parser_h_ +#define _include_jitcraft_parser_h_ + +#include +#include "AST.h" +#include "Scanner.h" +#include "Scopes.h" +#include "Strings.h" + +namespace ke { + +class Parser +{ + Zone *zone_; + CompileContext &cc_; + PoolAllocator &pool_; + Scanner scanner_; + Scope *scope_; + PoolList *unbound_; + bool encounteredReturn_; + + private: + void redeclarationError(BoundName *name); + bool match(TokenKind kind); + bool expect(TokenKind kind); + bool matchSemicolon(); + + Scope *enterScope(Scope::Kind kind); + + VariableDeclaration *variableDeclaration(); + bool matchName(String **name, TokenKind kind = TOK_NAME); + String *expectName(); + NameProxy *bind(Handle name); + Expression *primitive(); + Expression *call(Expression *callee); + Expression *index(Expression *left); + Expression *prefix(); + Expression *primary(); + Expression *unary(); + Expression *multiplication(); + Expression *addition(); + Expression *shift(); + Expression *bitand_(); + Expression *bitxor(); + Expression *bitor_(); + Expression *relational(); + Expression *equals(); + Expression *and_(); + Expression *or_(); + Expression *ternary(); + Expression *assignment(); + Expression *expression(); + + Statement *enum_(); + Statement *if_(); + Statement *block(Scope *scope = NULL); + Statement *function(TokenKind tok); + Statement *expressionStatement(); + Statement *while_(); + Statement *do_(); + Statement *forStatement(); + Statement *localStatement(); + Statement *globalStatement(); + Statement *returnStatement(); + + public: + Parser(Zone *zone, CompileContext &cc, const char *stream, size_t length); + + ParseTree *parse(); +}; + +} + +#endif // _include_jitcraft_parser_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Platform-Posix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Platform-Posix.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,101 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include "Platform.h" + +using namespace jc; + +static inline int +ToProtBits(OS::Protection prot) +{ + switch (prot) { + case OS::Prot_ReadWrite: + return PROT_READ | PROT_WRITE; + case OS::Prot_ReadWriteExecute: + return PROT_READ | PROT_WRITE | PROT_EXEC; + default: + assert(prot == OS::Prot_ReadExecute); + return PROT_READ | PROT_EXEC; + } +} + +Address +OS::MapMemory(size_t amount, MapMode mode, Protection prot_) +{ + int prot = ToProtBits(prot_); + void *addr = mmap(NULL, amount, prot, MAP_ANON | MAP_PRIVATE, -1, 0); + if (addr == MAP_FAILED) + return NULL; + + return Address(addr); +} + +bool +OS::CommitMemory(Address address, size_t amount, Protection prot_) +{ + return true; +} + +void +OS::UnmapMemory(Address address, size_t size) +{ + munmap(address, size); +} + +OS::AlignableMapping +OS::MapAlignedMemory(size_t amount, size_t alignment, MapMode mode) +{ + assert(amount >= alignment); + assert(IsPowerOfTwo(alignment)); + + if (alignment == kPageAlignment) { + Address address = MapMemory(amount, mode, Prot_ReadWrite); + if (!address) + return AlignableMapping(); + return AlignableMapping(address, alignment, amount); + } + + size_t requested = Min(amount * 2, amount + 2 * alignment); + Address address = MapMemory(amount, mode, Prot_ReadWrite); + if (!address) + return AlignableMapping(); + + size_t begin = 0; + Address aligned = AlignedBase(address, alignment); + if (!IsAligned(address, alignment)) { + // Shave off the first part of the unaligned region. + aligned += alignment; + begin = size_t(aligned - address); + UnmapAlignedMemory(address, begin); + } + + if (requested - begin > amount) { + // Shave off the last part of the unaligned region. + UnmapAlignedMemory(address + amount, requested - amount - begin); + } + + return AlignableMapping(aligned, alignment, amount); +} + +void +OS::UnmapAlignedMemory(Address address, size_t size) +{ + OS::UnmapMemory(address, size); +} + diff -r 06a0015b6a5c -r 32ad0523b309 src/Platform-Windows.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Platform-Windows.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,111 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ + +// :TODO: move into utility-style include +#define WIN32_LEAN_AND_MEAN +#include +#include "Platform.h" + +using namespace ke; + +static inline DWORD +ToProtBits(OS::Protection prot) +{ + switch (prot) { + case OS::Prot_ReadWrite: + return PAGE_READWRITE; + case OS::Prot_ReadWriteExecute: + return PAGE_EXECUTE_READWRITE; + default: + assert(prot == OS::Prot_ReadExecute); + return PAGE_EXECUTE_READ; + } +} + +Address +OS::MapMemory(size_t amount, MapMode mode, Protection prot_) +{ + DWORD prot = ToProtBits(prot_); + DWORD type = MEM_RESERVE; + if (mode == Map_Commit) + type |= MEM_COMMIT; + + return (Address)VirtualAlloc(NULL, amount, type, prot); +} + +bool +OS::CommitMemory(Address address, size_t amount, Protection prot_) +{ + DWORD prot = ToProtBits(prot_); + return !!VirtualAlloc(address, amount, MEM_COMMIT, prot); +} + +void +OS::UnmapMemory(Address address, size_t size) +{ + VirtualFree(address, 0, MEM_RELEASE); +} + +static Address +MapMemoryAt(Address address, size_t amount, OS::MapMode mode, OS::Protection prot_) +{ + DWORD bits = ToProtBits(prot_); + DWORD flags = MEM_RESERVE; + if (mode == OS::Map_Commit) + flags |= MEM_COMMIT; + + return (Address)VirtualAlloc((LPVOID)address, amount, flags, bits); +} + +OS::AlignableMapping +OS::MapAlignedMemory(size_t amount, size_t alignment, MapMode mode) +{ + Address addr = MapMemory(amount, mode, OS::Prot_ReadWrite); + if (!addr) + return AlignableMapping(NULL, alignment, 0); + + if (!IsAligned(addr, alignment)) { + UnmapMemory(addr, amount); + + // Try to grab the next available aligned address. + addr = AlignedBase(addr, alignment); + addr = MapMemoryAt(addr + alignment, amount, mode, OS::Prot_ReadWrite); + while (!addr) { + // Try to over-allocate to get the right region. This can race + // with other threads; we expect this to be rare. If this turns + // out to be a problem, the API is set up such that we can just + // return two pointers: the base, and the aligned address into + // the over-allocated region. + addr = MapMemory(amount + alignment, mode, OS::Prot_ReadWrite); + if (!addr) + return AlignableMapping(NULL, alignment, 0); + UnmapMemory(addr, amount + alignment); + addr = AlignedBase(addr, alignment); + addr = MapMemoryAt(addr + alignment, amount, mode, OS::Prot_ReadWrite); + } + } + + return AlignableMapping(addr, alignment, amount); +} + +void +OS::UnmapAlignedMemory(Address address, size_t size) +{ + OS::UnmapMemory(address, size); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Platform.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Platform.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,91 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_platform_h_ +#define _include_jitcraft_platform_h_ + +#include "Utility.h" + +namespace ke { + +namespace OS { + enum Protection { + Prot_ReadWrite, // rw-, for the heap + Prot_ReadExecute, // r-x, for code + Prot_ReadWriteExecute // rwx, if disabling r-x for code. + }; + + enum MapMode { + Map_Reserve, // don't ask for actual pages yet + Map_Commit // ask for pages right away + }; + + Address MapMemory(size_t amount, MapMode mode, Protection prot); + bool CommitMemory(Address address, size_t amount, Protection prot); + void UnmapMemory(Address address, size_t size); + + class AlignableMapping { + Address base_; + Address aligned_; + size_t size_; + + public: + AlignableMapping(Address ptr, size_t alignment, size_t size) + : base_(ptr), + size_(size) + { + aligned_ = base_; + if (!IsAligned(aligned_, alignment)) + aligned_ = AlignedBase(aligned_, alignment) + alignment; + } + + AlignableMapping() : base_(NULL), aligned_(NULL), size_(0) + { + } + + Address base() const { + return base_; + } + bool isValid() const { + return !!base_; + } + size_t size() const { + return size_; + } + Address aligned() const { + return aligned_; + } + }; + + // MapAlignedMemory is guaranteed to return zeroed memory. + AlignableMapping MapAlignedMemory(size_t amount, size_t alignment, MapMode mode); + void UnmapAlignedMemory(Address address, size_t size); + + // The default alignment of memory returned via MapMemory(). + // :TODO: better ifdefs +#ifdef _MSC_VER + static const size_t kPageAlignment = 64 * kKB; +#else + static const size_t kPageAlignment = 4 * kKB; +#endif + +} + +} + +#endif // _include_jitcraft_platform_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/PoolAllocator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/PoolAllocator.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include +#include "PoolAllocator.h" +#include "Zone.h" + +using namespace ke; + +PoolAllocator::PoolAllocator() + : reserved_(NULL), + last_(NULL) +{ +} + +PoolAllocator::~PoolAllocator() +{ + unwind(NULL); + if (reserved_) + free(reserved_); +} + +char * +PoolAllocator::enter() +{ + if (!last_) + return NULL; + return last_->ptr; +} + +void +PoolAllocator::leave(char *pos) +{ + unwind(pos); +} + +void +PoolAllocator::unwind(char *pos) +{ + while (last_) { + if (pos && pos >= last_->base && pos < last_->end) + break; + Pool *prev = last_->prev; + { + if (last_->size() <= kMaxReserveSize && + (!reserved_ || reserved_->size() < last_->size())) + { + if (reserved_) + free(reserved_); + reserved_ = last_; + } else { + free(last_); + } + } + last_ = prev; + } + + if (!last_) { + assert(!pos); + return; + } + + last_->ptr = pos; +} + +void * +PoolAllocator::slowAllocate(size_t actualBytes) +{ + size_t bytesNeeded = actualBytes + sizeof(Pool); + if (bytesNeeded < kDefaultPoolSize) + bytesNeeded = kDefaultPoolSize; + + Pool *pool; + if (reserved_ && reserved_->size() >= bytesNeeded) { + pool = reserved_; + reserved_ = NULL; + } else { + pool = (Pool *)malloc(bytesNeeded); + if (!pool) { + ZONE()->reportOutOfMemory(); + return NULL; + } + pool->base = (char *)(pool + 1); + pool->end = (char *)pool + bytesNeeded; + } + pool->ptr = pool->base + actualBytes; + pool->prev = last_; + last_ = pool; + return pool->base; +} + +void +PoolAllocationPolicy::reportOutOfMemory() +{ + ZONE()->reportOutOfMemory(); +} + +void +PoolAllocationPolicy::reportAllocationOverflow() +{ + ZONE()->reportError(Message_AllocationOverflow); +} + +void * +PoolAllocationPolicy::malloc(size_t bytes) +{ + return ZONE()->pool().allocate(bytes); +} + +void +PoolAllocationPolicy::free(void *ptr) +{ +} diff -r 06a0015b6a5c -r 32ad0523b309 src/PoolAllocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/PoolAllocator.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_pool_allocator_h_ +#define _include_jitcraft_pool_allocator_h_ + +#include +#include +#include +#include "Utility.h" +#include "Vector.h" + +namespace ke { + +// Allocates memory in chunks that are not freed until the entire allocator +// is freed. This is intended for use with large, temporary data structures. +class PoolAllocator +{ + friend class PoolAllocationScope; + + struct Pool { + char *base; + char *ptr; + char *end; + Pool *prev; + + size_t size() const { + return size_t(end - base); + } + }; + + static const size_t kDefaultPoolSize = 8 * 1024; + static const size_t kMaxReserveSize = 64 * 1024; + + private: + Pool *reserved_; + Pool *last_; + + private: + void unwind(char *pos); + void *slowAllocate(size_t actualBytes); + + public: + PoolAllocator(); + ~PoolAllocator(); + + void *allocate(size_t bytes) { + // Guarantee malloc alignment. + size_t actualBytes = Align(bytes, kMallocAlignment); + if (!last_ || (size_t(last_->end - last_->ptr) < actualBytes)) + return slowAllocate(actualBytes); + char *ptr = last_->ptr; + last_->ptr += actualBytes; + return ptr; + } + + char *enter(); + void leave(char *position); +}; + +class PoolScope +{ + PoolAllocator &pool_; + char *position_; + + public: + PoolScope(PoolAllocator &allocator) + : pool_(allocator), + position_(allocator.enter()) + { + } + ~PoolScope() + { + pool_.leave(position_); + } +}; + +class PoolObject +{ + public: + void *operator new(size_t size, PoolAllocator &pool) { + return pool.allocate(size); + } + void *operator new [](size_t size, PoolAllocator &pool) { + return pool.allocate(size); + } + + // Using delete on pool-allocated objects is illegal. + void operator delete(void *ptr, PoolAllocator &pool) { + assert(false); + } + void operator delete [](void *ptr, PoolAllocator &pool) { + assert(false); + } +}; + +class PoolAllocationPolicy +{ + protected: + void reportAllocationOverflow(); + void reportOutOfMemory(); + + public: + void *malloc(size_t bytes); + void free(void *ptr); +}; + +template +class PoolList + : public Vector, + public PoolObject +{ + public: + PoolList() + : Vector(PoolAllocationPolicy()) + { + } +}; + +} + +#endif // _include_jitcraft_pool_allocator_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Reference.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Reference.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Reference.h" +#include "Zone.h" +#include "Array.h" +#include "Heap-inl.h" + +using namespace ke; + +Reference * +Reference::NewStack(Zone *zone, void *location, Heap::Tenure tenure) +{ + Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); + if (!ref) + return NULL; + + ref->location_ = location; + return ref; +} + +Reference * +Reference::NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure) +{ + Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); + if (!ref) + return NULL; + + ref->location_ = &ref->slot_; + ref->slot_ = slot; + return ref; +} + +Reference * +Reference::NewArray(Zone *zone, Handle base, size_t index, Heap::Tenure tenure) +{ + Local ref(zone, Reference::cast(zone->allocate(MapKind_Reference, sizeof(Reference), tenure))); + if (!ref) + return NULL; + + ref->location_ = base->slotPtr(index); + ref->base_ = base; + return ref; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Reference.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Reference.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_reference_h_ +#define _include_jitcraft_reference_h_ + +#include "Heap.h" +#include "Barriers.h" +#include "Stack.h" + +namespace ke { + +class Array; + +// A Reference is a typed pointer to another location in memory. +class Reference : public Object +{ + void *location_; + LocalSlot slot_; + Barriered base_; + + public: + static Reference *NewStack(Zone *zone, void *location, Heap::Tenure tenure = Heap::Tenure_Default); + static Reference *NewStack(Zone *zone, const LocalSlot &slot, Heap::Tenure tenure = Heap::Tenure_Default); + static Reference *NewArray(Zone *zone, Handle base, size_t index, Heap::Tenure tenure = Heap::Tenure_Default); + + int getInt() { + return *reinterpret_cast(location_); + } + void setInt(int i) { + *reinterpret_cast(location_) = i; + } + float getFloat() { + return *reinterpret_cast(location_); + } + void setFloat(float f) { + *reinterpret_cast(location_) = f; + } + + // WARNING! This should only be for Native calls. + void *location() { + return location_; + } + + // Begin GC Descriptor. + static inline size_t offsetOfBase() { + return offsetof(Reference, base_); + } + // End GC Descriptor. + + static inline Reference *cast(Object *obj) { + assert(!obj || obj->is(MapKind_Reference)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_reference_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/RootScopes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/RootScopes.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,85 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include "RootScopes.h" + +using namespace ke; + +RootScopeManager::RootScopeManager() + : blocks_(&first_), + first_(NULL), + scope_(NULL) +{ +} + +RootScopeManager::~RootScopeManager() +{ + while (blocks_ != &first_) { + Block *temp = blocks_; + delete blocks_; + blocks_ = temp; + } +} + +bool +RootScopeManager::grow() +{ + assert(blocks_->cur == blocks_->end); + + Block *block = new Block(blocks_); + if (!block) { + // We are not prepared for this. We'll start dishing out NULL-valued + // ScopedRoots, and the engine generally assumes infallbility. Rather + // then NULL check all sorts of nodes on parse trees, or add boolean + // setters, for now we just abort. + fprintf(stderr, "Unrecoverable error: could not allocate a local root scope. Aborting!\n"); + abort(); + return false; + } + + blocks_ = block; + return true; +} + +void +RootScopeManager::unwindSlow(Object **pos) +{ + for (;;) { + if (pos >= blocks_->objects && pos <= blocks_->end) + break; + assert(blocks_ != &first_); + Block *temp = blocks_; + delete blocks_; + blocks_ = temp; + } + + blocks_->cur = pos; +} + +void +RootScopeManager::accept(VirtualObjectVisitor *visitor) +{ + Block *block = blocks_; + while (block) { + for (Object **ptr = block->objects; ptr < block->cur; ptr++) + visitor->visit(ptr); + block = block->prev; + } +} diff -r 06a0015b6a5c -r 32ad0523b309 src/RootScopes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/RootScopes.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,106 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_root_scopes_h_ +#define _include_jc_root_scopes_h_ + +#include "../include/keima.h" +#include "Objects.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class RootScope; + +// A RootScopeManager is essentially a specialized PoolAllocator that can +// only allocate Object ** entries. It is used to allow huge, temporary +// heap data structures like ASTs to hold safe references to objects. When +// the data structures is no longer needed, all of the roots are removed +// at once. +class RootScopeManager +{ + static const size_t kObjectsPerBlock = 4096; + + struct Block { + Object *objects[kObjectsPerBlock]; + Object **cur; + Object **end; + Block *prev; + + Block(Block *prev) + : cur(objects), + end(objects + kObjectsPerBlock), + prev(prev) + { + } + }; + + private: + bool grow(); + void unwindSlow(Object **position); + + public: + RootScopeManager(); + ~RootScopeManager(); + + Object **position() const { + return blocks_->cur; + } + + void unwind(Object **position) { + if (position >= blocks_->cur && position <= blocks_->end) { + blocks_->cur = position; + return; + } + unwindSlow(position); + } + + Object **make(Object *obj) { + assert(scope_); + if (blocks_->cur + 1 > blocks_->end) { + if (!grow()) + return NULL; + } + Object **ptr = blocks_->cur; + blocks_->cur++; + *ptr = obj; + return ptr; + } + + Ke::HandleScope **scope() { + return &scope_; + } + + void accept(VirtualObjectVisitor *visitor); + + private: + Block *blocks_; + Block first_; + Ke::HandleScope *scope_; +}; + +class RootScope : public Ke::HandleScope +{ + public: + // These are implemented in Zone.h. + inline RootScope(Zone *zone); +}; + +} + +#endif // _include_jc_root_scope_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Scanner.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Scanner.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include +#include "Scanner.h" +#include "Utility.h" + +using namespace ke; + +const char *ke::TokenNames[] = +{ +#define _(name, str) str, + TOKENMAP(_) +#undef _ + NULL +}; + +Scanner::Scanner(CompileContext &cc, const char *stream, size_t length) + : cc_(cc), + stream_(stream), + length_(length), + pushed_(false), + saved_('\0'), + pos_(stream_) +{ + cur_.line = 1; + cur_.col = 1; + last_ = cur_; +} + +char +Scanner::getChar() +{ + if (saved_ != '\0') { + char temp = saved_; + saved_ = '\0'; + return temp; + } + if (pos_ >= stream_ + length_) + return '\0'; + last_.col = cur_.col++; + return *pos_++; +} + +void +Scanner::putChar(char c) +{ + assert(saved_ == '\0'); + saved_ = c; + cur_.col = last_.col; +} + +void +Scanner::calcNewLine(char c) +{ + cur_.line++; + cur_.col = 1; + last_ = cur_; + + char o; + if (c == '\r' && ((o = getChar()) != '\n')) + putChar(o); +} + +void +Scanner::eatSingleLine() +{ + char c; + while ((c = getChar()) != '\0') { + if (c == '\n' || c == '\r') { + calcNewLine(c); + break; + } + } +} + +bool +Scanner::eatComment() +{ + char c; + + while ((c = getChar()) != '\0') { + while (c == '*') { + c = getChar(); + if (c == '\0') + return false; + if (c == '/') + return true; + } + if (c == '\r' || c == '\n') + calcNewLine(c); + } + + 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; +} + +static inline bool +IsHexDigit(char c) +{ + return IsDigit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +// Based on the logic for litchar() in sc2.c. +int +Scanner::readEscapeCode() +{ + char c = getChar(); + if (c == '\\') + return c; + + switch (c) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'e': + return 27; // Apparently \e is non-standard. + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case '\v': + return '\v'; + + case 'x': + { + unsigned digits = 0; + char r = 0; + + c = getChar(); + while (IsHexDigit(c) && digits < 2) { + if (IsDigit(c)) + c = (c << 4) + (c - '0'); + else + c = (c << 4) + (tolower(c) - 'a' + 10); + digits++; + c = getChar(); + } + + // Swallow a trailing ';' + if (c != ';') + putChar(c); + + return r; + } + + case '\'': + case '\"': + case '%': + return c; + + default: + { + if (IsDigit(c)) { + // \ddd + char r = 0; + while (IsDigit(c)) { + r = r * 10 + (c - '0'); + c = getChar(); + } + + // Swallow a trailing ; + if (c != ';') + putChar(c); + + return r; + } + cc_.reportError(begin(), Message_UnknownEscapeCode, c); + return INT_MAX; + } + } +} + +TokenKind +Scanner::stringLiteral() +{ + literal_.clear(); + + for (;;) { + char c = getChar(); + if (c == '\"') + break; + if (c == '\r' || c == '\n' || c == '\0') { + cc_.reportError(begin(), Message_UnterminatedString); + return TOK_ERROR; + } + if (c == '\\') { + int code = readEscapeCode(); + if (code == INT_MAX) + return TOK_ERROR; + c = char(code); + } + literal_ += c; + } + + return TOK_STRING_LITERAL; +} + +TokenKind +Scanner::numberLiteral(char first) +{ + literal_ = first; + + char c; + for (;;) { + c = getChar(); + if (!IsDigit(c)) + break; + literal_ += c; + } + + if (c != '.') { + putChar(c); + return TOK_INTEGER_LITERAL; + } + literal_ += c; + + c = getChar(); + if (!IsDigit(c)) { + cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); + 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)) { + cc_.reportError(begin(), Message_ExpectedDigitForFloat, c); + return TOK_ERROR; + } + literal_ += c; + for (;;) { + c = getChar(); + if (!IsDigit(c)) { + putChar(c); + break; + } + literal_ += c; + } + } + + return TOK_FLOAT_LITERAL; +} + +TokenKind +Scanner::identifier(char first) +{ + literal_ = first; + + char c; + for (;;) { + c = getChar(); + if (!isascii(c) || (!isalnum(c) && c != '_')) { + if (c != ':') + putChar(c); + break; + } + literal_ += c; + } + + if (literal_ == "new") + return TOK_NEW; + if (literal_ == "for") + return TOK_FOR; + if (literal_ == "return") + return TOK_RETURN; + if (literal_ == "if") + return TOK_IF; + if (literal_ == "else") + return TOK_ELSE; + if (literal_ == "enum") + return TOK_ENUM; + if (literal_ == "while") + return TOK_WHILE; + if (literal_ == "do") + return TOK_DO; + if (literal_ == "break") + return TOK_BREAK; + if (literal_ == "continue") + return TOK_CONTINUE; + if (literal_ == "forward") + return TOK_FORWARD; + if (literal_ == "public") + return TOK_PUBLIC; + if (literal_ == "const") + return TOK_CONST; + if (literal_ == "native") + return TOK_NATIVE; + return (c == ':') ? TOK_LABEL : TOK_NAME; +} + +TokenKind +Scanner::next() +{ + assert(!pushed_); + + for (;;) { + begin_ = cur_; + + char c = getChar(); + switch (c) { + case '/': + { + c = getChar(); + if (c == '/') { + eatSingleLine(); + break; + } + if (c == '*') { + if (!eatComment()) { + cc_.reportError(begin(), Message_UnterminatedComment); + return TOK_ERROR; + } + break; + } + putChar(c); + return TOK_SLASH; + } + + case '\n': + case '\r': + calcNewLine(c); + break; + + case ';': return TOK_SEMICOLON; + case '{': return TOK_LBRACE; + case '}': return TOK_RBRACE; + case '(': return TOK_LPAREN; + case ')': return TOK_RPAREN; + case '[': return TOK_LBRACKET; + case ']': return TOK_RBRACKET; + case '~': return TOK_TILDE; + case '?': return TOK_QMARK; + case ':': return TOK_COLON; + + case '.': + c = getChar(); + if (c == '.') { + c = getChar(); + if (c == '.') + return TOK_ELLIPSES; + } + cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); + return TOK_ERROR; + + case '*': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_MUL; + putChar(c); + return TOK_STAR; + + case '+': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_ADD; + if (c == '+') + return TOK_INCREMENT; + putChar(c); + return TOK_PLUS; + + case '&': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITAND; + if (c == '&') + return TOK_AND; + putChar(c); + return TOK_BITAND; + + case '|': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITOR; + if (c == '|') + return TOK_OR; + putChar(c); + return TOK_BITOR; + + case '^': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_BITXOR; + putChar(c); + return TOK_BITXOR; + + case '%': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_MOD; + putChar(c); + return TOK_PERCENT; + + case '-': + c = getChar(); + if (c == '=') + return TOK_ASSIGN_SUB; + if (c == '-') + return TOK_DECREMENT; + putChar(c); + return TOK_MINUS; + + case ',': return TOK_COMMA; + case '\0': return TOK_EOF; + + case '!': + c = getChar(); + if (c == '=') + return TOK_NOTEQUALS; + putChar(c); + return TOK_NOT; + + case '=': + c = getChar(); + if (c == '=') + return TOK_EQUALS; + putChar(c); + return TOK_ASSIGN; + + case '<': + c = getChar(); + if (c == '=') + return TOK_LE; + if (c == '<') { + c = getChar(); + if (c == '=') + return TOK_ASSIGN_SHL; + return TOK_SHL; + } + putChar(c); + return TOK_LT; + + case '>': + c = getChar(); + if (c == '=') + return TOK_GE; + if (c == '>') { + c = getChar(); + if (c == '>') { + c = getChar(); + if (c == '=') + return TOK_ASSIGN_USHR; + putChar(c); + return TOK_USHR; + } + return TOK_SHR; + } + putChar(c); + return TOK_GT; + + case ' ': + case '\t': + case '\f': + break; + + case '"': + return stringLiteral(); + + default: + if (c >= '0' && c <= '9') + return numberLiteral(c); + if (c == '_' || (isascii(c) && isalpha(c))) + return identifier(c); + cc_.reportError(begin_, Message_UnexpectedCharacter, c, uint8(c)); + return TOK_ERROR; + } + } +} + +TokenKind +Scanner::scan() +{ + if (pushed_) { + pushed_ = false; + return lastToken_; + } + lastToken_ = next(); + return lastToken_; +} + +TokenKind +Scanner::peek() +{ + if (pushed_) + return lastToken_; + scan(); + pushed_ = true; + return lastToken_; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Scanner.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Scanner.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_scanner_h_ +#define _include_jitcraft_scanner_h_ + +#include +#include "Token.h" +#include "CompileContext.h" + +namespace ke { + +class Scanner +{ + CompileContext &cc_; + const char *stream_; + size_t length_; + bool pushed_; + char saved_; + const char *pos_; + std::string literal_; + TokenKind lastToken_; + SourcePosition last_; + SourcePosition cur_; + SourcePosition begin_; + + private: + char getChar(); + void putChar(char c); + void eatSingleLine(); + bool eatComment(); + void calcNewLine(char c); + int readEscapeCode(); + TokenKind stringLiteral(); + TokenKind numberLiteral(char first); + TokenKind identifier(char first); + + TokenKind next(); + + public: + Scanner(CompileContext &cc, const char *stream, size_t length); + + static double ParseDouble(const char *string); + + TokenKind scan(); + TokenKind peek(); + TokenKind last() const { + return lastToken_; + } + + // Beginning source position of the last instruction. + SourcePosition begin() const { + return begin_; + } + + // Note: the literal may have embedded NUL characters. + const char *literal() const { + return literal_.c_str(); + } + size_t literal_length() const { + return literal_.size(); + } +}; + +} // namespace ke + +#endif // _include_jitcraft_scanner_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Scopes.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Scopes.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,273 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Scopes.h" +#include "Zone.h" +#include "Types.h" +#include "TypeManager.h" +#include "Strings.h" +#include "CompileContext.h" +#include "Environments.h" + +using namespace ke; + +Scope::Scope(PoolAllocator &pool, Kind kind, Scope *enclosing) + : pool_(pool), + locals_(0), + maxContainedLocals_(0), + numFunctions_(0), + numNatives_(0), + lifoSlot_(NULL), + kind_(kind), + enclosing_(enclosing) +{ +} + +bool +Scope::initialize() +{ + if (enclosing_ && !enclosing_->children_.append(this)) + return false; + return true; +} + +void +Scope::unlink() +{ + assert(empty()); + + assert(enclosing_->children_[enclosing_->children_.length() - 1] == this); + enclosing_->children_.pop(); + + // OOM here is swallowed and checked after compilation. + // :TODO: make the above true. + for (size_t i = 0; i < children_.length(); i++) + enclosing_->children_.append(children_[i]); +} + +Scope * +Scope::unlinkIfEmpty() +{ + if (empty()) { + unlink(); + return NULL; + } + return this; +} + +BoundName * +Scope::localLookup(Handle name) +{ + for (size_t i = 0; i < names_.length(); i++) { + if (names_[i]->name() == name) + return names_[i]; + } + return NULL; +} + +BoundName * +Scope::lookup(Handle name) +{ + for (Scope *scope = this; scope; scope = scope->enclosing()) { + if (BoundName *bound = scope->localLookup(name)) + return bound; + } + return NULL; +} + +Variable * +Scope::declareVariable(Handle name, const SourcePosition &pos) +{ + assert(!localLookup(name)); + + Variable *var = new (pool_) Variable(this, name, pos); + if (!names_.append(var)) + return NULL; + return var; +} + +Variable * +Scope::declareArgument(Handle name, const SourcePosition &pos) +{ + assert(kind() == FUNCTION); + Variable *var = declareVariable(name, pos); + if (!names_.append(var)) + return NULL; + if (!parameters_.append(var)) + return NULL; + return var; +} + +NamedFunction * +Scope::declareFunction(Handle name, const SourcePosition &pos, TokenKind token) +{ + assert(!localLookup(name)); + assert(kind() == GLOBAL); + + NamedFunction *fun = new (pool_) NamedFunction(this, name, pos, token); + if (!names_.append(fun)) + return NULL; + return fun; +} + +NamedEnum * +Scope::declareEnum(Handle name, const SourcePosition &pos) +{ + NamedEnum *e = new (pool_) NamedEnum(this, name, pos); + if (!names_.append(e)) + return NULL; + return e; +} + +NamedConstant * +Scope::declareConstant(Handle name, const SourcePosition &pos) +{ + NamedConstant *constant = new (pool_) NamedConstant(this, name, pos); + if (!names_.append(constant)) + return NULL; + return constant; +} + +void +Scope::allocateGlobals() +{ + assert(kind() == GLOBAL); + + for (size_t i = 0; i < children_.length(); i++) { + assert(children_[i]->kind() == FUNCTION); + children_[i]->allocate(); + } + + Local type(ZONE()); + for (size_t i = 0; i < names_.length(); i++) { + BoundName *name = names_[i]; + + if (name->kind() == BoundName::FUNCTION) { + NamedFunction *fun = name->toNamedFunction(); + if (fun->isNative()) + fun->allocate(numNatives_++); + else + fun->allocate(numFunctions_++); + } else if (name->kind() == BoundName::VARIABLE) { + allocate(name->toVariable()); + } + } +} + +void +Scope::allocate(Variable *var) +{ + if (!var->unallocated()) + return; + + if (kind() == GLOBAL) { + var->allocate(Variable::ENVIRONMENT, envVars_.length()); + envVars_.append(var); + } else { + var->allocate(Variable::STACK, maxContainedLocals_ + locals_); + locals_++; + } +} + +void +Scope::allocate() +{ + maxContainedLocals_ = 0; + for (size_t i = 0; i < children_.length(); i++) { + Scope *scope = children_[i]; + scope->allocate(); + if (scope->maxContainedLocals() > maxContainedLocals_) + maxContainedLocals_ = scope->maxContainedLocals(); + } + + for (size_t i = 0; i < parameters_.length(); i++) + parameters_[i]->allocate(Variable::PARAMETER, i); + + if (lifoSlot_) + allocate(lifoSlot_); + for (size_t i = 0; i < names_.length(); i++) { + if (names_[i]->kind() != BoundName::VARIABLE) + continue; + + allocate(names_[i]->toVariable()); + } + + maxContainedLocals_ += locals_; +} + +bool +Scope::Bind(CompileContext &cc, ParseTree *tree) +{ + Local name(ZONE()); + for (size_t i = 0; i < tree->unbound()->length(); i++) { + NameProxy *proxy = tree->unbound()->at(i); + name = proxy->name(); + BoundName *binding = tree->scope()->lookup(name); + + if (!binding) { + cc.reportError(proxy->pos(), Message_IdentifierNotFound, name->chars()); + return false; + } + + // We only allow global functions to be late-bound. + if (binding->kind() != BoundName::FUNCTION || + binding->scope()->kind() != Scope::GLOBAL) + { + cc.reportError(proxy->pos(), Message_IdentifierNotFound, name->chars()); + return false; + } + + proxy->bind(binding); + } + return true; +} + +void +Scope::Allocate(ParseTree *tree) +{ + tree->scope()->allocateGlobals(); +} + +void +Scope::setUsesLifoHeap() +{ + // Only one lifo slot is needed per scope. + if (lifoSlot_) + return; + + // If the parent of this scope is the function scope, then this is the + // function's block scope, and the lifo slot is part of the Frame instead. + // Note we can get a function scope here if we're making a temporary in a + // function with no local variables. + if (kind() == FUNCTION || enclosing()->kind() == FUNCTION) + return; + + Local type(ZONE(), ZONE()->types()->getPrimitive(PrimitiveType_Native)); + if (!type) + return; + + Local name(ZONE()); + lifoSlot_ = new (pool_) Variable(this, name, SourcePosition()); + lifoSlot_->setType(type); +} + +EnvironmentMap * +Scope::createEnvironmentMap(Zone *zone) +{ + return EnvironmentMap::New(zone, envVars_.length(), envVars_.buffer()); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Scopes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Scopes.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_scopes_h_ +#define _include_jitcraft_scopes_h_ + +#include "PoolAllocator.h" +#include "AST.h" +#include "NameBinding.h" +#include "Structures.h" + +namespace ke { + +class CompileContext; +class EnvironmentMap; + +class Scope : public PoolObject +{ + PoolAllocator &pool_; + PoolList names_; + PoolList children_; + PoolList parameters_; + unsigned locals_; + unsigned maxContainedLocals_; + unsigned numFunctions_; + unsigned numNatives_; + PoolList envVars_; + Variable *lifoSlot_; + + bool empty() const { + return names_.length() == 0; + } + void unlink(); + + public: + enum Kind { + GLOBAL, + FUNCTION, + BLOCK + }; + + public: + Scope(PoolAllocator &pool, Kind kind, Scope *enclosing); + bool initialize(); + + Kind kind() const { + return kind_; + } + Scope *enclosing() const { + return enclosing_; + } + unsigned locals() const { + return locals_; + } + unsigned maxContainedLocals() const { + return maxContainedLocals_; + } + unsigned numFunctions() const { + return numFunctions_; + } + unsigned numNatives() const { + return numNatives_; + } + unsigned numArguments() const { + return parameters_.length(); + } + unsigned numEnvironmentVars() const { + return envVars_.length(); + } + Variable *getArgument(size_t index) { + return parameters_[index]; + } + Variable *lifoSlot() { + return lifoSlot_; + } + void setUsesLifoHeap(); + + Scope *unlinkIfEmpty(); + BoundName *localLookup(Handle name); + BoundName *lookup(Handle name); + + NamedFunction *declareFunction(Handle name, const SourcePosition &pos, TokenKind token); + Variable *declareVariable(Handle name, const SourcePosition &pos); + Variable *declareArgument(Handle name, const SourcePosition &pos); + NamedEnum *declareEnum(Handle name, const SourcePosition &pos); + NamedConstant *declareConstant(Handle name, const SourcePosition &pos); + + EnvironmentMap *createEnvironmentMap(Zone *zone); + + void allocate(Variable *var); + void allocate(); + void allocateGlobals(); + + public: + static bool Bind(CompileContext &cc, ParseTree *tree); + static void Allocate(ParseTree *tree); + + private: + Kind kind_; + Scope *enclosing_; +}; + +} + +#endif // _include_jitcraft_scopes_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/SemanticAnalysis.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/SemanticAnalysis.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,1092 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "SemanticAnalysis.h" +#include "Types.h" +#include "Zone.h" +#include "TypeManager.h" +#include "Scopes.h" +#include "NameBinding.h" +#include "Heap-inl.h" +#include "Strings.h" + +using namespace ke; + +static inline bool +IsPrimitiveType(Type *type, PrimitiveType primitive) +{ + if (!type) + return false; + + if (!type->isPrimitive()) + return false; + + return primitive == type->primitive(); +} + +static inline bool +EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right, + BoxedPrimitive *out) +{ + switch (kind) { + case TOK_PLUS: + *out = BoxedPrimitive::Int(left.toInt() + right.toInt()); + return true; + + case TOK_SHL: + *out = BoxedPrimitive::Int(left.toInt() << right.toInt()); + return true; + + default: + return false; + } +} + +static bool +EvaluateForConstant(Expression *expression, BoxedPrimitive *out) +{ + switch (expression->kind()) { + case AstNode::kBinaryExpression: + { + BoxedPrimitive left; + BoxedPrimitive right; + BinaryExpression *b = expression->toBinaryExpression(); + if (!EvaluateForConstant(b->left(), &left) || + !EvaluateForConstant(b->right(), &right)) + { + return false; + } + return EvaluateBinaryOperation(b->token(), left, right, out); + } + + case AstNode::kNameProxy: + { + NameProxy *proxy = expression->toNameProxy(); + if (proxy->binding()->kind() != BoundName::CONSTANT) + return false; + + NamedConstant *c = proxy->binding()->toNamedConstant(); + if (!IsPrimitiveType(c->type(), PrimitiveType_Int32)) + return false; + + *out = c->value(); + return true; + } + + case AstNode::kIntegerLiteral: + *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value()); + return true; + + default: + return false; + } +} + +SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) + : zone_(zone), + cc_(cc), + tree_(tree), + scope_(NULL), + loop_(NULL) +{ +} + +bool +SemanticAnalysis::analyze() +{ + if (cc_.failures()) + return false; + + scope_ = tree_->scope(); + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + if (statement->isFunctionStatement()) { + FunctionStatement *funstmt = statement->toFunctionStatement(); + if (!preanalyze(funstmt)) + return false; + } + } + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + statement->accept(this); + } + + return !cc_.failures(); +} + +Type * +SemanticAnalysis::visitAndApplyType(Expression *expression) +{ + expression->accept(this); + return expression->type(); +} + +Type * +SemanticAnalysis::visitForLoad(Expression *expression) +{ + Local type(zone_, visitAndApplyType(expression)); + if (!type) + return NULL; + + if (type->isReference()) { + expression->setType(type->contained()); + } else if (expression->isIndexExpression()) { + if (type->isArray()) + expression->setType(type->contained()); + } else if (expression->isCallExpression()) { + if (type->isVoid()) { + CallExpression *call = expression->toCallExpression(); + cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); + return NULL; + } + } + + return expression->type(); +} + +// :TODO: this leaks an interior pointer. +static inline const char * +GetTypeName(Type *type) +{ + if (type->isArray()) + return "array"; + if (type->isFunction()) + return "function"; + if (type->isReference()) + type = type->contained(); + if (type->tag()) + return type->tag()->chars(); + return "int"; +} + +void +SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle type) +{ + const char *op = TokenNames[token]; + const char *name = GetTypeName(type); + + cc_.reportError(node->pos(), Message_UnaryTypeError, op, name); +} + +void +SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right) +{ + const char *op = TokenNames[token]; + const char *leftName = GetTypeName(left); + const char *rightName = GetTypeName(right); + + cc_.reportError(node->pos(), Message_BinaryTypeError, op, leftName, rightName); +} + +Type * +SemanticAnalysis::typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right) +{ + 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()) { + Type *floatType = left->isFloat() ? left : right; + Type *other = left->isFloat() ? right : left; + if (other->isFloat() || (other->isInt32() && !other->tag())) + return floatType; + } + } + + if (token == TOK_OR || token == TOK_AND) { + if (!canTest(node, left) || !canTest(node, right)) + return NULL; + return zone_->types()->getPrimitive(PrimitiveType_Int32); + } + + if (token >= TOK_EQUALS && token <= TOK_GE) { + if ((left->isFloat() || (left->isInt32() && !left->tag())) && + (right->isFloat() || (right->isInt32() && !right->tag()))) + { + return zone_->types()->getPrimitive(PrimitiveType_Int32); + } + binaryTypeError(node, token, left, right); + return NULL; + } + + if ((!left->isInt32() || left->tag()) || + (!right->isInt32() || right->tag())) + { + binaryTypeError(node, token, left, right); + return NULL; + } + + return left; +} + +bool +SemanticAnalysis::canTest(AstNode *node, Handle type) +{ + if (type->isArray() || (type->isInt32() && type->tag())) { + cc_.reportError(node->pos(), Message_ArrayIsNotBoolean); + return false; + } + return true; +} + +Type * +SemanticAnalysis::visitForLValue(Expression *expression) +{ + if (expression->isIndexExpression()) { + Type *type = visitAndApplyType(expression); + if (!type) + return NULL; + + if (type->isConst()) { + cc_.reportError(expression->pos(), Message_ReferenceIsConstant); + return NULL; + } + + return type->contained(); + } + + if (!expression->isNameProxy()) { + cc_.reportError(expression->pos(), Message_ExpectedLValue); + return NULL; + } + + NameProxy *proxy = expression->toNameProxy(); + if (!proxy->binding()->kind() == BoundName::VARIABLE) { + cc_.reportError(expression->pos(), Message_ExpectedLValue); + return NULL; + } + + Variable *var = proxy->binding()->toVariable(); + if (!var->type()) + return NULL; + + if (var->type()->isReference()) { + if (var->type()->isConst()) { + cc_.reportError(expression->pos(), Message_ReferenceIsConstant); + return NULL; + } + return var->type()->contained(); + } + return var->type(); +} + +SemanticAnalysis::ControlStatus +SemanticAnalysis::visitForControl(Statement *statement) +{ + ControlScope scope(&control_); + statement->accept(this); + return scope.status(); +} + +void +SemanticAnalysis::visit(NameProxy *name) +{ + name->setType(name->binding()->type()); +} + +void +SemanticAnalysis::visit(Assignment *node) +{ + Local exprType(zone_, visitForLoad(node->expression())); + if (!exprType) + return ; + + Local type(zone_, visitForLValue(node->lvalue())); + if (!type) + return; + + if (node->token() != TOK_ASSIGN) { + if (!typeCheckBinary(node, node->token(), type, exprType)) + return; + } + + if (!typeCheckAssignment(node, exprType, type, true)) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(IncDecExpression *node) +{ + Local type(zone_, visitForLValue(node->expression())); + if (!type) + return; + + assert(!type->isReference()); + + if ((!type->isInt32() || (type->isInt32() && type->tag())) && !type->isFloat()) { + unaryTypeError(node, node->token(), type); + return; + } + + node->setType(type); +} + +void +SemanticAnalysis::visit(UnaryExpression *node) +{ + Local type(zone_, visitForLoad(node->expression())); + if (!type) + return; + + switch (node->token()) + { + case TOK_NOT: + if (!canTest(node->expression(), type)) + return; + type = zone_->types()->getPrimitive(PrimitiveType_Int32); + if (!type) + return; + node->setType(type); + break; + + case TOK_MINUS: + if (!type->isFloat() && (!type->isInt32() || type->tag())) { + unaryTypeError(node, node->token(), type); + return; + } + node->setType(type); + break; + + default: + assert(node->token() == TOK_TILDE); + if (!type->isInt32() || type->tag()) { + unaryTypeError(node, node->token(), type); + return; + } + node->setType(type); + break; + } +} + +void +SemanticAnalysis::visit(TernaryExpression *node) +{ + Local type(zone_, visitForLoad(node->condition())); + if (!type) + return; + + if (!canTest(node->condition(), type)) + return; + + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + if (!left || !right) + return; + + if (left != right) { + binaryTypeError(node, TOK_QMARK, left, right); + return; + } +} + +void +SemanticAnalysis::visit(BinaryExpression *node) +{ + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + if (!left || !right) + return; + + Local result(zone_, typeCheckBinary(node, node->token(), left, right)); + if (!result) + return; + + node->setType(result); +} + +void +SemanticAnalysis::visit(ReturnStatement *node) +{ + // Set this early, so if we error we don't get more errors. + control_->returns(Returns_Always); + + if (!node->expression()) { + if (!function_->returnType()->isVoid()) + cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); + return; + } + + Local type(zone_, visitForLoad(node->expression())); + if (!type) + return; + + Local returnType(zone_, function_->returnType()); + if (!typeCheckAssignment(node, type, returnType, true)) + return; +} + +void +SemanticAnalysis::visit(ForStatement *node) +{ + LoopScope loop(&loop_); + + if (node->scope()) + scope_ = node->scope(); + + if (node->initialization()) + node->initialization()->accept(this); + + if (node->condition()) { + Local type(zone_, visitForLoad(node->condition())); + if (!type || !canTest(node->condition(), type)) { + return; + } + } + + if (node->update()) + node->update()->accept(this); + + ControlStatus status = visitForControl(node->body()); + + // If there is a condition, we treat this like a while loop. Otherwise, + // join point is only reachable if the loop contains a break. The + // termination status doesn't tell us anything yet. + if (node->condition() || loop.breaks()) + control_->merge(status, ControlStatus(false, Returns_Never)); + else + control_->terminate(status.returns()); + + if (node->scope()) + scope_ = scope_->enclosing(); +} + +void +SemanticAnalysis::visit(WhileStatement *node) +{ + LoopScope loop(&loop_); + + Local type(zone_, visitForLoad(node->condition())); + if (!type || !canTest(node->condition(), type)) { + return; + } + + ControlStatus status = visitForControl(node->body()); + + // Detect loops that will always have a fork in the control-flow graph. + // This will be a |while| loop, or a |do-while| loop whose body does + // not terminate. + // + // For now, we treat these as an |if| regardless of the direction the + // branch can take. That means the control-flow is always conditional. + if (node->token() == TOK_WHILE || + !status.terminates() || + loop.breaks()) + { + control_->merge(status, ControlStatus(false, Returns_Never)); + } else { + // At this point, we've got a do-while loop with a |return| or + // |continue|, meaning that the rest of the block is unreachable. + control_->terminate(status.returns()); + } +} + +void +SemanticAnalysis::visit(BlockStatement *node) +{ + if (node->scope()) + scope_ = node->scope(); + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *statement = node->statements()->at(i); + statement->accept(this); + } + if (node->scope()) + scope_ = scope_->enclosing(); +} + +void +SemanticAnalysis::visit(VariableDeclaration *node) +{ + Local tag(zone_, node->tag()); + Local type(zone_, zone_->types()->getPrimitiveForTag(tag)); + if (!type) + return; + + if (node->postDimensions()) { + Local init(zone_); + if (node->initialization()) { + if ((init = visitForLoad(node->initialization())) == NULL) + return; + } + + if (node->postDimensions()->length() > Type::MAX_UNSAFE_ARRAY_DEPTH) { + cc_.reportError(node->pos(), Message_MaximumArrayDepth); + return; + } + + // If we have an initializer, make sure it's the same type. + if (init && node->postDimensions()->length() != init->levels()) { + cc_.reportError(node->pos(), Message_ArrayInitializerDepth); + return; + } + + Local check(zone_, init); + for (size_t i = node->postDimensions()->length() - 1; i < node->postDimensions()->length(); i--) { + Expression *expr = node->postDimensions()->at(i); + int size = -1; + + if (expr) { + Local exprType(zone_, visitForLoad(expr)); + if (!exprType) + return; + + if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) { + cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); + return; + } + + BoxedPrimitive box; + if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { + size = box.toInt(); + if (size <= 0) { + cc_.reportError(expr->pos(), Message_BadArraySize); + return; + } + if (size >= INT_MAX / sizeof(int)) { + cc_.reportError(expr->pos(), Message_ArraySizeTooBig); + return; + } + + if (check) { + if (check->elements() == -1) { + cc_.reportError(expr->pos(), Message_ArraySizeCannotBeDetermined); + return; + } + + if (check->isCharArray()) { + if (check->elements() > size) { + cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig); + return; + } + } else if (check->elements() != size) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, unsigned(i)); + return; + } + } + } else { + if (init) { + cc_.reportError(node->pos(), Message_DynamicArrayHasInitializer); + return; + } + + // here until we get dynamic array support. + cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); + return; + } + } else { + // Infer the size from the initializer. + if (!init) { + cc_.reportError(node->pos(), Message_ArraySizeNotSpecified); + return; + } + if (init->elements() == -1) { + cc_.reportError(node->pos(), Message_ArraySizeCannotBeDetermined); + return; + } + size = init->elements(); + } + + type = zone_->types()->unsafeArray(type, size, false); + if (!type) + return; + + if (check) + check = check->contained(); + } + + // If this variable is stack allocated, we could use the LIFO heap + // to allocate it. If that's the case, make sure to reserve a slot + // so we can unwind the LIFO heap at the end of this scope. + if (!node->variable()->needsHeapAllocation()) + scope_->setUsesLifoHeap(); + } else { + if (node->initialization()) { + Local right(zone_, visitForLoad(node->initialization())); + if (!right) + return; + if (!typeCheckAssignment(node, type, right, true)) + return; + } + if (type->primitive() == PrimitiveType_Char) { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return; + } + } + + node->variable()->setType(type); +} + +void +SemanticAnalysis::visit(IntegerLiteral *node) +{ + node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); +} + +void +SemanticAnalysis::visit(ExpressionStatement *node) +{ + visitAndApplyType(node->expression()); +} + +bool +SemanticAnalysis::preanalyze(FunctionStatement *node) +{ + Local args(zone_, FixedArray::New(zone_, node->parameters()->length(), Heap::Tenure_Old)); + if (!args) + return false; + + // Apply types to arguments. + for (size_t i = 0; i < node->parameters()->length(); i++) { + Parameter *parameter = node->parameters()->at(i); + + Local tag(zone_, parameter->tag()); + Local type(zone_, zone_->types()->getPrimitiveForTag(tag)); + if (!type) + return false; + + if (parameter->reference()) { + if (parameter->dimensions()) { + cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference); + return NULL; + } + + type = zone_->types()->getReference(type, parameter->isConst()); + if (!type) + return false; + } else if (parameter->dimensions()) { + if (parameter->dimensions()->length() > Type::MAX_UNSAFE_ARRAY_DEPTH) { + cc_.reportError(parameter->variable()->pos(), Message_MaximumArrayDepth); + return false; + } + + if (node->fun()->isNative() && parameter->dimensions()->length() > 1) { + cc_.reportError(parameter->variable()->pos(), Message_MultiArrayInNative); + return false; + } + + for (size_t i = parameter->dimensions()->length() - 1; + i < parameter->dimensions()->length(); + i++) + { + int size = Type::UNKNOWN_SIZE; + if (Expression *expr = parameter->dimensions()->at(i)) { + BoxedPrimitive box; + if (!EvaluateForConstant(expr, &box)) { + cc_.reportError(expr->pos(), Message_ArraySizeMustBeConstant); + return false; + } + size = box.toInt(); + } + + type = zone_->types()->unsafeArray(type, size, parameter->isConst()); + if (!type) + return false; + } + } + + if ((type->isPrimitive() && type->primitive() == PrimitiveType_Char) || + (type->isReference() && type->contained()->primitive() == PrimitiveType_Char)) + { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return false; + } + + args->set(zone_, i, type); + parameter->variable()->setType(type); + } + + Local returnType(zone_); + if (node->hasExplicitReturn() || node->fun()->isNative()) + returnType = zone_->types()->getPrimitive(PrimitiveType_Int32); + else + returnType = zone_->types()->getVoid(); + if (!returnType) + return false; + + Local funtype(zone_); + if (node->fun()->isNative()) + funtype = zone_->types()->newNative(returnType, args, node->fun()->isNativeVariadic()); + else + funtype = zone_->types()->newFunction(returnType, args); + if (!funtype) + return false; + + // If the function already has a type, it was forward declared before, and + // now this is our chance to type check the second declaration. + if (node->fun()->type()) { + Local forward(zone_, node->fun()->type()); + if (!Type::Compare(funtype, forward)) { + cc_.reportError(node->pos(), Message_ForwardDeclareMismatches); + return false; + } + node->fun()->setForwardType(forward); + } + + node->fun()->setType(funtype); + + return true; +} + +void +SemanticAnalysis::visit(FunctionStatement *node) +{ + function_ = node->fun()->type(); + scope_ = node->scope(); + + ControlScope control(&control_); + if (node->body()) { + node->body()->accept(this); + + ControlStatus status = control.status(); + if (status.returns() == Returns_Sometimes) { + cc_.reportError(node->pos(), Message_NotAllPathsReturnValue); + } else if (status.returns() == Returns_Never) { + if (!function_->returnType()->isVoid()) + cc_.reportError(node->pos(), Message_MissingReturnValue); + } + } + + scope_ = NULL; + function_ = NULL; +} + +bool +SemanticAnalysis::typeCheckArrayAssignment(AstNode *node, Handle argGiven, Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local given(zone_, argGiven); + + if (!given->isArray()) { + cc_.reportError(node->pos(), Message_ExpectedArray); + return false; + } + if (given->levels() != actual->levels()) { + cc_.reportError(node->pos(), Message_ArrayDimensionsMismatch); + return false; + } + + unsigned level = 0; + while (given->isArray()) { + if (actual->elements() != -1) { + if (forAssign) { + if (given->elements() == -1 || actual->elements() == -1) { + cc_.reportError(node->pos(), Message_ArraySizeNotKnown); + return false; + } + + if (actual->isCharArray()) { + // String assignment can have a truncated |given|. + if (given->elements() > actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig); + return false; + } + } else { + if (given->elements() != actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + } else { + if (actual->elements() != -1 && given->elements() != actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + } + given = given->contained(); + actual = actual->contained(); + } + + if (given->primitive() != actual->primitive()) { + // :TODO: leaks an interior pointer. + const char *givenTag = given->tag() ? given->tag()->chars() : "_"; + const char *actualTag = given->tag() ? given->tag()->chars() : "_"; + cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag); + return false; + } + + return true; +} + +bool +SemanticAnalysis::typeCheckAssignment(AstNode *node, Handle argGiven, Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local given(zone_, argGiven); + + if (actual->isArray()) + return typeCheckArrayAssignment(node, given, actual, forAssign); + + if (given->isArray()) { + cc_.reportError(node->pos(), Message_CannotAssignArrayToScalar); + return false; + } + + // The given type may not have been dereferenced. + if (given->isReference()) + given = given->contained(); + if (actual->isReference()) + actual = actual->contained(); + + if (!given->isPrimitive() || !actual->isPrimitive()) { + cc_.reportError(node->pos(), Message_UnsupportedType); + return false; + } + + if (given->primitive() != actual->primitive()) { + // :TODO: leaks an interior pointer + const char *givenTag = given->tag() ? given->tag()->chars() : "_"; + const char *actualTag = actual->tag() ? actual->tag()->chars() : "_"; + cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag); + return false; + } + + return true; +} + +bool +SemanticAnalysis::typeCheckParameter(AstNode *node, Handle given, Handle actual) +{ + return typeCheckAssignment(node, given, actual, false); +} + +void +SemanticAnalysis::visit(CallExpression *node) +{ + Local type(zone_, visitForLoad(node->callee())); + if (!type) + return; + + if (!type->isFunction()) { + cc_.reportError(node->pos(), Message_CalleeNotFunction); + return; + } + if (node->arguments()->length() != type->parameters()->length()) { + if (!type->isNative() || + !type->isNativeVariadic() || + node->arguments()->length() < type->parameters()->length()) + { + cc_.reportError(node->pos(), Message_ArgumentCountMismatch); + return; + } + } + + // Currently, only calls from a NameProxy are possible. + NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction(); + if (fun->isForward()) { + cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); + return; + } + + for (unsigned i = 0; i < node->arguments()->length(); i++) { + // Note: we don't compute references unless the callee needs a + // reference type. + Expression *expression = node->arguments()->at(i); + Local given(zone_, visitAndApplyType(expression)); + if (!given) + return; + + // Since we skip a load, check for a call expression that returns void. + if (given->isVoid()) { + assert(expression->isCallExpression()); + CallExpression *call = expression->toCallExpression(); + cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue); + return; + } + + // If it's an IndexExpression, we special case the ability to compute + // a reference. + if (expression->isIndexExpression()) + given = given->contained(); + + bool mustComputeReference; + if (i >= type->parameters()->length()) { + // Special handling for natives. Arguments past the formal must + // always have references computed. However, arrays are already + // references for natives. + assert(type->isNative() && type->isNativeVariadic()); + mustComputeReference = !given->isReference() && !given->isArray(); + } else { + Local actual(zone_, type->parameterAt(i)); + if (!typeCheckParameter(expression, given, actual)) + return; + mustComputeReference = actual->isReference() && !given->isReference(); + } + + // If the actual is a reference, and the given is not, we'll need to + // compute a reference, which may be allocated on the stack. + if (mustComputeReference) + scope_->setUsesLifoHeap(); + } + + node->setType(fun->type()->returnType()); +} + +void +SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) +{ + // If both sides terminate in a return, we consider this a return as well. + if (left.returns() == Returns_Always && right.returns() == Returns_Always) { + terminate(Returns_Always); + return; + } + + ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) + ? Returns_Never + : Returns_Sometimes; + + if (left.terminates() && right.terminates()) + terminate(status); + else + returns(status); +} + +void +SemanticAnalysis::visit(IfStatement *node) +{ + Local type(zone_, visitForLoad(node->condition())); + if (!type) + return; + + if (!canTest(node, type)) + return; + + ControlStatus trueBranch = visitForControl(node->ifTrue()); + ControlStatus falseBranch = ControlStatus(false, Returns_Never); + + if (node->ifFalse()) + falseBranch = visitForControl(node->ifFalse()); + + control_->merge(trueBranch, falseBranch); +} + +void +SemanticAnalysis::visit(IndexExpression *node) +{ + Local left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + + if (!left || !right) + return; + + if (!right->isInt32()) { + cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger); + return; + } + if (!left->isArray()) { + cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); + return; + } + node->setType(left); +} + +void +SemanticAnalysis::visit(EnumStatement *node) +{ + // Forbid the blessed types. + Local tag(zone_, node->name()); + if (!strcmp(tag->chars(), "Float") || + !strcmp(tag->chars(), "String") || + !strcmp(tag->chars(), "any") || + !strcmp(tag->chars(), "bool")) + { + cc_.reportError(node->pos(), Message_InvalidEnumName); + return; + } + + Local type(zone_, zone_->types()->getPrimitiveForTag(tag)); + if (!type) + return; + + int value = 0; + for (size_t i = 0; i < node->entries()->length(); i++) { + EnumStatement::Entry &entry = node->entries()->at(i); + + BoxedPrimitive box = BoxedPrimitive::Int(value); + if (entry.expr) { + entry.expr->accept(this); + if (!EvaluateForConstant(entry.expr, &box)) { + cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); + return; + } + + assert(box.type() == PrimitiveType_Int32); + value = box.toInt(); + } + + entry.name->set(type, box); + value++; + } +} + +void +SemanticAnalysis::visit(StringLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Char)); + if (!type) + return; + + type = Type::NewArray(zone_, type, node->string()->length() + 1, true); + if (!type) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(FloatLiteral *node) +{ + Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); + if (!type) + return; + + node->setType(type); +} + +void +SemanticAnalysis::visit(BreakStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_BreakOutsideLoop); + return; + } + + // Note: ignore breaks that occur in unreachable control-flow. + if (!control_->status().terminates()) + loop_->addBreak(); + + control_->terminate(Returns_Never); +} + +void +SemanticAnalysis::visit(ContinueStatement *node) +{ + if (!loop_) { + cc_.reportError(node->pos(), Message_ContinueOutsideLoop); + return; + } + + // Note: ignore continues that occur in unreachable control-flow. + if (!control_->status().terminates()) + loop_->addContinue(); + + control_->terminate(Returns_Never); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/SemanticAnalysis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/SemanticAnalysis.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,204 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_semantic_analysis_h_ +#define _include_semantic_analysis_h_ + +#include +#include "AST.h" + +namespace ke { + +class SemanticAnalysis : public AstVisitor +{ + Zone *zone_; + CompileContext &cc_; + ParseTree *tree_; + Scope *scope_; + ScopedRoot function_; + + enum ReturnStatus + { + Returns_Never, + Returns_Sometimes, + Returns_Always + }; + + // Tuple that describes whether a path terminates control flow, and + // if it is guaranteed to end in a return. + class ControlStatus + { + bool terminates_; + ReturnStatus returns_; + + public: + ControlStatus(bool terminates, ReturnStatus returns) + : terminates_(terminates), + returns_(returns) + { + } + + bool terminates() const { + return terminates_; + } + ReturnStatus returns() const { + return returns_; + } + }; + + // Used to keep track of terminating control flow and return statements + // inside control structures. + class ControlScope + { + ReturnStatus returns_; + ControlScope **prevp_; + ControlScope *prev_; + bool terminates_; + + public: + ControlScope(ControlScope **prev) + : returns_(Returns_Never), + prevp_(prev), + prev_(*prev), + terminates_(false) + { + *prevp_ = this; + } + + ControlStatus status() const { + return ControlStatus(terminates_, returns_); + } + + // We allow multiple terminators, but only the first one takes effect. + // For example, |break; continue;| is legal but the continue is + // unreachable. + void returns(ReturnStatus status) { + if (terminates_) + return; + if (status > returns_) + returns_ = status; + } + void terminate(ReturnStatus status) { + if (terminates_) + return; + terminates_ = true; + returns_ = status; + } + void merge(const ControlStatus &left, const ControlStatus &right); + + ~ControlScope() + { + *prevp_ = prev_; + } + }; + + class LoopScope + { + LoopScope **prevp_; + LoopScope *prev_; + unsigned breaks_; + unsigned continues_; + + public: + LoopScope(LoopScope **prev) + : prevp_(prev), + prev_(*prev), + breaks_(0), + continues_(0) + { + *prevp_ = this; + } + + ~LoopScope() + { + *prevp_ = prev_; + } + + void addBreak() { + breaks_++; + } + void addContinue() { + continues_++; + } + unsigned breaks() const { + return breaks_; + } + unsigned continues() const { + return continues_; + } + }; + + ControlScope *control_; + LoopScope *loop_; + + private: + bool canTest(AstNode *node, Handle type); + void unaryTypeError(AstNode *node, TokenKind token, Handle type); + void binaryTypeError(AstNode *node, TokenKind token, Handle left, Handle right); + Type *typeCheckBinary(AstNode *node, TokenKind token, Handle left, Handle right); + + // These check if one type is compatible with another type, for various + // purposes, like: + // return (expr) matches function return type + // call(a, b) matches expected arg type + // a = b matches declared type + // + bool typeCheckArrayAssignment(AstNode *node, Handle given, Handle actual, bool forAssign); + bool typeCheckAssignment(AstNode *node, Handle given, Handle actual, bool forAssign); + bool typeCheckParameter(AstNode *node, Handle given, Handle actual); + + Type *visitForLoad(Expression *expression); + bool preanalyze(FunctionStatement *node); + Type *visitForLValue(Expression *expression); + Type *visitAndApplyType(Expression *expression); + ControlStatus visitForControl(Statement *statement); + + void markCallResultUsed(CallExpression *call); + + public: + SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree); + bool analyze(); + + public: + void visit(NameProxy *name); + void visit(Assignment *node); + void visit(BinaryExpression *node); + void visit(ReturnStatement *node); + void visit(ForStatement *node); + void visit(BlockStatement *node); + void visit(VariableDeclaration *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); + void visit(WhileStatement *node); + void visit(BreakStatement *node); + void visit(ContinueStatement *node); + void visit(StringLiteral *node); + void visit(IncDecExpression *node); + void visit(UnaryExpression *node); + void visit(TernaryExpression *node); +}; + +} // namespace ke + +#endif // _include_semantic_analysis_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Spaces-inl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Spaces-inl.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,59 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_spaces_inl_h_ +#define _include_jitcraft_spaces_inl_h_ + +namespace ke { + +inline Address +HeapStack::allocate(size_t nbytes) +{ + nbytes = Align(nbytes, kObjectAlignment); + + if (cursor_ + nbytes > chunk_->end()) + return NULL; + + Address ptr = cursor_; + cursor_ += nbytes; + return ptr; +} + +inline void +HeapStack::unwind(Address to) +{ + assert(to <= cursor_ && to >= chunk_->first()); + cursor_ = to; +} + +inline Address +OldSpace::allocate(size_t nbytes) +{ + assert(IsAligned(nbytes, kObjectAlignment)); + + if (cursor_ + nbytes > last_->end()) + return allocateSlow(nbytes); + + Address addr = cursor_; + cursor_ += nbytes; + return addr; +} + +} + +#endif // _include_jitcraft_spaces_inl_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Spaces.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Spaces.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,307 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Zone.h" +#include "Spaces.h" +#include "Platform.h" +#include "Spaces-inl.h" + +using namespace ke; + +HeapStack::HeapStack() + : chunk_(NULL), + cursor_(NULL) +{ +} + +HeapStack::~HeapStack() +{ + if (chunk_) + HeapChunk::Destroy(chunk_); +} + +bool +HeapStack::initialize() +{ + chunk_ = HeapChunk::New(NULL, kStackSize); + cursor_ = chunk_->first(); + return true; +} + +HeapChunk * +HeapChunk::New(HeapChunk *prev, size_t size) +{ + OS::AlignableMapping map = + OS::MapAlignedMemory(size, HeapChunk::kAlignment, OS::Map_Commit); + + if (!map.base()) + return NULL; + + HeapChunk *chunk = reinterpret_cast(map.aligned()); + chunk->base_ = map.base(); + chunk->end_ = map.base() + map.size(); + if (prev) + prev->next_ = chunk; + + return chunk; +} + +void +HeapChunk::Destroy(HeapChunk *chunk) +{ + OS::UnmapAlignedMemory(chunk->base(), chunk->size()); +} + +void +HeapChunk::resetMarkBits() +{ + memset(markBits(), 0, kMarkBytes); +} + +FreeList::FreeList() + : small_objects_(NULL), + medium_objects_(NULL), + large_objects_(NULL), + huge_objects_(NULL) +{ +} + +void +FreeList::add(FreedObject *obj) +{ + if (obj->size() < kMinObjectSize) + return; + + FreedObject **list; + if (obj->size() < kMaxSmallObject) + list = &small_objects_; + else if (obj->size() < kMaxMediumObject) + list = &medium_objects_; + else if (obj->size() < kMaxLargeObject) + list = &large_objects_; + else + list = &huge_objects_; + + if (*list) + (*list)->setNext(obj); + *list = obj; +} + +FreedObject * +FreeList::pick(size_t size) +{ + if (size < kMinObjectSize && small_objects_) { + FreedObject *obj = small_objects_; + small_objects_ = small_objects_->next(); + return obj; + } + if (size < kMaxSmallObject && medium_objects_) { + FreedObject *obj = medium_objects_; + medium_objects_ = medium_objects_->next(); + return obj; + } + if (size < kMaxMediumObject && large_objects_) { + FreedObject *obj = large_objects_; + large_objects_ = large_objects_->next(); + return obj; + } + if (huge_objects_) { + FreedObject *obj = huge_objects_; + huge_objects_ = huge_objects_->next(); + return obj; + } + return NULL; +} + +void +FreeList::reset() +{ + small_objects_ = NULL; + medium_objects_ = NULL; + large_objects_ = NULL; + huge_objects_ = NULL; +} + +OldSpace::OldSpace(Zone *zone) + : zone_(zone), + first_(NULL), + last_(NULL) +{ +} + +bool +OldSpace::initialize() +{ + first_ = HeapChunk::New(first_, HeapChunk::kDefaultSize); + last_ = first_; + cursor_ = first_->first(); + + return !!first_; +} + +OldSpace::~OldSpace() +{ + while (first_) { + HeapChunk *temp = first_->next(); + HeapChunk::Destroy(first_); + first_ = temp; + } +} + +Address +OldSpace::allocateSlow(size_t nbytes) +{ + if (FreedObject *obj = freelist_.pick(nbytes)) { + Address address = reinterpret_cast
(obj); + size_t actual = obj->size(); + assert(actual >= nbytes); + if (actual != nbytes) + markRegionFree(address + nbytes, actual - nbytes); + + return address; + } + + // Free unused stuff at the end of the current chunk. + if (cursor_ != last_->end()) + markRegionFree(cursor_, last_->end() - cursor_); + + HeapChunk *chunk; + if (last_->next()) { + // The next chunk is guaranteed to be empty. + chunk = last_->next(); + } else { + chunk = HeapChunk::New(last_, HeapChunk::kDefaultSize); + if (!chunk) + return NULL; + } + + last_ = chunk; + cursor_ = chunk->first(); + + assert(cursor_ + nbytes <= chunk->end()); + + Address address = cursor_; + cursor_ += nbytes; + return address; +} + +void +OldSpace::markRegionFree(Address start, size_t length) +{ + FreedObject *obj = reinterpret_cast(start); + new (obj) FreedObject(zone_->getMap(MapKind_FreeRegion), length); + + if (length < sizeof(FreedObject)) { + // Can't encode a list in this object, but it's marked free, so we're done. + return; + } + + freelist_.add(obj); +} + +void +OldSpace::freeUnusedPages() +{ + HeapChunk **prev = &first_; + HeapChunk *empty = NULL; + while (*prev) { + HeapChunk *chunk = *prev; + if (chunk->isEmpty()) { + *prev = chunk->next(); + if (empty) { + // Remove this chunk. + HeapChunk::Destroy(chunk); + continue; + } + empty = chunk; + } + prev = chunk->nextp(); + } + + if (empty) { + *empty->nextp() = NULL; + *last_->nextp() = empty; + } +} + +void +OldSpace::resetFreeList() +{ + freelist_.reset(); +} + +LargeObjectSpace::LargeObjectSpace() + : first_(NULL), + last_(NULL) +{ +} + +LargeObjectSpace::~LargeObjectSpace() +{ + while (first_) { + HeapChunk *temp = first_->next(); + HeapChunk::Destroy(first_); + first_ = temp; + } +} + +bool +LargeObjectSpace::initialize() +{ + return true; +} + +Address +LargeObjectSpace::allocate(size_t nbytes) +{ + assert(IsAligned(nbytes, kObjectAlignment)); + + // Always allocate a new chunk for large objects. + HeapChunk *chunk = HeapChunk::New(last_, nbytes); + if (!chunk) + return NULL; + + if (!first_) + first_ = chunk; + last_ = chunk; + + return chunk->first(); +} + +void +LargeObjectSpace::sweep() +{ + if (!first_) + return; + + HeapChunk **pprev = &first_; + do { + HeapChunk *chunk = *pprev; + Object *obj = reinterpret_cast(chunk->first()); + MarkBit bit = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsWhite(bit)) { + *pprev = chunk->next(); + HeapChunk::Destroy(chunk); + } else { + assert(HeapChunk::IsBlack(bit)); + HeapChunk::Clear(bit); + pprev = chunk->nextp(); + last_ = chunk; + } + } while (*pprev); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Spaces.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Spaces.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,434 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_spaces_h_ +#define _include_jitcraft_spaces_h_ + +#include +#include "Utility.h" +#include "Platform.h" + +namespace ke { + +class Zone; +class Object; +class Map; + +static const size_t kObjectAlignment = sizeof(Object *); + +// Every object gets two successive mark bits. These bits represent various +// states during garbage collection: +// +// WHITE (00): The object is dead or unmarked. +// GREY (11): The object is live but its children have not been marked. +// BLACK (10): The object and all its children have been marked. +// +// These colors assist in maintaining the "tri-color invariant", explained +// in Heap.h. The bits are organized such that transitioning an object from +// White to Black requires one bit change, and Black to Grey requires another. +class MarkBit +{ + uint32 *word_; + uint32 bit_; + + public: + MarkBit(uint32 *word, uint32 bit) + : word_(word), + bit_(bit) + { + assert(bit_ < sizeof(*word_) * 8); + } + + bool isSet() const { + return !!(*word_ & (1 << bit_)); + } + void set() { + *word_ |= (1 << bit_); + } + void unset() { + *word_ &= ~(1 << bit_); + } + uint32 *word() { + return word_; + } + MarkBit next() const { + if (bit_ == (sizeof(*word_) * 8) - 1) + return MarkBit(word_ + 1, 0); + return MarkBit(word_, bit_ + 1); + } +}; + +class HeapChunk +{ + HeapChunk *next_; + + // Base address, from OS::AlignableMapping. + Address base_; + + // End of the allocated region. + Address end_; + + uintptr_t flags_; + + public: + enum Flag { + HAS_OVERFLOWED_OBJECT = (1 << 0), + EMPTY = (1 << 1) + }; + + public: + static const size_t kDefaultSize = 1 * kMB; + static const size_t kAlignment = 1 * kMB; + + // Maximum size of an object that will fit in a page. We limit this + // specifically to help JITs: the computation of a[i] has a special + // instruction form on most CPUs, but the index might not be sign- + // extended on 64-bit architectures. We limit the size such that: + // + // index * sizeof(Object *) + // + // Is guaranteed to not overflow a signed 32-bit integer. + static const size_t kMaxObjectSize = INT_MAX / sizeof(Object *); + + // Size of a HeapChunk header. + static const size_t kHeaderSize = + sizeof(HeapChunk *) + + sizeof(Address) + + sizeof(Address) + + sizeof(Address); + + // All objects must be allocated in the first kAlignment bytes of the + // chunk. We need two mark bits for every allocatable object, since there + // are three possible states: White (00), Black (11), and Grey (10). These + // bits are stored immediately after the HeapChunk header, and before the + // first allocatable object. + // + // Although object alignment is only at word boundaries, we guarantee that + // every object is at least two words. This means we don't need to reserve + // two mark bits for every object, just one mark bit for every allocatable + // address. That also means a pair of mark bits can cross a byte boundary. + // + // --- Math --- + // + // The number of mark bits needed is: + // + // MaxObjectsPerChunk = (kAlignment / kObjectAlignment) + // -> 256KB (128KB on 64-bit) + // + // The number of bytes needed for these mark bits is: + // + // NumMarkBytes = MaxObjectsPerChunk / 8 + // = 32KB (16KB on 64-bit) + // + // The number of uint32s needed to store these bytes is: + // + // NumMarkWords = NumMarkBytes / sizeof(uint32) + // = 8KB (4KB on 64-bit) + // + static const size_t kMaxObjectsPerChunk = kAlignment / kObjectAlignment; + static const size_t kMarkBits = kMaxObjectsPerChunk; + static const size_t kMarkBytes = kMarkBits / 8; + static const size_t kMarkWords = kMarkBytes / sizeof(uint32); + static const size_t kMarkBitsPerWord = sizeof(uint32) * 8; + + static const size_t kMarkBitsOffset = kHeaderSize; + static const size_t kFirstObject = kMarkBitsOffset + kMarkBytes; + + // Threshold for allocating an object in the large object space. + static const size_t kLargeObjectThreshold = kAlignment - kFirstObject; + + static inline void StaticAsserts() { + STATIC_ASSERT(kHeaderSize == sizeof(HeapChunk)); + STATIC_ASSERT(IS_ALIGNED(kHeaderSize, kObjectAlignment)); + STATIC_ASSERT(IS_ALIGNED(kFirstObject, kObjectAlignment)); + } + + public: + static HeapChunk *New(HeapChunk *prev, size_t size); + static void Destroy(HeapChunk *chunk); + + public: + HeapChunk *next() { + return next_; + } + Address base() { + return base_; + } + Address address() { + return reinterpret_cast
(this); + } + Address end() { + return end_; + } + size_t size() { + return end() - base(); + } + Address first() { + return reinterpret_cast
(this) + kFirstObject; + } + HeapChunk **nextp() { + return &next_; + } + uint32 *markBits() { + return reinterpret_cast(reinterpret_cast
(this) + kMarkBitsOffset); + } + void resetMarkBits(); + + // Marking functions. + static inline MarkBit GetMarkBit(Object *obj) { + uintptr_t address = reinterpret_cast(obj); + + HeapChunk *chunk = reinterpret_cast(address & ~(kAlignment - 1)); + assert(Address(obj) >= chunk->first() && Address(obj) < chunk->end()); + + size_t index = (address & (kAlignment - 1)) / kObjectAlignment; + assert(index < kMaxObjectsPerChunk); + + uint32 *word = chunk->markBits() + (index / kMarkBitsPerWord); + uint32 bit = index & (kMarkBitsPerWord - 1); + return MarkBit(word, bit); + } + static inline Address FromMarkBit(HeapChunk *chunk, uint32 *word, uint32 bit) { + // word = bits + (index / kMarkBitsPerWord) + // word - bits = index / kMarkBitsPerWord + // (word - bits) * kMarkBitsPerWord = index + assert(word >= chunk->markBits() && word < chunk->markBits() + kMarkWords); + size_t index = ((word - chunk->markBits()) * kMarkBitsPerWord) + bit; + return chunk->address() + (index * kObjectAlignment); + } + static inline bool IsWhite(const MarkBit &bit) { + assert(bit.isSet() || !bit.next().isSet()); + return !bit.isSet(); + } + static inline bool IsBlack(const MarkBit &bit) { + return bit.isSet() && !bit.next().isSet(); + } + static inline bool IsGrey(const MarkBit &bit) { + return bit.isSet() && bit.next().isSet(); + } + static inline bool IsBlackOrGrey(const MarkBit &bit) { + return bit.isSet(); + } + static inline void WhiteToBlack(MarkBit &bit) { + assert(IsWhite(bit)); + bit.set(); + } + static inline void BlackToGrey(MarkBit &bit) { + assert(IsBlack(bit)); + bit.next().set(); + } + static inline void GreyToBlack(MarkBit &bit) { + assert(IsGrey(bit)); + bit.next().unset(); + } + static inline void Clear(MarkBit &bit) { + bit.unset(); + bit.next().unset(); + } + + bool hasOverflowedObject() { + return !!(flags_ & HAS_OVERFLOWED_OBJECT); + } + void setHasOverflowedObject() { + flags_ |= HAS_OVERFLOWED_OBJECT; + } + bool isEmpty() { + return !!(flags_ & EMPTY); + } + void setEmpty() { + flags_ |= EMPTY; + } +}; + +template +class ChunkObjectIteratorBase +{ + HeapChunk *chunk_; + Address top_; + SizeEvaluator evaluator_; + Address current_; + + public: + ChunkObjectIteratorBase(HeapChunk *start, Address top, const SizeEvaluator &evaluator) + : chunk_(start), + top_(top), + evaluator_(evaluator), + current_(start ? start->first() : NULL) + { + } + + Object *next() { + while (current_ && current_ != top_) { + Object *obj = reinterpret_cast(current_); + size_t size = evaluator_(obj, obj->map()); + current_ += size; + if (current_ == chunk_->end()) { + chunk_ = chunk_->next(); + current_ = chunk_ ? chunk_->first() : NULL; + } + if (obj->is(MapKind_FreeRegion)) + continue; + } + return NULL; + } +}; + +class FreedObject +{ + Map *map_; + size_t bytes_; + FreedObject *next_; + + public: + FreedObject(Map *map, size_t bytes) + : map_(map), + bytes_(bytes) + { + assert(bytes_ >= sizeof(Map *) + sizeof(size_t)); + if (bytes_ >= sizeof(FreedObject)) + next_ = NULL; + } + + void setNext(FreedObject *obj) { + assert(bytes_ >= sizeof(FreedObject)); + next_ = obj; + } + FreedObject *next() { + assert(bytes_ >= sizeof(FreedObject)); + return next_; + } + size_t size() { + return bytes_; + } +}; + +// Free-region list used in old spaces. Based on V8's. Each object pool +// is used to allocate objects of the previous pool's size. For example, +// the medium object pool is used to allocate small objects. +class FreeList +{ + private: + static const size_t kMinObjectSize = 32; + static const size_t kMaxSmallObject = 255; + static const size_t kMaxMediumObject = (2 * kKB) - 1; + static const size_t kMaxLargeObject = (16 * kKB) - 1; + + public: + FreeList(); + + public: + void add(FreedObject *obj); + FreedObject *pick(size_t size); + void reset(); + + private: + FreedObject *small_objects_; + FreedObject *medium_objects_; + FreedObject *large_objects_; + FreedObject *huge_objects_; +}; + +// The heap stack is designed for structures which are known to not escape the +// program stack, and can thus be quickly allocated in a LIFO manner. +// +class HeapStack +{ + OS::AlignableMapping map_; + HeapChunk *chunk_; + Address cursor_; + uintptr_t mask_; + + private: + static const size_t kStackSize = 1024 * 1024; + + public: + HeapStack(); + ~HeapStack(); + bool initialize(); + + public: + Address position() const { + return cursor_; + } + inline Address allocate(size_t nbytes); + inline void unwind(Address to); + + inline bool contains(Object *obj) { + return (reinterpret_cast(obj) & mask_) == reinterpret_cast(chunk_); + } + + template + ChunkObjectIteratorBase begin(const Evaluator &evaluator) { + return ChunkObjectIteratorBase(chunk_, cursor_, evaluator); + } +}; + +class OldSpace +{ + Zone *zone_; + HeapChunk *first_; + HeapChunk *last_; + Address cursor_; + FreeList freelist_; + + public: + OldSpace(Zone *zone); + ~OldSpace(); + + bool initialize(); + + public: + inline Address allocate(size_t nbytes); + Address allocateSlow(size_t nbytes); + void markRegionFree(Address start, size_t size); + void freeUnusedPages(); + + template + ChunkObjectIteratorBase begin(const Evaluator &evaluator) { + return ChunkObjectIteratorBase(first_, cursor_, evaluator); + } + + HeapChunk *first() { + return first_; + } + void resetFreeList(); +}; + +class LargeObjectSpace +{ + HeapChunk *first_; + HeapChunk *last_; + + public: + LargeObjectSpace(); + ~LargeObjectSpace(); + + bool initialize(); + + public: + Address allocate(size_t nbytes); + + HeapChunk *first() { + return first_; + } + void sweep(); +}; + +} + +#endif // _include_jitcraft_spaces_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Stack.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Stack.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,321 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include "Stack.h" +#include "Zone.h" +#include "Strings.h" +#include "Modules.h" +#include "Heap-inl.h" + +using namespace ke; + +Stack::Stack(Zone *zone) + : zone_(zone), + buffer_(NULL), + end_(NULL) +{ +} + +Stack::~Stack() +{ + free(buffer_); +} + +bool +Stack::initialize() +{ + buffer_ = (char *)malloc(STACK_SIZE); + if (!buffer_) + return false; + + top_.sp = reinterpret_cast(buffer_); + top_.pc = NULL; + top_.fp = NULL; + end_ = buffer_ + STACK_SIZE; + return true; +} + +InvokeArgs::~InvokeArgs() +{ + stack_->popInvokeFrame(*this); +} + +NativeArgs::~NativeArgs() +{ + stack_->popNativeFrame(*this); +} + +static inline size_t +SizeForCodeFrame(Code *code) +{ + return sizeof(Frame) + sizeof(LocalSlot) * (code->nlocals() + code->maxStackDepth()); +} + +bool +Stack::pushInvokeFrame(Handle fun, InvokeArgs &args) +{ + size_t nbytes = SizeForCodeFrame(fun->code()) + + sizeof(LocalSlot) * fun->nargs(); + if (reinterpret_cast(top_.sp) + nbytes >= end_) { + assert(false); + return false; + } + + memset(top_.sp, 0, sizeof(LocalSlot) * fun->nargs()); + args.args_ = top_.sp; + args.nargs_ = fun->nargs(); + args.stack_ = this; + top_.sp += fun->nargs(); + + Frame *frame = reinterpret_cast(top_.sp); + new (frame) Frame(top_.fp, top_.pc, fun, fun->code(), zone_->heap().lifoPosition()); + + top_.pc = frame->code()->start(); + top_.sp = frame->stackBase(); + top_.fp = frame; + + return true; +} + +bool +Stack::pushModuleFrame(Handle module, InvokeArgs &args) +{ + size_t nbytes = SizeForCodeFrame(module->code()); + if (reinterpret_cast(top_.sp) + nbytes >= end_) { + assert(false); + return false; + } + + args.args_ = top_.sp; + args.nargs_ = 0; + args.stack_ = this; + + Frame *frame = reinterpret_cast(top_.sp); + new (frame) Frame(top_.fp, top_.pc, NULL, module->code(), zone_->heap().lifoPosition()); + + top_.pc = frame->code()->start(); + top_.sp = frame->stackBase(); + top_.fp = frame; + + return true; +} + +bool +Stack::pushNativeFrame(Handle native, NativeArgs &args, unsigned argc) +{ + size_t nbytes = sizeof(intptr_t) * argc + sizeof(Frame); + if (reinterpret_cast(top_.sp) + nbytes >= end_) { + assert(false); + return false; + } + + memset(top_.sp, 0, sizeof(intptr_t) * argc); + args.argsBase_ = top_.sp; + args.stack_ = this; + top_.sp = reinterpret_cast(reinterpret_cast(top_.sp) + argc); + + Frame *frame = reinterpret_cast(top_.sp); + new (frame) Frame(top_.fp, top_.pc, native, zone_->heap().lifoPosition()); + + top_.pc = NULL; + top_.sp = reinterpret_cast(frame + 1); + top_.fp = frame; + + return true; +} + +Frame * +Stack::pushInlineFrame(Handle fun) +{ + Code *code = fun->code(); + size_t nbytes = SizeForCodeFrame(code); + if (reinterpret_cast(top_.sp) + nbytes >= end_) { + zone_->reportError(Message_TooMuchRecursion); + return NULL; + } + + Frame *frame = reinterpret_cast(top_.sp); + new (frame) Frame(top_.fp, top_.pc, fun, code, zone_->heap().lifoPosition()); + memset(frame->locals(), 0, sizeof(LocalSlot) * code->nlocals()); + + top_.pc = frame->code()->start(); + top_.sp = frame->stackBase(); + top_.fp = frame; + + return frame; +} + + +Frame * +Stack::popInlineFrame() +{ + zone_->heap().unwindLifo(top_.fp->lifoPosition()); + + Frame *prev = top_.fp->prev(); + Function *fun = top_.fp->fun(); + + top_.pc = top_.fp->prevpc(); + top_.sp = reinterpret_cast(top_.fp) - fun->nargs(); + top_.fp = prev; + return prev; +} + +void +Stack::popInvokeFrame(InvokeArgs &args) +{ + zone_->heap().unwindLifo(top_.fp->lifoPosition()); + + top_.pc = top_.fp->prevpc(); + top_.sp = args.args_; + top_.fp = top_.fp->prev(); +} + +void +Stack::popNativeFrame(NativeArgs &args) +{ + zone_->heap().unwindLifo(top_.fp->lifoPosition()); + + top_.pc = top_.fp->prevpc(); + top_.sp = args.argsBase_; + top_.fp = top_.fp->prev(); +} + +void +Frame::accept(VirtualObjectVisitor *visitor) +{ + visitor->visit(reinterpret_cast(&callee_)); + visitor->visit(reinterpret_cast(&code_)); +} + +static void +TraceFunctionFrame(VirtualObjectVisitor *visitor, Frame *fp, LocalSlot *sp, bytecode *pc) +{ + Code *code = fp->code(); + LocalSlot *slots = fp->locals(); + + // Trace local variables. + for (unsigned i = 0; i < code->nlocals(); i++) { + Type *type = code->localType(i); + if (type->isTraceable()) + visitor->visit(&slots[i].obj); + } + + // Trace arguments, if a function call. Note that this means we usually + // mark arguments twice, once on the caller side and once on the callee. + // This makes it easier to deal with invoke frames which have no caller. + if (Function *fun = fp->maybeFun()) { + for (unsigned i = 0; i < fun->nargs(); i++) { + Type *type = fun->argType(i); + if (type->isTraceable()) + visitor->visit(&fp->arg(i).obj); + } + } + + // Finally, trace stack slots. Not every gc-point in the bytecode will + // have a safepoint; since they are actually uncommon, we don't emit + // them unless at least one stack slot would be marked. + if (uint8 *safepoint = code->safepointAt(code->pcToOffset(pc))) { + LocalSlot *base = fp->stackBase(); + unsigned stackDepth = unsigned(sp - fp->stackBase()); + assert(stackDepth <= code->maxStackDepth()); + + for (unsigned i = 0; i < stackDepth; i++) { + if (safepoint[i / 8] & (1 << (i % 8))) + visitor->visit(&base[i].obj); + } + } +} + +void +Stack::accept(VirtualObjectVisitor *visitor) +{ + LocalSlot *sp = top_.sp; + bytecode *pc = top_.pc; + + for (Frame *frame = top_.fp; frame; frame = frame->prev()) { + if (!frame->isNative()) + TraceFunctionFrame(visitor, frame, sp, pc); + frame->accept(visitor); + + sp = reinterpret_cast(frame); + pc = frame->prevpc(); + } +} + +bool +Stack::isInCallStack(Handle module) +{ + for (Frame *frame = top_.fp; frame; frame = frame->prev()) { + if (frame->code()->module() == module) + return true; + } + return false; +} + +StackIter::StackIter(Zone *zone) + : pc_(zone->stack().top()->pc), + fp_(zone->stack().top()->fp) +{ +} + +bool +StackIter::done() const +{ + return !fp_; +} + +const char * +StackIter::function() const +{ + if (fp_->isNative()) + return fp_->native()->name()->chars(); + if (Function *fun = fp_->maybeFun()) + return fun->name()->chars(); + return ""; +} + +unsigned +StackIter::lineNumber() const +{ + if (fp_->isNative()) + return 0; + if (fp_->maybeFun()) { + unsigned offs = fp_->code()->pcToOffset(pc_); + return fp_->code()->pcToSource(offs).line; + } + return fp_->code()->lineNumber(); +} + +const char * +StackIter::source() const +{ + if (fp_->isNative()) + return ""; + return fp_->code()->module()->file() + ? fp_->code()->module()->file()->chars() + : "(anonymous)"; +} + +void +StackIter::prev() +{ + pc_ = fp_->prevpc(); + fp_ = fp_->prev(); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Stack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Stack.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,233 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_stack_h_ +#define _include_jc_stack_h_ + +#include +#include "Utility.h" +#include "Code.h" +#include "Functions.h" +#include "VirtualObjectVisitor.h" + +namespace ke { + +class Object; +class Array; +class Reference; +class Frame; +class Stack; +class Zone; +class Module; + +union LocalSlot +{ + int i32; + float f; + Address address; + Object *obj; + Array *array; + Reference *ref; + Function *fun; + Module *module; +}; + +struct Top +{ + LocalSlot *sp; + bytecode *pc; + Frame *fp; +}; + +class Frame +{ + friend class Stack; + + Frame *prev_; + bytecode *prevpc_; + union { + Function *fun_; + Native *native_; + Object *callee_; + }; + Code *code_; + Address lifoPosition_; + + public: + Frame(Frame *prev, bytecode *prevpc, Function *fun, Code *code, Address lifoPosition) + : prev_(prev), + prevpc_(prevpc), + fun_(fun), + code_(code), + lifoPosition_(lifoPosition) + { + } + + Frame(Frame *prev, bytecode *prevpc, Native *native, Address lifoPosition) + : prev_(prev), + prevpc_(prevpc), + native_(native), + code_(NULL), + lifoPosition_(lifoPosition) + { + } + + LocalSlot *locals() { + return reinterpret_cast(this + 1); + } + LocalSlot *stackBase() { + return locals() + code_->nlocals(); + } + Function *fun() const { + assert(fun_ && callee_->is(MapKind_Function)); + return fun_; + } + Function *maybeFun() const { + return (fun_ && callee_->is(MapKind_Function)) ? fun_ : NULL; + } + bool isNative() const { + return callee_ && callee_->is(MapKind_Native); + } + Native *native() const { + assert(isNative()); + return native_; + } + Code *code() const { + assert(code_); + return code_; + } + Frame *prev() const { + return prev_; + } + bytecode *prevpc() const { + return prevpc_; + } + Address lifoPosition() const { + return lifoPosition_; + } + LocalSlot &arg(unsigned slot) { + assert(slot < fun()->nargs()); + return *(reinterpret_cast(this) - slot - 1); + } + void accept(VirtualObjectVisitor *visitor); +}; + +// RAII object for calling a function from C++. +class InvokeArgs +{ + friend class Stack; + + Stack *stack_; + LocalSlot *args_; + unsigned nargs_; + + public: + InvokeArgs() + : stack_(NULL), + args_(NULL), + nargs_(0) + { + } + + ~InvokeArgs(); + + LocalSlot &arg(unsigned index) { + assert(index < nargs_); + return args_[nargs_ - index - 1]; + } +}; + +// RAII object for calling a native from C++. +class NativeArgs +{ + friend class Stack; + + Stack *stack_; + LocalSlot *argsBase_; + + public: + NativeArgs() + : stack_(NULL), + argsBase_(NULL) + { + } + + ~NativeArgs(); + + intptr_t *args() { + return reinterpret_cast(argsBase_); + } +}; + +class Stack +{ + friend class InvokeArgs; + friend class NativeArgs; + + private: + static const size_t STACK_SIZE = 1024 * 1024; + + public: + Stack(Zone *zone); + ~Stack(); + + bool initialize(); + + public: + Frame *pushInlineFrame(Handle fun); + Frame *popInlineFrame(); + + bool pushInvokeFrame(Handle fun, InvokeArgs &args); + bool pushModuleFrame(Handle module, InvokeArgs &args); + bool pushNativeFrame(Handle native, NativeArgs &args, unsigned argc); + + Top *top() { + return &top_; + } + bool isInCallStack(Handle module); + void accept(VirtualObjectVisitor *visitor); + + private: + void popInvokeFrame(InvokeArgs &args); + void popNativeFrame(NativeArgs &args); + + private: + Zone *zone_; + char *buffer_; + char *end_; + Top top_; +}; + +class StackIter +{ + bytecode *pc_; + Frame *fp_; + + public: + StackIter(Zone *zone); + + bool done() const; + const char *function() const; + unsigned lineNumber() const; + const char *source() const; + void prev(); +}; + +} + +#endif // _include_jc_stack_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/StringPool.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/StringPool.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "StringPool.h" +#include "Strings.h" +#include "Spaces.h" + +using namespace ke; + +StringPool::StringPool(Zone *zone) + : zone_(zone), + set_(ZoneAllocatorPolicy()) +{ +} + +StringPool::~StringPool() +{ +} + +bool +StringPool::initialize() +{ + if (!set_.init(32)) + return false; + return true; +} + +String * +StringPool::add(const char *str, size_t length) +{ + StringSet::Insert i = set_.findForAdd(CharsAndLength(str, length)); + if (i.found()) + return *i; + + Local obj(zone_, String::New(zone_, str, length, Heap::Tenure_Old)); + if (!obj) + return NULL; + + set_.add(i, obj); + return obj; +} + +void +StringPool::sweep() +{ + for (StringSet::iterator i(&set_); !i.empty(); i.next()) { + String *obj = *i; + MarkBit bits = HeapChunk::GetMarkBit(obj); + if (HeapChunk::IsWhite(bits)) + i.erase(); + else + assert(HeapChunk::IsBlack(bits)); + } + set_.checkDensity(); +} diff -r 06a0015b6a5c -r 32ad0523b309 src/StringPool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/StringPool.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_pool_h_ +#define _include_jitcraft_string_pool_h_ + +#include "AllocatorPolicies.h" +#include "HashTable.h" +#include "Strings.h" + +namespace ke { + +class Zone; +class String; + +// Guarantee that strings with the same contents will map to one pointer. +class StringPool +{ + class CharsAndLength { + const char *str_; + size_t length_; + + public: + CharsAndLength(const char *str, size_t length) + : str_(str), + length_(length) + { + } + + const char *str() const { + return str_; + } + size_t length() const { + return length_; + } + }; + + struct Policy + { + typedef String * Payload; + + static uint32 hash(const CharsAndLength &key) { + return HashCharSequence(key.str(), key.length()); + } + + static bool matches(const CharsAndLength &key, String *string) { + if (string->length() != key.length()) + return false; + return memcmp(string->chars(), key.str(), key.length() * sizeof(char)) == 0; + } + }; + + typedef HashTable StringSet; + + public: + StringPool(Zone *zone); + ~StringPool(); + bool initialize(); + + public: + String *add(const char *str, size_t len); + void sweep(); + + private: + Zone *zone_; + StringSet set_; +}; + +} + +#endif // _include_jitcraft_string_pool_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Strings.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Strings.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,43 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Strings.h" +#include "Zone.h" +#include "Heap-inl.h" + +using namespace ke; + +String * +String::New(Zone *zone, const char *from, Heap::Tenure tenure) +{ + return New(zone, from, strlen(from), tenure); +} + +String * +String::New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure) +{ + size_t bytes = SizeFor(length); + Local string(zone, String::cast(zone->allocate(MapKind_String, bytes, tenure))); + if (!string) + return NULL; + + string->length_ = length; + memcpy(string->chars(), from, length + 1); + + return string; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Strings.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Strings.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,54 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_string_h_ +#define _include_jitcraft_string_h_ + +#include "Heap.h" + +namespace ke { + +// A fixed-length string containing UTF-8 characters. +class String : public Object +{ + uint32 length_; + + public: + static String *New(Zone *zone, const char *from, Heap::Tenure tenure = Heap::Tenure_Default); + static String *New(Zone *zone, const char *from, size_t length, Heap::Tenure tenure = Heap::Tenure_Default); + + unsigned length() { + return length_; + } + char *chars() { + return reinterpret_cast(this + 1); + } + + static size_t SizeFor(unsigned length) { + return Align(sizeof(String) + sizeof(char) * (length + 1), kObjectAlignment); + } + + static inline String *cast(Object *obj) { + assert(!obj || obj->is(MapKind_String)); + return reinterpret_cast(obj); + } +}; + +} + +#endif // _include_jitcraft_string_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Structures.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Structures.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,46 @@ +/* vim: set ts=4 sw=4 tw=99 et + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "Structures.h" +#include "Types.h" + +using namespace ke; + +StructureBufferBuilder::StructureBufferBuilder() + : size_(0) +{ +} + +bool +StructureBufferBuilder::add(Handle type, unsigned *offset) +{ + *offset = size_; + + if (!type->isTraceable()) { + // For now, assume the size of primitives is 4-byte. + assert(type->isInt32() || type->isFloat()); + size_ += 4; + return true; + } + + if (!offsets_.append(size_)) + return false; + + size_ += sizeof(Object *); + return true; +} diff -r 06a0015b6a5c -r 32ad0523b309 src/Structures.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Structures.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,60 @@ +/* vim: set ts=4 sw=4 tw=99 et + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jc_structure_h_ +#define _include_jc_structure_h_ + +#include "Objects.h" +#include "Handles.h" +#include "Vector.h" + +namespace ke { + +class Type; +class String; +class FixedArray; + +// When describing composite structures, the GC must be able to trace interior +// references to Objects. To make this possible, maps that describe composite +// structures must provide a list of offsets into their objects that are +// traceable. StructureBufferBuilder assists with this. +class StructureBufferBuilder +{ + public: + StructureBufferBuilder(); + + bool add(Handle type, unsigned *offset); + + unsigned structureSize() const { + return size_; + } + unsigned traceableOffsetCount() const { + return offsets_.length(); + } + const unsigned *offsets() { + return offsets_.buffer(); + } + + private: + Vector offsets_; + unsigned size_; +}; + +} + +#endif // _include_jc_structure_h_ diff -r 06a0015b6a5c -r 32ad0523b309 src/Token.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Token.h Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#ifndef _include_jitcraft_token_h_ +#define _include_jitcraft_token_h_ + +namespace ke { + +struct SourcePosition +{ + int line; + int col; + + SourcePosition() + : line(-1), + col(-1) + { + } + + SourcePosition(unsigned line, unsigned col) + : line(line), + col(col) + { + } +}; + +// 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'") \ + _(FOR, "'for'") \ + _(RETURN, "'return'") \ + _(NAME, "") \ + _(INTEGER_LITERAL, "") \ + _(FLOAT_LITERAL, "") \ + _(STRING_LITERAL, "") \ + _(IF, "") \ + _(ELSE, "") \ + _(ENUM, "") \ + _(LABEL, "