Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 16 Sep 2004
Offline Last Active Dec 29 2008 11:35 AM

#3668643 Emmiting assembly for calling member functions

Posted by on 05 July 2006 - 04:35 AM

Original post by HellRaiZer
The way i'm handling the above piece of code now, is this:

1) I have an identifier followed by period ('.'). Check to see if it's an object. It is.
1.1) In case to be able to do anything on it (access its members) i have to move it somewhere. Move it to _this, so i know where to search for its members.

'.' is only an expression saying: add an offset to given address. It can be treated same as '+'.
is nothing more than
obj + offset1 + offset2 + offset3

Original post by HellRaiZer
1.2) After this step, previous _this is on the stack, and _this now holds 'object'.

Remove that completely.

Original post by HellRaiZer
2) Check to see if the identifier following the period is a function or a variable. If it is a variable go to 1. Else go to 3.

And here (at function call) you can finally begin thinking about the computed address as an 'object'.

Original post by HellRaiZer
3) We reached the actual function call. For every argument, calculate the expression using the stack and leave the result on the stack.

Remember, that in following sample:
obj1.memb1[expr1].func1( expr2, expr3 )
expressions are:
expr1, expr2, expr3
Don't forget about the expr1!

Original post by HellRaiZer
4) At this point the stack has arguments pushed, _this pointer points to member2 (the last variable), so call the function.

And here the point you should start calcuating the actual 'this' pointer, at the last possible moment.

Original post by HellRaiZer
If i understood what you suggested correctly, i have to first find the actual function call, assemble all expressions (function arguments) and then change _this to point to member2?
Is this correct?

Not quite. It is best shown on a "sample":
obj1.memb1.memb2[expr1].func1( expr2, expr3 )

1. calculate and push on the stack:
expr3, expr2, expr1 (in that order).
Watch, that the whole "sample" is a perfectly good expression, so expr2, for example should be calculated with exactly the same strategy.
2. calculate 'member operator' ('.')
3. calculate 'member operator' ('.')
4. calculate 'element operator' ('[]') using expr1 (yes, we know where it is, since we calculated it last)
5. call the function (you can move the result of previous calculation to _this pointer if you like, or just push it on the stack, just like c++ does)

So, first all the sub-expressions (recursively, in (reversed) order that you'd like to use them later), then the main expression (from left to right, just like you'd calculate "a+b+c+d"). Note that with such strategy, you'll never use "this" pointer before is is necessary to call a function.

Uff, gosh, I know this is messy, I'll check on my old compiler later, to see how I exactly did things.

#3668325 Emmiting assembly for calling member functions

Posted by on 04 July 2006 - 11:15 PM

Original post by HellRaiZer
The problem starts from the AST representation of the member function call. When emitting assembly code for a call, the first thing that is visible is the object on which we want to call a function. This means that i have to somehow
set the _this register to point to the correct object, before assembling the function argument expressions. This is correct from one point of view, because if i was doing it the other way (first assembling function arguments, and then setting _this register to point to the correct object) i would have pushed too many unnessecary things on stack.

So the only reason you're going with AST is optimization, is that correct?

I've written two compilers currently, and I was always caling functions the "other way" - first all arguments, in reversed order, then "this", then call (possibly virtual). There are numerous reasons things can go wrong if you're not calling this way. For example: in my language, object pointed to by "this" might have been replaced by one of the subfunctions, so by the point of the call, value from saved _this might have been invalid (crash!), or simply wrong virtual function might be called.

A* g_a = new A;
function int changeA(A* a)
g_a = a;
return 6;
g_a->func1( 7, changeA(new B), 18 ); // g_a has to be accessed last.

Having said that, I guess that it is unlikely for this to matter in your scripting language. But you've got to think about logical consequences of optimizations, before performing them.

Ok, more on the topic:
If you've decided to make _this register the only one (besides _stack) that can do memory accesses, then you're stuck with constantly swapping _this. If you really want do do optimizations, make other registers able to do such magic.

mov _this, _stack[myVariable0 + 0]; // myVariable0
mov _reg_0, _this[someMember + 0];
mov _this, _stack[myVariable1 + 0];
mov _reg_1, _this[someMember + 0];
add _reg_0, _reg_1;
mov _this, _stack[myVariable0 + 0]; // myVariable0 again!
mov _reg_1, _this[someOtherMember + 0];
add _reg_0, _reg_1;
// now: let _reg_2 also access memory
mov _this, _stack[myVariable0 + 0]; // myVariable0
mov _reg_0, _this[someMember + 0];
mov _reg_2, _stack[myVariable1 + 0];
mov _reg_1, _reg_2[someMember + 0];
add _reg_0, _reg_1;
//mov _this, _stack[myVariable0 + 0]; // myVariable0 again? not needed!
mov _reg_1, _this[someOtherMember + 0];
add _reg_0, _reg_1;

Of course, with this you do not need to have _this register at all, just as Trap suggested.

Offtopic: 666th post on teh forums!

[Edited by - deffer on July 5, 2006 8:15:24 AM]