User:Pedro Oval/Mono code memory usage/CIL

From Second Life Wiki
< User:Pedro Oval‎ | Mono code memory usage
Revision as of 17:41, 30 May 2014 by Pedro Oval (talk | contribs) (Bytecode not included, so mentioning the assembler is out of place. Other fixes.)
Jump to navigation Jump to search

Here's the CIL code generated by the script below, which gives some insight of what's happening under the hood.

Compilation was done using the viewer's built-in compiler as suggested by User:Becky Pippen/LSL Performance. Script that gets compiled:

<lsl> integer i; u(){}

default {

   state_entry()
   {
       integer x;
       integer y = 0;
       integer z;
       float f;
       float g;
       vector v;
       list a;
       ;;;;;;;;;{{{{{{{{}}}}}}}}
       return;
       x;
       (x);
       (integer)x;
       (float)x;
       f;
       (float)f;
       (integer)f;
       v;
       v.z;
       -x;
       ~x;
       !x;
       --x;
       ++x;
       x--;
       x++;
       x=y;
       x==y;
       x=y=z;
       f=x;
       f=(float)x;
       x!=y;
       x+y;
       x-y;
       x+-y;
       x*y;
       x/y;
       x%y;
       x&y;
       x&&y;
       x|y;
       x||y;
       x^y;
       x<<y;
       x>>y;
       x<y;
       x>y;
       x<=y;
       x>=y;
       x+=y;
       x-=y;
       x+=-y;
       x*=y;
       x/=y;
       x%=y;
       if (x) ;
       if (x) ; else ;
       0;
       x^x;
       1;
       -1;
       0xffffffff;
       ALL_SIDES;
       x|~x;
       x+1;
       -~x;
       x-1;
       x+-1;
       ~-x;
       x*y+y-1;
       (x+1)*y-1;
       ~(~x*y)
       while (x) ;
       do ; while (x);
       for (;x;) ;
       @label; if (x) jump label;
       0.0;
       f=0.0;
       f=0;
       <0.0, 0.0, 0.0>;
       ZERO_VECTOR;
       <0, 0, 0>;
       <-1.0, -1.0, 0.0>;
       TOUCH_INVALID_TEXCOORD;
       <-1, -1, 0>;
       <0xffffffff, 0xffffffff, 0>;
       v+v;
       v-v;
       v*v;
       v%v;
       [];
       a=[];
       a+[x];
       a+x;
       [x]+a;
       x+a;
       [x];
       []+x;
       (list)x;
       [x,y];
       []+x+y;
       (string)a;
       i;
       u();
   }

} </lsl>

Compiled and annotated result:

.assembly extern mscorlib {.ver 1:0:5000:0}
.assembly extern LslLibrary {.ver 0:1:0:0}
.assembly extern LslUserScript {.ver 0:1:0:0}
.assembly extern ScriptTypes {.ver 0:1:0:0}
.assembly 'LSL_y' {.ver 0:0:0:0}
.class public auto ansi serializable beforefieldinit LSL_y extends [LslUserScript]LindenLab.SecondLife.LslUserScript
{
  .field public int32 'i'
  .method public hidebysig  specialname  rtspecialname instance default void .ctor ()  cil managed
  {
    .maxstack 500
    // Globals:
    // integer i;
    ldarg.0
    ldc.i4.0
    stfld int32 LSL_y::'i'

    ldarg.0
    call instance void [LslUserScript]LindenLab.SecondLife.LslUserScript::.ctor()
    ret
  }
  .method public hidebysig instance default void 'gu'() cil managed
  {
    .maxstack 500
    ret
  }


  .method public hidebysig instance default void edefaultstate_entry() cil managed
  {
    .maxstack 500
    .locals init (int32, int32, int32, float32, float32, class [ScriptTypes]LindenLab.SecondLife.Vector, class [mscorlib]System.Collections.ArrayList)
    // integer x;
    ldc.i4.0
    stloc.s 0
    // integer y;
    ldc.i4 0
    stloc.s 1
    // integer z;
    ldc.i4.0
    stloc.s 2
    // float f;
    ldc.r8 0
    stloc.s 3
    // float g;
    ldc.r8 0
    stloc.s 4
    // vector v;
    ldc.r8 0
    ldc.r8 0
    ldc.r8 0
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    stloc.s 5
    // list a;
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    stloc.s 6

    // ;;; {{{ }}} generate no code

    // return;
    ret

    // x;
    ldloc.s 0
    pop

    // (x);
    ldloc.s 0
    pop

    // (integer)x;
    ldloc.s 0
    pop

    // (float)x;
    ldloc.s 0
    conv.r8
    pop

    // f;
    ldloc.s 3
    pop

    // (float)f;
    ldloc.s 3
    pop

    // (integer)f;
    ldloc.s 3
    call int32 [LslLibrary]LindenLab.SecondLife.LslRunTime::ToInteger(float32)
    pop

    // v;
    ldloc.s 5
    pop

    // v.z;
    ldloca.s 5
    ldfld float32 class [ScriptTypes]LindenLab.SecondLife.Vector::z
    pop

    // -x;
    ldloc.s 0
    neg
    pop

    // ~x;
    ldloc.s 0
    not
    pop

    // !x;
    ldloc.s 0
    ldc.i4.0
    ceq
    pop

    // --x;
    ldloc.s 0
    ldc.i4.1
    sub
    dup
    stloc.s 0
    pop

    // ++x;
    ldloc.s 0
    ldc.i4.1
    add
    dup
    stloc.s 0
    pop

    // x--;
    ldloc.s 0
    ldloc.s 0
    ldc.i4.1
    sub
    dup
    stloc.s 0
    pop
    pop

    // x++;
    ldloc.s 0
    ldloc.s 0
    ldc.i4.1
    add
    dup
    stloc.s 0
    pop
    pop

    // x = y;
    ldloc.s 1
    dup
    stloc.s 0
    pop

    // x == y;
    ldloc.s 1
    ldloc.s 0
    ceq
    pop

    // x = y = z;
    ldloc.s 2
    dup
    stloc.s 1
    dup
    stloc.s 0
    pop

    // f = x;
    ldloc.s 0
    conv.r8
    dup
    stloc.s 3
    pop

    // f = (float)x;
    ldloc.s 0
    conv.r8
    dup
    stloc.s 3
    pop

    // x != y; implemented as: !(x==y)
    ldloc.s 1  // this part is x == y
    ldloc.s 0
    ceq
    ldc.i4.0   // this part is 'not'
    ceq
    pop

    // x + y;
    ldloc.s 1
    ldloc.s 0
    add
    pop

    // x - y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Subtract(int32, int32)
    pop

    // x + -y;
    ldloc.s 1
    neg
    ldloc.s 0
    add
    pop

    // x * y;
    ldloc.s 1
    ldloc.s 0
    mul
    pop

    // x / y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Divide(int32, int32)
    pop

    // x % y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Modulo(int32, int32)
    pop

    // x & y;
    ldloc.s 1
    ldloc.s 0
    and
    pop

    // x && y; implemented as: !(!x | !y)
    ldloc.s 1    // !x
    ldc.i4.0
    ceq
    ldloc.s 0    // !y
    ldc.i4.0
    ceq
    or           // !x | !y
    ldc.i4.0     // !(!x | !y)
    ceq
    pop

    // x | y;
    ldloc.s 1
    ldloc.s 0
    or
    pop

    // x || y; implemented as: !!(x | y)
    ldloc.s 1    // x | y
    ldloc.s 0
    or
    ldc.i4.0     // !(x | y)
    ceq
    ldc.i4.0     // !!(x | y)
    ceq
    pop

    // x ^ y;
    ldloc.s 1
    ldloc.s 0
    xor
    pop

    // x << y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::ShiftLeft(int32, int32)
    pop

    // x >> y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::ShiftRight(int32, int32)
    pop

    // x < y; implemented as: y > x
    ldloc.s 1
    ldloc.s 0
    cgt
    pop

    // x > y; implemented as: y < x;
    ldloc.s 1
    ldloc.s 0
    clt
    pop

    // x <= y; implemented as: !(y < x)
    ldloc.s 1
    ldloc.s 0
    clt
    ldc.i4.0
    ceq
    pop

    // x >= y; implemented as: !(y > x)
    ldloc.s 1
    ldloc.s 0
    cgt
    ldc.i4.0
    ceq
    pop

    // x += y;
    ldloc.s 1
    ldloc.s 0
    add
    dup
    stloc.s 0
    pop

    // x -= y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Subtract(int32, int32)
    dup
    stloc.s 0
    pop

    // x += -y;
    ldloc.s 1
    neg
    ldloc.s 0
    add
    dup
    stloc.s 0
    pop

    // x *= y;
    ldloc.s 1
    ldloc.s 0
    mul
    dup
    stloc.s 0
    pop

    // x /= y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Divide(int32, int32)
    dup
    stloc.s 0
    pop

    // x %= y;
    ldloc.s 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Modulo(int32, int32)
    dup
    stloc.s 0
    pop

    // if (x) ;
    ldloc.s 0
    brfalse LabelTempJump0
    // THEN branch code would go here
LabelTempJump0:

    // if (x) ; else ;
    ldloc.s 0
    brfalse LabelTempJump1
    // THEN branch code would go here
    br LabelTempJump2
LabelTempJump1:
    // ELSE branch code would go here
LabelTempJump2:

    // 0;
    ldc.i4 0
    pop

    // x ^ x;
    ldloc.s 0
    ldloc.s 0
    xor
    pop

    // 1;
    ldc.i4 1
    pop

    // -1; note the negative sign takes code
    ldc.i4 1
    neg
    pop

    // 0xffffffff; note it equals -1 without taking memory
    ldc.i4 -1
    pop

    // ALL_SIDES; ditto
    ldc.i4 -1
    pop

    // x | ~x;
    ldloc.s 0
    not
    ldloc.s 0
    or
    pop

    // x + 1;
    ldc.i4 1
    ldloc.s 0
    add
    pop

    // -~x;
    ldloc.s 0
    not
    neg
    pop

    // x - 1;
    ldc.i4 1
    ldloc.s 0
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Subtract(int32, int32)
    pop

    // x + -1;
    ldc.i4 1
    neg
    ldloc.s 0
    add
    pop

    // ~-x;
    ldloc.s 0
    neg
    not
    pop

    // x*y + y - 1;
    ldc.i4 1
    ldloc.s 1
    ldloc.s 1
    ldloc.s 0
    mul
    add
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Subtract(int32, int32)
    pop

    // (x + 1)*y - 1;
    ldc.i4 1
    ldloc.s 1
    ldc.i4 1
    ldloc.s 0
    add
    mul
    call int32 [LslUserScript]LindenLab.SecondLife.LslUserScript::Subtract(int32, int32)
    pop

    // ~(~x*y);
    ldloc.s 1
    ldloc.s 0
    not
    mul
    not
    pop

    // while (x) ;
LabelTempJump3:
    ldloc.s 0
    brfalse LabelTempJump4
    br LabelTempJump3
LabelTempJump4:

    // do ; while (x);
LabelTempJump5:
    ldloc.s 0
    brtrue LabelTempJump5

    // for (; x; ) ;
LabelTempJump6:
    ldloc.s 0
    brfalse LabelTempJump7
    br LabelTempJump6
LabelTempJump7:

    // @label; if (x) jump label;
'label':
    ldloc.s 0
    brfalse LabelTempJump8
    br 'label'
LabelTempJump8:

    // 0.0;
    ldc.r8 (00 00 00 00 00 00 00 00)
    pop

    // f = 0.0;
    ldc.r8 (00 00 00 00 00 00 00 00)
    dup
    stloc.s 3
    pop

    // f = 0;
    ldc.i4 0
    conv.r8
    dup
    stloc.s 3
    pop

    // <0.0, 0.0, 0.0>;
    ldc.r8 (00 00 00 00 00 00 00 00)
    ldc.r8 (00 00 00 00 00 00 00 00)
    ldc.r8 (00 00 00 00 00 00 00 00)
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // ZERO_VECTOR;
    ldc.r8 (00 00 00 00 00 00 00 00)
    ldc.r8 (00 00 00 00 00 00 00 00)
    ldc.r8 (00 00 00 00 00 00 00 00)
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // <0, 0, 0>;
    ldc.i4 0
    conv.r8
    ldc.i4 0
    conv.r8
    ldc.i4 0
    conv.r8
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // <-1.0, -1.0, 0.0>;
    ldc.r8 (00 00 00 00 00 00 f0 3f)
    neg
    ldc.r8 (00 00 00 00 00 00 f0 3f)
    neg
    ldc.r8 (00 00 00 00 00 00 00 00)
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // TOUCH_INVALID_TEXCOORD;
    ldc.r8 (00 00 00 00 00 00 f0 bf)
    ldc.r8 (00 00 00 00 00 00 f0 bf)
    ldc.r8 (00 00 00 00 00 00 00 00)
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // <-1, -1, 0>;
    ldc.i4 1
    neg
    conv.r8
    ldc.i4 1
    neg
    conv.r8
    ldc.i4 0
    conv.r8
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // <0xffffffff, 0xffffffff, 0>;
    ldc.i4 -1
    conv.r8
    ldc.i4 -1
    conv.r8
    ldc.i4 0
    conv.r8
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'CreateVector'(float32, float32, float32)
    pop

    // v + v;
    ldloc.s 5
    ldloc.s 5
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'Add'(class [ScriptTypes]LindenLab.SecondLife.Vector, class [ScriptTypes]LindenLab.SecondLife.Vector)
    pop

    // v - v;
    ldloc.s 5
    ldloc.s 5
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'Subtract'(class [ScriptTypes]LindenLab.SecondLife.Vector, class [ScriptTypes]LindenLab.SecondLife.Vector)
    pop

    // v * v;
    ldloc.s 5
    ldloc.s 5
    call float32 class [LslUserScript]LindenLab.SecondLife.LslUserScript::'Multiply'(class [ScriptTypes]LindenLab.SecondLife.Vector, class [ScriptTypes]LindenLab.SecondLife.Vector)
    pop

    // v % v;
    ldloc.s 5
    ldloc.s 5
    call class [ScriptTypes]LindenLab.SecondLife.Vector class [LslUserScript]LindenLab.SecondLife.LslUserScript::'Modulo'(class [ScriptTypes]LindenLab.SecondLife.Vector, class [ScriptTypes]LindenLab.SecondLife.Vector)
    pop

    // [];
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    pop

    // a = [];
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    dup
    stloc.s 6
    pop

    // a + [x];
    ldloc.s 0
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(object, class [mscorlib]System.Collections.ArrayList)
    ldloc.s 6
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(class [mscorlib]System.Collections.ArrayList, class [mscorlib]System.Collections.ArrayList)
    pop

    // a + x;
    ldloc.s 0
    ldloc.s 6
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(int32, class [mscorlib]System.Collections.ArrayList)
    pop

    // [x] + a;
    ldloc.s 6
    ldloc.s 0
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(object, class [mscorlib]System.Collections.ArrayList)
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(class [mscorlib]System.Collections.ArrayList, class [mscorlib]System.Collections.ArrayList)
    pop

    // x + a;
    ldloc.s 6
    ldloc.s 0
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(class [mscorlib]System.Collections.ArrayList, object)
    pop

    // [x];
    ldloc.s 0
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(object, class [mscorlib]System.Collections.ArrayList)
    pop

    // []+x;
    ldloc.s 0
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(int32, class [mscorlib]System.Collections.ArrayList)
    pop

    // (list)x;
    ldloc.s 0
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList(object)
    pop

    // [x, y];
    ldloc.s 0
    box [mscorlib]System.Int32
    ldloc.s 1
    box [mscorlib]System.Int32
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(object, class [mscorlib]System.Collections.ArrayList)
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Prepend(object, class [mscorlib]System.Collections.ArrayList)
    pop

    // []+x+y;
    ldloc.s 1
    ldloc.s 0
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::CreateList()
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(int32, class [mscorlib]System.Collections.ArrayList)
    call class [mscorlib]System.Collections.ArrayList class [LslUserScript]LindenLab.SecondLife.LslUserScript::Append(int32, class [mscorlib]System.Collections.ArrayList)
    pop

    // (string)a;
    ldloc.s 6
    call string [LslLibrary]LindenLab.SecondLife.LslRunTime::ListToString(class [mscorlib]System.Collections.ArrayList)
    pop

    // i; (global).
    ldarg.0
    ldfld int32 LSL_y::'i'
    pop

    // u();
    ldarg.0
    call instance void class LSL_y::'gu'()

    // end of event
    ret
  }

}

Important notes:

  • ldloc.s 0 (opcode 11, argument 00) is probably contracted to the abbreviation ldloc.0 (opcode 06) at assembly time, resulting in the length of 2 obtained in the tests. Or possibly the server-side compiler added an optimization of this case (!). Either way, the evidence for this is confirmed by the fact that x; takes 2 bytes if it's one of the first four local variables (there are 1-byte opcodes for ldloc.0 through ldloc.3), and 3 bytes otherwise. This applies to all appearances of this opcode and the similar stloc.s.
  • Local variable definitions seem to take a variable and big number of bytes (averaging about 47 bytes for local integers) if there is a function call anywhere in the event where they are defined. Reason unknown.
  • Backward jumps seem to take many more bytes than what the CIL result above suggests. Forward jumps, however, seem predictable enough.
  • As things are now, having more than 256 local variables causes wraparound, making the 257th be an alias to the 1st, and so on. If they aren't the same type, an exception is raised.
  • Global variable and function names do take code memory.