User:Pedro Oval/Mono code memory usage/CIL: Difference between revisions
Jump to navigation
Jump to search
Pedro Oval (talk | contribs) All at once |
Pedro Oval (talk | contribs) m Bytecode not included, so mentioning the assembler is out of place. Other fixes. |
||
| Line 1: | Line 1: | ||
Here's the CIL code generated by the script below, which gives some insight of what's happening under the hood. | 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]]. | Compilation was done using the viewer's built-in compiler as suggested by [[User:Becky Pippen/LSL Performance]]. | ||
Script that gets compiled: | Script that gets compiled: | ||
| Line 823: | Line 822: | ||
'''Important notes:''' | '''Important notes:''' | ||
* <code>ldloc.s 0</code> (opcode 11, argument 00) is probably contracted to the abbreviation <code>ldloc.0</code> (opcode 06) at assembly time, resulting in the length of 2 obtained in the [[User:Pedro Oval/Mono code memory usage|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 <code>x;</code> takes 2 bytes if it's one of the first four 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 <code>stloc.s</code>. | * <code>ldloc.s 0</code> (opcode 11, argument 00) is probably contracted to the abbreviation <code>ldloc.0</code> (opcode 06) at assembly time, resulting in the length of 2 obtained in the [[User:Pedro Oval/Mono code memory usage|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 <code>x;</code> 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 <code>stloc.s</code>. | ||
* 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. | * 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. | ||
* As things are now, having more than 256 local variables causes wraparound, making the 257th be an alias to the 1st, and so on. | * 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. | * Global variable and function names do take code memory. | ||
Revision as of 16:41, 30 May 2014
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 abbreviationldloc.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 thatx;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 similarstloc.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.