Advertisement Jump to content
  • Advertisement

Ro_Akira

Member
  • Content Count

    245
  • Joined

  • Last visited

Community Reputation

212 Neutral

About Ro_Akira

  • Rank
    Member

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Switching between "Program Database" and "Program Database for Edit and Continue" and observing the compiler output appears to support that theory!
  2. I had /GS- set for the last example (i.e. buffer security check off), and still had the 72 bytes. As cache_hit correctly points out, the 0x40 bytes are reduced to the bare minimum required in a release build, even with optimizations off: void process () { 00401020 push ecx int a = 1; my_func (a, 2); 00401021 lea eax,[esp] 00401024 push eax 00401025 push 1 00401027 mov dword ptr [esp+8],2 0040102F call 00401000 } 00401034 add esp,0Ch 00401037 ret
  3. So it took me so long to write the previous post, I pretty much figured out what was happening... Showing all the code was the key, illustrating the source of that address (See line 00529423 in the previous post). I recompiled without any runtime or buffer overflow checks, and it simplifies the output somewhat. I have commented on the assembly as best as I could. Feel free to point out any errors.: void my_func (int a, const int& b) { 00582D30 push ebp 00582D31 mov ebp,esp 00582D33 sub esp,40h 00582D36 push ebx 00582D37 push esi 00582D38 push edi } 00582D39 pop edi 00582D3A pop esi 00582D3B pop ebx 00582D3C mov esp,ebp 00582D3E pop ebp 00582D3F ret void procedure1 () { 00582D60 push ebp // Save old stack frame (base pointer) 00582D61 mov ebp,esp // New stack frame, starting from current stack pointer 00582D63 sub esp,48h // Allocating 72 bytes on the stack, for some reason 00582D66 push ebx // Save register 00582D67 push esi // Save register 00582D68 push edi // Save register int a = 1; 00582D69 mov dword ptr [ebp-4],1 // Use first 4 bytes of the 72 allocated, for 'a' my_func (a , 2); 00582D70 mov dword ptr [ebp-48h],2 // Use the last 4 bytes of the 72 allocated, for '2' // ('2' goes from [ebp-48h] to [ebp-48h+4], which is [ebp-44h], which is in the allocated range) 00582D77 lea eax,[ebp-48h] // Load effective address of '2' into eax register 00582D7A push eax // Push parameter 2 // (last parameter is first on the stack due to __cdecl calling convention) 00582D7B mov ecx,dword ptr [ebp-4] // Move value of 'a' into ecx 00582D7E push ecx // Push parameter 1 00582D7F call 005165EE // Call the my_func function 00582D84 add esp,8 // Pop parameters just pushed } 00582D87 pop edi - Restore register 00582D88 pop esi - Restore register 00582D89 pop ebx - Restore register 00582D8A mov esp,ebp // Restore the stack pointer to the base pointer 00582D8C pop ebp // Restore the calling stack frame 00582D8D ret // Return control to calling function There are some surprises here for me. I would have expected a single push for 'a'. Instead the compiler appears to allocate a bunch of space at the start (in this case 72 bytes), and uses that. Presumably a single sub op, and some movs are faster than push ops? A similar surprise with the single add to esp instead of two pops, given that it's a debug build.
  4. Thanks for the replies! So, maybe my example wasn't so clear. My apologies. I deliberately chose debug mode to avoid things being optimized away. I give here the full original code (Update: In the next post I give the same sample but without the run-time and buffer overflow checks turned on): void my_func (int a, const int& b) { // Nothing here } void procedure1 () { 00529420 push ebp 00529421 mov ebp,esp 00529423 sub esp,0D8h 00529429 push ebx 0052942A push esi 0052942B push edi 0052942C lea edi,[ebp-0D8h] 00529432 mov ecx,36h 00529437 mov eax,0CCCCCCCCh 0052943C rep stos dword ptr es:[edi] int a = 1; 0052943E mov dword ptr [a],1 my_func (a , 2); 00529445 mov dword ptr [ebp-0D4h],2 0052944F lea eax,[ebp-0D4h] 00529455 push eax 00529456 mov ecx,dword ptr [a] 00529459 push ecx 0052945A call my_func (5165EEh) 0052945F add esp,8 00582DA2 pop edi 00582DA3 pop esi 00582DA4 pop ebx 00582DA5 add esp,0D8h 00582DAB cmp ebp,esp 00582DAD call @ILT+37260(__RTC_CheckEsp) (513191h) 00582DB2 mov esp,ebp 00582DB4 pop ebp 00582DB5 ret The reason I'm asking this is because I'm doing the whole "my own compiler/VM" thing. I'm at the point where I need the compiler to handle function calls where the result of the expression for a parameter cannot be just left on the stack. I have an idea of how to do it, but figured looking at how VC++ 2005 did it would be a good start. As has been pointed out, and as can be seen in the assembly, the '2' is part of an immediate mode mov instruction. The '2' is part of the instruction steam (which we can't take the address of), so it mov's it into a memory location so we can eventually push the address of that memory on the stack for the function call. This works fine and I think I can understand why the '2' is not just pushed onto the stack, and it's address given: Take a function and call: void my_func2 (int a, const int& b, int c); // Function my_func2 (1, 2, 3); // Call Given a __cdecl calling convention, we can see that 3 would be pushed on first, then the '2', then '2's address, and finally '1'. But the function call expects functions in a fixed order on the stack, some offset from ebp, and we've got an erroneous '2' stuck in the middle. So, I can understand choosing another address to stuff '2' into. What I don't understand is how it came up with that address. My own plan of handling this was to pre-process all parameters that needed temporary addresses, and push them onto the stack first, and do the final parameter pushing afterwards (because those parameters would now have addresses available, as a result of the pre-process step). popsoftheyear, are you suggesting this memory has been allocated by the compiler before, it has noted it as being unused, and so it's using it? Am I understanding it correctly? Or is there more of a plan from the compiler for these cases. P.S. I chose references instead of pointers, because this simple case does not happen with pointers. For example: void my_func2 (int a, const int* const b, int c); // Function my_func2 (1, 2, 3); // Cannot convert to pointer my_func2 (1, &2, 3); // Cannot take address (no address to take) // Interestingly, the compiler can sit back and relax, while you make the temporary variable and take it's address manually: int x = 2; my_func2 (1, &x, 3); Update: See next post!
  5. void my_func (int a, const int& b) ... int a = 1; 0052943E mov dword ptr [a],1 my_func (a , 2); 00529445 mov dword ptr [ebp-0D4h],2 0052944F lea eax,[ebp-0D4h] 00529455 push eax 00529456 mov ecx,dword ptr [a] 00529459 push ecx 0052945A call my_func (5165EEh) 0052945F add esp,8 This is a bit of code generated by VC++ 2005 Debug. I understand how the assembly is working. At 00529445, I understand why it's moving 2 into 'a' stack location off the base pointer, and then pushing the address on the stack for the function call. But what I'd like to know is how it chose that address? This is code from the start of a function, so ebp-0D4h appears to be way out somewhere on the stack?
  6. I'm also in the same own language/VM/own engine position - it's fun! It might be worth taking a look at UnrealScript and the Quake 3 in addition to Lua/Python. Quake 3's source was released, so you can see how everything was implemented.
  7. Ro_Akira

    Help with C++ Pointers, References et al

    Quote:The author mentions something about data on the heap being interchangeable between functions, but I can do that by returning a regular pointer or even a reference, or would that create a memory leak?. You mean like this? int* return_value () { int ret = 5; return &ret; } void other_func () { int* my_int = return_value (); // What variable does my_int point to now? } Returning the address or reference to a stack allocated object is probably not what you want, since the data the pointer/reference is pointing to is no longer valid. Placing an object on the stack is useful if: 1. The object lifetime is fixed within a function, and the object can fit on the stack. AND 2. The object can fit on the stack. The stack may be only about 1MB (Platform specific). The "free store" (heap) is usually much larger. Use the stack if you can, otherwise use the free store. Quote:if I create an object on the heap and none of it's members are pointers, does that mean that those members will be created on the heap or the stack? If you create an object on the heap, all of it will be allocated on the heap. That includes the data members. Likewise for the stack. That's not to say that some of it's members can't be pointing to either a heap or stack object... Quote:...references and pointers, is there a performance advantage to be gained by using one instead of the other? If there is, I wouldn't say it's much. Usually that's not the determining factor when deciding which one to use.
  8. Ro_Akira

    dinput8d.dll and Vista

    Okay, new information. Now that I have access to a Windows 2000 machine, I enabled the debug runtime there - and I met the same assertion that I had with Vista. So I presume it's somewhat valid. Here's the details of the assertion via Visual C++ 2005 Express: BOOL CALLBACK DirectInput81::EnumJoysticksCallback (const DIDEVICEINSTANCE* pdidInstance, VOID* pContext) { DirectInput81* const current (DirectInput81::current); if (!current) return DIENUM_STOP; HRESULT ret; DirectInput81::JoystickInterface& new_joystick (DirectInput81::new_joystick); new_joystick.Reset (); // Obtain an interface to the enumerated joystick ret = current->di->CreateDevice (pdidInstance->guidInstance, &new_joystick.device, NULL); if (ret != DI_OK || new_joystick.device == NULL) { current->logger << "Error: CreateDevice() failed with " << ret << endl; return DIENUM_STOP; } if (FAILED (ret)) return DIENUM_CONTINUE; // Set the joystick format ret = new_joystick.device->SetDataFormat (&c_dfDIJoystick2); if (ret != DI_OK) { new_joystick.device->Release (); new_joystick.device = 0; current->logger << "Error: SetDataFormat() failed with " << ret << endl; return DIENUM_STOP; } // Set the cooperative level - DEBUGGER STOPS AT THIS LINE ret = new_joystick.device->SetCooperativeLevel (current->window, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); . . . // In the output window DINPUT8: Assertion failed: `pHeader' at d:\builds\nt32_chk\multimedia\directx\dinput\dx8\dll\disubcls.c(595) TestJoystick.exe has triggered a breakpoint Has anyone else met this assertion before?
  9. I'm running Windows Vista Business (not by choice), and I found that the DirectX Control Panel has the DirectInput tab disabled (all of the controls on it are greyed out). I found out from this DirectX FAQ that sometimes it is a problem caused by an incompatible runtime and SDK. I installed the June 2007 SDK, and I've looked into the "Developer Runtime" directory from the setup extraction directory, only to find a version of dinput8d.dll in the Win2k and WinXP subdirectories, but none in the Vista subdirectory. Has anyone else come across this, and is there an explanation behind it? I've tried search engines on this matter and they've not been too much help. Oh, and MSDN too. I tried copying the dinput8d.dll from the WinXP directory into Windows\System32, but it seemed to fail internally with an assertion.
  10. Thanks for your reply Antheus. I understand that void is not unrelated, and you are correct in your suggestion that it be avoided. I hope you can see in the code above, the problem I was trying to overcome. The 'Function' takes a factory object which it uses to create an object. But it can't know the particular object. It does know that it's going to be derived from 'B' (this is reasonable, since otherwise it wouldn't be able to call any functions, virtual or otherwise on it).
  11. Okay, I've realised some things: class B { public: virtual void Print () { cout << "B" << endl; } }; class D : public B { public: void Print () { cout << "D" << endl; } }; template<typename Type> class Base { public: virtual Type* GetObject () = 0; }; template<typename Type, typename BaseType> class Derived: public Base<BaseType> { public: Type* GetObject () { return new Type; } }; void Function (Base<B>& object_maker) { B* b = object_maker.GetObject (); // Gives a D object b->Print (); delete b; } int main() { Derived<D, B> d; Function (d); } That should compile and run (minus headers/namespaces).
  12. Apologies for posting an obviously untested example. However, the original question remains. MSVC++ Express gives this: error C2555: 'Derived<Type>::GetObject': overriding virtual function return type differs and is not covariant from 'Base::GetObject'
  13. This doesn't work: class Base { virtual void* GetObject () = 0; }; template<typename Type> class Derived : public Base { Type* GetObject (); // Error if Type != void }; Is there a good reason a Type* cannot be considered covariant from void*? Edit: added ' : public Base' - thanks Captain_Thunder Edit: and made Base::GetObject pure virtual - thanks Antheus [Edited by - Ro_Akira on June 17, 2007 2:51:40 PM]
  14. Ro_Akira

    How to move things??

    The order is important. When you call glutSolidSphere, it draws the sphere at the place dictated by the current ModelView matrix. When you call glTranslatef, it changes the current ModelView matrix. Clearly, it does not affect the sphere now, because it has been drawn already. // Everything after this call is translated by 1.0 in the x-axis glTranslatef(1.0f ,0.0f, 0.0f); // Uses the current matrix, altered by the glTranslatef call above DrawStuff ();
  15. Alpha_ProgDes, as I understand it, it is the order in which the classes are specified, not the type of the base pointer one is initializing, or providing to 'delete'. I am sure it is a lot easier for compilers this way, and also a lot more deterministic for everyone in general. int main() { SecondBase *b = new Derived; FirstBase* fb = reinterpret_cast<FirstBase*> (b); // This doesn't change anything delete fb; } With the order in which ToohrVyk has specified the classes, the order of construction is FirstBase, SecondBase, and then Derived. As always, the order of destruction is the reverse of construction. Hence the appearance of 'SB' before 'FB'. And of course, the virtual destructors in the base classes ensure this proper calling of destuctors. This results in the destruction order Derived, FirstBase, SecondBase: class Derived : public SecondBase, public FirstBase { ...
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!