Why does u64::trailing_zeros() generate branched assembly when branchless works? - optimization

This function:
pub fn g(n: u64) -> u32 {
n.trailing_zeros()
}
generates assembly with a branch:
playground::g:
testq %rdi, %rdi
je .LBB0_1
bsfq %rdi, %rax
retq
.LBB0_1:
movl $64, %eax
retq
This alternative function:
pub fn g(n: u64) -> u32 {
if n == 0 { u32::MAX } else { n.trailing_zeros() }
} ^^^^^^^^
generates assembly without a branch:
playground::g:
bsfq %rdi, %rcx
xorl %eax, %eax
cmpq $1, %rdi
sbbl %eax, %eax
orl %ecx, %eax
retq
It turns out that the branch gets created only when the constant returned is 64. Returning 0, or u32::MAX, or any other number generates branchless assembly.
Why is this? Just a quirk of the optimizer or there's a reason?
I'm trying to create performant, branchless code.
Using Rust 1.65 release profile

trailing_zeros corresponds to the cttz LLVM intrinsic.
That intrinsic just so happens to compile to the following instructions on x86-64:
g: # #g
test rdi, rdi
je .LBB0_1
bsf rax, rdi
ret
.LBB0_1:
mov eax, 64
ret
The output of that intrinsic is the bit width of the integer when the input value is 0. LLVM is able to recognize the redundant operation and remove it, which is why u64::BITS or just 64 in your conditional result in the same machine code as just the intrinsic.
It appears that using any other number results in the compiler recognizing the intrinsic branch as dead code, which is therefore removed:
e: # #e
xor ecx, ecx
bsf rax, rdi
cmove eax, ecx
ret
Instead, a single conditional move is generated. I believe this variance in output is just a quirk of the LLVM x86-64 assembler when certain intrinsics are involved.
You can reproduce the same discrepancy with C using clang. godbolt
It might be worth opening an LLVM issue for this, but only if the branchless version is actually better.
this LLVM issue may be related

Related

Why do these two variables sync up in NASM

I am a beginner in NASM and I have encountered something I can not understand. Given this code:
global main
extern printf
section .text
main:
mov qword [VAR_0], 1 ; Init first variable
mov qword [VAR_1], 2 ; Init second variable
mov rdi, format ; Print first variable -> outputs 2
mov rsi, [VAR_0]
mov eax, 0
call printf
mov rdi, format ; Print second variable -> outputs 2
mov rsi, [VAR_1]
mov eax, 0
call printf
section .bss
VAR_0: resq 0
VAR_1: resq 0
section .data
format db "%d", 10, 0
Why does the program output
2
2
Instead of
1
2
I am compiling it with
nasm -felf64 test.s
gcc test.o
And simply running it as
./a.out
I am at the end of my wits with this.
The problem is that you are misusing the resq directive. The proper use is:
IDENTIFIER: resq number_quad_words_to_reserve
In your case you have:
VAR0: resq 0
This reserves a total of zero quad words. Modifying each of these to:
VAR0: resq 1
VAR1: resq 1
will correct the behavior that you are observing.

Assembly $ - operator [duplicate]

This question already has an answer here:
What is $ in nasm assembly language? [duplicate]
(1 answer)
Closed 9 years ago.
I came across this following code:
SYS_EXIT equ 1
SYS_WRITE equ 4
STDIN equ 0
STDOUT equ 1
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg1
mov edx, len1
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg2
mov edx, len2
int 0x80
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, msg3
mov edx, len3
int 0x80
mov eax,SYS_EXIT ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg1 db 'Hello, programmers!',0xA,0xD
len1 equ $ - msg1
msg2 db 'Welcome to the world of,', 0xA,0xD
len2 equ $ - msg2
msg3 db 'Linux assembly programming! '
len3 equ $- msg3
with intuition i can make out that len1, len2 and len3 are variables holding the lengths of the three strings and that the $ - operator is fetching the length of it..
but i am not able to understand properly how the syntax to find the length works.. can anyone, please tell me how it does and give me links for further reading, to understand this concept..
Thanks in advance...
$ evaluates to the "current address", so $ - msg1 means "the current address minus the address with the label msg1". This calculates the length of the string that starts at msg1.
Your snippet looks like it might be NASM. Is it? Anyway, NASM has documentation of its special tokens $ and $$.

win32 very low level assembly - application startup issue

I am busy programming a win32 program in assembly with a form and buttons... The problem is windows modify my variables in ram. The place were a store my hInstance and hwnd variables. I have found a workaround, but it is not an elegant solution. I would like to know why windows modify my variables and also were can I find documentation which describe the start up of an application.
MyWndProc:
push EBP
mov EBP, ESP
mov eax, [EBP + 12]
cmp eax, WM_DESTROY
jne MyWndProc_j2
push 0
call PostQuitMessage
jmp MyWndProc_j1
MyWndProc_j2:
cmp eax, WM_CREATE
jne MyWndProc_j1
mov eax, [EBP+8]
push eax
call CreateControls
add esp, 4
MyWndProc_j1:
mov eax, [EBP + 20]
push eax
mov eax, [EBP + 16]
push eax
mov eax, [EBP + 12]
push eax
mov eax, [EBP + 8]
push eax
call DefWindowProcA
pop EBP
ret
segment .data
Wtitle db 'My Window',0
ClassName db 'myWindowClass',0
editClass db 'EDIT',0
buttonName db 'OK',0
buttonClass db 'BUTTON',0
textName db 'My textbox',0
textClass db 'edit',0
formEdit db 'This is just a mem test', 0
windowsVar1 dd 0
windowsVar2 dd 0
windowsVar3 dd 0
windowsVar4 dd 0
windowsVar5 dd 0
windowsVar6 dd 0
windowsVar7 dd 0
windowsVar8 dd 0
aMsg dd 0
hwnd dd 0
hwnd2 dd 0
hwnd3 dd 0
hInstance dd 0
old_proc dd 0
nCmdShow dd 0
hfDefault dd 0
MyWndProc is the callback function from windows. At the 27'th call from windows, it modify the last 7 variables. If I switch the position of the last 8 variables with windowsVarx, then it still modifies hwnd, hwnd2 ... without modifying windowsVarx. Where x is from 1 to 8
CreateControls:
push EBP
mov EBP, ESP
push 0
push 0
call GetModuleHandleA
push eax
push IDC_MAIN_BUTTON
mov eax, [EBP+8] ;hwnd
push eax
push 24
push 100
push 220
push 50
mov eax, WS_CHILD
or eax, BS_DEFPUSHBUTTON
or eax, WS_TABSTOP
or eax, WS_VISIBLE
push eax
push buttonName
push buttonClass
push 0
call CreateWindowExA
mov [hwnd2], eax
push DEFAULT_GUI_FONT
call GetStockObject
mov [hfDefault], eax
push 0
mov eax, [hfDefault]
push eax
push WM_SETFONT
mov eax, [hwnd2]
push eax
call SendMessageA
push 0
push 0
call GetModuleHandleA
push eax
push IDC_MAIN_EDIT
mov eax, [EBP+8] ;hwnd
push eax
push 100
push 200
push 100
push 50
mov eax, WS_CHILD
or eax, ES_MULTILINE
or eax, ES_AUTOVSCROLL
or eax, ES_AUTOHSCROLL
or eax, WS_VISIBLE
push eax
push 0
push editClass
push WS_EX_CLIENTEDGE
call CreateWindowExA
mov [hwnd3], eax
push 0
mov eax, [hfDefault]
push eax
push WM_SETFONT
mov eax, [hwnd3]
push eax
call SendMessageA
push Wtitle
push 0
push WM_SETTEXT
mov eax, [hwnd3]
push eax
call SendMessageA
pop EBP
ret
The following function is the message loop, which collect and dispatch.
MyMessageLoop:
push 0
push 0
push 0
push aMsg
call GetMessageA
cmp eax, 0
je MyMessageLoop_j1
push aMsg
call TranslateMessage
push aMsg
call DispatchMessageA
jmp MyMessageLoop
MyMessageLoop_j1:
ret
Your problem explanation isn't really clear. But you should remember that by calling a system call you may indeed end up with different values in your registers. I don't know about Windows, but on amd64 Linux, the kernel (which executes the system call) is required only to preserve the values of the registers r12 and up. The values in all the other registers may be changed and therefore will most probably not be the same after returning from the system call.
In order to remedy that, simply store the variables on your function's stack before calling the system.
It might appear that Windows is modifying your data, but as others have pointed out, it's more likely a bug or some other corruption in your code is causing problems.
It's almost impossible for people to determine the runtime behaviour of your entire program from snippets and programming in assembly almost always causes problems rarely seen when higher-level languages are used.
The best advice is to use a debugger and either step through the code or set a data breakpoint on the variables being modified. Data breakpoints are designed to stop your program on the instruction that performs the data modification.
You could also look at what the actual values of the data that are overwriting your variables - it might give you some clue as to where or why the memory is being overwritten.
The reason for people's sarcasm is that in your second sentence you are assuming Windows is to blame for your program not working. In many situations, blaming the Operating System is a good sign that the developer does not understand something or is reluctant to accept that they have made a mistake. The end result is almost always someone else pointing out the mistake.
Where do I start? Your code formatting is very hard to read. Hope a Uni is not teaching you it. Anyways, your WindowProc was VERY wrong. After EVERY message you handle, you DO NOT call DefWindowProc, most of the messages you just return 0 in eax.
After your call to CreateControls, you don't need add esp, 4 as long as at the end of CreateControls you do ret 4 or ret 4 * NumOfParamsPassed.
I fixed your WindowProc and the window now shows. I also removed the many unneeded movs.
MyWndProc:
push ebp
mov ebp, esp
mov eax, dword ptr[ebp + 3 * 4] ; same as [ebp + 12]
cmp eax, WM_CREATE
je _CREATE
cmp eax, WM_CLOSE
je _CLOSE
PassThrough:
push dword ptr[ebp + 5 * 4]; same as [ebp + 20]
push dword ptr[ebp + 4 * 4]; same as [ebp + 16]
push dword ptr[ebp + 3 * 4]; same as [ebp + 12]
push dword ptr[ebp + 2 * 4]; same as [ebp + 8]
call DefWindowProc
jmp _DONE
_CLOSE:
push 0
call PostQuitMessage
jmp _RET0
_CREATE:
push dword ptr[ebp + 2 * 4]
call CreateControls
;add esp, 4
_RET0:
xor eax, eax
_DONE:
pop ebp
ret 4 * 4 ; <----- you were missing this!!!!!
Why are you adding 4 to esp after a call to CreateControls? You are pushing 1 dword onto the stack, does CreateControls not cleanup the stack itself? You wrote that function? If it does adjust the stack with something like ret 4*1 then that add esp, 4 is screwing everything up. That snippet of code does nothing for us, plus there are so many uneeded movs and jmps there
#David Heffernan thanks I just started writing all my programs in Assembly over 10 years ago and well aware of how and what registers need to be preserved and when you DO NOT need to save them, just because a complier saves all registers in the prologue, doesn't mean it's correct.

Linux assembly; argument list going haywire

I'm new to programming in any sort of assembly, and since I've heard that NASM-type assembly for Linux is comparatively simple to DOS based assembly, I decided to give it a try.
This is my program thus far:
section .data
opening: db 'Opening file...',10
openingLen: equ $-opening
opened: db 'File opened.',10
openedLen: equ $-opened
bad_params: db 'Usage: writeFile filename.ext',10
bad_paramsLen: equ $-bad_params
not_opened: db 'Unable to open file. Halted.',10
not_openedLen: equ $-not_opened
hello: db 'Hello, this is written to a file'
helloLen: equ $-hello
success: db 'Successfully wrote to file.',10
successLen: equ $-success
section .bss
file: resd 1
section .text
global _start:
_start:
pop ebx ; pop number of params
test ebx,2 ; make sure there are only 2
jne bad_param_list
pop ebx
mov eax,4 ; write out opening file msg
mov ebx,1
mov ecx,opening
mov edx,openingLen
int 80h
mov eax,5 ; open file
pop ebx
mov ecx,64
mov edx,777o ; permissions of file
int 80h
mov dword [file],eax
test dword [file],0
jle bad_open
mov eax,4 ; write successful open message
mov ebx,1
mov ecx,opened
mov edx,openedLen
int 80h
mov ebx,file ; write to file (4 already in eax)
mov ecx,hello
mov edx,helloLen
int 80h
mov eax,6 ; close file
mov ebx,file
int 80h
mov eax,4 ; write successfully written msg
mov ebx,1
mov ecx,success
mov edx,successLen
int 80h
mov eax,1 ; exit
mov ebx,0
int 80h
bad_param_list:
mov eax,4 ; write that params are bad
mov ebx,1
mov ecx,bad_params
mov edx,bad_paramsLen
int 80h
mov eax,1 ; exit with code 1
mov ebx,1
int 80h
bad_open:
mov eax,4 ; write that we couldn't open the file
mov ebx,1
mov ecx,not_opened
mov edx,not_openedLen
int 80h
mov eax,1 ; exit with code 2
mov ebx,2
int 80h
The goal is to write a string of text to a file without library functions; I'm only using the Linux kernel. I had a few problems with missing brackets here and there, and all the rest of mistakes that you'd expect from a noob to assembly, but I think this is mostly under control now.
Here's my issue: From what I know, the first four lines of this program should pop the number of arguments off the stack, jump to bad_param_list if there is not only one parameter (aside from the program name), and pop the program name off the stack.
But this is not what happens. Here's some sample I/O, reformatted for clarity:
$./writeFile
Opening file...
Unable to open file. Halted.
$./writeFile x
Usage: writeFile filename.ext
$./writeFile x x
Usage: writeFile filename.ext
$./writeFile x x x
Opening file...
Unable to open file. Halted.
$./writeFile x x x x
Opening file...
Unable to open file. Halted.
$./writeFile x x x x x
Usage: writeFile filename.ext
$./writeFile x x x x x x
Usage: writeFile filename.ext
What I've noticed is that if you take the number of arguments including the name of the program, divide by 2, and discard the decimal, if the answer is odd, you'll get my usage error, but if the answer is even, you'll get the unable to open error. This is true up until at least 10 arguments!
How the heck did I manage to do this? And how do I get it to have the expected result?
Instead of
test ebx,2
you want
cmp ebx,2
test performs a bitwise AND between the arguments and throws the result away, except for setting the flags. So, in particular ZF will be set if the two arguments have no 1-bits in positions that match. (In your particular case, this works out as setting ZF to the complement of the second-to-lowest bit of ebx).
Conversely cmp subtracts its arguments and throws away the result after setting flags. In that case, ZF will be set if the two arguments are equal.

Pointers and Loops

This one has been bothering me for a while now: Is there a difference (e.g. memory-wise) between this
Pointer *somePointer;
for (...)
{
somePointer = something;
// do stuff with somePointer
}
and this
for (...)
{
Pointer *somePointer = something;
// do stuff with somePointer
}
If you want to use the pointer when you're done with the loop, you need to do the first one.
Pointer *somePointer;
Pointer *somePointer2;
for(loopA)
{
if(meetsSomeCriteria(somePointer)) break;
}
for(loopB)
{
if(meetsSomeCriteria(somePointer2)) break;
}
/* do something with the two pointers */
someFunc(somePointer,somePointer2);
Well, first, in you second example somePointer will be valid only inside the loop (it's scope), so if you want to use it outside you have to do like in snippet #1.
If we turn on assembly we can see that the second snipped needs only 2 more instructions to execute:
Snippet 1:
for(c = 0; c <= 10; c++)
(*p1)++;
0x080483c1 <+13>: lea -0x8(%ebp),%eax # eax = &g
0x080483c4 <+16>: mov %eax,-0xc(%ebp) # p1 = g
0x080483c7 <+19>: movl $0x0,-0x4(%ebp) # c = 0
0x080483ce <+26>: jmp 0x80483e1 <main+45> # dive in the loop
0x080483d0 <+28>: mov -0xc(%ebp),%eax # eax = p1
0x080483d3 <+31>: mov (%eax),%eax # eax = *p1
0x080483d5 <+33>: lea 0x1(%eax),%edx # edx = eax + 1
0x080483d8 <+36>: mov -0xc(%ebp),%eax # eax = p1
0x080483db <+39>: mov %edx,(%eax) # *p1 = edx
0x080483dd <+41>: addl $0x1,-0x4(%ebp) # c++
0x080483e1 <+45>: cmpl $0xa,-0x4(%ebp) # re-loop if needed
0x080483e5 <+49>: jle 0x80483d0 <main+28>
Snippet 2:
for(c = 0; c <= 10; c++) {
int *p2 = &g;
(*p2)--;
}
0x080483f0 <+60>: lea -0x8(%ebp),%eax # eax = &g
0x080483f3 <+63>: mov %eax,-0x10(%ebp) # p2 = eax
0x080483f6 <+66>: mov -0x10(%ebp),%eax # eax = p2
0x080483f9 <+69>: mov (%eax),%eax # eax = *p2
0x080483fb <+71>: lea -0x1(%eax),%edx # edx = eax - 1
0x080483fe <+74>: mov -0x10(%ebp),%eax # eax = p2
0x08048401 <+77>: mov %edx,(%eax) # *p2 = edx
0x08048403 <+79>: addl $0x1,-0x4(%ebp) # increment c
0x08048407 <+83>: cmpl $0xa,-0x4(%ebp) # loop if needed
0x0804840b <+87>: jle 0x80483f0 <main+60>
Ok, the difference is in the first two instructions of snippet #2 which are executed at every loop, while in the first snippet they're executed just before entering the loop.
Hope I was clear. ;)
Well, with the first version you only have to release once, after the loop. With the second version you can't use the pointer from outside the loop, so you need to release inside the loop. Memory-wise it shouldn't matter that much, but you do have allocation overhead in your second example I think.
check out a similar answer on stackoverflow here with some good answers. However this is probably compiler/language independent...