Reputation: 102356
I'm a tad bit rusty on my MASM, so I don't really recall what to do here (if anything needs to be done at all). I have a MASM (X86) routine that looks as follows. It has two local variables taking up 5 bytes total:
MSC_ASM_GenerateBlock PROC buffer:DWORD,bsize:DWORD,safety:DWORD
LOCAL val:DWORD, rc:BYTE ;; local variables
MWSIZE EQU 4 ;; machine word size
.WHILE bsize >= MWSIZE && safety > 0
;; RDRAND is not available prior to VS2012. Just emit
;; the byte codes using DB. This is `rdrand eax`.
DB 0Fh, 0C7h, 0F0h
setc rc
...
.ENDW
...
MSC_ASM_GenerateBlock ENDP
When I check the disassembly, I see:
> dumpbin.exe /DISASM rdrand-x86.obj
Dump of file rdrand-x86.obj
_MSC_ASM_GenerateBlock:
00000000: 55 push ebp
00000001: 8B EC mov ebp,esp
00000003: 83 C4 F8 add esp,0FFFFFFF8h
00000006: EB 1D jmp 00000025
00000008: 0F C7 F0 rdrand eax
0000000B: 0F 92 45 FB setb byte ptr [ebp-5]
0000000F: 80 7D FB 00 cmp byte ptr [ebp-5],0
...
I believe add esp, 0FFFFFFF8h
is another way to say sub esp, 08h
.
As Joshua pointed out, the difference between an add esp
and sub esp
is the flags after the operation. The assembler's confusion or selection of instructions could be based on the fact that the assembler does not get to see the RDRAND
context. Rather, it only sees the jmp
based on CY
, and the assembler believes the flags are not in a good state.
Why is MASM generating a non-intuitive add
that depends on unsigned integer wrap? And more importantly, is it OK?
I performed a port of the routing to MASM64/ML64 using the wider machine words. It produces the same code (modulo machine word size):
Dump of file rdrand-x64.obj
MSC_ASM_GenerateBlock:
0000000000000000: 55 push rbp
0000000000000001: 48 8B EC mov rbp,rsp
0000000000000004: 48 83 C4 F0 add rsp,0FFFFFFFFFFFFFFF0h
0000000000000008: EB 1D jmp 0000000000000037
0000000000000010: 48 0F C7 F0 rdrand rax
000000000000001D: 0F 92 45 F7 setb byte ptr [rbp-9]
0000000000000024: 80 7D F7 00 cmp byte ptr [rbp-9],0
...
Upvotes: 3
Views: 1992
Reputation: 4922
I have no idea why MASM would think that's a good idea but unsigned overflow will work (so this is safe), and even more confusingly this is a safe substitution to make even on a 64 bit processor because the 32 bit operand will zero out the high 32 bits of the register! See this for an explanation.
Also, since both instructions are 3 bytes long there isn't any inefficiency there.
Upvotes: 0
Reputation: 43317
Odd compiler. Very odd.
add esp, 0FFFFFFF8h
is exactly the same as
sub esp, 8h
except it sets the flag bits differently. It's fine, and yes it depends on unsigned integer wrap. Not a problem because assembly is inherently non-portable. If you wanna know why you'll have to ask Microsoft, and they probably don't know anymore.
Upvotes: 7