Pliant talk forum

Pliant talk forum

Discussion: Pliant bytecode

Portability to computers powered by non i386 based processors
can easily be achieved through introducing bytecode based
execution model to Pliant.
Message posted by hubert.tonneau on 2003/02/10 23:16:55
Since we already have clean operating system independant layer, introducing
a bytecode execution machinery to Pliant would be an easy way to bring
complete portability to Pliant.

I now see the GCC interface as the way to improve speed for some functions
that could be used on top of the bycode execution engine, just as
it is on top of the built i386 code generator.

The problem with bytecode execution is that it's slow. Here is an implementation
prototype preposal which is the lightest (so the fastest) I could imagine.
Feel free to comment or suggest improvements that could reduce the bytecode
speed cost.

struct Processor {
  Int registers[32]; };
// stack pointer is register 0

typedef Byte *(*ByteCodeInstruction)(Byte *code,struct Processor *env);

static ByteCodeInstruction bytecode_instructions[256];

void bytecode_execute(Byte *code,struct Processor *env) {
    code=(bytecode_instructions[*code])(code,env); }

// return
Byte *bc_return(Byte *code,struct Processor *env) {
  return null; }

// call bytecode routine
Byte *bc_call(Byte *code,struct Processor *env) {
  bytecode_execute(*(Address *)(code+1),env);
  return code+1+sizeof(Address); }

// if Ri jump
Byte *bc_jumpif(Byte *code,struct Processor *env) {
    return *(Address *)(code+2);
  return code+2+sizeof(Address); }

// Ri := cst
Byte *bc_immediat(Byte *code,struct Processor *env) {
  env->registers[code[1]]=*(Int *)(code+2);
  return code+2+sizeof(Int); }

// Ri := Rj+cst map Int
Byte *bc_load(Byte *code,struct Processor *env) {
  env->registers[code[1]]=*(int *)(env->registers[code[2]]+*(Int *)(code+3));
  return code+3+sizeof(Int); }

// Rj+cst map Int := Ri
Byte *bc_store(Byte *code,struct Processor *env) {
  *(int *)(env->registers[code[2]]+*(Int *)(code+3))=env->registers[code[1]];
  return code+3+sizeof(Int); }

// Ri := Rj + Rk
Byte *bc_add(Byte *code,struct Processor *env) {
  return code+4; }

// Ri := f Rj
Byte *bc_call1(Byte *code,struct Processor *env) {
  env->registers[code[1]]=( (Int (*)(Int)) *(Address *)(code+3) )(env->registers[code[2]]);
  return code+3+sizeof(Address); }

// Ri := f Rj Rk
Byte *bc_call2(Byte *code,struct Processor *env) {
  env->registers[code[1]]=( (Int (*)(Int,Int)) *(Address *)(code+4) )(env->registers[code[2]],env->registers[code[3]]);
  return code+4+sizeof(Address); }

void bytecode_setup() {
  bytecode_instructions[8]=bc_call2; }

Message posted by maybe max on 2003/03/04 08:37:51

You are performing at least a function call (through a function pointer)
for each bytecode instruction.

This is SLOW.

Please get some good article about optimization of bytecode interpreters (VM), like:

and of course zillion of other articles.
Message posted by maybe Hubert Tonneau on 2003/03/04 23:56:19
Very interesting reading. Thanks for the pointer.

Also the execution model I want for the Pliant bytecode must be real processor
like, so registers based rather than stack based.
I mean that a want to have an instruction that looks like:
  register1 := register2 + register3
as opposed to:
  push operand 1
  push operand 2
  store result

The reason is that I want the Pliant bytecode to help me clean up the code
in order to ease writting native code generators for other processors.
So it will be all the more true if the bytecode machinery looks like a simple

Now about optimising though writing the function address in the code rather
than using a code op (that requires to go through a table), then inlining
several consecutive instructions though copying portions of code,
I'm not sure I want to go that far, because I prefer spending extra energy
on writing native code generators.