Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2010-08

Passing parameters through the stack to interfaces

6 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Joost van der Sluis

Hi all,

It seems like that when you call a method of an interface, a 'hidden' wrapper is generated by the compiler to adjust the 'self' parameter so that it does not refers to the interface-pointer, but the actual class-pointer.

The problem is only that the extra 'call' which is made adds one extra value to the stack, so that the parameters which are passed on the stack are all shifted. This is especially troublesome when the method is defined as cdecl and the 'self' parameter itself is also passed using the stack. (self is not on the location EBP+8 anymore, but EBP+12)

See the attached example. It will crash when 'self' is referenced within the call to the method though an interface.

I don't know what's the best way to fix this.

I see three solutions:

1: replace the 'call' in the wrapper with a 'jmp'. I've tried this and it works, but when the function returns, the 'self'-class-pointer is not converted to the interface-pointer. But I don't see why this is done in the first place?!? Other problem is that you get a somewhat strange function-name in your stacktrace. (the name of the wrapper)

2: See if we can let the caller do the
interface-pointer-to-class-pointer conversion. That way the wrapper is not necessary anymore. Leads to somewhat faster code, though less compact. But this is only possible if the caller has enough information to do the conversion. I'm not sure if that's the case.

3: Use some tricks in the wrapper to shift the stack-frame. But I don't see a proper (thread-friendly) way to do this.

4: Make the hidden wrapper a real wrapper and let it push all parameters on the stack again in the proper order. This generates a lot of overhead, offcourse.

I would say that 2 is the best option, but I'm not sure if it is maybe that option 1 is a good idea?

Joost.

Reply by email

Sergei Gorelkin
to Joost van der Sluis

Joost van der Sluis пишет:

Show quoted text 11 lines
... I don't know what's the best way to fix this. I see three solutions: 1: replace the 'call' in the wrapper with a 'jmp'. I've tried this and it works, but when the function returns, the 'self'-class-pointer is not converted to the interface-pointer. But I don't see why this is done in the first place?!? Other problem is that you get a somewhat strange function-name in your stacktrace. (the name of the wrapper)

I just tested, Delphi does it this way.

Show quoted text 6 lines
2: See if we can let the caller do the interface-pointer-to-class-pointer conversion. That way the wrapper is not necessary anymore. Leads to somewhat faster code, though less compact. But this is only possible if the caller has enough information to do the conversion. I'm not sure if that's the case.

Unfortunately, the caller cannot make any assumptions about the implementor's internal structure. The callee side may as well be a non-Pascal code.

Regards,
Sergei

Reply by email

Jonas Maebe
to Joost van der Sluis: It seems like that when you call a method of an interface, a 'hidden' wrapper is…
Joost van der Sluis wrote 6 lines
1: replace the 'call' in the wrapper with a 'jmp'. I've tried this and it works, but when the function returns, the 'self'-class-pointer is not converted to the interface-pointer. But I don't see why this is done in the first place?!?

It's described in the comments:

***
(1): The code is the following
set self parameter to correct value
call mangledname
set self parameter to interface value
ret

This is different to case (0) because in theory, the caller could reuse the data pushed on the stack so we've to return it unmodified because self is const.
***

However, at least nowadays self is a value parameter rather than a const parameter, so it shouldn't matter anymore.

Jonas

Reply by email

Joost van der Sluis
to Sergei Gorelkin: Joost van der Sluis пишет: I just tested, Delphi does it this way. Unfortunately,…
Sergei Gorelkin wrote 12 lines
Joost van der Sluis пишет: > ... > I don't know what's the best way to fix this. > 2: See if we can let the caller do the > interface-pointer-to-class-pointer conversion. That way the wrapper is > not necessary anymore. Leads to somewhat faster code, though less > compact. But this is only possible if the caller has enough information > to do the conversion. I'm not sure if that's the case. > Unfortunately, the caller cannot make any assumptions about the implementor's internal structure. The callee side may as well be a non-Pascal code.

The caller doesn't have to make any assumptions. It passes the 'self' pointer on the stack, since it knows that the callee wants that.

But I see now that this can't work. Because the 'callee' expects the interface-pointer, not the class-pointer. And we can change that for pascal-callee's, but not for others.

So it comes down on the same thing: this option can't work. I'll try option 1 as you and Jonas pointed out that shoudn't be a problem.

Joost.

Reply by email

Joost van der Sluis
to Jonas Maebe: It's described in the comments: *** (1): The code is the following set self parameter to…
Jonas Maebe wrote 22 lines
On 05 Aug 2010, at 15:18, Joost van der Sluis wrote: > 1: replace the 'call' in the wrapper with a 'jmp'. I've tried this and > it works, but when the function returns, the 'self'-class-pointer is > not > converted to the interface-pointer. But I don't see why this is done > in > the first place?!? It's described in the comments: *** (1): The code is the following set self parameter to correct value call mangledname set self parameter to interface value ret This is different to case (0) because in theory, the caller could reuse the data pushed on the stack so we've to return it unmodified because self is const. ***

I didn't found the code yet, changed the 'call' to 'jmp' by editing the assembly-file. So I didn't saw the comment.

However, at least nowadays self is a value parameter rather than a const parameter, so it shouldn't matter anymore.

Ok, I'll try if I can fix this now, thanks for the help.

Joost

Reply by email

Hans-Peter Diettrich
to Joost van der Sluis: It seems like that when you call a method of an interface, a 'hidden' wrapper is…
It seems like that when you call a method of an interface, a 'hidden' wrapper is generated by the compiler to adjust the 'self' parameter so that it does not refers to the interface-pointer, but the actual class-pointer.

This is common practice, also in C++.

Show quoted text 5 lines
The problem is only that the extra 'call' which is made adds one extra value to the stack, so that the parameters which are passed on the stack are all shifted. This is especially troublesome when the method is defined as cdecl and the 'self' parameter itself is also passed using the stack. (self is not on the location EBP+8 anymore, but EBP+12)

This argument shift IMO could be fixed by tail-call optimization.

DoDi

Reply by email