On 4/24/2012 5:33 AM, BartC wrote:
> "BGB" <
cr8...@hotmail.com> wrote in message
> news:jn4jb7$si6$1...@news.albasani.net...
>
>> var a; //generic variable, will still (normally) use dynamic types
>> var i:int; //will now use a boxed integer
>> var j:long; //likewise, uses a boxed integer
>> var f:double; //and this uses a boxed double
>
> These look like what I call 'type-hints' in my dynamic language. I've tried
> adding these several times, and it always gets very untidy. They work
> though, doubling performance usually.
I more often call them type annotations.
as-is, they are not really "hints", as a hint would imply "this variable
should only hold this", but with the language making little or no
attempt to enforce this. some earlier versions of BGBScript were more
this way (meaning that messing up with an annotation was a good way to
get strange results and/or crashes, this being the case with the
2006-era VM).
in my case, the current behavior is more like in C:
the variable *will* hold this type, and attempting to store a value into
such a variable will result in it being coerced. currently, this is
handled be the front-end, which would emit a "CAST" opcode if needed
(more because I didn't want to put the logic in the execution path).
currently though, there is no checking for the case of "well, this is
obviously wrong, so complain about it", but I could do this eventually
(the frontend currently assumes it can cast anything to anything).
with the new type logic though, it is possible I could make it a bit
more specific (warning about down-casts, and storing a non-number type
into a numeric variable, ...).
also possible would have been to go the Java route:
types don't match *exactly*, so treat it as an error.
personally I don't like this, as I don't like having to type extra casts
simply because the compiler is nit-picky.
> But, I've also been working on a conventional compiler for a statically
> typed language (for one main purpose: to implement an interpreter and
> runtime for the dynamic one), and I think I will try and combine the two
> languages: static and dynamic typing together. (And have the choice of
> statically compiled - necessary for standalone executables - and
> interpreted
> bytecode.)
>
most of the native code is currently written in C.
there are a few bits and pieces of dynamically-generated assembler in
the mix, but for the most part it is C.
most of what library code exists is written in BGBScript and just sort
of hackishly shoved into the DLL (admittedly, currently in source-form).
there is not a lot of this code, as "write a big comprehensive class
library" hasn't really been a huge priority.
in this way, the VM is sort of the inverse of the JVM: relatively little
work is currently actually done in the language itself, as it mostly
just passes most stuff off to C code.
and, "well, the standard library is currently only about 4 kloc of BS
code, much of which itself hasn't really been tested" doesn't sound all
that impressive.
> I've attempted this a few times, and always failed (there are some other
> subtle differences to sort out too). However I'm more
> confident this time... The language would need to cope with the following
> main types:
>
> * Primitives (like in C)
> * Fixed bound data (arrays, strings, etc) (like in C, with a few extras)
> * Flex data (again, arrays, strings, etc) (not like C...)
> * Variant types
>
> (Variant types are challenging within a strictly typed language, because
> they break all the rules. For example, to do P^ (dereference P), then
> usually P has to have a type of 'pointer to T', and the type of P^ will be
> T. But with variants, P can also be a variant type, and P^ will also have a
> variant type!)
>
in my case, variant is a special type case in itself, or more like, it
is "the general case", with most other things being subsets.
previously, this meant opcodes like:
ADD //add, generic
ADD_FN //add, fixnum
ADD_FL //add, flonum
these were not strict in recent VMs, due to technical reasons, and
infact more indicated the "range" of the type than the exact encoding
(IOW: "fixnum" ops would mean, "assume int32 range and optimize for the
fixnum case").
but, now with:
ADD_XI, ADD_XL, ...
it becomes a bit more strict, since these types assume a specific
representation and will not type-check.
>> [aside:
>> thing is, my opcode count is already a little "absurd" compared with many
>> other VMs, grr... much of this is due to a lot of "compound special case
>> ops", most related either to interpreter performance, language semantics,
>> or things which seemed more convenient to add as opcodes than to rig up
>> logic for "magic built-in methods" or similar (things like "obj.clone()"
>> and "obj.toString()" are opcodes...).
>
> (I've reduced the opcode count in my bytecode, by breaking the link between
> a specific bytecode, and a specific handler to execute that opcode. That
> means there are only dozens of bytecodes, but hundreds of handlers (and
> lots
> of metadata around to link them up using automatic tools).
>
> For example, the bytecode instruction:
>
> push p
>
> will invoke different handlers, depending on whether p represents a static
> variable, a frame variable, an integer constant, etc.
I had previously wanted to do it more like:
LOAD i //load int
LOAD j //load int
ADD //notes: int+int->int
DUP //notes: int->int,int
MUL //notes: int*int->int
but was feeling too lazy in the case of the interpreter to rig up the
logic for modeling type-propagation in the stack (this is something done
in most of my past JITs though, and is much more troublesome apart from
either directly executing the instructions, or using per-opcode logic
which directly simulates the stack operations).
it seemed like much less effort to just start spitting out type-specific
opcodes, since the front-end generally already knows most of this stuff.
> In the case of arithmetic ops, the operator code is one of the operands:
>
> binop op
>
> These get fixed up automatically, at loadtime, with a specific handler
> ('add' for example) if one exists, otherwise to a more generic one. So
> the advantages of having dedicated bytecodes is retained, without
> needing to manage hundreds of them.)
>
there is a similar opcode in my case ("BINARY"), but it has ended up
mostly relegated to rarely used operations.
this was partly because, until fairly recently, the interpreter used a
giant "switch()" block, and so using more very-specific (typically
compound) opcodes meant higher performance (and had a more significant
performance impact, due to needing to decode the argument, invoke a
secondary "switch()", ...).
now, compound opcodes still have an advantage, but there is a little
less pressure for compact opcodes (and many of the newer opcodes are
2-byte opcodes anyways).
I just recently added a few type-hint prefix opcodes:
PF_HINT_XI //integer hint
PF_HINT_XL //long hint
PF_HINT_XF //float hint
PF_HINT_XD //double hint
PF_HINT_S //signature hint
which may be used for many of the more "general" opcodes (such as array
loads/stores, field loads/stores, ...).
I wanted these as single-byte opcodes, and (annoyingly) ended up moving
several opcodes out of the single-byte range to make space (two for
multidimensional array access, and 4 for 3-element EXCH forms, all of
which are rarely used, whereas type-hinting is likely to be a bit more
common, so I felt it justified).
FWIW, there is also a "PF_WIDE" opcode, intended mostly to allow 32-bit
jumps (most other jumps are 16-bits). note that this is unnecessary for
index variables (variable-indices, literal-table / constant-pool
entries, ...) which use variable-length values (just, using a VLI for a
jump is "problematic").
this leaves the pile of new type-specialized opcodes at:
arithmetic ops (the bulk of the new opcodes);
type-conversion ops;
compare-ops (1);
compare-and-jump ops (1);
lexical-variable load/store ops.
the BGBScript VM does compares differently than the JVM does.
1. the JVM typically had typed-compare operations, which returned -1,0,1
depending on the results, and would execute (generic) conditional
operations based on this (using ==0, !=0, ==1, ==-1, >=0, <=0).
in my case, the compare ops checked for a specific condition (equal,
not-equal, less-than, greater-than, less-equal, greater-equal), and
would return a boolean.
the "compare-and-jump" opcodes worked similar, doing a typed compare and
jump as a single opcode.
with typed forms, this essentially results in a much larger number of
opcodes related to compares and jumps (approx 48 for the new ILFD ops,
24 for compare, and 24 more for compare-and-jump).
the VM has a number of other complex conditional jump forms which were
not type-specialized, but the possibility of using type-hint prefixes
seems possible.
example:
PF_HINT_XI //integer hint
JMP_GE_FNC 24, foo //if(value>=24) goto foo
which would require 6 bytes.
although, this does render the 'FN' part of the opcode name meaningless.
(well, it is that, or wasting an extra operation to push an integer on
the stack to use "JMP_GE_XI", or using 12 more opcodes for things like
"JMP_GE_XIC" and "JMP_LT_XLC" or similar...).
or such...