5.22 LTS - Register Destruction Threadsafe

Just starting out? Need help? Post your questions and find answers here.
Toni6
User
User
Posts: 45
Joined: Mon Apr 23, 2012 1:39 pm

5.22 LTS - Register Destruction Threadsafe

Post by Toni6 »

It seems that PB destroys the ecx register if you compile with /THREAD (aka: Create threadsafe executable)

Now i wonder if PB also destroys other registers besides eax (automatic variable initialization) at the start.

Example:

Code: Select all

CompilerIf #PB_Compiler_Thread = 0
  Debug "You must compile with /THREAD (aka: Create threadsafe executable) to reproduce this bug."
CompilerEndIf

DisableDebugger

Procedure.l VirtualClassExport_Method_Add(a.l, b.l)
  
  ;!mov ecx, 0 <- this works !???
  
  Protected reg_ecx.l
  
  !mov [p.v_reg_ecx], ecx
  
  MessageRequester("Register Destruction Bug", "ECX = "+Str(reg_ecx))
  
  ProcedureReturn a + b
EndProcedure

!mov ecx, 3
VirtualClassExport_Method_Add(6, 1)
User_Russian
Addict
Addict
Posts: 1603
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: 5.22 LTS - Register Destruction Threadsafe

Post by User_Russian »

Necessary to Fred added support thiscall (And also fastcall), and then this problem does not arise.
I do not understand what is the complexity of adding new agreement calling functions?

http://www.purebasic.fr/english/viewtop ... ll#p442400
http://www.purebasic.fr/english/viewtop ... ll#p419222
User_Russian
Addict
Addict
Posts: 1603
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: 5.22 LTS - Register Destruction Threadsafe

Post by User_Russian »

There is also a problem in the transmission strings, even if threadsafe off.

Code: Select all

DisableDebugger
EnableASM

Procedure TestProc(String.s)
  Protected *This
  mov *This, ecx
  
  MessageRequester(String, Str(*This))
EndProcedure

mov ecx, 10
TestProc("1234")
Fred
Administrator
Administrator
Posts: 18409
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: 5.22 LTS - Register Destruction Threadsafe

Post by Fred »

ecx is a volatile register in x86, so no wonder it gets destroyed :)

http://en.wikibooks.org/wiki/X86_Assemb ... _Languages
User_Russian
Addict
Addict
Posts: 1603
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: 5.22 LTS - Register Destruction Threadsafe

Post by User_Russian »

Fred wrote:ecx is a volatile register in x86, so no wonder it gets destroyed :)
Sorry, but how, then, to realize the calling thiscall. PB does not support it. Using assembler is also not suitable.
What do you advise? Thank you.

Are there plans to add support and thiscall and fastcall? You can see that the emulation of these agreements using assembler - with errors.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: 5.22 LTS - Register Destruction Threadsafe

Post by Danilo »

User_Russian wrote:Sorry, but how, then, to realize the calling thiscall. PB does not support it. Using assembler is also not suitable.
What do you want to do? Write object-oriented classes with PB, that get called from Microsoft VisualC++?

Thiscall is used for calling member functions in C++ classes, and PB does not support classes nor member functions in classes.
Without knowing what you want to do, it does not make much sense to write functions with thiscall support with a procedural programming language,
because there is no object associated with the procedural function.

Code: Select all

ProcedureT TestProc(String.s)  
  MessageRequester(String, Str(This))
EndProcedure

TestProc("1234") ; What 'object' does PB put into ECX for ProcedureT?
                 ; There is no object associated with the procedure
Could you explain some more and give some more details of what *exactly* you are trying to do? Write DLL for MSVC++, using Interfaces, etc.?
Wouldn't it make sense to use a object-oriented programming language to write object-oriented classes and member functions? Nobody wants to
write libraries for Java/C#/Ruby using QBASIC. ;)
User_Russian
Addict
Addict
Posts: 1603
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: 5.22 LTS - Register Destruction Threadsafe

Post by User_Russian »

Danilo wrote:What do you want to do? Write object-oriented classes with PB, that get called from Microsoft VisualC++?
Yes. And I already made it. But the problem with register ecx when transmitting string in a procedure or if enabled threadsafe.
Danilo wrote:Wouldn't it make sense to use a object-oriented programming language to write object-oriented classes and member functions?
Complete compatibility is required with classes VisualC++ 8, otherwise will not work. Not suitable other compilers C++, for example, MinGV, Borland C++ Builder, etc. The code compiles but does not work correctly.

And using PureBasic, you can create a working code. :) But the problem with register ecx. :?
For example, VisualC++ code.

Code: Select all

#include "Vsm.hpp"

class DSOURDEVICE : public IDSIMMODEL
{
public:
	INT isdigital (CHAR *pinname);
	VOID setup (IINSTANCE *inst, IDSIMCKT *dsim);
	VOID runctrl (RUNMODES mode);
	VOID actuate (REALTIME time, ACTIVESTATE newstate);
	BOOL indicate (REALTIME time, ACTIVEDATA *data);
	VOID simulate (ABSTIME time, DSIMMODES mode);
	VOID callback (ABSTIME time, EVENTID eventid);
private:
	IINSTANCE *inst;
	IDSIMCKT *ckt;

	IDSIMPIN *Pin1;
	IDSIMPIN *Pin2;
	IDSIMPIN *Pin3;
	IDSIMPIN *Pin4;

	BOOL state;
};



extern "C" IDSIMMODEL __declspec(dllexport) * createdsimmodel (CHAR *device, ILICENCESERVER *ils)
{ 
	if (ils->authorize(0x80808081))
		return new DSOURDEVICE;
	else
		return NULL;
}

extern "C" VOID __declspec(dllexport) deletedsimmodel (IDSIMMODEL *model)
{
	delete (DSOURDEVICE *)model;
}

INT DSOURDEVICE::isdigital (CHAR *pinname)
{
	return 1;
}

VOID DSOURDEVICE::setup (IINSTANCE *instance, IDSIMCKT *dsimckt)
{
	inst = instance;
	ckt = dsimckt;
	Pin1 = instance->getdsimpin("Pin1", true);
	Pin2 = instance->getdsimpin("Pin2", true);
	Pin3 = instance->getdsimpin("Pin3", true);
	Pin4 = instance->getdsimpin("Pin4", true);

	Pin3->setstate(SHI);
	Pin4->setstate(SHI);
	ckt->setcallback(1000000000000, this, 0x25);
}

VOID DSOURDEVICE::runctrl (RUNMODES mode)
{
}

VOID DSOURDEVICE::actuate (REALTIME time, ACTIVESTATE newstate)
{
	
}

BOOL DSOURDEVICE::indicate (REALTIME time, ACTIVEDATA *data)
{	
	return TRUE;
}

VOID DSOURDEVICE::simulate (ABSTIME time, DSIMMODES mode)
{	
/*	Pin3->setstate(time, 1, Pin1->istate());
	Pin4->setstate(time, 1, Pin2->istate());*/
}


VOID DSOURDEVICE::callback (ABSTIME time, EVENTID eventid)
{
	if (eventid == 0x25)
	{
		if (ishigh(Pin3->istate()))
			Pin3->setstate(time, 1, SLO);
		else
			Pin3->setstate(time, 1, SHI);
		ckt->setcallback(time + 1000000000000, this, 0x25);
	}
}
The translated code to PureBasic.

Code: Select all

XIncludeFile "vsm.pbi"

Structure ClassParam
  *vt ;pointer to virtual table as first entry 
  ; Приватные переменные класса.
  *inst.IINSTANCE
  *ckt.IDSIMCKT
  *Pin1.IDSIMPIN
  *Pin2.IDSIMPIN
  *Pin3.IDSIMPIN
  *Pin4.IDSIMPIN
  State.b
EndStructure 

EnableExplicit
EnableASM

Declare ModelNEW()

ProcedureCDLL createdsimmodel(device.s, *ils.ILICENCESERVER)
  Protected *Result=0
  
  If *ils\Authorize($80808081)
    *Result=ModelNEW()
  EndIf
  
  ProcedureReturn *Result
EndProcedure

ProcedureCDLL deletedsimmodel(*model.IDSIMMODEL)
  If *model
    FreeMemory(*model) 
  EndIf
EndProcedure

Procedure isdigital(*PinName)
  Protected *This.ClassParam
  MOV *This, ecx
  
  ProcedureReturn 1 ; В компоненте все выводы цифровые.
EndProcedure

Procedure setup(*instance.IINSTANCE, *dsimckt.IDSIMCKT)
  Protected *This.ClassParam
  MOV *This, ecx
  
  *This\inst = *instance ; Сохраняем указатели на объекты.
  *This\ckt  = *dsimckt
  
  ; Сохраняем указатели на объекты выводов модели.
  *This\Pin1 = *instance\getdsimpin("Pin1", #True)
  *This\Pin2 = *instance\getdsimpin("Pin2", #True)
  *This\Pin3 = *instance\getdsimpin("Pin3", #True)
  *This\Pin4 = *instance\getdsimpin("Pin4", #True)
  
  *This\Pin3\setstate3(#SHI)
  *This\Pin4\setstate3(#SHI)
  *This\ckt\setcallback(1000000000000, *This, $25)
  
EndProcedure

Procedure runctrl(mode.RUNMODES)
  Protected *This.ClassParam
  MOV *This, ecx
EndProcedure

Procedure actuate(time.REALTIME, newstate.ACTIVESTATE)
  Protected *This.ClassParam
  MOV *This, ecx
EndProcedure

Procedure indicate(time.REALTIME,  *aData.ACTIVEDATA)
  Protected *This.ClassParam
  MOV *This, ecx
  
  ProcedureReturn #True
EndProcedure

Procedure simulate(time.ABSTIME, mode.DSIMMODES)
  Protected *This.ClassParam
  MOV *This, ecx
  
  ;*This\Pin3\setstate2(time, 1, *This\Pin1\istate())
  ;*This\Pin4\setstate2(time, 1, *This\Pin2\istate())
  
EndProcedure

Procedure callback(time.ABSTIME, eventid.EVENTID)
  Protected *This.ClassParam
  MOV *This, ecx
  
  If eventid = $25
    If ishigh(*This\Pin3\istate())
      *This\Pin3\setstate2(time, 1, #SLO)
    Else
      *This\Pin3\setstate2(time, 1, #SHI)
    EndIf
    *This\ckt\setcallback(time + 1000000000000, *This, $25)
  EndIf
  
EndProcedure

Procedure ModelNEW()   ; Создание объекта.
  Protected *this.ClassParam=0
  
  *this = AllocateMemory(SizeOf(ClassParam)) 
  If *this
    *this\vt = ?ClassFunct
  EndIf   
  
  ProcedureReturn *this 
EndProcedure

DataSection
  ClassFunct:
  Data.i @isdigital()
  Data.i @setup()
  Data.i @runctrl()
  Data.i @actuate()
  Data.i @indicate()
  Data.i @simulate()
  Data.i @callback()
EndDataSection
For conversion of calls from stdcall to thiscall, in interfaces, use this macro.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: 5.22 LTS - Register Destruction Threadsafe

Post by Danilo »

Could you add a stub?

Code: Select all

DisableDebugger
EnableASM


Global *TestProcStub_ECX  = 0
Global *TestProcStub_PROC = 0

Interface IClassParam
    TestProc(String.s)
EndInterface

Structure ClassParam
  *vt ;pointer to virtual table as first entry 
  ; variables
  *inst.IINSTANCE
  *ckt.IDSIMCKT
  *Pin1.IDSIMPIN
  *Pin2.IDSIMPIN
  *Pin3.IDSIMPIN
  *Pin4.IDSIMPIN
  State.b
EndStructure 

Procedure New()
  *this.ClassParam = AllocateMemory(SizeOf(ClassParam)) 
  If *this
    *this\vt = ?ClassFunct
  EndIf
  ProcedureReturn *this 
EndProcedure






x.IClassParam = New()

mov ecx, 10
x\TestProc("A-B-C-D")



End


Procedure TestProc(*This, String.s)
  MessageRequester(String, Str(*This))
EndProcedure

TestProcStub:
    !MOV [ESP+4], ECX
    *TestProcStub_PROC = @TestProc() ; @Procedure() not working with InlineASM
    !JMP [p_TestProcStub_PROC]
    



DataSection
  ClassFunct:
  Data.i ?TestProcStub ; @ TestProc()
EndDataSection
User_Russian
Addict
Addict
Posts: 1603
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: 5.22 LTS - Register Destruction Threadsafe

Post by User_Russian »

This will make the code confusing.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: 5.22 LTS - Register Destruction Threadsafe

Post by Danilo »

User_Russian wrote:This will make the code confusing.
You are right. I wrote 2 macro for you to make it less confusing:
DefThisCallProc() - defines a procedure as thiscall
ThisCallEntryPoint() - put thiscall-procedure entry point into data section

Code: Select all

;--[ ThisCall.pbi ]--------------------------------------------------------
DisableDebugger

Procedure GetAddressIntoEAX(address)
    ProcedureReturn address
EndProcedure

Macro DefThisCallProc(_procedureName_)
    !JMP l_#_procedureName_#_thiscallentrypoint_end
    ThisCallEntryPoint:
    GetAddressIntoEAX( @_procedureName_() )
    ;!POP  dword EDX ; return address in EDX
    ;!PUSH dword ECX ; push ECX onto stack
    ;!PUSH dword EDX ; return address back onto stack
    ;!JMP  dword EAX ; jump to function entry point
    !MOV dword EDX, [ESP]    ; return address in EDX
    !SUB dword ESP, 4        ; add stack variable
    !MOV dword [ESP+4], ECX  ; move ECX onto stack
    !MOV dword [ESP  ], EDX  ; return address back onto stack
    !JMP dword EAX           ; jump to function entry point
    ThisCallEntryPoint_End:
EndMacro

Macro ThisCallEntryPoint(_procedureName_)
    !dd l_#_procedureName_#_thiscallentrypoint
EndMacro

Macro ThisCallEntryPointIntoEAX(_procedureName_)
    !MOV EAX, l_#_procedureName_#_thiscallentrypoint
EndMacro

EnableDebugger
;--------------------------------------------------------------------------

DisableDebugger


; Test thiscall functions
!PUSH dword 123
ThisCallEntryPointIntoEAX(testproc1)
!MOV  dword ECX, 10
!CALL EAX


!PUSH dword 456
!PUSH dword 123
ThisCallEntryPointIntoEAX(testproc2)
!MOV  dword ECX, 11
!CALL EAX


Prototype myThisCallFunction(String1.s,String2.s)
Define func.myThisCallFunction
ThisCallEntryPointIntoEAX(testproc3)
!MOV dword [v_func], EAX
!MOV  dword ECX, 12345
func("ABCD","EFGH")


End



; example functions

Procedure TestProc1(This, Number)
    DefThisCallProc(testproc1)                                       ; define this procedure as thiscall:
                                                                     ; - must be first line in Procedure
                                                                     ; - procedure name must be lowercase!
    MessageRequester("Info", "This: "+Str(This)+" - "+Str(Number))
EndProcedure

Procedure TestProc2(This, Number1, Number2)
    DefThisCallProc(testproc2)                                       ; define this procedure as thiscall:
                                                                     ; - must be first line in Procedure
                                                                     ; - procedure name must be lowercase!
    MessageRequester("Info", "This: "+Str(This)+" - "+Str(Number1)+":"+Str(Number2))
EndProcedure

Procedure TestProc3(This, String1.s, String2.s)
    DefThisCallProc(testproc3)                                       ; define this procedure as thiscall:
                                                                     ; - must be first line in Procedure
                                                                     ; - procedure name must be lowercase!
    MessageRequester("Info", "This: "+Str(This)+" - "+String1+":"+String2)
EndProcedure


DataSection
    ClassFunct:
        ThisCallEntryPoint(testproc1) ; data with thiscall procedure entry point
        ThisCallEntryPoint(testproc2)
        ThisCallEntryPoint(testproc3)
EndDataSection
In my opinion it looks good with use of this 2 macros.
'This'/'Self' (ECX) is put into the 1st procedure arg, see examples.
Post Reply