CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 16
  1. #1
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Representing and dynamically creating a managed object in assembly

    Hello all,

    I am back yet again. I have a 64 bit library written in c++ that handle numbers of arbitrary length. The main part of the classes are in c++/cli, but all of the math stuff is written in 64 bit assembly (passed through a native c++ base class). I have been just passing in the arrays that hold the numbers to the assembly routines, but am ready to start implementing more of the structure in assembly language for various reasons, but have run into another problem I have been unable to find a solution to.

    There will be three different structures I want to represent in assembly language BI (for BigInteger) BD (for BigDecimal) and BF (for BigFraction). I am figuring the structures to look like this:

    Code:
    			BI				struct
    
    				_sign		qword		0
    				_num		qword		0
    
    			BI				ends
    
    			BD				struct
    
    				_sign		qword		0
    				_decimalplace	qword	0
    				_num		qword		0
    
    			BD				ends
    
    			BF			struct
    
    				_sign		qword		0
    				_numerator	qword		0
    				_denominator	qword	0
    
    			BF			ends
    The sign in each of them will either be 0(for positive) or 1(for Negative), the decimalplace in BD holds the position of the decimal point counting from the least significant digit in String form. The actual numbers themselves are held in the arrays in the _num, _numerator, and _denominator variables and all of the qword values are treated as unsigned integers, the arrays are variable in size to accomadate however big of a number you wish. I figure calculating the position of the arrays in BI and BD will be pretty straight forward (for when one of these objects is returned from an algorithm) but the _denominator in BF could be problematic since it follows immediately after the _numerator, but we cannot know the size of the array in advance (though the size of all the arrays are stored in the first element of each array).

    Anyways, if I am going to implement this I need to be able to create these objects dynamically within the assembly code. I was thinking I could create these objects in special routines that can be called when needed and thought something like this might work:

    Code:
    
    	BuildBI PROC _arraysize:qword
    
    		LOCAL _as:qword
    		LOCAL _signpos:qword
    		LOCAL _numpos:qword
    
    		push	rdi
    		mov		_as, rcx
    		add		rcx, 16
    		sub		rsp, 28h
    		call	GetMem64
    		add		rsp, 28h
    		cmp		rax, 0
    		je		MallocError
    		mov		qword ptr[rax],0
    		mov		_signpos, rax
    		add		rax, 8
    		mov		_numpos, rax
    		mov		rcx, _as
    		mov		qword ptr[rax], rcx
    		mov		rdi, 1
    
    ClearArray:
    
    		mov		qword ptr[rax+rdi*8], 0
    		inc		rdi
    		cmp		rdi, qword ptr[rax]
    		jna		ClearArray
    		mov		rax, _signpos
    		pop		rdi
    		RET
    
    	BuildBI ENDP
    
    	BuildDec PROC _arraysize:qword
    
    		LOCAL _as:qword
    		LOCAL _signpos:qword
    		LOCAL _decimalplacepos:qword
    		LOCAL _numpos:qword
    
    		push	rdi
    		mov		_as, rcx
    		add		rcx, 32
    		sub		rsp, 28h
    		call	GetMem64
    		add		rsp, 28h
    		cmp		rax, 0
    		je		MallocError
    		mov		qword ptr[rax],0
    		mov		_signpos, rax
    		add		rax, 8
    		mov		_decimalplacepos, rax
    		mov		qword ptr[rax], 0
    		mov		rcx, _as
    		add		rax, 8
    		mov		_numpos, rax
    		mov		qword ptr[rax], rcx
    		mov		rdi, 1
    
    ClearArray:
    
    		mov		qword ptr[rax+rdi*8], 0
    		inc		rdi
    		cmp		rdi, qword ptr[rax]
    		jna		ClearArray
    		mov		rax, _signpos
    		pop		rdi
    		RET
    
    	BuildDec ENDP
    
    	BuildFract PROC _arraysize1:qword, _arraysize2:qword
    
    		LOCAL _as1:qword
    		LOCAL _as2:qword
    		LOCAL _first:qword
    		LOCAL _signpos:qword
    		LOCAL _numeratorpos:qword
    		LOCAL _denominatorpos:qword
    
    		push	rdi
    		mov		_first, 1
    		mov		_as1, rcx
    		mov		_as2, rdx
    		add		rcx, 16
    		add		rcx, _as2
    		sub		rsp, 28h
    		call	GetMem64
    		add		rsp, 28h
    		cmp		rax, 0
    		je		MallocError
    		mov		_signpos, rax
    		mov		qword ptr[rax],0
    		add		rax, 8
    		mov		_numeratorpos, rax
    		mov		rcx, _as1
    		mov		qword ptr[rax], rcx
    		mov		rdi, 1
    
    ClearArray:
    
    		mov		qword ptr[rax+rdi*8], 0
    		inc		rdi
    		cmp		rdi, qword ptr[rax]
    		jna		ClearArray
    		cmp		_first, 0
    		je		GoOut
    		mov		rdx, rax
    		mov		rax, rdi
    		mul		eight
    		add		rax, rdx
    		mov		_denominatorpos, rax
    		mov		rcx, _as2
    		mov		qword ptr[rax], rcx
    		mov		rdi, 1
    		mov		_first, 0
    		jmp		ClearArray
    
    GoOut:
    
    		mov		rax, _signpos
    		pop		rdi
    		RET
    
    	BuildFract ENDP
    and these would be called from Algs like the following to create return values, temporary variables, and such:

    Code:
    	
    	BMultiply64 PROC _val1:BI, _val2:BI
    
    		LOCAL v1s:qword
    		LOCAL v2s:qword
    		LOCAL rs:qword
    		LOCAL v1:qword
    		LOCAL v2:qword
    		LOCAL r:qword
    		LOCAL s:qword
    		LOCAL list:qword
    		LOCAL _mynum:qword
    		LOCAL _tlist:qword
    		LOCAL _ret:BI
    
    		push	rbx
    		push	rdi
    		push	rsi
    		push	r8
    		push	r11
    		push	r12
    
    		cmp		initialized, 0
    		jne		AllreadyInitialized
    		call	BDoInit
    
    AllreadyInitialized:
    
    	;we add the lengths of the two values and increment it, the return value will be no bigger than one more than the total
    
    		lea		r11, _val1._num
    		lea		r12, _val2._num
    		mov		rax, qword ptr[r11]		
    		add		rax, qword ptr[r12]				
    		inc		rax	
    		push	rax
    		inc		rax						
    		mul		eight					
    		mov		rcx, rax				
    		sub		rsp, 28h				
    		clc								
    		call	BuildBI							
    		add		rsp, 28h
    
    
    
    		mov		_ret, rax
    		lea		r8, _ret._num
    
    
    
    		pop		rax	
    		cmp		r8, 0
    		je		MallocError					
    		mov		qword ptr[r8], rax
    		mov		rcx, qword ptr[r8]
    
    ZeroLoop:
    
    	;zero out the return val
    
    		mov		qword ptr[r8+rcx*8], 0
    		loopne	ZeroLoop
    		mov		rdi, 1
    		mov		rsi, 1
    		mov		rcx, 1
    		mov		rdx, 0
    
    MultLoop:
    
    	;the main mult loop moves the current element in val1 into rax, multiplies it by the current element in _val2
    	;and adds and stores the result in the return val at the (_val1->Pos-1)+(_val2->Pos-1)+1 position
    	;the carry is kept in rdx till the begginging of the loop then temporarily stored in rbx, then rbx is added to the same position in the return val
    	;after running through all the elements in _val1, the alg moves to the next element in val2
    
    		mov		rbx, rdx
    		mov		rax, qword ptr[r11+rdi*8]
    		mul		qword ptr[r12+rsi*8]
    		add		qword ptr[r8+rcx*8], rax
    		adc		rdx, 0
    		add		qword ptr[r8+rcx*8], rbx
    		adc		rdx, 0
    		inc		rcx
    		inc		rdi
    		cmp		rdi, qword ptr[r11]
    		jna		MultLoop
    		mov		qword ptr[r8+rcx*8], rdx
    		mov		rdx, 0
    		mov		rdi, 1
    		inc		rsi
    		mov		rcx, rsi
    		cmp		rsi, qword ptr[r12]
    		jna		MultLoop
    		jmp		GoOut
    
    MallocError:
    
    	;if there was an error requesting memory try to get the error code, store it in the return value and send it back to managed code
    
    		mov		rcx, 32
    		sub		rsp, 78h
    		call	GetMem64
    		add		rsp, 78h
    		cmp		rax, 0
    		je		ErrorOut
    
    
    
    		mov		_ret, rax
    		lea		r8, _ret._num
    
    
    
    		sub		rsp, 28h
    		clc
    		call	GetLastError
    		add		rsp, 28h
    		mov		qword ptr[r8], 0
    		mov		qword ptr[r8+8], 0
    		mov		qword ptr[r8+16], rax
    		jmp		GoOut
    
    ErrorOut:
    
    		pop		r12
    		pop		r11
    		pop		r8
    		pop		rsi
    		pop		rdi
    		pop		rbx
    		mov		rax, 0
    		cld
    		RET
    
    GoOut:
    
    		mov		rdi, qword ptr[r8]
    
    CheckForZerosLoop:
    
    	;our final check for leading zeros and exit
    
    		cmp		qword ptr[r8+rdi*8], 0
    		ja		OutOfHere
    		cmp		rdi, 1
    		jna		OutOfHere
    		dec		rdi
    		mov		qword ptr[r8], rdi
    		jmp		CheckForZerosLoop
    
    OutOfHere:
    
    		mov		rax, _val1._sign
    		xor		rax, _val2._sign
    		mov		_ret._sign, rax
    		mov		rax, r8
    		pop		r12
    		pop		r11
    		pop		r8
    		pop		rsi
    		pop		rdi
    		pop		rbx
    		cld
    		
    		RET
    
    	BMultiply64 ENDP
    The above algorithm is supposed to take 2 BI values, multiply them together, and return a BI value, but I have a few questions:

    1. So the multiply routine calls into the CreateBI routine to obtain a BI object dynamically with an array allready sized to hold the result, but how am I supposed to inform the runtime of the location of the array within the object so that the lea instruction will compute the correct address? In the create routine, I call for enough memory to hold the array plus a few additional bytes, then I set the very first byte up as the location for the _sign and the very next byte for the first byte of the array (which will hold the size of the array). I can see how the runtime could look at the BI struct and easily find out the location (8 bytes after the _sign value), but the _denominator array in BF would come after the _numerator array and since the _numerator is variable in size I figure I have to supply the necessary info so that the correct memory is being accessed, but how do I do that.

    2. Since I never know the size of the arrays needed ahead of time, I have been declaring the arrays in my algorithms as if they were a single qword. I use them as you would a normal array, but if you were just looking at the declarations you wouldnt recognize them as arrays, this works great so far, but I have a strong feeling something in the c runtime will complain when I try passing these objects from/to the native c++ code. How am I supposed to delare an array when I wont know the size of the array until runtime?

    3. Is there a way to handle these objects when it doesnt have an actual name? for instance, after calling the create routine, the address to the _sign of the object is returned in the rax register, is there a way to use the object in the register? will something like [rax]._num work?

    4. Is there anything that yall can see might cause me a headache that I havent identified yet?

    5. In the current version of my algorithms, the declaration for the algs would look like this:

    Multiply64 PROC _val1:PTR qword, _val2:PTR qword

    Do I declare the BI structures with or without the PTR part?

    Multiply64 PROC _val1:PTR BI, _val2:PTR BI or

    Multiply64 PROC _val1:BI, _val2:BI

    I am sure Ill be coming up with more questions as I try to implement this, but these are what I see as possible problems that I have not been able to find information on yet, I am trying to come up with a clear plan before implementing it.

    Thanks in advance,
    Last edited by AKRichard; January 2nd, 2013 at 04:19 AM.

  2. #2
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Well, you know, my knowledge of x64 is merely theoretical and not really deep, so some of the stuff below may be somewhat speculative. However, my comments are mostly not architecture-specific, so I hope there's not too much which is blatantly off...

    Quote Originally Posted by AKRichard View Post
    1. So the multiply routine calls into the CreateBI routine to obtain a BI object dynamically with an array allready sized to hold the result, but how am I supposed to inform the runtime of the location of the array within the object so that the lea instruction will compute the correct address? [...]
    Keep in mind that assemply language is inherently not type-safe (and in fact that's one of its advantages, to the surprise of many arrived developers, I suppose... ). That means that once you have something in one of the general-purpose registers, you can use it as whatever you like. OTOH that puts the whole responsibility of making sure what you're doing actually makes sense on your shoulders, including the hard-to-debug consequences in case you do something silly... From the assembly language perspective, an address (IOW pointer) is nothing else but yet another integral scalar value; it just depends on what you do with it. IMO it's most illustrative to envision the LEA instruction as some fancy sort of ADD instruction that features all the address calculation modes supported by the CPU. (In fact I have already seen LEA being intentionally abused as a three-operand ADD instruction in the quest for highly optimized code.)

    [...] In the create routine, I call for enough memory to hold the array plus a few additional bytes, then I set the very first byte up as the location for the _sign and the very next byte for the first byte of the array (which will hold the size of the array). I can see how the runtime could look at the BI struct and easily find out the location (8 bytes after the _sign value), but the _denominator array in BF would come after the _numerator array and since the _numerator is variable in size I figure I have to supply the necessary info so that the correct memory is being accessed, but how do I do that.
    At the level of your question (and in particular your code snippets), there's no runtime involved whatsoever, neither native nor managed. Assembly language doesn't feature a runtime of it's own, it just may make use of other language's runtimes (preferably that of the host program language, naturally) and OS-level APIs. See #4 for further comments on handling such a data structure.

    2. Since I never know the size of the arrays needed ahead of time, I have been declaring the arrays in my algorithms as if they were a single qword. I use them as you would a normal array, but if you were just looking at the declarations you wouldnt recognize them as arrays, this works great so far, but I have a strong feeling something in the c runtime will complain when I try passing these objects from/to the native c++ code. How am I supposed to delare an array when I wont know the size of the array until runtime?
    What your assembly routines look like to the host language solely depends on how you set up the declarative prototypes for these routines on the host language side. What you pass to your assembly routines and back out technically is just a bunch of bits, typically interpreted as integer scalars (actual ints or pointers) or floating point values, but in the end it may be just anything you like. (See #1.)

    3. Is there a way to handle these objects when it doesnt have an actual name? for instance, after calling the create routine, the address to the _sign of the object is returned in the rax register, is there a way to use the object in the register? will something like [rax]._num work?
    In assembly language you'd handle that just like in any other language supporting low-level memory handling (like C++): with pointers. The fact that a C++ pointer variable (intentionally ignoring temporaries) does have a name is rather distracting from the actual point: The object it points to is nameless (at the language level). It may simplify comprehension to just envision the general-purpose CPU registers as a limited set of variables with extremely fast access. Due to the natural limitation on the total count of these variables, they're commonly just used temporarily. However, that temporary use may span quite a bunch of instructions, like with the RBP register being used as the frame pointer across the entire runtime of a function. (However, RBP has a quite dedicated use, so this example actually is a rather untypical border case.)

    Your addressing example basically will probably work, though you'll quite likely need to qualify it as something like BI ptr [rax]._num. Keep in mind that the content of GP registers is "type-neutral". Explicitly declaring memory variables as pointers is mostly useful when using memory-indirect addressing (i.e. using the address in memory as such without loading it into a register beforehand), but in the vast majority of cases it's not strictly required.

    4. Is there anything that yall can see might cause me a headache that I havent identified yet?
    You know having a variable-length array in any position in a struct except the very end of the memory layout isn't strictly impossible, but it dramatically complicates things. Modifying the length of an "inner" array will require you to move around the fields after that array in memory. In your design, the fields that need to be moved are likely to be VLAs of considerable size themselves, so moving them is likely to be rather expensive. Additionally, address calculations needed to reach any of these arrays except the first one need to account for the length of any of the arrays before it, incurring one extra indirection for each array you need to skip. Keeping the actual arrays out of the struct is much more straightforward and will improve performance. But by far the more important aspect is that it will dramatically simplify your code design, thereby literally saving you a lot of headaches.

    Moreover, except for some environments using it for string storage (string type implementations in historical Pascal and interpreted BASIC runtimes and Win32 for the BSTR type which, among other uses, is used by VB) I don't know of anything using such a VLA design with a length specification at the start of the object in memory. And even then they use pointers to these string objects and don't make them middle-members of some otherwise statically layouted structs. C++, neither native nor managed, doesn't feature any standard library data structure like this. You may adapt this concept to C++ by wrapping it into a class of your own, but that would mean quite some effort without (IMO) actually gaining anything.

    5. In the current version of my algorithms, the declaration for the algs would look like this:

    [...]
    As stated above, in assembly language you're free to do almost any messing around with data types you like. The assembly language declaration of the parameters in your function prototype is mostly for your own conveniwence in using the parameters inside your function. The assembly prototype and what declaration you expose to the host language client code are mostly independent of each other, except for the seuence of parameters and perhaps some name mangling issues, but these would be rather marginal technical details.

    Thanks in advance
    HTH
    Last edited by Eri523; January 3rd, 2013 at 06:47 AM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  3. #3
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly

    Hey bud, how you doing? I was hoping you were still around, but when I was going through the posts last night I didnt see any posts by you.

    Like I mentioned, I have all of the algorithms implemented such that I just pass in the arrays instead of the entire object, strictly speaking, only the BigInteger library directly uses the algorithms, BigDecimal and BigFraction use BigIntegers internally (one in BigDecimal and 2 in BigFraction). Ive been very impressed with the performance gain in the BigInteger library since I moved all of the math stuff into assembly language, Im finally on par with Microsofts implementation for the add, subtract, and multiply routines, Im slightly faster doing Division, Im running 2 to 3 times faster for exponentiation and modular exponentiation, but still slightly slower in modular reduction. While these improvements in the BigInteger class also sped up the other two, it didnt gain nearly as much because there is overhead in those classes prepping the arrays to be sent into the algorithms and I was hoping to speed them up by moving them into the assembly language routines also.


    You know having a variable-length array in any position in a struct except the very end of the memory layout isn't strictly impossible, but it dramatically complicates things. Modifying the length of an "inner" array will require you to move around the fields after that array in memory.
    Ya, thats what I figured, and that why I put the arrays at the end of the struct. Though the part about modifying them hasnt been an issue mainly because I implemented those libraries as immutables, so instead of changing the arrays within an object, a new object is created with the new values and returned.



    Additionally, address calculations needed to reach any of these arrays except the first one need to account for the length of any of the arrays before it, incurring one extra indirection for each array you need to skip. Keeping the actual arrays out of the struct is much more straightforward and will improve performance. But by far the more important aspect is that it will dramatically simplify your code design, thereby literally saving you a lot of headaches.
    as I mentioned above, that is how it is currently implemented, and working on them this way has been easy. But I am only passing the arrays in right now and I am never passing in one of the arrays from the other 2 libraries because additional steps have to be taken first (like multiplying one value or the other by a power of 10 in order to line up the decimal places for BigDecimal or making the denominators equal in order to do addition/subtraction in BigFraction) These adjustments (as well as my implementation of all the math functions for these classes (sin, cos, log, ln, exp, sqr, millerRabbin and such)) are what Im wanting to move into assembly language, but in order to do that I need the entire object, not just the arrays. I could arrange the algorithms such that I pass in each of the fields in the structures sperately without much of a problem, it just doesnt look as clean as passing in a structure.

    Ive allready have the native classes setup to do some of the translating neccessary for the managed and assembly sections to be able to communicate, and Ive been looking it over to see how I might go about translating the entire object between the managed and assembly sections, I just thought there was a more elegant way to pass the info between them. Ive been looking at all kinds of 32 bit code that makes direct use of a native c++ object, but Ive been having a bugger of a time accomplishing the same thing in 64 bit.


    At the level of your question (and in partucular your code snippets), there's no runtime involved whatsoever, neither native nor managed. Assembly language doesn't feature a runtime of it's own, it just may make use of other language's runtimes (preferably that of the host program language, naturally) and OS-level APIs. See #4 for further comments on handling such a data structure.
    When I mentioned the runtime I was trying to figure out what the c runtime would do if I passed one of the assembly structures to it as a return value. I couldnt figure out how they were handling the objects so that both the c runtime and the assembly code could use them directly. Ive come to the conclusion that I am either completely ignorant, or I wasnt seeing all the code, maybe they had something implemented either in assembly or c++ doing translating between the two (or maybe they had macros set up for it). Anyways, the 32 bit examples Ive been looking at look more elegant and natural (to a c++ programmer) then the brute force method Ive been implementing. But then most of the examples Ive been looking arent dynamically creating the objects either, most of the examples Ive been looking at are either being passed the objects or the objects are such that creating and destroying the objects is a very minimal process.




    Well crap, I was hoping youd tell that I should have been using structures all along for an assembly level representation of the objects, sounds like I am stuck with the brute force method. Ive had to go to some major lengths to make sure that my assembly code doesnt leak memory and is safe for multi-threaded apps (I still struggle debugging the assembly code when run in a multi-threaded app).

    Thanks for the imput though you asnwered my questions. It was good to see you again.

  4. #4
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    Hey bud, how you doing? [...]
    Well, I must admit there have been better times for me, but then again, there's always something to complain about and it's not too bad after all.

    [...] I was hoping you were still around, but when I was going through the posts last night I didnt see any posts by you.
    Actually, I already had opened the post editor to start writing post #2 about 12 hours earlier, when I got disturbed by a lengthy phone call, and then I didn't have the time to start it again until late at night.

    Like I mentioned, I have all of the algorithms implemented such that I just pass in the arrays instead of the entire object, strictly speaking, only the BigInteger library directly uses the algorithms, BigDecimal and BigFraction use BigIntegers internally (one in BigDecimal and 2 in BigFraction). [...]
    But in your declarations from the OP your BF and BD structs do not have full-fledged BIs as components: The component objects just feature the array part, thereby lacking the sign. If they would contain real BIs as components, featuring a sign, even if that is a constant value dummy one, they were BI-compatible on the binary level and you could use the same routines on them that you use on "solo" BIs (an issue you mention further down your post).

    [...] Though the part about modifying them hasnt been an issue mainly because I implemented those libraries as immutables, so instead of changing the arrays within an object, a new object is created with the new values and returned.
    As I recall now (and if my memory doesn't fail me), you already mentioned both immutability of your objects and multithreading concerns in earlier threads, just not in the same place, so I somehow failed to put the two in context with each other. Actually, immutability is really good news with regard to multithreading, since immutable objects are thread-safe per se! (Except during construction and destruction, which are the only occasions where even the state of immutable objects does change, but these border cases should be relatively easy to control.)

    Ive allready have the native classes setup to do some of the translating neccessary for the managed and assembly sections to be able to communicate, and Ive been looking it over to see how I might go about translating the entire object between the managed and assembly sections, I just thought there was a more elegant way to pass the info between them. Ive been looking at all kinds of 32 bit code that makes direct use of a native c++ object, but Ive been having a bugger of a time accomplishing the same thing in 64 bit.
    Wrapping up objects created by native code to make them .NET-friendly is something I have no practical experience with, nor realy that much of an idea, not even in 32-bit code. But I'm ready to discuss that as well and learn something new...

    In a recent thread in the C++/CLI section there has been a simple but IMO interesting example of a scenario where it was useful to introduce a thin C++/CLI interfacing layer in the middle when interfacing C# to a native C++ library.

    When I mentioned the runtime I was trying to figure out what the c runtime would do if I passed one of the assembly structures to it as a return value. I couldnt figure out how they were handling the objects so that both the c runtime and the assembly code could use them directly. Ive come to the conclusion that I am either completely ignorant, or I wasnt seeing all the code, maybe they had something implemented either in assembly or c++ doing translating between the two (or maybe they had macros set up for it). Anyways, the 32 bit examples Ive been looking at look more elegant and natural (to a c++ programmer) then the brute force method Ive been implementing. But then most of the examples Ive been looking arent dynamically creating the objects either, most of the examples Ive been looking at are either being passed the objects or the objects are such that creating and destroying the objects is a very minimal process.
    In C++ it's generally preferable to pass objects by (const) reference or at least by pointer over passing them by value to avoid the necessity for the C++ code (not necessarily the runtime) to copy the objects, which is expensive for large objects. (You'd also need to make your classes safely copyable for passing by value, but I don't think that's much of an issue with the structs you have now, and you may need that anyway, even when not passing by value.) And passing immutable objects by value is particularly pointless, since the prevalent reason to pass objects by value is to allow the callee to modify the object without affecting the caller's original, and modifying immutable objects is impossible anyway.

    In the above I'm referring to native C++; things may be different on the .NET side. BTW, is the .NET representation of your objects a reference or a value type? Microsoft's System::Numerics::BigInteger (I guess that's what you meant by "Microsoft" in your performance comparison) is a value type, BTW. I don't really understand the consequences of that by now, though, since I didn't inspect the internal layout of BigInteger objects using the debugger yet. And it's also immutable...

    One way to make your design more elegant may be using real BI instances as component values rather than raw VLAs, as pointed out above. Another idea may be to directly or indirectly derive your classes from a common base class (not necessarily your BI, but something else than System::Object or System::ValueType , maybe or maybe not abstract.)
    Last edited by Eri523; January 4th, 2013 at 08:49 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  5. #5
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly


    One way to make your design more elegant may be using real BI instances as component values rather than raw VLAs, as pointed out above. Another idea may be to directly or indirectly derive your classes from a common base class (not necessarily your BI, but something else than System::Object or System::ValueType , maybe or maybe not abstract.)
    lol, ok maybe I am ignorant. In the managed code the BD and BF classes actually use BI (well BD inherits from BI while the BF class just instantiates 2 instances of BI).

    The code I put in the OP wasnt something I had tried implementing, it was something I put in there on the spot as an example of what I was doing.

    Code:
    			BI				struct
    
    				_sign			qword		0
    				_num			qword		0
    
    			BI				ends
    
    			BD				struct
    
    				_decimalplace	qword	0
    				_num			BI		{}
    
    			BD				ends
    
    			BF			struct
    
    				_numerator		BI		{}
    				_denominator	BI	{}
    
    			BF			ends
    that even looks cleaner, now I just have to find time to try it out.

  6. #6
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly


    BTW, is the .NET representation of your objects a reference or a value type?
    I believe it would be classed a reference type according to the msdn library http://msdn.microsoft.com/en-us/libr...(v=VS.80).aspx


    Reference Types


    Reference types include the following:

    String


    All arrays, even if their elements are value types


    Class types, such as Form


    Delegates
    and not to mention it is declared:

    public ref class BI{

    and immutability is the way to go as far as Im concerned for a project like this, however, because of the immutability on a class such as this, whenever you do +-/*%, Pow, ModPow or anything that changes it, an entire new object is created, the effect on a class such as this (since it is a value type however microsoft defines it) is that during execution there are a whole bunch of instances being created and destroyed. Because of that fact I have often considered taking the immutability out of it, at the same time Im afraid to because of how much time it took me to get this version of it thread safe.


    Ive been playing with the idea of the structures a little bit, but I am having a problem with the named pointers. Right now I have for the structures:

    Code:
    			BI				struct
    
    				_sign			qword		0
    				_num			qword		0
    
    			BI				ends
    
    			BD				struct
    
    				_decimalplace	qword	0
    				_num			BI		{}
    
    			BD				ends
    
    			BF			struct
    
    				_numerator		BI		{}
    				_denominator	BI	{}
    
    			BF			ends
    now I wrote a little routine just to investigate into it a little bit, and just looking at the BI structures (one thing at a time). if I use the lea instruction on the _sign and _num variables, I get and address for the _num variable that is 8 greater then the _sign, which is exactly what I expected. I can use instances of these structures that are passed in, locally declared variables, and variables declared in the dseg section just fine, by that I mean I can use instructions such as _val._sign and _val._num[rdi] and such just fine with expected results. However, when trying to create one of these dynamically I am having trouble assigning the _sign and _num the adresses from the requested memory. The CreateBI routine looks like this:

    Code:
    
    	BuildBI PROC _arraysize:qword
    
    		LOCAL _as:qword
    		LOCAL _signpos:qword
    		LOCAL _numpos:qword
    		LOCAL _ret:BI
    
    		lea		r10, _ret._sign
    		lea		r11, _ret._num
    		push	rdi
    		mov		_as, rcx
    		lea		r12, _ret
    		mov		rcx, _as
    		add		rcx, 16
    		sub		rsp, 28h
    		call	GetMem64
    		add		rsp, 28h
    		cmp		rax, 0
    		je		MallocError
    		mov		qword ptr[rax],0
    		mov		_ret._sign, rax
    		add		rax, 8
    		mov		_ret._num, rax
    		mov		rcx, _as
    		mov		qword ptr[rax], rcx
    		mov		rdi, 1
    		lea		r8, _ret._num
    
    ClearArray:
    
    		mov		_ret._num[rdi], 0
    		inc		rdi
    		cmp		rdi, qword ptr[rax]
    		jna		ClearArray
    		mov		rax, _signpos
    		pop		rdi
    		RET
    
    MallocError:
    
    	BuildBI ENDP
    I know that using a local variable in this way is completely ignorant, but I wanted to see how it could be done with a known instance of it. When I try to assign the address to the _sign and _num variables in the above instance, it is storing the values at the address they are currently pointing to instead of changing the address. Ive also tried using a statement such as mov [rcx].BI._sign, rax (with rcx containing the return value for the requested memory). BTW the above statement works for accessing variables in the structure if I do something like lea rcx, _val1, but again, its not changing the address it points to, it just changes the value at the address it is pointing to. How do I change where it points to?

    I also tried things like mov _ret, rax (where rax points to the first memory address returned from the request for memory) and mov BI ptr _ret, rax. In both cases I get an error saying instruction operands must be the same size.

    I can use what is returned from the CreateBI routine with instructions such as mov rcx, qword ptr[rax] (this would move the sign into rcx), and mov rdx, qword ptr[rax+8] or mov rdx, qword ptr[rax+rdi*8+8] (the first moves the 0 element of the array into rdx, and the second moving the rdi element into rdx), and I can use instructions such as mov rdx, [rax].BI._num[rdi]. even though I havent tried yet, I am sure that the BD will not be a problem either, but I am sure the BF structure is going to be a problem if I cant point the _denominator (since it is the second BI listed) to the correct memory location. even if I were to do something like make one request for memory large enough for both arrays, I dont know at design time how big array 1 will be, I am sure with the way it is set up now, that if I were to look into the BF with the lea instruction it would show me _numerator._sign as the base address, _numerator._num would equal _numerator._sign+8, and _denominator._sign would equal _numerator._sign + 16, and finally _denominator._num would equal _numerator._sign + 24. Any ideas on how to change the address they are pointing to? Is there a way to declare the array in the structure as an array without specifying how many elements it has?

  7. #7
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    I believe it would be classed a reference type according to the msdn library http://msdn.microsoft.com/en-us/libr...(v=VS.80).aspx

    [...]
    Well, ok, according to this simple cassification, that I knew of, yours probably is a reference type. But I'm afraid that in practice this classifiation isn't exactly comprehensive. For instance, KeyValuePair<TKey, TValue> is a value type, but no-one keeps you from specifying arbitrary reference types as one or both of the generic type parameters, and in fact that's something I do myself quite frequently. So what sort of type would then be the result of that generic instantiation? Another example is System::Numerics::BigInteger itself: As a value type it is required to be fixed-size, nor, as I interpret the above classification by MS, may it contain any reference to an internal representation array that is not part of the type itself. And representing an numeric quantity of arbitrary precision that way wouldn't even be possible, would it? Taking the classification strictly, not even System::String ^ members would be possible in a value type, but although I can't name any off the top of my head now, there definitely are some in the framework library.

    and immutability is the way to go as far as Im concerned for a project like this, however, because of the immutability on a class such as this, whenever you do +-/*%, Pow, ModPow or anything that changes it, an entire new object is created, the effect on a class such as this (since it is a value type however microsoft defines it) is that during execution there are a whole bunch of instances being created and destroyed. Because of that fact I have often considered taking the immutability out of it, at the same time Im afraid to because of how much time it took me to get this version of it thread safe.
    Have a look at this development of memory consumption (red any yellow curves) shown by my own performance monitoring tool, monitoring another instance of itself:

    Name:  DotNetMemory.png
Views: 1205
Size:  54.8 KB

    The program progressively collects and stores data, which naturally results in a gradual increase of memory demands. But on mid-term, the .NET-typical oscillation caused by ongoing allocation, abandoning and garbage-collecting objects is just about equally significant. Yes, that does incur some performance overhead, but according to my own measurements, it is surprisingly low (about 2% on average IIRC, even with such a heavy use of allocation/dellocation like here). That's the price you pay for the convenience of managed code, and of course you may avoid the overhead by simply not using it. There also is the possibility of doing some fine-tuning of GC behavior, but that's really advanced...

    Ive been playing with the idea of the structures a little bit, but I am having a problem with the named pointers. [...]
    Probably that's mostly because they are no pointers. I'm not really good at x64, but I'm relatively sure they're concrete fields.

    I know that using a local variable in this way is completely ignorant, but I wanted to see how it could be done with a known instance of it. When I try to assign the address to the _sign and _num variables in the above instance, it is storing the values at the address they are currently pointing to instead of changing the address. Ive also tried using a statement such as mov [rcx].BI._sign, rax (with rcx containing the return value for the requested memory). BTW the above statement works for accessing variables in the structure if I do something like lea rcx, _val1, but again, its not changing the address it points to, it just changes the value at the address it is pointing to. How do I change where it points to?
    Using variable-length arrays (as opposed to pointers to VLAs) inside a struct already is an advanced technique, but having concrete instances of such variable-size structs as local variables on the stack is even so advanced that it's close to impossible; at least it's extremely impractical.

    Despite the fact that MOV translates into "move", that instruction is not able to move objects around in memory. It just moves chunks of data of at most register size between memory locations (which includes registers here). The high-level language equivalent is the assignment of a primitive type. What you'd need to do instead is the equivalent of a C++ memcpy(). In x86 assembly this would be done using a REP MOVSx instruction, where the x represents the data chunk size. Despite its harmless look, this instruction actually is a loop. I'm sure there's an equivalent instruction in x64 (perhaps even with a very similar name).

    Also, you're trying to construct an object as a local variable on the stack here, and then to return that object to the caller. This is not possible with an object of this size in assembly language, and even C++ actually just "emulates" that process, which is why it needs to make object copies in this situation (intentionally ignoring advanced optimizations like copy elision here for simplicity). And this would be returning by value, what I think is what you're trying to avoid here. Returning an object constructed on the stack by pointer or reference isn't possible without copying it, because otherwise you'd be returning a pointer/referecnce to an object that has gone out of scope, and you probably know from C++ that your program is definitely doomed when you do that.

    That's why it's definitely natural to use a dynamically allocated object referenced by a pointer (that is easy to return) here rather than contructing the object on the stack.
    Last edited by Eri523; January 8th, 2013 at 08:11 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  8. #8
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly

    Have a look at this development of memory consumption (red any yellow curves) shown by my own performance monitoring tool, monitoring another instance of itself:
    I wouldve thought it wouldve have made a bigger ding in performance than that. Though when you think about it, when Im running my tests, there are thousands of objects per second being created/destroyed and every one of those objects will range from a few hundred to a few hundred thousand bytes. I believe I am about to the point on the managed side and the assembly side as fast as I can get it till I learn something new.

    As far as using structures go, I have something working, though Im not sure if I like it yet. I ended up getting rid of the idea of the actual data inside the structure, what I did was create an array for each type, the elements in the array hold pointers to the actual data. I decided to do an entry for each element I had listed in the structure so that I wouldnt be confined to having all the data in contiguous memory, this way if I want the array in a different location from the sign or decimalplace it wont break anything. I had to create a routine in the assembly language side to translate what c++ was passing to it, not as elegant as Id like it to be, but it is kicking out correct results, I havent even started profiling it yet, but it appears to be about as fast as the other way and I havent even started looking for ways to optimize it yet.

    The high-level language equivalent is the assignment of a primitive type. What you'd need to do instead is the equivalent of a C++ memcpy(). In x86 assemply this would be done using a REP MOVSx instruction, where the x represents the data chunk size. Desite its harmless look, this instruction actually is a loop. I'm sure there's an equivalent instruction in x64 (perhaps even with a very similar name).
    ya the REP modifier is still in use, Im not sure about the movsx, I think I had used that instruction when I was keeping up the 32 bit version. As far as data chunk my arrays are of unsigned __int64 so I am using the full 64 bits, in truth, I think thats my biggest advantage over MS BigInteger (which I beleive is using 2s complement in a byte array) Im taking care of in one instruction what takes them 8 to do.
    Last edited by AKRichard; January 6th, 2013 at 08:07 AM.

  9. #9
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    As far as using structures go, I have something working, though Im not sure if I like it yet. I ended up getting rid of the idea of the actual data inside the structure, what I did was create an array for each type, the elements in the array hold pointers to the actual data. I decided to do an entry for each element I had listed in the structure so that I wouldnt be confined to having all the data in contiguous memory, this way if I want the array in a different location from the sign or decimalplace it wont break anything. [...]
    So in your BF, for instance, you have an array of pointers with two entries, each of which points to a VLA (or BI?) representing the numerator and denominator, respectively? Technically, an array of pointers inside the struct and a number equivalent to the pointer array size of individually named pointer members are pretty much the same, but the latter would syntactically be much more readable since the members can have descriptive names.

    You say you have something working, but still be careful not to return a pointer to an out-of-scope object from one of your functions. This even may superficially seem to work, but just as long as you don't access the invalid object after it has been overwritten, and that definitely will happen sometime, so that situation is a ticking time bomb.

    ya the REP modifier is still in use, Im not sure about the movsx, I think I had used that instruction when I was keeping up the 32 bit version. As far as data chunk my arrays are of unsigned __int64 so I am using the full 64 bits, in truth, I think thats my biggest advantage over MS BigInteger (which I beleive is using 2s complement in a byte array) Im taking care of in one instruction what takes them 8 to do.
    I'd have expected the 64-bit string move instruction to be named MOVSQ but, according to the AMD manual volume 3, that doesn't exist, nor do the other MOVSx variants. (There actually is a MOVSX, but that's something completely different.) So you'll need to do some further research on this, if you want to use it.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  10. #10
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly

    You say you have something working, but still be careful not to return a pointer to an out-of-scope object from one of your functions. This even may superficially seem to work, but just as long as you don't access the invalid object after it has been overwritten, and that definitely will happen sometime, so that situation is a ticking time bomb.
    Ya for the time being I scrapped my memory management routine since it wasnt designed for these structures, and I have local variables in each routine to keep track of what objects were created within the routine and which havent been so that at the end the correct objects can be destroyed and the correct ones be kept.

    I just remembered reading somewhere, that when passing an object to assembly code from c++ that the actual value passed in is equavilent to the "this" pointer. I have been trying to figure out what was going on, when using the value passed into the assembly routines I have to do something like
    Code:
     mov   r11    qword ptr[rcx]
    , then I could use instructions such as :

    Code:
    mov   rax, [r11].BI._sign
    and such. the only reason why I am asking is because if that is the fact then I can get rid of the translating sections of code in both assembly and c++. On the c++ side I have a native class with two variables in it both unisgned __int64* the first one is the _sign and when I define it I call for enough memory to hold one unsigned __int64 element, the second value is the array holding the value. So if I am understanding this correctly, the "this" pointer could be looked at as an array itself holding pointers to the data itself like I have setup in the structure allready. I didnt even try this out yet, but it make since to me that thats how they pass an object.

  11. #11
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    I just remembered reading somewhere, that when passing an object to assembly code from c++ that the actual value passed in is equavilent to the "this" pointer. [...]
    Perhaps my assumptions about the context that refers to are incorrect, but I'd say this specifically refers to passing the object by pointer or reference, when what actually gets passed is the address of the object rather than the object itself. Now, what gets actually passed when passing an object A a; by pointer, is &a, i.e. the address of a from any perspective where a is in scope. And this is exactly the same as this as seen from inside of an a member function (or, more elaborate, an A member function called on the instance a). However, IMO that rather explains the nature of the this pointer than how objects are passed to assembly language routines.

    I have been trying to figure out what was going on, when using the value passed into the assembly routines I have to do something like
    Code:
     mov   r11    qword ptr[rcx]
    , then I could use instructions such as :

    Code:
    mov   rax, [r11].BI._sign
    and such. [...]
    Again, I'm not exactly sure about the 64-bit calling convention, but given that rcx holds a pointer to a pointer to a BI object, then yes. Also, I'd expect the second statement to rather look something like

    Code:
    mov   rax, BI ptr [r11]._sign
    but that may be x64 assembly language details I'm not aware of.

    [...]the only reason why I am asking is because if that is the fact then I can get rid of the translating sections of code in both assembly and c++. [...]
    I don't know your translation code, but as I understand it, the main thing you'd get rid of that way is the necessity to make object copies, likely but not necessariliy incurred by passing them by value. And that may give you a nice performance boost.

    [...] So if I am understanding this correctly, the "this" pointer could be looked at as an array itself holding pointers to the data itself like I have setup in the structure allready.
    Whell, if that's the memory layout of the C++ object, then that's it. There's nothing more to that as I understand it.

    And we're still not covering how to wrap the objects to make them .NET-friendly, but as you seem to be testing your code in a .NET environment all the way, you already must have done that somehow.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  12. #12
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly


    Code:
    mov   rax, BI ptr [r11]._sign
    but that may be x64 assembly language details I'm not aware of.
    ya, in x64 the only way I could get it to work was:

    Code:
    mov   rax, [r11].BI._sign
    I actually got that out of "The art of assembly language" in the section on variables that talked about pointers to structures.


    And we're still not covering how to wrap the objects to make them .NET-friendly, but as you seem to be testing your code in a .NET environment all the way, you already must have done that somehow.
    ya, and this is the biggest bottleneck to performance using structures in the assembly code. I have code translating the .net object to native c++, the from native c++ to assembly, then translating from assembly to native c++, and from native to managed c++. as the code stands right now, using structures is taking some 4 to 5 times longer then when I was just passing the arrays in. I believe I see how to improve that. Ive been toying with the idea of making this mainly a native c++ class for a while, and if I can figure out how to pass the return values from the assembly routines back to the native code as a native object that would improve it quite a bit I am just trying to figure out why they pass the object as a "this" pointer and exactly what the "this" pointer is. If it is just a pointer to the pointer of the object being passed then I could easily change the CreateBI routine to reflect this, but that seems like an unnecessary level of abstraction and leaves me wondering if it has some other meaning/purpose.

    Ive been sick with the flu the last few days and havent been awake long enough to actually try anything out, at least I know what direction to try now. To my way of thinking, since I was having to do some of the pre-calculation logic in .NET if I can get this running correctly, I should see a performance boost over what I currently have without using the structures.

  13. #13
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    ya, in x64 the only way I could get it to work was:

    Code:
    mov   rax, [r11].BI._sign
    I actually got that out of "The art of assembly language" in the section on variables that talked about pointers to structures.
    Curiosity made me do some more research on this specific syntax thing...

    According to my historic MASM 6.0 manual (section 3.2.4.1 Indirect Operands with 16- and 32-Bit Registers) the syntax using the ptr keyword I suggested probably wouldn't even have worked with that as suggested. It would've needed some bracketing:

    Code:
    mov   rax, (BI ptr [r11])._sign
    Admittedly, it loses quite a bit of coolness that way... But a few pages down (section 3.3.2 Defining Register Types with ASSUME) I found a nice syntactic trick I wasn't even aware of during all these decades. It's possible to declare that a certain register holds a pointer to a certain struct type (which of course is the more useful the more struct field accesses you do through that register):

    Code:
    ASSUME r11:PTR BI
    
    ; ...
    
    mov   rax, [r11]._sign
    
    ; ...
    
    ASSUME r11:NOTHING
    Of course I'm just blindly mapping syntactic MASM32 contructs to MASM64 code, which may be completely fictuous. In particuar, which you may already know, the much more common use of the ASSUME directive in 32-bit assembly language was to specify which segment register points to which concrete segment. A feature that may not exist at all in x64, or only is necessary in quite low-level code. It's just that I found the syntax you're using now to be a bit unintuitive, and perhaps I could give some hints to find a more intuitive variant. And of course you may completely ignore my hints in case you're perfectly fine with the syntax you're currently using.

    In fact, the vast majority of the stuff I did myself in assembly language was so low-level that usually I just used hard-coded numeric field offsets when accessing data structures. So I'm not really familiar with that high-level syntax (and even less with the higher-level contructs as already mentioned in earlier posts in other threads).

    BTW, another note on intuitiveness (is this really an english word? ) of assembly language source code: In the ancient times when I used that language on a regular basis, it was common practice to comment almost every single code line, and I somehow hope it still is. This is particularly useful when you're holding many values/pointers in registers and/or holding them there for long sections of code, since CPU registers lack one really useful feature of high-level language variables: descriptive names. It's also particularly useful if the human reader of the code is rather unfamiliar with the execution environment and/or concepts used. This earlier post of mine may illustrate this: http://forums.codeguru.com/showthrea...86#post1988986. It's not only 32-bit but also inline assembler, but I think it still communicates the point.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  14. #14
    Join Date
    Aug 2009
    Location
    Finally back in Alaska
    Posts
    141

    Re: Representing and dynamically creating a managed object in assembly

    BTW, another note on intuitiveness (is this really an english word? ) of assembly language source code: In the ancient times when I used that language on a regular basis, it was common practice to comment almost every single code line, and I somehow hope it still is. This is particularly useful when you're holding many values/pointers in registers and/or holding them there for long sections of code, since CPU registers lack one really useful feature of high-level language variables: descriptive names. It's also particularly useful if the human reader of the code is rather unfamiliar with the execution environment and/or concepts used
    Most of my assembly code is commented, though not every line, I usually give a description of what is going on for a block of code at a time, but on some of the funky parts that are especially hard to follow I do go down to a line by line comment, and at the beggining of each routine I give a brief over all description of the routine along with a description of dedicated registers. Then I also have a few registers that are used the same in every routine, r11 allways holds the pointer to the first value passed in, r12 holds the pointer to the second, and in the routines that require 3 values......r13. r8 allways holds the pointer to the return value. rax, rdx are used for most calculations (forced to use them for certain calculations) rcx, rbx usually hold counters if needed, and rdi, rsi are the indexes into the arrays (rcx also indexes into an array for the multiplication routine). Also in all my routines I push every register used in the routine onto the stack right at the start, except rax, rcx, and rdx, the restore them right before leaving. I know that some of those are listed as volatile, but it was easier for me to expect all registers not used for passing variables to remain in tact across function calls.

    All those extra registers in x64 are great, makes it scores easier to code in when you can dedicate the registers. Anyways, most of the code I put in these posts were written almost as I was writing the post. When I am stuck on something, I tend not to comment it till I get it working, or at least have a clear direction of how to proceed with it. There are times Ill spend hours trying to figure out how to make the program do what I want it to do all to pretty much erase it all when I figure it out. In fact I think the only routine that was commented was the multiply routine (thats also the only routine I WASNT working on lol).

    Admittedly, it loses quite a bit of coolness that way... But a few pages down (section 3.3.2 Defining Register Types with ASSUME) I found a nice syntactic trick I wasn't even aware of during all these decades. It's possible to declare that a certain register holds a pointer to a certain struct type (which of course is the more useful the more struct field accesses you do through that register):
    Im finding out why they have so many addressing modes the hard way, some modes work way better in certain situations then others. I can see where the assume directive could come in handy, and was surprised to see it used like that. I thought the assume directive was more of a debugging tool to catch the when something in the program ends in a state other than expected (like assume some part of the program is to be in such and such a state and if its not throw an error).


    As far as the original purpose of this post, Ive got something working, but in order to bring it to a usefull state is going to take me some time. I am pretty much completely rewriting all the c++ code(both native and managed), but the good news is that it is starting to look as I original thought it should look. With the native c++ being the main dll and the managed side just being the wrapper. It also reminded me why I had abandond the concept. When I created an instance of the native class in the managed class, the managed object was never getting destroyed (and because the destructor was never being called the finalizer that destroys the native object was never getting called). I think its because I am using a native pointer when I should be creating my own pointer type that destroys the native object when the managed object that holds it goes out of scope.

  15. #15
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Representing and dynamically creating a managed object in assembly

    Quote Originally Posted by AKRichard View Post
    [...] r11 allways holds the pointer to the first value passed in, r12 holds the pointer to the second, and in the routines that require 3 values......r13. [...]
    IIRC this is a calling convention thing, right? (Already when writing part of the posts above, I wanted to read up on the x64 calling convention, but didn't find the respective MSDN page again. And I'm still not really satified with the way the forum search works, so I hardly use it currently...) In this case the very name of the register is a valuable hint on what it means, but it would be much more helpful to know of what type the parameter is (well, in assembly language, if it's not integral, it most proabably is a pointer to semething, but then it's important to know what "something" that is) and what it means.

    All those extra registers in x64 are great, makes it scores easier to code in when you can dedicate the registers. [...]
    Yep, that's great indeed. In x86 that was possible only for functions operating on an extremely small number of variables, which in most cases would mean they're extremely simple. Now that makes me think some mechanism to assign descriptive names to registers at function scope would be a nice thing to have in x64 assembly. That way they would much more resemble high-level language variables, reducing the need for comments. Technically, such a mechanism could be very simple, starting with something like the C++ #define. Is there something like that, by any chance?

    [...] When I am stuck on something, I tend not to comment it till I get it working, or at least have a clear direction of how to proceed with it. There are times Ill spend hours trying to figure out how to make the program do what I want it to do all to pretty much erase it all when I figure it out. [...]
    OIC. As I know myself, that wouldn't work for me: Once I'd have got it working, I'd most probably be too lazy to start another pass of work just to add the comments... In addition, comments can even help to get the thing working, for instance, suddenly spotting an inconsistency between your comments and the actual code may be an indication of a bug. And finally, at any stage of program development, comments are a great aid in discussion. Sometimes I had some hard time figuring out what your uncommented code does...

    Im finding out why they have so many addressing modes the hard way, some modes work way better in certain situations then others.
    Knowing which addressing mode to use in a given situation is part of the job of an assembly language programmer but something that those programming in high-level languages aren't concerned with, since the compiler makes this decision for them. That's by nature of assembly language...

    I can see where the assume directive could come in handy, and was surprised to see it used like that. [...]
    Yeah, as I said, I didn't know that use of ASSUME myself, yet.

    [...] I thought the assume directive was more of a debugging tool to catch the when something in the program ends in a state other than expected (like assume some part of the program is to be in such and such a state and if its not throw an error).
    That would be much like the C++ assert(), and as a runtime state check, that only works by generating executable code. Wouldn't really be so assemblyish... OTOH I don't even know of any high-level language compiler that is clever enough to anticipate runtime states not matching the programmer's concept at compile time, so I don't think it's reasonable to expect a low-level tool like an assembler to be able to do that. Remember the frequently cited statement about (native) C++, that its combination of power and programmer freedom makes it easy to shoot yoursef in the foot by using it incorrectly. Extending that picture, assembly language makes it easy to hit both feet with a single shot, and maybe cause some more damage...

    [...] When I created an instance of the native class in the managed class, the managed object was never getting destroyed (and because the destructor was never being called the finalizer that destroys the native object was never getting called). I think its because I am using a native pointer when I should be creating my own pointer type that destroys the native object when the managed object that holds it goes out of scope.
    In fact, that's one of the rather complex aspects of interop. There are some specific templates available in C++/CLI, and I think I recall having seen one named gcroot being used in such a context. I found the MSDN page Language Features for Targeting the CLR to be a great starting point for researches on topics like this, short of knowing anything more specific to start from. Of course templates are a library feature rather than a lanuage feature, but at least some of those can be found starting from there as well, following the links on the page.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured