www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - Problem using small inout parameters with inline asm

reply Sean Kelly <sean f4.ca> writes:
I can workaround this by using a temporary pointer variable, but it 
doesn't seem like this should be necessary.


C:\code\d>type test.d
void fn( inout byte b )
{
     asm
     {
         mov EAX, b;
     }
}


int main()
{
     byte b;
     fn( b );
}
C:\code\d>dmd test
test.d(5): bad type/size of operands 'mov'
Dec 20 2005
parent reply Tiago Gasiba <tiago.gasiba gmail.com> writes:
Sean Kelly schrieb:

 I can workaround this by using a temporary pointer variable, but it
 doesn't seem like this should be necessary.
 
 
 C:\code\d>type test.d
 void fn( inout byte b )
 {
      asm
      {
          mov EAX, b;
      }
 }
 
 
 int main()
 {
      byte b;
      fn( b );
 }
 C:\code\d>dmd test
 test.d(5): bad type/size of operands 'mov'
How about doing this: mov EAX,0; mov AL, b; The input parameter is a single byte and should not be loaded directly into EAX. Note that there is no sign extension! The following is a dissasembled version of the program. <snip> Disassembly of section .gnu.linkonce.t_D5test12fnFKgZv: 0804bbe4 <_D5test12fnFKgZv>: 804bbe4: 55 push ebp 804bbe5: 8b ec mov ebp,esp 804bbe7: 83 ec 04 sub esp,0x4 804bbea: 89 45 fc mov DWORD PTR [ebp-4],eax 804bbed: b8 00 00 00 00 mov eax,0x0 804bbf2: 8a 45 fc mov al,BYTE PTR [ebp-4] 804bbf5: c9 leave 804bbf6: c3 ret 804bbf7: 90 nop Disassembly of section .gnu.linkonce.t_Dmain: 0804bbf8 <_Dmain>: 804bbf8: 55 push ebp 804bbf9: 8b ec mov ebp,esp 804bbfb: 83 ec 04 sub esp,0x4 804bbfe: c6 45 fc 00 mov BYTE PTR [ebp-4],0x0 804bc02: 8d 45 fc lea eax,[ebp-4] 804bc05: e8 da ff ff ff call 804bbe4 <_D5test12fnFKgZv> 804bc0a: b8 0d 00 00 00 mov eax,0xd 804bc0f: e8 04 00 00 00 call 804bc18 <_assert_5test1> 804bc14: c9 leave 804bc15: c3 ret 804bc16: 90 nop 804bc17: 90 nop <snip> At address 804bbfe only one byte is pushed into the stack - the other values are "trash". If you really want to do it, try this: mov EAX, int ptr b; Compiles well: <snip> 0804bbe4 <_D5test12fnFKgZv>: 804bbe4: 55 push ebp 804bbe5: 8b ec mov ebp,esp 804bbe7: 83 ec 04 sub esp,0x4 804bbea: 89 45 fc mov DWORD PTR [ebp-4],eax 804bbed: 8b 45 fc mov eax,DWORD PTR [ebp-4] 804bbf0: c9 leave 804bbf1: c3 ret 804bbf2: 90 nop 804bbf3: 90 nop Disassembly of section .gnu.linkonce.t_Dmain: 0804bbf4 <_Dmain>: 804bbf4: 55 push ebp 804bbf5: 8b ec mov ebp,esp 804bbf7: 83 ec 04 sub esp,0x4 804bbfa: c6 45 fc 00 mov BYTE PTR [ebp-4],0x0 804bbfe: 8d 45 fc lea eax,[ebp-4] 804bc01: e8 de ff ff ff call 804bbe4 <_D5test12fnFKgZv> 804bc06: b8 0d 00 00 00 mov eax,0xd 804bc0b: e8 04 00 00 00 call 804bc14 <_assert_5test1> 804bc10: c9 leave 804bc11: c3 ret 804bc12: 90 nop 804bc13: 90 nop <snip> Best, Tiago -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Dec 21 2005
parent reply Sean Kelly <sean f4.ca> writes:
Tiago Gasiba wrote:
 Sean Kelly schrieb:
 
 I can workaround this by using a temporary pointer variable, but it
 doesn't seem like this should be necessary.


 C:\code\d>type test.d
 void fn( inout byte b )
 {
      asm
      {
          mov EAX, b;
      }
 }


 int main()
 {
      byte b;
      fn( b );
 }
 C:\code\d>dmd test
 test.d(5): bad type/size of operands 'mov'
How about doing this: mov EAX,0; mov AL, b; The input parameter is a single byte and should not be loaded directly into EAX.
I disagree :-) inout parameters are pointers and so will always be 32 bits on x86.
 Note that there is no sign extension!
 The following is a dissasembled version of the program.
 
 <snip>
 Disassembly of section .gnu.linkonce.t_D5test12fnFKgZv:
 
 0804bbe4 <_D5test12fnFKgZv>:
  804bbe4:       55                      push   ebp
  804bbe5:       8b ec                   mov    ebp,esp
  804bbe7:       83 ec 04                sub    esp,0x4
  804bbea:       89 45 fc                mov    DWORD PTR [ebp-4],eax
  804bbed:       b8 00 00 00 00          mov    eax,0x0
  804bbf2:       8a 45 fc                mov    al,BYTE PTR [ebp-4]
  804bbf5:       c9                      leave
  804bbf6:       c3                      ret
  804bbf7:       90                      nop
 Disassembly of section .gnu.linkonce.t_Dmain:
 
 0804bbf8 <_Dmain>:
  804bbf8:       55                      push   ebp
  804bbf9:       8b ec                   mov    ebp,esp
  804bbfb:       83 ec 04                sub    esp,0x4
  804bbfe:       c6 45 fc 00             mov    BYTE PTR [ebp-4],0x0
  804bc02:       8d 45 fc                lea    eax,[ebp-4]
  804bc05:       e8 da ff ff ff          call   804bbe4 <_D5test12fnFKgZv>
  804bc0a:       b8 0d 00 00 00          mov    eax,0xd
  804bc0f:       e8 04 00 00 00          call   804bc18 <_assert_5test1>
  804bc14:       c9                      leave
  804bc15:       c3                      ret
  804bc16:       90                      nop
  804bc17:       90                      nop
 <snip>
 
 At address 804bbfe only one byte is pushed into the stack - the other values
are "trash".
Really? I thought "byte ptr" meant that 'b' is a pointer to a byte. Shouldn't it be 32 bits then? Sean
Dec 21 2005
next sibling parent Tiago Gasiba <tiago.gasiba gmail.com> writes:
Sean Kelly schrieb:

 Tiago Gasiba wrote:
 Sean Kelly schrieb:
 
 I can workaround this by using a temporary pointer variable, but it
 doesn't seem like this should be necessary.


 C:\code\d>type test.d
 void fn( inout byte b )
 {
      asm
      {
          mov EAX, b;
      }
 }


 int main()
 {
      byte b;
      fn( b );
 }
 C:\code\d>dmd test
 test.d(5): bad type/size of operands 'mov'
How about doing this: mov EAX,0; mov AL, b; The input parameter is a single byte and should not be loaded directly into EAX.
I disagree :-) inout parameters are pointers and so will always be 32 bits on x86.
 Note that there is no sign extension!
 The following is a dissasembled version of the program.
 
 <snip>
 Disassembly of section .gnu.linkonce.t_D5test12fnFKgZv:
 
 0804bbe4 <_D5test12fnFKgZv>:
  804bbe4:       55                      push   ebp
  804bbe5:       8b ec                   mov    ebp,esp
  804bbe7:       83 ec 04                sub    esp,0x4
  804bbea:       89 45 fc                mov    DWORD PTR [ebp-4],eax
  804bbed:       b8 00 00 00 00          mov    eax,0x0
  804bbf2:       8a 45 fc                mov    al,BYTE PTR [ebp-4]
  804bbf5:       c9                      leave
  804bbf6:       c3                      ret
  804bbf7:       90                      nop
 Disassembly of section .gnu.linkonce.t_Dmain:
 
 0804bbf8 <_Dmain>:
  804bbf8:       55                      push   ebp
  804bbf9:       8b ec                   mov    ebp,esp
  804bbfb:       83 ec 04                sub    esp,0x4
  804bbfe:       c6 45 fc 00             mov    BYTE PTR [ebp-4],0x0
  804bc02:       8d 45 fc                lea    eax,[ebp-4]
  804bc05:       e8 da ff ff ff          call   804bbe4 <_D5test12fnFKgZv>
  804bc0a:       b8 0d 00 00 00          mov    eax,0xd
  804bc0f:       e8 04 00 00 00          call   804bc18 <_assert_5test1>
  804bc14:       c9                      leave
  804bc15:       c3                      ret
  804bc16:       90                      nop
  804bc17:       90                      nop
 <snip>
 
 At address 804bbfe only one byte is pushed into the stack - the other
 values are "trash".
Really? I thought "byte ptr" meant that 'b' is a pointer to a byte. Shouldn't it be 32 bits then? Sean
Change the program to the following: <snip> void fn( inout byte b ){ static byte x; x = b; b = 0x12; } int main() { byte b; fn( b ); } <snip> compile and examine the assembly generated code. The function "fn" looks like this: <snip> 0804bbe4 <_D5test12fnFKgZv>: 804bbe4: 55 push ebp 804bbe5: 8b ec mov ebp,esp 804bbe7: 8a 08 mov cl,BYTE PTR [eax] 804bbe9: 88 0d 00 d8 05 08 mov ds:0x805d800,cl 804bbef: c6 00 12 mov BYTE PTR [eax],0x12 804bbf2: 5d pop ebp 804bbf3: c3 ret <snip> EAX contains the memory pointer and, as you can clearly see, CL is loaded with "b", i.e. a single byte. The output to "b" is also a single byte, namely 0x12. Therefore, I think that accessing it with "int ptr" is a bad idea. Proof of concept: <snip> void fn( inout byte x ){ int y; asm { mov EAX, int ptr[x]; inc byte ptr [EAX]; mov ECX, int ptr [EAX]; mov y, ECX; } printf("y=%x\n",y); } int main() { byte x = 0x12; fn( x ); printf("X=%x \n",x); return 0; } <snip> Output: y=400dad13 X=13 Clearly variable "y" is not being properly used! However, "x" is correctly incremented and 0x13 is displayed in the output. Best, Tiago -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Dec 21 2005
prev sibling next sibling parent Tiago Gasiba <tiago.gasiba gmail.com> writes:
Sean Kelly schrieb:

 Tiago Gasiba wrote:
 Sean Kelly schrieb:
 
 I can workaround this by using a temporary pointer variable, but it
 doesn't seem like this should be necessary.


 C:\code\d>type test.d
 void fn( inout byte b )
 {
      asm
      {
          mov EAX, b;
      }
 }


 int main()
 {
      byte b;
      fn( b );
 }
 C:\code\d>dmd test
 test.d(5): bad type/size of operands 'mov'
How about doing this: mov EAX,0; mov AL, b; The input parameter is a single byte and should not be loaded directly into EAX.
I disagree :-) inout parameters are pointers and so will always be 32 bits on x86.
Oh! I have just realized that I have messed up a bit... shame on me. That's what happens when I reply too fast. You're totally right - pointers are 32bit.
 
 Really?  I thought "byte ptr" meant that 'b' is a pointer to a byte.
 Shouldn't it be 32 bits then?
True! Although I have messed it up a little, I did manage to give the correct solution on my last message: mov EAX, int ptr[x]; // 32 bit pointer inc byte ptr [EAX]; // address a single byte Best, Tiago -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Dec 21 2005
prev sibling parent reply Chris Lajoie <ctlajoie___remove___this___ ___gmail.com> writes:
 I disagree :-)  inout parameters are pointers and so will always be 32 
 bits on x86.
I don't know assembly, but I am really trying to understand this thread. inout parameters are dereferenced implicitly.. so in the asm it is probably being dereferenced the same way it would in normal code (so it wouldn't be a 32 bit ptr, but a byte instead). Are you saying that is incorrect, and it should not be getting dereferenced if it is in asm { ... } context? Chris
Dec 26 2005
parent Tiago Gasiba <tiago.gasiba gmail.com> writes:
Chris Lajoie schrieb:

 
 I disagree :-)  inout parameters are pointers and so will always be 32
 bits on x86.
I don't know assembly, but I am really trying to understand this thread. inout parameters are dereferenced implicitly.. so in the asm it is probably being dereferenced the same way it would in normal code (so it wouldn't be a 32 bit ptr, but a byte instead). Are you saying that is incorrect, and it should not be getting dereferenced if it is in asm { ... } context? Chris
What Sean meant was that the argument that gets passed with an inout parameter is a pointer, like in the following declaration: void fn( byte *ptn ){} The pointer itself (the container for the memory address) is 32bit long, while the pointer's memory address points to a byte (byte *). Therefore, the true argument that is passed is a 32bit value, which is why it must be loaded like: mov EAX, int ptr[x]; // 32 bit pointer inc byte ptr [EAX]; // address a single byte The first intruction takes a 32bit value into EAX (the memory address) and the second increments the byte that is pointed by it. Tiago -- Tiago Gasiba (M.Sc.) - http://www.gasiba.de Everything should be made as simple as possible, but not simpler.
Dec 27 2005