# HG changeset patch # User David Anderson # Date 1341911473 25200 # Node ID b40b0e18a9188682f97ed9779ceed37e6da5046b # Parent b765a6383849cc1ca74c8e23d1eaeed3998b1769 Added support for ISourcePawnEngine natives. diff -r b765a6383849 -r b40b0e18a918 include/keima.h --- a/include/keima.h Mon Jul 09 22:25:52 2012 -0700 +++ b/include/keima.h Tue Jul 10 02:11:13 2012 -0700 @@ -204,10 +204,25 @@ { public: // Returns the name of a function, which may be NULL. If not NULL, the - // string is guaranteed to be interned/ + // string is guaranteed to be interned. Local name(); }; +// A native is an ISourcePawnEngine-compatible function provided by C++. +class KE_EXPORT Native +{ + public: + // Returns the name of a native, which is never NULL. The string is + // guaranteed to be interned. + Local name(); + + // Returns the function pointer of the native. + void *function(); + + // Sets the function pointer of the native. + void bind(void *function); +}; + // A module represents a single compiled file, and contains functions and // global variables for that file (as well as exported types). class KE_EXPORT Module @@ -216,6 +231,14 @@ // Returns publics that are based on forwards. unsigned numPublics(); Local getPublic(unsigned index); + + // Returns natives. + unsigned numNatives(); + Local getNative(unsigned index); + + // Sets the native identity pointer, which is passed as the first argument + // to natives. + void setNativeIdentity(void *ptr); }; class Keima diff -r b765a6383849 -r b40b0e18a918 jitcraft/API.cpp --- a/jitcraft/API.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/API.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -220,6 +220,30 @@ } 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)); @@ -240,6 +264,20 @@ 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) { diff -r b765a6383849 -r b40b0e18a918 jitcraft/BytecodeCompiler.cpp --- a/jitcraft/BytecodeCompiler.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/BytecodeCompiler.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -595,17 +595,29 @@ { Local funType(zone_, node->callee()->type()); - for (size_t i = 0; i < node->arguments()->length(); i++) { + 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()); - Local actual(zone_, Type::cast(funType->parameters()->at(i))); - if (actual->isReference()) { - if (given->isReference()) { - // The input type is already a reference, so we're just passing - // it on to the callee. - arg->accept(this); - } else if (arg->isNameProxy()) { + if (i >= funType->parameters()->length()) + assert(funType->isNative() && funType->isNativeVariadic()); + else + assert(!given->isReference() || !funType->parameterAt(i)->isArray()); + + // If a native is passed to a native, it must only be one level deep. + assert(!funType->isNative() || !given->isArray() || given->levels() == 1); + + bool mustPushReference = !given->isArray() && !given->isReference(); + + if (mustPushReference) { + if (arg->isNameProxy()) { // :TODO: test with functions and globals // Compute a reference to the name. Variable *var = arg->toNameProxy()->binding()->toVariable(); @@ -628,7 +640,12 @@ } } - __ callglobal(node->callee()->toNameProxy()->binding()->toNamedFunction()->slot()); + NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction(); + + if (fun->isNative()) + __ callnative(fun->slot(), node->arguments()->length()); + else + __ callglobal(fun->slot()); __ drop(node->arguments()->length()); } @@ -654,6 +671,19 @@ 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()); @@ -779,6 +809,10 @@ 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); @@ -788,6 +822,8 @@ #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()) { @@ -803,5 +839,7 @@ moduleData_->module->setPublics(list); } + moduleData_->module->setNatives(moduleData_->natives); + return moduleData_->module; } diff -r b765a6383849 -r b40b0e18a918 jitcraft/BytecodeCompiler.h --- a/jitcraft/BytecodeCompiler.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/BytecodeCompiler.h Tue Jul 10 02:11:13 2012 -0700 @@ -31,6 +31,7 @@ { ScopedRoot module; PoolList > publics; + ScopedRoot natives; }; class BytecodeCompiler : public AstVisitor diff -r b765a6383849 -r b40b0e18a918 jitcraft/BytecodeEmitter.h --- a/jitcraft/BytecodeEmitter.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/BytecodeEmitter.h Tue Jul 10 02:11:13 2012 -0700 @@ -338,6 +338,13 @@ writeOp(OP_CALLGLOBAL); writeUint32(index); } + 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: diff -r b765a6383849 -r b40b0e18a918 jitcraft/Functions.cpp --- a/jitcraft/Functions.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Functions.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -36,3 +36,16 @@ 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 b765a6383849 -r b40b0e18a918 jitcraft/Functions.h --- a/jitcraft/Functions.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Functions.h Tue Jul 10 02:11:13 2012 -0700 @@ -67,6 +67,9 @@ 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) { @@ -75,6 +78,45 @@ } }; +// 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 b765a6383849 -r b40b0e18a918 jitcraft/Interpreter.cpp --- a/jitcraft/Interpreter.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Interpreter.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -119,6 +119,58 @@ 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); + + // :TODO: detect errors. + return true; +} + bool ke::Interpret(Zone *zone, Frame *entryFrame, LocalSlot *result) { @@ -637,6 +689,30 @@ 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()) { + // :TODO: error + assert(false); + return false; + } + + int result; + void *nativeIdentity = top.fp->code()->module()->nativeIdentity(); + if (!InvokeNative(zone, native, nativeIdentity, top.sp, argc, &result)) { + assert(false); + return false; + } + + top.sp -= argc; + PUSH_I32(top, result); + break; +} + default: assert(false); return false; diff -r b765a6383849 -r b40b0e18a918 jitcraft/Messages.tbl --- a/jitcraft/Messages.tbl Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Messages.tbl Tue Jul 10 02:11:13 2012 -0700 @@ -46,4 +46,7 @@ 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") \ No newline at end of file +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") diff -r b765a6383849 -r b40b0e18a918 jitcraft/Modules.cpp --- a/jitcraft/Modules.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Modules.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -35,5 +35,7 @@ module->functions_ = functions; module->publics_ = NULL; + module->natives_ = NULL; + module->nativeIdentity_ = NULL; return module; } diff -r b765a6383849 -r b40b0e18a918 jitcraft/Modules.h --- a/jitcraft/Modules.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Modules.h Tue Jul 10 02:11:13 2012 -0700 @@ -28,6 +28,8 @@ { Barriered functions_; Barriered publics_; + Barriered natives_; + void *nativeIdentity_; public: static Module *New(Zone *zone, unsigned nfunctions); @@ -44,9 +46,23 @@ FixedArray *publics() { return publics_; } + FixedArray *natives() { + return natives_; + } + void setPublics(Handle publics) { publics_ = publics; } + void setNatives(Handle natives) { + natives_ = natives; + } + + void *nativeIdentity() { + return nativeIdentity_; + } + void setNativeIdentity(void *identity) { + nativeIdentity_ = identity; + } // Begin GC Descriptor. static inline size_t offsetOfFunctions() { @@ -55,6 +71,9 @@ static inline size_t offsetOfPublics() { return offsetof(Module, publics_); } + static inline size_t offsetOfNatives() { + return offsetof(Module, natives_); + } // End GC Descriptor. static inline Module *cast(Object *obj) { diff -r b765a6383849 -r b40b0e18a918 jitcraft/NameBinding.cpp --- a/jitcraft/NameBinding.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/NameBinding.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -32,7 +32,8 @@ NamedFunction::NamedFunction(Scope *scope, Handle name, const SourcePosition &pos, TokenKind token) : BoundName(scope, name, pos), - token_(token) + token_(token), + isVariadicNative_(false) { } diff -r b765a6383849 -r b40b0e18a918 jitcraft/NameBinding.h --- a/jitcraft/NameBinding.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/NameBinding.h Tue Jul 10 02:11:13 2012 -0700 @@ -87,6 +87,7 @@ unsigned slot_; TokenKind token_; ScopedRoot forwardType_; + bool isVariadicNative_; public: NamedFunction(Scope *scope, Handle name, const SourcePosition &pos, TokenKind token); @@ -103,6 +104,9 @@ bool isPublic() const { return token_ == TOK_PUBLIC; } + bool isNative() const { + return token_ == TOK_NATIVE; + } void setForwardType(Handle type) { forwardType_ = type; } @@ -113,6 +117,14 @@ 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_; } diff -r b765a6383849 -r b40b0e18a918 jitcraft/ObjectVisitor.h --- a/jitcraft/ObjectVisitor.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/ObjectVisitor.h Tue Jul 10 02:11:13 2012 -0700 @@ -79,15 +79,23 @@ size_t iterateModule(Object *obj) { visitField(obj, Module::offsetOfFunctions()); visitField(obj, Module::offsetOfPublics()); + visitField(obj, Module::offsetOfNatives()); 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()); } @@ -143,6 +151,9 @@ case MapKind_Array: return Align(iterateArray(Array::cast(obj), ArrayMap::cast(map)), kObjectAlignment); + case MapKind_Native: + return iterateNative(obj); + case MapKind_MetaArray: return iterateArrayMap(ArrayMap::cast(obj)); @@ -181,6 +192,9 @@ 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); diff -r b765a6383849 -r b40b0e18a918 jitcraft/Objects.h --- a/jitcraft/Objects.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Objects.h Tue Jul 10 02:11:13 2012 -0700 @@ -46,6 +46,7 @@ MapKind_Module, MapKind_Function, MapKind_String, + MapKind_Native, MapKind_FreeRegion, // Special map denoting freed space. // Used to represent Arrays. This does not have a canonical map. diff -r b765a6383849 -r b40b0e18a918 jitcraft/Opcodes.cpp --- a/jitcraft/Opcodes.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Opcodes.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -209,6 +209,10 @@ 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); diff -r b765a6383849 -r b40b0e18a918 jitcraft/Opcodes.tbl --- a/jitcraft/Opcodes.tbl Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Opcodes.tbl Tue Jul 10 02:11:13 2012 -0700 @@ -184,3 +184,14 @@ // push(sp[-n]) OPDEF(OP_PICK2, 60, "pick2", 1, 2, 3) OPDEF(OP_PICK3, 61, "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, 62, "callnative", 9, -1, 1) \ No newline at end of file diff -r b765a6383849 -r b40b0e18a918 jitcraft/Parser.cpp --- a/jitcraft/Parser.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Parser.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -902,6 +902,21 @@ 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; @@ -944,7 +959,7 @@ } Statement *body = NULL; - if (token != TOK_FORWARD) { + if (token != TOK_FORWARD && token != TOK_NATIVE) { body = block(); if (!body) return NULL; @@ -979,11 +994,12 @@ case TOK_FORWARD: case TOK_PUBLIC: + case TOK_NATIVE: { TokenKind kind = scanner_.scan(); if ((statement = function(kind)) == NULL) return NULL; - if (kind == TOK_FORWARD && !matchSemicolon()) + if ((kind == TOK_FORWARD || kind == TOK_NATIVE) && !matchSemicolon()) return NULL; break; } diff -r b765a6383849 -r b40b0e18a918 jitcraft/Reference.h --- a/jitcraft/Reference.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Reference.h Tue Jul 10 02:11:13 2012 -0700 @@ -51,6 +51,11 @@ *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_); diff -r b765a6383849 -r b40b0e18a918 jitcraft/Scanner.cpp --- a/jitcraft/Scanner.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Scanner.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -371,6 +371,8 @@ return TOK_PUBLIC; if (literal_ == "const") return TOK_CONST; + if (literal_ == "native") + return TOK_NATIVE; return (c == ':') ? TOK_LABEL : TOK_NAME; } @@ -415,6 +417,16 @@ case '[': return TOK_LBRACKET; case ']': return TOK_RBRACKET; + 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 == '=') diff -r b765a6383849 -r b40b0e18a918 jitcraft/Scopes.cpp --- a/jitcraft/Scopes.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Scopes.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -29,6 +29,7 @@ locals_(0), maxContainedLocals_(0), numFunctions_(0), + numNatives_(0), lifoSlot_(NULL), kind_(kind), enclosing_(enclosing) @@ -155,7 +156,10 @@ if (name->kind() == BoundName::FUNCTION) { NamedFunction *fun = name->toNamedFunction(); - fun->allocate(numFunctions_++); + if (fun->isNative()) + fun->allocate(numNatives_++); + else + fun->allocate(numFunctions_++); } } } diff -r b765a6383849 -r b40b0e18a918 jitcraft/Scopes.h --- a/jitcraft/Scopes.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Scopes.h Tue Jul 10 02:11:13 2012 -0700 @@ -33,6 +33,7 @@ unsigned locals_; unsigned maxContainedLocals_; unsigned numFunctions_; + unsigned numNatives_; Variable *lifoSlot_; bool empty() const { @@ -66,6 +67,9 @@ unsigned numFunctions() const { return numFunctions_; } + unsigned numNatives() const { + return numNatives_; + } unsigned numArguments() const { return parameters_.length(); } diff -r b765a6383849 -r b40b0e18a918 jitcraft/SemanticAnalysis.cpp --- a/jitcraft/SemanticAnalysis.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/SemanticAnalysis.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -546,11 +546,6 @@ if (!type) return false; - if (type->primitive() == PrimitiveType_Char) { - cc_.reportError(node->pos(), Message_CannotDeclareStringCell); - return false; - } - if (parameter->reference()) { if (parameter->dimensions()) { cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference); @@ -562,7 +557,12 @@ return false; } else if (parameter->dimensions()) { if (parameter->dimensions()->length() > Type::MAX_UNSAFE_ARRAY_DEPTH) { - cc_.reportError(node->pos(), Message_MaximumArrayDepth); + 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; } @@ -586,6 +586,13 @@ } } + 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); } @@ -594,7 +601,11 @@ if (!returnType) return false; - Local funtype(zone_, zone_->types()->newFunction(returnType, args)); + 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; @@ -737,8 +748,13 @@ return; } if (node->arguments()->length() != type->parameters()->length()) { - cc_.reportError(node->pos(), Message_ArgumentCountMismatch); - return; + 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. @@ -761,13 +777,23 @@ if (expression->isIndexExpression()) given = given->contained(); - Local actual(zone_, type->parameterAt(i)); - if (!typeCheckParameter(expression, given, actual)) - return; + 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 (actual->isReference() && !given->isReference()) + if (mustComputeReference) scope_->setUsesLifoHeap(); } diff -r b765a6383849 -r b40b0e18a918 jitcraft/Stack.cpp --- a/jitcraft/Stack.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Stack.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -54,6 +54,11 @@ stack_->popInvokeFrame(*this); } +NativeArgs::~NativeArgs() +{ + stack_->popNativeFrame(*this); +} + static inline size_t SizeForCodeFrame(Code *code) { @@ -86,6 +91,30 @@ 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) { @@ -138,14 +167,24 @@ } 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(&fun_)); + visitor->visit(reinterpret_cast(&callee_)); visitor->visit(reinterpret_cast(&code_)); } static void -TraceFrame(VirtualObjectVisitor *visitor, Frame *fp, LocalSlot *sp, bytecode *pc) +TraceFunctionFrame(VirtualObjectVisitor *visitor, Frame *fp, LocalSlot *sp, bytecode *pc) { Code *code = fp->code(); LocalSlot *slots = fp->locals(); @@ -190,7 +229,10 @@ bytecode *pc = top_.pc; for (Frame *frame = top_.fp; frame; frame = frame->prev()) { - TraceFrame(visitor, frame, sp, pc); + if (!frame->isNative()) + TraceFunctionFrame(visitor, frame, sp, pc); + frame->accept(visitor); + sp = reinterpret_cast(frame); pc = frame->prevpc(); } diff -r b765a6383849 -r b40b0e18a918 jitcraft/Stack.h --- a/jitcraft/Stack.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Stack.h Tue Jul 10 02:11:13 2012 -0700 @@ -56,7 +56,11 @@ Frame *prev_; bytecode *prevpc_; - Function *fun_; + union { + Function *fun_; + Native *native_; + Object *callee_; + }; Code *code_; Address lifoPosition_; @@ -70,6 +74,15 @@ { } + 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); } @@ -77,13 +90,17 @@ return locals() + code_->nlocals(); } Function *fun() const { - assert(fun_); + assert(fun_ && callee_->is(MapKind_Function)); return fun_; } Function *maybeFun() const { - return fun_; + return (fun_ && callee_->is(MapKind_Function)) ? fun_ : NULL; + } + bool isNative() const { + return callee_ && callee_->is(MapKind_Native); } Code *code() const { + assert(code_); return code_; } Frame *prev() const { @@ -127,9 +144,32 @@ } }; +// 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; @@ -145,6 +185,7 @@ Frame *popInlineFrame(); bool pushInvokeFrame(Handle fun, InvokeArgs &args); + bool pushNativeFrame(Handle native, NativeArgs &args, unsigned argc); Top *top() { return &top_; @@ -154,6 +195,7 @@ private: void popInvokeFrame(InvokeArgs &args); + void popNativeFrame(NativeArgs &args); private: char *buffer_; diff -r b765a6383849 -r b40b0e18a918 jitcraft/Token.h --- a/jitcraft/Token.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Token.h Tue Jul 10 02:11:13 2012 -0700 @@ -57,6 +57,8 @@ _(FORWARD, "") \ _(PUBLIC, "") \ _(CONST, "") \ + _(NATIVE, "") \ + _(ELLIPSES, "...") \ _(PLUS, "+") \ _(MINUS, "-") \ _(STAR, "*") \ diff -r b765a6383849 -r b40b0e18a918 jitcraft/TypeManager.cpp --- a/jitcraft/TypeManager.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/TypeManager.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -97,6 +97,13 @@ return Type::NewFunction(zone_, returnType, args); } +Type * +TypeManager::newNative(Handle returnType, Handle args, bool variadic) +{ + // :TODO: cache this. + return Type::NewNative(zone_, returnType, args, variadic); +} + void TypeManager::accept(VirtualObjectVisitor *visitor) { diff -r b765a6383849 -r b40b0e18a918 jitcraft/TypeManager.h --- a/jitcraft/TypeManager.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/TypeManager.h Tue Jul 10 02:11:13 2012 -0700 @@ -47,6 +47,7 @@ Type *getReference(Handle type, bool isConst); Type *unsafeArray(Handle contained, int elements, bool isConst); Type *newFunction(Handle returnType, Handle args); + Type *newNative(Handle returnType, Handle args, bool variadic); void accept(VirtualObjectVisitor *visitor); }; diff -r b765a6383849 -r b40b0e18a918 jitcraft/Types.cpp --- a/jitcraft/Types.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Types.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -111,6 +111,21 @@ return type; } +Type * +Type::NewNative(Zone *zone, Handle returnType, Handle parameters, bool variadic) +{ + Local type(zone, Type::cast(zone->allocate(MapKind_Type, sizeof(Type), Heap::Tenure_Old))); + if (!type) + return NULL; + + type->kind_ = NATIVE; + type->contained_ = NULL; + type->returnType_ = returnType; + type->parameters_ = parameters; + type->variadicNative_ = variadic; + return type; +} + bool Type::Compare(Type *left, Type *right) { diff -r b765a6383849 -r b40b0e18a918 jitcraft/Types.h --- a/jitcraft/Types.h Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Types.h Tue Jul 10 02:11:13 2012 -0700 @@ -42,7 +42,8 @@ PRIMITIVE, ARRAY, REFERENCE, - FUNCTION + FUNCTION, + NATIVE }; private: @@ -59,6 +60,7 @@ static Type *NewArray(Zone *zone, Handle contained, int elements, bool isConst); static Type *NewReference(Zone *zone, Handle contained, bool isConst); static Type *NewFunction(Zone *zone, Handle returnType, Handle parameters); + static Type *NewNative(Zone *zone, Handle returnType, Handle parameters, bool isVariadic); static bool Compare(Type *left, Type *right); @@ -77,7 +79,14 @@ return kind() == REFERENCE; } bool isFunction() { - return kind() == FUNCTION; + return kind() == FUNCTION || kind() == NATIVE; + } + bool isNative() { + return kind() == NATIVE; + } + bool isNativeVariadic() { + assert(isNative()); + return variadicNative_; } PrimitiveType primitive() { assert(isPrimitive()); @@ -169,6 +178,7 @@ // Functions only. Barriered returnType_; Barriered parameters_; + bool variadicNative_; }; class BoxedPrimitive diff -r b765a6383849 -r b40b0e18a918 jitcraft/Zone.cpp --- a/jitcraft/Zone.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/Zone.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -78,6 +78,8 @@ return false; if ((maps_[MapKind_String] = allocateMap(MapKind_String, sizeof(Map))) == NULL) return false; + if ((maps_[MapKind_Native] = allocateMap(MapKind_Native, sizeof(Map))) == NULL) + return false; if ((maps_[MapKind_FreeRegion] = allocateMap(MapKind_FreeRegion, sizeof(Map))) == NULL) return false; diff -r b765a6383849 -r b40b0e18a918 jitcraft/main.cpp --- a/jitcraft/main.cpp Mon Jul 09 22:25:52 2012 -0700 +++ b/jitcraft/main.cpp Tue Jul 10 02:11:13 2012 -0700 @@ -61,6 +61,16 @@ } #endif +intptr_t PrintToServer(void *blah, intptr_t *params) +{ + assert(params[0] == 3); + const char *fmt = reinterpret_cast(params[1]); + const char *str = reinterpret_cast(params[2]); + int *d = reinterpret_cast(params[3]); + printf(fmt, str, *d); + return 16; +} + int main(int argc, char **argv) { if (argc < 2) { @@ -76,7 +86,14 @@ if (!module) return 1; - + Ke::Local native = module->getNative(0); + native->bind(reinterpret_cast(PrintToServer)); + module->setNativeIdentity(reinterpret_cast(5)); + + int result; + Ke::Local fun = module->getPublic(0); + if (Ke::Keima::Invoke(fun, NULL, 0, &result)) + printf("%d\n", result); } Ke::Keima::Shutdown();