"Leopold Toetsch" <[EMAIL PROTECTED]> wrote:
Depending on the arch (32 vs 64 bits) one of these opcodes is suboptimal. With a new "L" (Long) register type the functionality could be handled transparently:

  $L0 = pio.'tell'()


Yes, but as you add more register types you get a combinatorial blow-up on various opcodes.

This depends on the implementation of 'opcodes'. With the current scheme any such extension isn't really implementable because of the combinatorial opcode explosion. I've written a (still internal) proposal that would prevent the combinatorial issue. It (or some similar thing) would be indeed necessary to even think about more register types like int8, int16, int32/int64 or float32.

Iff we want those register types. .NET is interesting in that it recognises int8, int16 etc as fundemental types, but on the stack only recognizes int32, int64 and native int (out of the integer types anyway). If you want to have an int8 then you just do the ops in an int32 and then use a conv.i1 instruction. I think the wisdom here is, where do we actually really need to support int8. And it's in arrays that it matters most for example, for size reasons.

My understanding was that "I" registers were native integers so you could get good performance, and you used a PMC if you wanted some guarantees about what size you were talking about.

In the long run, we certainly don't want to use PMCs for e.g. immplementing bytes (int8) or some such for performance reasons. 'long long' aka int64 is usually supported by compilers and has for sure by far better performanche than a BigInt PMC. Actually using 'int8' or 'float32' is usually only important, if you have huge arrays of these. That means that there's a very limited need for opcodes using these types, just some basic math mainly and array fetch/store. Or in other words: what is supported by the hardware CPU.

If it's just arrays, then we can provide a (Fixed|Resizable)ByteArray PMC, etc. I don't think we need any instructions to specially handle doing 8-bit arithmetic. Maybe we want something to truncate a 32-bit to an 8-bit etc, maybe throwing an exception on overflow.

The register allocator would map 'L0' either to a pair (I0, I1) on 32 bit arch or just to 'I0' on 64 bit arch.
Yes, but surely it becomes somewhat more than just a mapping problem? For example, what do we do about:

add L0, L1, L2
mul L0, L1, L2

I don't see any problem with above code.

The register mapping rules would be something like:
- Lx occupies registers I(2x, 2x+1) - this is compile time,
that is 'L1' prevents 'I2' and 'I3' from being assigned by the register allocator
- the runtime mapping isn't portable due to endianess and sizeof types
  'L1' might be 'I1' on 64-bit arch or (I2,I3) or (I3,I2) on 32-bit arch
Yup, and I really, really don't like the idea of making our bytecode format non-portable. Part of the point of having a VM is portability, right?

- if you write PASM, overlapping Ix/Ly may cause warnings or errors, but could be used in a non-portable way, if you know what you are doing on a specific platform.

You still didn't address my question with these points, though.

mul L0, L1, L2

Isn't just a case of churning out something like:-

mul I0, I2, I4
mul I1, I3, I5

So it's not just so simple as a "map 1 L to 2 Is" problem.

Jonathan

Reply via email to